agentleFS
andreymudriandreymudri/fleetmates/skills/finishing-a-development-branch/SKILL.md

finishing-a-development-branch

Skill3 starsChanged 11 days agoMITView on GitHub

Use when implementation is complete and needs integrating - re-runs the gate to verify each phase, then decides how the run branch lands.

187 other projects use a version of this.

Adopted by

Most recently taken up by

Used with

Projects with it also use chinese-code-review, chinese-commit-conventions and 10 more.

Other files people use alongside this one.

Files found in the same public projects as a version of this one, more often than they turn up anywhere else. Each project counts once, large collections count for less, and this file's own repository is left out.
  • Leodorareluctant259Skill

    chinese-code-review

    中文代码审查规范——在保持专业严谨的同时,用符合国内团队文化的方式给出有效反馈

    Also in 8 projects that use this

  • Leodorareluctant259Skill

    chinese-commit-conventions

    中文 Git 提交规范 — 适配国内团队的 commit message 规范和 changelog 自动化

    Also in 8 projects that use this

  • obraSkill

    diagnosing-superpowers

    Use when a superpowers session went wrong and your human partner wants to know why — repeated work, ignored plans, stumbles, poor results, a skill that didn't fire, "it took too long", "why is it so expensive", "what is it doing" — or wants to build a bug report for the superpowers maintainers, for the current session or a past one identified by id or path, on any harness.

    Also in 20 projects that use this

  • kntismSkill

    condition-based-waiting

    Use when tests have race conditions, timing dependencies, or inconsistent pass/fail behavior - replaces arbitrary timeouts with condition polling to wait for actual state changes, eliminating flaky tests from timing guesses

    Also in 3 projects that use this

  • MicrosoftSkill

    testing-anti-patterns

    Use when writing or changing tests, adding mocks, or tempted to add test-only methods to production code - prevents testing mock behavior, production pollution with test-only methods, and mocking without understanding dependencies

    Also in 4 projects that use this

  • MicrosoftSkill

    root-cause-tracing

    Use when errors occur deep in execution and you need to trace back to find the original trigger - systematically traces bugs backward through call stack, adding instrumentation when needed, to identify source of invalid data or incorrect behavior

    Also in 5 projects that use this

  • +3 more from Leodorareluctant259

The file

We split this skill into the rules it gives an agent

7 rules. 0 are common, 4 are distinctive, and 3 are very specific to this project.

  • 0

    Common

    Most of the 172 edited versions we read keep these.

  • 4

    Distinctive

    This author's own choices. Worth a look.

  • 3

    Very specific

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

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    If .fleetmates/<run-id>/status.json exists and has a non-empty status.gates, this is a fleet run — but a record in status.gates is a report …

    line 19
  2. Very specific:

    Use status.gates only to see which phases the run executed.

    line 36
  3. Distinctive:

    If you find yourself about to commit implementation work straight onto the run branch instead of a teammate branch, stop — that's the wrong …

    line 78
  4. Distinctive:

    Do not sweep by hand: a hand-run git worktree remove --force or git branch -D supplies neither the recomputed phase gate nor the ancestry …

    line 117
  5. Distinctive:

    Before proposing integration, check for parked or deferred findings from review — anything a gate check or reviewer flagged but did not …

    line 131
  6. Distinctive:

    Before any push, run on this machine the CI steps this machine can run, so the remote CI confirms rather than discovers:

    line 142
  7. Very specific:

    Ask the user before pushing the run branch, unless ci.push is auto, in which case push without asking.

    line 165

“Common” is measured: kept by at least 6 in 10 of the 172 edited versions of this file we could read. “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

Finishing a Development Branch

Work is not finished because the code looks done. It is finished when a gate you just ran says so — not when a record claims one already did.

The completion gate

Work is not finished because the code looks done, and never because "the tests looked green" or "the teammate said it was done" earlier in the conversation. Which check applies depends on what state the run is in — check in this order.

1. Fleet run: status.gates is recorded

If .fleetmates/<run-id>/status.json exists and has a non-empty status.gates, this is a fleet run — but a record in status.gates is a report written by the agents being enforced, and status.json is agent-writable, so it is never trusted as evidence. A recorded verdict of PASS proves nothing by itself:

{
  "gates": {
    "<phaseName>": {
      "verdict": "PASS",
      "failed": [],
      "skipped": [],
      "pending": [],
      "recordedAt": 1785952191621
    }
  }
}

Use status.gates only to see which phases the run executed. For each of those phases, re-run the gate now:

