agentleFS
ryanmoelterryanmoelter/plume/.claude/skills/release-cycle/SKILL.md

release-cycle

Skill0 starsChanged 10 days agoApache-2.0View on GitHub

Turn the roadmap's To do queue in Linear into a shipped release. Coordinator stays small; parallel worktree agents implement; merge to a release branch, debug-build for manual evaluation, iterate, then release per docs/releasing.md.

  • Commits and pushes

The file

We split this skill into the rules it gives an agent

5 rules. About 0 are common, 3 are distinctive, and 2 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈3

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 2

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    Write the package table into the plan file before launching anything.

    line 25
  2. Distinctive:

    Do not poll. Completion notifications arrive on their own.

    line 60
  3. Very specific:

    Only after the user approves.

    line 109
  4. Distinctive:

    Leave the draft uncommitted, so no draft ever reaches the history.

    line 115
  5. Very specific:

    Do not install: Every install updates through Sparkle or Homebrew, so never run scripts/install-release.sh as part of a release.

    line 124

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Release cycle

Run this when the user says "make the next release" or asks to work through the roadmap queue. You are the coordinator. Keep your own context small: read only the roadmap (through the roadmap skill), docs/releasing.md, and agent reports. Delegate every implementation, build, and verification to subagents.

Inputs

  • Optional: extra items beyond the To do queue. Recommend two or three cheap, high-value Backlog issues — prefer small estimates — and ask which to include. Always ask about a Needs decision issue before pulling it in — the decision in its description has to be settled first.
  • Target version. Default: bump the minor for a feature release, the patch for fixes only.

1. Plan the packages

Pull main first (git pull --ff-only) and read the latest tag. A checkout can sit releases behind, and planning against it gets both the footprints and the version wrong. The next version follows the latest tag, not the version the user names from memory.

Invoke the roadmap skill to list the To do queue, and read each issue's description. Group the queued issues into work packages so that no two packages edit the same files. An issue names files only where they matter, so read the code to find the real footprint before clustering.

  • S-only groups (estimate 2) go to Sonnet; anything M or larger (estimate 3+), unsized, or touching the headless wire protocol, goes to Opus. Always set model explicitly.
  • Cross-cutting passes (accessibility identifiers, a styling sweep) touch every view. Run them after the merge, on the release branch, not in parallel.
  • Note the seams where packages share a file anyway (ChatTabView, ChatComposer are the usual ones) and tell each agent which other agents are in that file so they keep their footprint minimal.

Write the package table into the plan file before launching anything.

Once the plan is approved, create the release issue as a placeholder through the roadmap skill. Title it Release <version>, put it in team Plume, project Admin, and set it to In Progress. Its description is GitHub release: *link added on publish.*. Its relatedTo lists the previous release's Release <version> issue and every issue planned for this release. Add an issue to it if it joins mid-cycle, and remove one if it drops out.

<prefix> in the commands below is the branch prefix of whoever runs the cycle, such as ryanm. Pass it to every agent.

2. Launch implementers

One Agent call per package, all in one message, with isolation: "worktree" and the chosen model. Each prompt carries the package description plus the rule block below verbatim.

You are running inside a dedicated git worktree of <repo root>. First run `pwd` to learn your
worktree root. HARD BOUNDARY: every file you read, edit, build, or run a command against must live
under that root. Never cd out of it, never touch the parent repo or sibling worktrees.

Read CLAUDE.md at your worktree root first and follow it. Your assigned Linear issues state the items; read each one, then read the code before planning.

- Create a branch: `git checkout -b <prefix>/<slug>`. Commit on it, early and often.
- NEVER run `git stash`. The stash ref is shared by every worktree, so a concurrent agent's pop takes your
  entry and you get theirs. Use `git worktree`-local means instead: commit a WIP, or copy files aside.
- Verify with `xcodebuild -scheme Plume -destination 'platform=macOS' build` and
  `xcodebuild -scheme Plume -destination 'platform=macOS' test -only-testing:PlumeTests`.
  Confirm test names scroll past. Two suites fail in ANY worktree for environmental reasons:
  `SessionJSONLReaderTests.encodingResolvesADirectoryClaudeCodeHasUsed` and `SurfaceCommandTests`.
  Report those as environmental; anything else is yours to fix. Add tests for pure logic.
- Roadmap: do NOT touch Linear. Report which issue identifiers you completed; the coordinator moves
  them to Done.
- Comments: terse, why-only, per CLAUDE.md. No changelog-style comments.
- If you hit a decision only the user can make, stop and report it rather than guessing.
- Do not take screenshots. (Only one agent may screenshot at a time; the coordinator runs any visual check as a single serial pass after the merge.)

