-
Notifications
You must be signed in to change notification settings - Fork 0
Integrate A Worker
Note
Goal: Land a finished worker's worker/<slug> branch on main — but only if the integrated result still passes the full deterministic battery — then fold the worker's progress into the mainline log and tidy up its worktree + branch.
Prereqs: the developer-workflows plugin installed (Install crickets plugins); a worker you spawned in a worktree whose /work session is finished (its worker/<slug> branch holds the per-task commits); a clean main working tree (no in-flight changes); you on main in the repo root, not inside the worktree.
Use /integrate-worker <name> when a worker has finished its plan and you — the coordinator — decide it is the one to land next. It is the closing step of the coordinator lifecycle: /plan --stage → /plan --activate → /spawn-worker → run /work inside the worktree → /integrate-worker. After it ships, the spawn→work→integrate worker lifecycle is complete.
<name> is the same activated named-plan slug the worker was spawned on (foo, PLAN-foo, and PLAN-foo.md all normalize to foo). Merge order is human-decided: the command integrates the one worker you name, when you name it — it never auto-sequences merges across workers. For the full command surface (arguments, guards, exit codes), see Named plans.
Important
Four guarantees this command holds: main is never left broken (a red gate on the integrated tree hard-resets main back to where it started), it never pushes (the merge is local — pushing stays your act), progress promotion is additive (the worker's progress-<slug>.md is appended into the mainline progress.md, never deleted; the vault named-plan pair is left untouched), and integration is serialized — it lands one worker at a time. Build fans out N-wide; integration is single-writer (an advisory .git/integrate.lock is held across the merge + gate, so a second concurrent /integrate-worker blocks rather than racing on main).
Building parallelizes; integrating does not. Run workers N-wide in their own worktrees, but land them one at a time — /integrate-worker holds a per-repo advisory lock for the duration of each landing, so a second concurrent integration waits for the first to finish (or roll back) instead of racing on the shared integration branch and version registry. The integrator is the single writer of marketplace.json + dist/ (see the Development lifecycle design).
When you publish the integrated branch (step 4 below), go through branch protection and required CI — push the branch, wait for the required checks to go green, then squash-merge the PR. Never land it with a protection bypass such as gh pr merge --admin: that was the anti-pattern from the first concurrent run (a worker admin-merged past the very protection it had authored), and the integration flow now forbids it. A squash-merge through the protected path is recoverable (revertable) — announce it and proceed; the bypass is not on the table.
Worker branches defer the version bump — the integrator owns it. A worker/<slug> branch commits its src/ change plus dist/ regenerated at the version already on main (it does not bump group.yaml version:), so disjoint-plugin branches never touch the shared marketplace.json version registry and cannot collide on it. The serialized integrator does the bump + marketplace.json + final regen once, from current main, one landing at a time — making generated-artifact production a single serialized writer (the Development lifecycle design). The build-time version-bump gate is therefore advisory on a worker branch and authoritative on main; the dist-sync gate stays authoritative everywhere.
-
Stand on the integration branch with a clean tree. From the repo root (not inside the worktree), check out the integration branch — normally
main— and make sure its working tree is clean (git statusshows nothing to commit). A dirty tree is a hard refusal: commit or stash any in-flight changes first. Confirm the worker's/worksession is finished and itsworker/<slug>branch holds the per-task commits. -
Run
/integrate-worker <name>. Pass the worker's name — the same activated named-plan slug it was spawned on (foo,PLAN-foo, andPLAN-foo.mdall normalize tofoo). Add--project-root <path>only if the repo root isn't your cwd. The command wrapsintegrate_worker.py, which wires the real gate (bash scripts/check-all.sh) onto the merged tree:/integrate-worker my-featureYou choose which worker lands and when — the command integrates the one you name and never auto-sequences merges across workers.
-
Read the outcome the command reports (the three rows below in Outcomes): GREEN → merged, gate passed, progress promoted, worktree + branch pruned (exit
0); RED gate → merged then hard-reset back, worktree kept, gate output printed (exit2); CONFLICT →git merge --abort, worktree kept (exit2). The command surfaces the helper's own message verbatim — read that line, don't re-derive it. -
On GREEN, push
mainyourself. The integration is local — the command never pushes. Once you're satisfied with what landed, rungit pushto publish the integrated branch:git push -
On RED or CONFLICT, fix inside the surviving worktree, then re-run. The worktree is left intact for inspection. For a red gate, read the printed gate output and fix the worker's plan in its own
/worksession inside the worktree; for a conflict, resolve the divergence in the worker's branch (e.g. mergemaininto it inside the worktree, re-run its gates). Then re-run/integrate-worker <name>.
The integration runs scripts/check-all.sh (the full 10-gate battery) on the post-merge / integrated tree — not the worker branch in isolation — so an integration conflict between the worker's work and newer main is actually caught. There are three outcomes:
| Outcome | What /integrate-worker does |
Your worktree | Exit |
|---|---|---|---|
| GREEN | merges worker/<slug> → main (--no-ff), gate passes, appends progress-<slug>.md into progress.md, then prunes the worktree + deletes the merged branch |
removed (work landed) | 0 |
| RED gate | merged, but the gate failed on the integrated tree → hard-resets main back to the captured pre-merge HEAD; prints the gate output |
left intact for inspection | 2 |
| CONFLICT | the merge itself conflicted → git merge --abort
|
left intact | 2 |
| graceful-skip | the located agentm resolver reports no resolvable _harness/
|
untouched | 1 |
After a GREEN integration main carries the worker's commits but is not pushed — run git push yourself once you're ready.
After a GREEN integration, confirm what landed:
-
git log --oneline --merges -1onmainshows the--no-ffintegration merge commit forworker/<slug>. - The mainline
progress.mdcarries the appendedprogress-<slug>.mdentries plus a one-line integration record (the worker's namedprogress-<slug>.mdis kept — promotion is additive). -
git worktree listno longer shows the worker's worktree, andgit branch --list worker/<slug>is empty (pruned via the safegit branch -d).
After a RED gate or CONFLICT (exit 2), confirm nothing changed:
-
git log --oneline -1onmainis the same commit it was before you ran the command (the rollback hard-reset it back, or the merge was aborted before any commit). -
git worktree liststill shows the worker's worktree andworker/<slug>still exists — left intact for you to fix and re-run.
-
/integrate-workerrefused (exit 2) and changed nothing. Every guard runs before any merge, so a refusal leavesmainand the worktree untouched. It refuses on: an empty/singleton name; a missingworker/<slug>branch; an undiscoverable worktree; a dirtymainworking tree (commit or stash your in-flight changes first); or an unresolvable plan/progress pair. Fix the named condition, then re-run. -
The gate went red and
mainrolled back. This is by design —mainis never left broken. Read the printed gate output, fix the worker's plan inside the surviving worktree (its/worksession), then re-run/integrate-worker <name>. -
The merge conflicted. The command aborted the merge and left the worktree intact. Resolve the divergence in the worker's branch (rebase/merge
maininto it inside the worktree, re-run its/workgates), then re-run. -
Worktrees piling up after integrations? Run the read-only
doctor_worktrees.pyprobe — it lists everyworker/<slug>worktree and classifies each (active · merged-but-unpruned · orphaned · dangling-marker) with its plan mapping. It mutates nothing; you prune on demand. See Named plans.
- Spawn a worker in a worktree — the open of the lifecycle this command closes: hand an activated named plan to a worker in its own checkout.
-
Named plans — the lookup:
/integrate-worker's arguments, guards, exit codes, and thedoctor_worktrees.pyprobe. - Run a named plan — author + stage + activate the plan a worker binds to.
- Developer safety design — worktrees first-class but operator-initiated — the norm that sanctions the worker worktrees this command merges and prunes.
-
Development lifecycle design — gate the integrated tree — why the gate runs on the merged tree and
mainhard-resets on red rather than gating the worker branch in isolation. - Developer Workflows — the phase-loop plugin this command belongs to.
🔧 How-to
- Install plugins
- Using code review
- Provision a repo's wiki
- Declare a project's Architecture
- Maintain a wiki — wiki-watcher
- Review a change — code review
- In-flight decision review — /doubt
- Author a design (pending)
- Run a named plan
- Spawn a worker in a worktree
- Run isolated tasks
- Configure main branch protection
- Integrate a worker
- See every active plan
- Run a coordinator-directed worker team (pending)
- Install the vault backend (pending)
- Sync a project board