node "<fleetmates root>/scripts/cli.mjs" gate --run <runId> --plan <planPath> --root <project root> --phase <name>

gate recomputes fileset and ownership from git at the moment it runs — it does not read or trust the old record. If any phase's fresh run does not exit 0, the work is not finished — name the phase and stop.

2. Inline run: a run directory exists but gates are absent or empty

executing-plans runs plans inline, task by task, and never writes a gate manifest or a verdict — that's by design, not an omission. If status.json exists but status.gates is absent or empty, the run is inline, and the absent gates are expected, not a fault to fix here.

There is no fleet history to derive fileset or ownership from, so run the gate solo:

node "<fleetmates root>/scripts/cli.mjs" gate --no-fleet --root <project root>

--no-fleet is the only way the enforcement checks are skipped, and it runs the project's full test suite (and any other command checks fleetmates.gate.json declares) fresh — a remembered result, or a run from earlier in this session, is not evidence. Once it exits 0, proceed.

3. No run directory at all

Someone may have finished work on a branch without ever calling init-run — there's no .fleetmates/<run-id>/ to read. Run the gate solo the same way as case 2, confirm it exits 0 from fresh output, and proceed the same way.

Branch taxonomy

Two kinds of branch exist in this plugin, and they are not interchangeable:

  • Teammate branches are scratch. Each teammate does its work on its own branch, inside its own worktree, and that branch is deleted by prune-run for each worktree it removes, once the run branch provably contains it.
  • The run branch is the deliverable. It is the only branch that matters once the run is done, and tm-integrator is the sole writer of it. Teammate branches merge into the run branch; the run branch never merges into a teammate branch, and no other role pushes to it directly.

If you find yourself about to commit implementation work straight onto the run branch instead of a teammate branch, stop — that's the wrong direction for this model.

Worktree and branch cleanup

A finished run leaves a worktree and a scratch branch per task. The --yes flag runs git worktree remove --force on every worktree it lists as prunable, and that discards uncommitted and untracked changes in it without asking. On Windows, it also follows a junction a worktree holds, so a worktree provisioned with a junction back into the repository — the kind a fresh worktree's own dependency install might use as a shortcut, such as a junction into the repository's real node_modules — has that target's contents deleted too, not just the worktree's own. Without --yes the command removes nothing, and it prints the worktrees and branches it would act on if nothing changes before the --yes run — both runs recompute the gate from scratch, but not which of those branches would actually be deleted — that verdict is computed only inside the removal itself. Run it first without --yes to read the plan, then add --yes to remove what it lists:

node "<fleetmates root>/scripts/cli.mjs" prune-run --run <runId> --plan <planPath> --root <project root> [--yes]

It removes a task's worktree only where that task's phase gate recomputes to PASS, and it deletes the worktree's branch only where git merge-base --is-ancestor proves the run branch already contains it. That proof is against the ref derive takes directly off git symbolic-ref --quiet HEAD, not off an abbreviated name, and derive refuses to produce a run branch at all when HEAD is detached, when HEAD points outside refs/heads/, or when the name that ref strips to is itself a ref path — so nothing a teammate can plant under refs/heads/ changes which ref this proof or the deletion it authorises runs against. That proof is not something a bare git branch -D makes on its own: -D deletes whatever branch it is given without asking whether the run branch contains it — it refuses only a branch a registered worktree still holds checked out, which is why prune-run removes the worktree first — and the plain -d measures "merged" against the branch's upstream or your current HEAD, never against the run branch. It never touches the main worktree, and it never removes another run's task worktree, but it does force-remove a leaked merge-preview worktree — a scratch worktree under the system temp directory — regardless of which run's gate created it, because a killed gate cannot run its own cleanup. Every worktree it examines and declines to remove is printed with the reason; it examines worktrees, not bare branches, so a task branch whose worktree is already gone is not reported either way.

Do not sweep by hand: a hand-run git worktree remove --force or git branch -D supplies neither the recomputed phase gate nor the ancestry proof above — it only does what the flag itself says, on whatever you point it at.

What this does not clean up: .fleetmates/<run-id>/ stays on disk on purpose. Delete it yourself when you no longer want the record: resume reads it to continue a run, while rebuild-state reads it twice: once to refuse when it exists, since it exists for the case where the directory is already gone, and once to keep the run branch it recorded — delete the directory and a later rebuild-state run from any other checkout records that checkout as the run branch, permanently, and complete --enforcement-only can no longer verify completion for the rest of the run. It is gitignored.

Surface unresolved findings

