Agent skill
release-cycle
Use when releasing features - covers issues, branches, tests, commits, PRs, versioning, and publishing
Install this agent skill to your Project
npx add-skill https://github.com/nouamanecodes/lettactl/tree/main/.skills/release-cycle
SKILL.md
Workflow
- Issue - Every feature needs a GitHub issue first
- Branch - Create feature branch from main
- Implement - Build and test the feature
- E2E Test - Write test in
tests/e2e/tests/ - README - Update if feature changes CLI usage or adds commands
- Commit - Conventional commit format
- PR - Create, merge, delete branch, close issue
- Version - Bump package.json, tag, release
- Roadmap - Update issue #2 with completed feature
E2E Tests
Modular test runner that auto-discovers and runs all tests from tests/e2e/tests/.
# Run ALL tests (discovers .sh and .js automatically)
LETTA_BASE_URL=http://localhost:8283 ./tests/e2e/script.sh
# Run single test
./tests/e2e/run-single.sh XX-test-name
# Run filtered subset
./tests/e2e/script.sh --filter "4*" # Tests 40-49
./tests/e2e/script.sh --filter "block*" # Tests matching "block*"
Test Structure
Tests live in tests/e2e/tests/ with naming convention XX-name.sh or XX-sdk-name.js.
CLI Tests (.sh) - For commands and UX:
#!/bin/bash
set -e
source "$(dirname "$0")/../lib/common.sh"
AGENT="e2e-XX-name"
section "Test: Feature Name"
preflight_check
mkdir -p "$LOG_DIR"
delete_agent_if_exists "$AGENT"
$CLI apply -f "$FIXTURES/fleet.yml" --root "$FIXTURES" --agent "$AGENT" > $OUT 2>&1
agent_exists "$AGENT" && pass "Agent created" || fail "Agent not created"
delete_agent_if_exists "$AGENT"
print_summary
SDK Tests (.js) - For programmatic API (src/sdk.ts):
#!/usr/bin/env node
const path = require('path');
const { pass, fail, info, section, printSummary, preflightCheck } = require('../lib/common');
const { LettaCtl } = require(path.join(__dirname, '..', '..', '..', 'dist', 'sdk'));
async function run() {
section('Test: SDK Feature Name');
preflightCheck();
const ctl = new LettaCtl({ root: tmpDir });
await ctl.deployFleet(config);
pass('Feature works');
printSummary();
}
run().catch(err => { console.error('FATAL:', err); process.exit(1); });
Test Helpers
From tests/e2e/lib/common.sh:
pass "message"/fail "message"- Record test resultagent_exists "name"- Check if agent existsoutput_contains "text"- Check $OUT contains textoutput_not_contains "text"- Check $OUT doesn't contain textdelete_agent_if_exists "name"- Cleanup helperpreflight_check- Verify server and CLI readyprint_summary- Show pass/fail counts
Fixture Conventions
Fixtures in tests/e2e/fixtures/:
fleet.yml- Initial state for most testsfleet-updated.yml- Modified state for update/diff tests
Memory block value sync requires agent_owned: false:
memory_blocks:
- name: policies
value: "Policy content..."
agent_owned: false # Required for lettactl to sync value on apply
Without agent_owned: false, block values won't sync (agent owns and could have modified them).
Test Output
The runner aggregates results:
[1/48] 01-minimal (CLI) ... PASS (4 checks, 9s)
[2/48] 42-sdk-fleet (SDK) ... PASS (15 checks, 7s)
...
Summary:
Tests run: 48
Checks passed: 336
Checks failed: 0
README Updates
Update README.md when the feature:
- Adds new CLI commands or flags
- Changes default behavior
- Adds new configuration options
Skip README update for:
- Internal refactors
- Bug fixes that don't change usage
- Performance improvements
Pre-Commit Checks
# Run unit tests before committing
pnpm test
# Run single e2e test if applicable (works for both .sh and .js tests)
LETTA_BASE_URL=http://localhost:8283 ./tests/e2e/run-single.sh XX-test-name
Commit Format
# Features (use --no-verify ONLY after tests pass)
git commit -m "feat: add async polling for send command" --no-verify
# Fixes
git commit -m "fix: prevent timeout on long responses" --no-verify
# Build/release
git commit -m "build: bump v0.9.2" --no-verify
Rules:
- 5-7 words max
- NO Co-Authored-By or any author attribution
- No emojis
- Lowercase start
- Run
pnpm testbefore committing - Use
--no-verifyonly AFTER tests pass
PR & Merge
# Create PR
gh pr create --fill
# Merge and delete branch (use --admin to bypass branch protection)
gh pr merge --squash --delete-branch --admin
# Close the issue
gh issue close <issue-number>
Always close the related issue after merging the PR.
Git Safety
NEVER use destructive git commands:
git reset --hardgit clean -fgit push --forcegit checkout .
Use safe alternatives:
git pullto sync with remotegit stashto save uncommitted changesgh pr mergehandles local sync automatically
Breaking Changes
When a feature changes YAML syntax or removes backward compatibility:
- Ask first - Confirm with user before proceeding with breaking changes
- Minor version bump - Breaking changes bump minor version (e.g., 0.10.0 → 0.11.0)
- Roadmap marker - Add
(warning: breaking syntax change)to the completed item in roadmap
Example roadmap entry:
- [x] Make agent_owned field mandatory on memory blocks (#193) (warning: breaking syntax change)
Version & Release
# 1. Bump version in package.json (x.x.x → x.x.x+1)
# For breaking changes: bump minor version (x.x.x → x.y.0)
# 2. Commit
git add package.json
git commit -m "build: bump v0.9.2"
# 3. Tag
git tag v0.9.2
# 4. Push
git push && git push --tags
# 5. Create release with auto-generated notes
gh release create v0.9.2 --generate-notes
Roadmap Update
After release, add completed feature to issue #2:
# View current roadmap
gh issue view 2
# Edit to add new completed item at top of Completed section
gh issue edit 2 --body "..."
Format: - [x] Feature description (#issue-number)
Quick Reference
| Step | Command |
|---|---|
| New branch | git checkout -b feat/issue-name |
| Run single test | ./tests/e2e/run-single.sh XX-test-name |
| Run all CLI tests | ./tests/e2e/script.sh |
| Create PR | gh pr create --fill |
| Merge PR | gh pr merge --squash --delete-branch --admin |
| Close issue | gh issue close <number> |
| Tag release | git tag vX.X.X && git push --tags |
| Publish release | gh release create vX.X.X --generate-notes |
| Update roadmap | gh issue edit 2 --body "..." |
Didn't find tool you were looking for?