Report back: branch name, worktree absolute path, commit SHAs, a one-paragraph summary, test
results, and anything left unverified.

Do not poll. Completion notifications arrive on their own. Use the wait to write or refine this skill and the plan.

3. Merge to a release branch

git checkout -b <prefix>/release-<version> main
git merge --no-ff <prefix>/<slug>     # smallest package first, largest last

If an agent used the auto-generated worktree-agent-* branch instead of creating <prefix>/<slug>, merge that branch by name; the worktree list shows which is which. A trivial conflict you resolve yourself. A non-trivial one goes to a Sonnet agent with both sides and the two package summaries. Build after the last merge before moving on.

Then run the cross-cutting packages (step 1). An isolation: "worktree" agent forks from origin/main, not from your current branch — in the 0.13.0 run that was a commit dozens behind local main, since nothing had been pushed, so tell it to start with git checkout -B <prefix>/<slug> <prefix>/release-<version> before editing (a plain git checkout <prefix>/release-<version> fails because the primary checkout already has that branch out), or its annotations land on stale files and every shared file conflicts. Merge it the same way.

An issue is Done once the user has reviewed and approved it directly, or once it merges into main, which needs the user's confirmation. Merging into a release branch is not Done. Move each issue to Done through the roadmap skill once it qualifies.

Hand-offs from implement agents

An agent running the implement skill may send a branch with SendMessage once the user approves it. The message names the issue, branch, worktree, commits, base, signing state and changelog bullets. Merge it like a package, re-signing any unsigned commit. Then reply to the sender with SendMessage, naming the merge SHA, so it can clean up its worktree. Its issue stays open until the release reaches main.

Clean up as each package lands

Remove a package's worktree and branch as soon as its work is on the release branch, rather than saving it all for after the release. A dozen stale worktrees make git worktree list useless for seeing what is still live, and every one of them holds a full DerivedData-adjacent checkout.

git worktree unlock <path>    # agent worktrees are created locked
git worktree remove --force <path>
git worktree prune
git branch -D <prefix>/<slug> worktree-agent-<id>

git branch --merged is the check, but it answers about commits, not content. A branch whose work reached the release branch by cherry-pick or by a re-signing rebase reads as unmerged, because its original SHAs are not ancestors. Confirm with git diff <branch>..HEAD --stat instead — an empty diff means the content landed and the branch is safe to delete.

Remove each removed worktree's DerivedData and debug data too. Quit its debug app first (only the PID from pgrep -f "^<app>/Contents/MacOS/Plume"). Its DerivedData is the ~/Library/Developer/Xcode/DerivedData/Plume-* whose info.plist has WorkspacePath equal to <worktree>/Plume.xcodeproj; read it with /usr/libexec/PlistBuddy -c 'Print :WorkspacePath' <dir>/info.plist. Delete only an exact match, never the primary checkout's. Its debug data is ~/Library/Application Support/Plume.debug-<worktreeName>/.

Keep the worktree of any package whose work is not finished, even if some of it merged. A package that was partly cherry-picked still holds the dropped commits, and they are the starting point for the follow-up agent.

4. Debug build for manual evaluation

Delegate to a Sonnet verifier, on the release branch in the primary checkout:

  • Debug build, then PlumeTests. Report failures verbatim.
  • Launch the built Plume.app from DerivedData (-showBuildSettings | awk '$1 == "BUILT_PRODUCTS_DIR"'). Debug writes to Plume.debug/ (Plume.debug-<worktree>/ from a worktree), so it runs beside the installed app.
  • Launch it with env -u CLAUDE_CODE_CHILD_SESSION -u CLAUDECODE. The app inherits the shell that started it and passes that to every terminal tab it spawns, so launching from an agent's shell leaves each tab's claude reading itself as a nested session and skipping its transcript — the chat UI then shows nothing.
  • Walk the process tree from Plume's own PID and read the unified log for errors. Never pgrep -x Plume or global-grep claude.

Tell the user the app is running and give a per-package checklist of what to try. Each round of feedback becomes targeted agents on the release branch (Sonnet for tweaks, Opus for behavior bugs), then rebuild and relaunch through the verifier.

5. Release