Before proposing integration, check for parked or deferred findings from review — anything a gate check or reviewer flagged but did not block on. Report them explicitly here, individually, with enough detail to judge. Do not let them get buried in a "looks good" summary. The user decides whether an unresolved finding blocks integration; that call is not yours to make silently.

Remote CI

A local gate runs on one machine, and the project's GitHub Actions may run on others. Do this step after every phase's gate has passed again and before presenting the integration options.

Before any push, run on this machine the CI steps this machine can run, so the remote CI confirms rather than discovers:

node "<fleetmates root>/scripts/cli.mjs" ci-local --root <project root> [--dry-run] [--keep-home] [--no-platform-sim]

It reads the workflows that run on a branch push or a pull request, takes each job whose runs-on (or matrix.os list) is this operating system, and runs that job's run: steps in order, with the step's shell, its env, its working-directory, and GITHUB_ENV and GITHUB_PATH carried to the next step. Each job runs in an environment shaped like a fresh hosted runner rather than this shell: an empty HOME and XDG directories (the toolchain directories of mise, asdf, volta, rustup, cargo, pyenv, nvm and bun, and the npm cache, stay visible), and the platform simulator's symlinked temp dir, core.autocrlf=true and umask 077, with the workflow's own env applied over them. A test that passes only because of this machine's own config, or only on a canonical temp path, fails here instead of on the runner. --keep-home and --no-platform-sim turn either off; with the user's own HOME, a git config --global step is undone after its step and the run names each file it restored. What it cannot reproduce it skips and names: a uses: action, an expression other than matrix.os, runner.os and runner.temp, an if: other than a runner.os comparison, an unusual shell, and every job for another operating system. Those are what the remote CI is still for. Exit 0 means every step it ran passed; exit 1 names the step that failed and nothing after it ran, so fix it before pushing, and do not push; exit 3 means no step runs on this machine, so say that the remote CI is the only check. --dry-run lists what would run and runs nothing.

Ask the user before pushing the run branch, unless ci.push is auto, in which case push without asking. The ci-wait command only reads; it never pushes, so the push is this step's own action. If the user declines the push, remote CI was not read: say so, and present the run as unverified by CI.

Two config keys shape this step: ci.push is ask (the default) or auto, and ci.waitMinutes is an integer from 1 to 720, 60 by default. Set either through the CLI, in the tracked fleetmates.gate.json by default or with --local in the gitignored fleetmates.local.json; any other value is refused with exit 2. The config list command does not print them, so read one back with config get:

node "<fleetmates root>/scripts/cli.mjs" config set ci.push auto --root <project root> [--local]
node "<fleetmates root>/scripts/cli.mjs" config set ci.waitMinutes 30 --root <project root> [--local]
node "<fleetmates root>/scripts/cli.mjs" config get ci.push --root <project root>

Then wait for the remote CI of the branch tip:

node "<fleetmates root>/scripts/cli.mjs" ci-wait --branch <run branch> --root <project root> [--minutes <1-720>]

It reads the GitHub Actions workflow runs of the branch's tip commit through gh api every 30 seconds, for up to ci.waitMinutes minutes (60 when unset), or --minutes when given. It judges whole workflow runs, not single jobs: every run on the tip must be completed with conclusion success or skipped, and any other conclusion, neutral included, is a failure. A re-run counts by its latest attempt. A run still queued or in progress is pending, and so is a tip on which no run has started yet. Whether the project has workflows at all is read from the tip commit's tree, not from the working tree.

Its exit code is the answer:

  • 0: every workflow run on the tip passed. It also exits 0, printing no CI workflows: skipped, when the tip commit has no .github/workflows; tell the user the project has no remote CI, so this step was skipped.
  • 1: a run failed. It prints each failed run with its conclusion and its link.
  • 3: runs were still pending when the window closed. It prints them with their links.
  • 4: remote CI could not be read: gh is not installed or not logged in, the response was not readable, or the tip has more than 100 workflow runs.
  • 2: a bad invocation, such as a branch that does not exist or a --minutes outside 1 to 720.

A red, pending or unreadable CI means the merge is not offered: report what ci-wait printed, links included, and stop. Fix a failure on a task branch, re-run the gate, push and wait again. Keeping the branch for further work stays available.

Integration options

Once a freshly re-run gate has passed for every phase and any parked findings are on the table, present the choice — do not pick one unilaterally:

  1. Merge to the default branch.
  2. Open a pull request for external review before it lands.
  3. Keep the branch for further work — integration isn't forced just because the gate passed.

Carry out whichever the user picks; don't default to merging in the absence of an answer.

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.