Only after the user approves. Follow docs/releasing.md; do not duplicate it here.

  • Draft the release notes as an unreleasable section. Write a new top section in CHANGELOG.md headed ## Draft: <version> (<build>). That section becomes the GitHub release body, and the update window shows it, so the user reviews and finishes it by hand. package-release.sh refuses to package while the top heading says Draft:.
    • Draft from what shipped: this cycle's Done issues, plus git log <last-tag>..HEAD --no-merges --format='%s' for work that had no issue.
    • End it with a ### Recently list of a few highlights from the previous three or so releases. The update window shows only the newest section, so this is what reaches a user who skipped releases.
    • Match the existing sections' voice. Use user-facing bullets that lead with the change ("Send images in chat", "Fix …"), with sub-bullets for detail. Leave out internal work (refactors, test-only changes, tooling) unless a user would notice it.
    • Leave the draft uncommitted, so no draft ever reaches the history.
  • Bump MARKETING_VERSION and CURRENT_PROJECT_VERSION in the app target's Debug and Release blocks only, to the numbers in the draft heading. Commit the bump alone (git add Plume.xcodeproj/project.pbxproj), leaving CHANGELOG.md out of the commit.
  • Tell the user the draft is ready for review, and wait. They edit the section and remove Draft: from the heading to approve it. Then confirm the top heading is exactly ## <version> (<build>), and commit CHANGELOG.md on its own.
  • Merge the release branch into main (--no-ff), then move every shipped issue still open to Done through the roadmap skill.
  • Delegate the Release build and tests to Sonnet, telling it not to install — xcodebuild -configuration Release clean build, then PlumeTests, then verify the built bundle in BUILT_PRODUCTS_DIR (PlistBuddy, codesign, otool) rather than the installed one.
  • If commits are signed here (git config commit.gpgsign prints true), check every one is signed before anything is pushed: git log --format='%G? %h %s' <last-tag>..main | grep -v '^G' must print nothing. Agents fall back to --no-gpg-sign when the signing agent locks mid-run, and re-signing after the fact means rewriting every later commit and force-pushing the tag. Re-sign the offenders on the release branch before merging it (git rebase --force-rebase --rebase-merges main recreates and signs everything; resolve replayed conflicts by taking the file from the original merge commit, and confirm with git diff <old-tip> HEAD --quiet that the tree is unchanged).
  • Tag v<version> on main's HEAD — the release merge — and push main with the tag. package-release.sh refuses unless the tag points at HEAD.
  • Publish through Sparkle and Homebrew, per docs/releasing.md → Sharing a build: scripts/package-release.sh (DMG, appcast, draft GitHub release), then the user publishes the draft, then scripts/update-tap.sh <version>. Ask before publishing and before bumping the tap; both are outward-facing.
  • Once the release is published, finish the Release <version> placeholder from step 1. Put the GitHub release link in its description, and check that relatedTo names every issue that shipped plus the previous release's issue, so the releases form a chain. Then mark it Done.
  • Do not install. Every install updates through Sparkle or Homebrew, so never run scripts/install-release.sh as part of a release.
  • Clean up anything step 3 left: git worktree list and git branch --list '<prefix>/*' should hold nothing from this cycle, and no Plume-* DerivedData folder should point at a removed worktree.

Gotchas

  • git stash is shared across worktrees. In the first run, three agents stashed to measure a test baseline and popped each other's work; one recovered from git fsck --unreachable. The rule block now forbids it. If it happens anyway, git stash list labels usually say whose work an entry holds; restore with git checkout <stash> -- <paths>.
  • -only-testing with a name matching nothing prints ** TEST SUCCEEDED ** having run nothing. Confirm names scrolled past.
  • Tell every agent to run its verification in the foreground. Three agents in the 0.3.3 run handed a build or test to a background watcher and ended their turn waiting for a notification that never came — one of them left finished work uncommitted. They resume fine with a message saying to poll in the foreground instead, but it costs a round trip each time. Say it in the prompt.
  • A failed push is not a failed commit. If your SSH key lives in a password manager's agent, git push signs through it, so a locked agent fails with sign_and_send_pubkey: signing failed and Permission denied (publickey). There is no --no-gpg-sign equivalent — the only fix is unlocking, so ask. This is unrelated to the commit-signing fallback in CLAUDE.md, and the commits themselves may all be signed while the push still fails.
  • A schema change meets the installed store for the first time on the first launch after updating. Watch the log for a Plume.store.<timestamp>.bak move.
  • Roadmap edits: only the coordinator writes to Linear. An issue is Done once the user has reviewed and approved it directly, or once it merges into main, which needs the user's confirmation. Merging into a release branch is not Done. An agent's turn ending is not Done either.
  • The installed Plume runs git status on this repo on a timer, so a long rebase in the primary checkout can hit index.lock: File exists. git rebase --continue picks up where it stopped; quit Plume first for anything long.
  • Six parallel xcodebuilds are slow but each worktree gets its own DerivedData. Do not try to share one.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.