em-ship-feature — EM-led Autonomous Feature Execution
Consume an em-feature-plan planning bundle and ship it. The product: shipped code on a working branch + a single live-updated execution-report.md that tells the user what shipped, what's blocked, and what the staff engineer flagged.
The skill is EM-led: an Engineering Manager orchestrator parses the plan, dispatches one max-effort senior-engineer subagent per task in DAG-driven parallel waves, lets each senior delegate the actual coding to whichever external CLI has quota (codex → gemini → claude sonnet, run-wide degradation), runs a senior↔coder debate per task to catch plan errors before they become code errors, and finishes with one max-effort staff-engineer review pass over the merged diff.
Violating the letter of the rules is violating the spirit of the rules. No exceptions, no rationalizations.
When to Use / Skip
Use when:
- A planning bundle exists at
docs/<feature-slug>/(produced byem-feature-plan). - You want to ship the whole feature autonomously in one session —
/effort maxis active and--dangerously-skip-permissionsis active and the working tree is clean. - You're willing to accept best-effort delivery with a blocker report (not infinite retry). If you need true don't-stop semantics, wrap this skill in
ralph-loop.
Skip when:
- No plan exists yet → run
/superdev:em-feature-plan <slug>first. - The plan is
team-feature-plan's output (different schema) — out of scope for v1. - You want per-task interactive review → use the original
/superdev:superdevworkflow per task instead. - Single-task fixes — use
claude-plans-codex-executesdirectly.
Activation
This skill is invoked in two ways:
- Explicit slash command:
/superdev:em-ship-feature <feature-slug>— the slug is the same oneem-feature-planproduced (the directory name underdocs/). - Implicit triggers — user messages matching: "ship this feature", "execute the plan", "EM-run this feature", "build out the tasks", "let the team ship this", "go build this end-to-end". When matched, the skill prompts for the feature-slug if not in the message.
Pipeline composition:
team-brainstorm (optional) → em-feature-plan → em-ship-feature
The skill assumes the em-feature-plan artifact set at docs/<feature-slug>/:
tasks.md(the DAG: Foundation list + Frontier tasks with dependency edges)engineer-*-tasks.md(one or more, parsed for per-task details)engineering-standards.md(the rules floor)planning.md(architectural context)
If any are missing, Phase 0 halts with the copy-paste command to run em-feature-plan first.
Optional outer wrapping: for users who want infinite-retry on blocked tasks rather than best-effort,
/ralph-loop /superdev:em-ship-feature <slug> --completion-promise "all tasks shipped, all tests green"
The skill itself does not invoke ralph-loop; users opt in by wrapping.
Hard Gates (cannot be skipped)
/effort maxactive. EM main thread + every senior subagent + the staff engineer all need claude max effort. Self-check: the skill bundles this confirmation into Phase 0'sAskUserQuestion(question 1, "are both required flags active?"). If user answers "no", halt with the copy-paste command:/effort max.--dangerously-skip-permissionsactive. Skill assumes session-level permission bypass. Same Phase 0 bundled confirmation as gate #1. If user answers "no", halt with the copy-paste invocation: re-launch Claude Code withclaude --dangerously-skip-permissions.em-feature-planoutput exists and is complete.docs/<feature-slug>/must containtasks.md, at least oneengineer-*-tasks.md,engineering-standards.md, andplanning.md. Phase 0 checks each. If any missing → halt with: "Run/superdev:em-feature-plan <slug>first." No silent fallback to a different planning source.Senior + Staff engineer subagents =
model: opus. Every per-task seniorAgentdispatch and the single staff-engineerAgentdispatch explicitly setmodel: opus. Non-negotiable — under-powered planners and reviewers degrade the entire pipeline's ceiling.Foundation sequential, Frontier parallel. Phase 1 dispatches Foundation senior subagents one at a time (they wire shared interfaces; parallel writes would collide). Phase 2 dispatches all DAG-ready frontier tasks in one message with multiple
Agentcalls. Sequential where parallel is safe = workflow failure. Parallel where conflict-prone = correctness failure.Bounded retries everywhere. Per-task debate ≤ 2 rounds · per-task verify restart ≤ 1 · staff-engineer BLOCKER fix-up ≤ 1 per finding. Never unbounded recovery. After the cap, mark BLOCKED / land in report.
Run-wide coder degradation, no probing. Once a coder family hits quota, it is OUT for the rest of the run. No per-task re-probe. Run state:
coder ∈ {codex, gemini, sonnet}, monotonic in degradation order.Phase 0 bundled confirmation is the ONLY interactive checkpoint. No mid-run user prompts. Other "I would normally ask" moments become auto-decisions noted in the report.
docs/<feature-slug>/execution-report.mdis the single authoritative output. Conversation-only summaries are addenda; the report file is what tells the user (and downstream tooling) what shipped.Same-family review per task. The reviewer in Step 2 of the per-task pipeline is from the same coder family currently active (codex →
codex-reviewer, gemini →gemini-reviewer, sonnet → general-purpose Agent withmodel: sonnet). Cross-family review loses the rigor lens.Working tree must be clean before Phase 0 proceeds.
git status --porcelainmust return empty before any per-task pipeline runs. Reason: thegemini-revieweragent enforces its read-only contract by post-callgit status --porcelain+git checkout -- .+git clean -fd. If the tree has pre-existing uncommitted state when gemini-reviewer runs, the cleanup logic would destructively wipe it. Phase 0 verifies this before the first dispatch. If unclean → halt with: "Commit or stash uncommitted changes first, then re-run." This gate is mandatory even when the run is expected to stay on codex (the run might degrade to gemini mid-flight).EM is the only dispatcher. Subagents must NEVER be told to dispatch other subagents. This runtime exposes the
Agenttool only to the main thread; general-purpose subagents haveTaskCreate/Updatebut notAgent. The per-task pipeline's Step 2 (reviewer dispatch) and Step 4 (coder dispatch) are EM-initiated, NOT senior-initiated. Steps 3 (debate), 5 (verify), and 6 (commit) are EM-inline (no dispatch). Telling a senior subagent to "now dispatch the reviewer" or "now dispatch the coder" will fail with no Agent tool available. This gate is what makes the per-task pipeline actually work in this runtime — and what differentiates this redesign from the original spec (which assumed senior subagents could dispatch internally). Sibling skillem-feature-planhas the same constraint codified as its own Hard Gate #10.
Spirit: violating the letter of these rules is violating the spirit of the rules. No exceptions, no rationalizations.
Workflow
5 phases — 1 interactive Phase 0 (bundled approval), then 4 autonomous phases.
| # | Phase | Owner | What happens |
|---|---|---|---|
| 0 | Bootstrap | EM (main thread, talks to user) | Hard-gate checks (incl. working-tree-clean) · parse artifacts · build task DAG · bundled AskUserQuestion (gate confirmation + git strategy) · initialize execution-report.md |
| 1 | Foundation sweep | EM dispatches senior subagents sequentially | Process Foundation Sprint tasks one at a time (shared-interface conflict rule from em-feature-plan) |
| 2 | Frontier fan-out | EM dispatches senior subagents in parallel waves (DAG-driven) | Topological dispatch: every task whose deps are met fans out in one message; re-batch as waves clear |
| 3 | Staff review | 1 staff-engineer subagent (model: opus) |
Reviews ALL shipped code across 3 lenses: bugs · simplification · codebase style. BLOCKER findings → 1 fix-up pass; MAJOR/MINOR → land in report |
| 4 | Finalize | EM (main thread) | Update execution-report.md with staff findings + summary, commit per chosen git strategy, print final status |
Phase 0 — Bootstrap (the only interactive phase)
Step 0.1 — File-existence + working-tree hard gates
Check each file exists. If any missing, halt with the copy-paste fix:
docs/<slug>/tasks.md
docs/<slug>/engineer-*-tasks.md (at least one match)
docs/<slug>/engineering-standards.md
docs/<slug>/planning.md
Use Bash("test -f docs/<slug>/<file>") for each. If any fail:
Cannot find docs/<slug>/<missing-file>. Run this first:
/superdev:em-feature-plan <slug>
Then re-run /superdev:em-ship-feature <slug>.
Then verify the working tree is clean (Hard Gate #11):
git status --porcelain
If output is non-empty:
Working tree is not clean. Commit or stash uncommitted changes first:
git status # see what's uncommitted
git stash # OR commit explicitly
/superdev:em-ship-feature <slug> # re-run after clean
Reason: gemini-reviewer's read-only enforcement may destructively
revert any pre-existing uncommitted state if the run degrades to
gemini mid-flight. Hard Gate #11.
Then verify HEAD is on a named branch (detached HEAD breaks git symbolic-ref --short HEAD downstream — it returns empty, which silently corrupts working_branch and gh pr create --base):
git symbolic-ref --short HEAD >/dev/null 2>&1
If exit code is non-zero (detached HEAD):
HEAD is detached. The skill needs a named branch for the per-task
dispatch and for any auto-PR. Check out (or create) a branch first:
git checkout -b <some-name>
/superdev:em-ship-feature <slug>
Then capture base_sha (used by Phase 3's diff range and Phase 4.1's file count):
base_sha=$(git rev-parse HEAD)
Store base_sha for use in Phase 3 and Phase 4. This is the commit that everything shipped during the run is measured against.
Step 0.2 — Parse artifacts in parallel
Issue ALL of the following as parallel Read calls in ONE message:
Read(docs/<slug>/tasks.md)
Read(docs/<slug>/engineer-1-tasks.md)
Read(docs/<slug>/engineer-2-tasks.md) [if exists]
Read(docs/<slug>/engineer-N-tasks.md) [for each engineer packet]
Read(docs/<slug>/engineering-standards.md)
Read(docs/<slug>/planning.md)
Read(docs/<slug>/allocations.md) [OPTIONAL — produced by em-feature-plan Phase 7 if a team roster was provided; absence is not an error]
If allocations.md doesn't exist, skip it silently — em-ship-feature degrades gracefully to slot-only attribution (current pre-allocation behavior). If allocations.md exists but is malformed (no "Slot → Engineer mapping" section parseable), log a warning in the report's Notes section and proceed slot-only.
Step 0.3 — Build the task graph (and optional engineer attribution)
Parse tasks.md:
- Foundation list — under the "Foundation Sprint" header. Ordered sequential list.
- Frontier DAG — under the "Parallel Frontiers" header. Each task has
dependencies: [<task-id list>]. Build the adjacency map.
Parse each engineer-*-tasks.md:
- Per task: id, title, acceptance criteria, TDD red list, interfaces owned/consumed, files to touch.
- Build
task_to_slotmap (e.g.,{"F-1": "engineer-1", "T-3": "engineer-2", ...}) from which packet file each task appeared in. Cross-reference with the DAG: every task in the engineer packets should appear intasks.md.
If allocations.md was loaded in Step 0.2, parse the "Slot → Engineer mapping" table:
- Build
slot_to_engineermap (e.g.,{"engineer-1": {name: "Alice", level: "Senior", capacity: 1.0, skills: [...], skill_match: "Strong"}, ...}). - Build
task_to_engineermap by composingtask_to_slot∘slot_to_engineer(e.g.,{"F-1": {name: "Alice", ...}, ...}). - Capture risk callouts from the "Risk callouts" section verbatim into
allocation_risks(list of strings). - Capture unallocated capacity from "Unallocated capacity" section into
unallocated_summary.
If allocations.md was NOT loaded, set slot_to_engineer, task_to_engineer, allocation_risks, unallocated_summary to null. Downstream phases check for null before injecting engineer context.
Step 0.4 — Print run summary
Feature: <name from README.md or planning.md>
Slug: <slug>
Tasks: <N> foundation + <M> frontier = <N+M> total
Max parallel frontier width: <max-out-degree of DAG>
Coder default: codex (fallback chain: gemini → sonnet)
Working branch: <result of `git symbolic-ref --short HEAD`>
Allocation: <"loaded — <count> engineers, <count> tasks attributed" | "skipped (no allocations.md present)" | "skipped (malformed allocations.md — see report Notes)">
<If allocation loaded AND allocation_risks is non-empty: "⚠️ <count> allocation risk(s) flagged in allocations.md — surfaced in report Notes. Will not block the run (Hard Gate #8).">
Step 0.5 — Bundled AskUserQuestion (THE only interactive checkpoint)
AskUserQuestion(questions: [
{
question: "Confirm both required flags are active before this run goes autonomous (the skill cannot detect them programmatically).",
header: "Flags active?",
multiSelect: false,
options: [
{ label: "Yes, both /effort max and --dangerously-skip-permissions are active", description: "Proceed to git-strategy question." },
{ label: "No — halt and let me enable them first", description: "Halt the skill. Copy-paste fix: /effort max ; relaunch Claude Code with --dangerously-skip-permissions." }
]
},
{
question: "Git strategy for this run?",
header: "Git strategy",
multiSelect: false,
options: [
{ label: "Atomic commits per task on current branch (Recommended)", description: "One commit per shipped task on the current branch; one final commit for the report. You push and create the PR yourself." },
{ label: "Atomic commits per task on new branch feature/<slug>", description: "Same as recommended but on a fresh branch named feature/<slug>. Doesn't pollute current branch." },
{ label: "Single squash commit at end on current branch", description: "Accumulate all changes on branch, squash into one commit at end with the report as the body. Clean history, but lose per-task attribution and bisectability." },
{ label: "Atomic commits + auto-push + auto-PR at end", description: "Like recommended, plus push the branch and open a PR at end with the execution report as the body. Warning: external visibility — PR appears immediately to your teammates. Only pick if you authorize PR creation in advance." }
]
}
])
If question 1 answer is "No, halt", stop immediately with the copy-paste fix.
If question 2 answer is "auto-push + auto-PR", verify gh CLI is installed (Bash("gh --version")); if not, halt with brew install gh && gh auth login. Also record the base branch (typically main) for later gh pr create --base <base>.
Store the git-strategy answer for use in Step 6 of the per-task pipeline and in Phase 4.
Step 0.5b — Create the working branch (only if strategy requires it)
If git_strategy is "Atomic commits per task on new branch feature/" OR "Atomic commits + auto-push + auto-PR at end":
# Record the base branch first (used by gh pr create later)
base_branch=$(git symbolic-ref --short HEAD)
# Detect existing branch before checkout -b (re-runs of the skill with the same slug otherwise fail silently
# and the next `git symbolic-ref --short HEAD` returns the original branch — commits then land on the wrong branch).
if git show-ref --verify --quiet "refs/heads/feature/<slug>"; then
HALT: "Branch feature/<slug> already exists (likely from a prior partial run). Either:
git branch -D feature/<slug> # delete it
git checkout feature/<slug> # OR resume on it (pick git strategy 'current branch' on re-run)
Then re-run /superdev:em-ship-feature <slug>."
fi
git checkout -b feature/<slug>
If git_strategy is "Atomic commits per task on current branch" OR "Single squash commit at end on current branch": no branch change, working_branch = current branch.
After this step, capture the final working_branch:
working_branch=$(git symbolic-ref --short HEAD)
This value is passed to every senior subagent dispatch (line 2 of every senior prompt).
Step 0.6 — Initialize the report skeleton
Use Write on docs/<slug>/execution-report.md:
# Execution Report — <feature-name>
**Feature slug:** <slug>
**Started:** <ISO timestamp from `date -u +%Y-%m-%dT%H:%M:%SZ`>
**Finished:** —
**Status:** RUNNING
**Coder trajectory:** codex
**Git strategy:** <chosen-strategy>
**Allocation:** <"loaded from allocations.md (<N> engineers)" | "skipped (no allocations.md)" | "skipped (malformed allocations.md)">
**Final commit:** —
## Run summary
Tasks: <N+M> total · 0 shipped · 0 blocked · 0 files changed · 0 tests added
## Tasks
| ID | Title | Owner | Status | Coder | Files | Tests | Notes |
|----|-------|-------|--------|-------|-------|-------|-------|
| F-1 | <title> | <engineer name from task_to_engineer if loaded, else "(engineer-N)" slot label> | PENDING | — | — | — | — |
| F-2 | <title> | <owner> | PENDING | — | — | — | — |
| T-1 | <title> | <owner> | PENDING | — | — | — | — |
| T-N | <title> | <owner> | PENDING | — | — | — | — |
## Staff-engineer review findings
### Blockers
(none yet)
### Major
(none yet)
### Minor
(none yet)
## Per-task blockers (with reproducer + suggested next step)
(none yet)
## Open questions / Notes
<If allocation_risks is non-empty, render this section verbatim at the START of the Notes section so reviewers see it immediately:>
### Allocation risks flagged by em-feature-plan Phase 7
<for each risk in allocation_risks: render as a bullet>
- <risk text verbatim>
(These risks are surfaced for visibility. Per Hard Gate #8, the skill does NOT pause for user input on them; if the run completes despite them, address in a follow-up. If they caused task failures, the per-task BLOCKED reason will reference the relevant risk.)
<If unallocated_summary present and non-empty:>
### Unallocated team capacity
<unallocated_summary verbatim>
<If neither, just write:>
(none yet)
Populate the task table with every task ID and title from the parsed artifacts. Fill the Owner column from task_to_engineer if allocation was loaded; otherwise use the slot label (e.g., "(engineer-1)") as a fallback. All status PENDING.
Step 0.7 — Print transition message
Tell the user:
Going autonomous from here. No more interactive checkpoints until completion or fatal error.
Watch docs/<slug>/execution-report.md for live status.
Then proceed to Phase 1.
Phase 1 — Foundation sweep (sequential)
Foundation Sprint tasks share interfaces (ports, DI wiring, error model, test harness, CI scaffolding). Parallel writers would collide. Dispatch one at a time.
Loop
For each Foundation task in the order they appear in tasks.md:
- Run the Per-Task Pipeline (see that section below).
- On the senior subagent's return, update the task's row in
execution-report.md:- Always
Readthe report file FIRST to get its current state — the report has been mutated by prior task updates, never edit blind. - Then use
Editto flip the task's row from PENDING to SHIPPED or BLOCKED, fill in Owner (from the per-task result's OWNER field, set at dispatch time), coder/files/tests/notes. - If the senior's NOTES indicates a coder trajectory transition (e.g. degraded from codex to gemini), also
Editthe report header'sCoder trajectory:field to record it (e.g.codex → gemini at F-3).
- Always
- If status is SHIPPED, the commit (made by the senior in Step 6 of the per-task pipeline) is already on the branch.
- If status is BLOCKED:
- Foundation BLOCKED is high-impact — most Frontier tasks transitively depend on Foundation. Continue with subsequent Foundation tasks (their dependencies may be different), but accept that many Frontier tasks will end up BLOCKED.
- Do not abort the run.
- If the
Agenttool itself errored (not a structured BLOCKED block — actual tool error / timeout / dispatch failure): mark the task BLOCKED with the error message as the reason, do not abort. - Move to the next Foundation task.
After all Foundation tasks complete (SHIPPED or BLOCKED), proceed to Phase 2.
Phase 2 — Frontier fan-out (parallel waves, DAG-driven)
Wave-dispatch loop
Each task's full per-task pipeline (Steps 1-6) runs sequentially WITHIN the task, but the planning step (Step 1) is batched across all in-flight tasks in the wave so the senior subagents work in parallel. Steps 2-6 process each task sequentially after its plan returns.
- Compute the ready set: every Frontier task whose
dependencies:list is now fully SHIPPED. - Subtract tasks already SHIPPED or BLOCKED from previous waves.
- If the ready set is empty:
- Check for transitively blocked tasks — any task whose dependency is BLOCKED. Mark those BLOCKED with reason
dependency blocked: <upstream-task-id>and update their rows in the report. - If after that the ready set is still empty, exit Phase 2 → proceed to Phase 3.
- Check for transitively blocked tasks — any task whose dependency is BLOCKED. Mark those BLOCKED with reason
- Batch Step 1 across the wave: dispatch ALL ready tasks' Senior-plan subagents in ONE message with multiple
Agentcalls (one per task). This is the parallel-fan-out hard requirement (Hard Gate #5). - Wait for all Step 1 dispatches in the batch to return.
- For each returned task, run Steps 2-6 sequentially through the per-task pipeline:
- Dispatch the reviewer (Step 2)
- Inline debate (Step 3), possibly with another reviewer dispatch for round 2
- Dispatch the coder (Step 4), with run-wide quota-degradation if
QUOTA_EXCEEDED:is returned - Inline verify (Step 5), with possibly one reviewer + coder restart on material fail
- Inline commit (Step 6) if SHIPPED
- Aggregate per-task result;
Readthe report first, thenEditthe row (never edit blind). - If the senior's debate or the coder's NOTES indicates a coder-trajectory transition, also
Editthe report header'sCoder trajectory:field.
- On
Agent-tool dispatch errors (the tool itself errored, not a structured return): mark the task BLOCKED with the error as reason, do not abort the wave. - Go to step 1 (compute next ready set).
Wave dispatch example
If after Foundation completes, tasks T-1, T-2, T-3 all have only Foundation dependencies (all met), the EM batches Step 1 in ONE message:
Agent(description: "Senior plan — T-1", subagent_type: "general-purpose", model: "opus", prompt: <Step 1 plan prompt for T-1>)
Agent(description: "Senior plan — T-2", subagent_type: "general-purpose", model: "opus", prompt: <Step 1 plan prompt for T-2>)
Agent(description: "Senior plan — T-3", subagent_type: "general-purpose", model: "opus", prompt: <Step 1 plan prompt for T-3>)
All three plan in parallel. Once all three plans return, the EM processes T-1's Steps 2-6 sequentially, then T-2's, then T-3's. Within each task's Steps 2-6, the EM dispatches the reviewer (Step 2), runs Step 3 inline, dispatches the coder (Step 4), runs Steps 5-6 inline.
Advanced (optional) parallelism: an implementation may also batch Step 2 reviewer dispatches across tasks at the same pipeline stage (e.g., once all wave tasks finish Step 1, dispatch all their Step 2 reviewers in one message). This requires per-task state tracking and careful interleaving — skip if the simpler "Step 1 batched, Steps 2-6 sequential" pattern works fast enough.
Quota detection during the wave
If a returned coder subagent indicates quota exhaustion (e.g., QUOTA_EXCEEDED: prefix), the EM flips the run state to the next coder family for all subsequent dispatches (Hard Gate #7: run-wide degradation, no probing). Tasks already past Step 4 in the current wave keep their current-coder result; the next task's Step 2 (reviewer dispatch) uses the new coder family per Hard Gate #10.
Update the Coder trajectory field in the report's header (e.g., codex → gemini at T-3).
Per-Task Pipeline (Steps 1-6, dispatched by the EM main thread)
This is the heart of the skill. The EM (main thread) coordinates 3 subagent dispatches per task in sequence and runs the connective tissue (debate, verify, commit) inline using Bash + Edit. Mirrors claude-plans-codex-executes step-for-step in spirit, restructured to honor the runtime constraint that subagents cannot dispatch other subagents (only the main thread has the Agent tool).
| Step | Owner | What | Mechanism |
|---|---|---|---|
| 1. Plan | Senior (claude max) subagent | Per-task plan: files · permissions · subtasks · key decisions citing engineering-standards.md · TDD red list | Agent dispatch (returns plan markdown) |
| 2. Review | Coder-family reviewer subagent | AGREE / CONCERN / QUESTION / SUMMARY critique of the plan | Agent dispatch (returns critique verbatim) |
| 3. Debate | EM (main thread, inline) | Reads critique, decides updates, produces updated plan. ≤ 2 rounds. Round 2 unresolved → EM makes final call, notes in report. | No dispatch — EM is claude max via /effort max (Hard Gate #1), competent to debate |
| 4. Execute | Coder subagent | Write the code | Agent dispatch (returns stdout + git diff stat) |
| 5. Verify | EM (main thread, inline) | Bash check commands · grep coder stdout for silent denials · trivial fix inline · material fail → 1 restart from Step 2 · second fail → BLOCKED | No dispatch — running shell commands needs no model |
| 6. Commit | EM (main thread, inline) | Bash: git add + git commit with Co-Authored-By trailer for the coder family used | No dispatch — git is just shell |
3 subagent dispatches per task in the happy path (Senior-plan + Reviewer + Coder). +1 reviewer dispatch per extra debate round (max 1 extra). +2 dispatches per verify restart (Reviewer + Coder, max 1 restart). Worst case per task: 6 subagent dispatches.
Why EM-inline for Steps 3, 5, 6: the runtime does not expose the Agent tool to general-purpose subagents — only the main thread can dispatch other subagents. Steps 3 / 5 / 6 don't need delegation: debate is reasoning the EM can do directly (the EM is also claude max via Hard Gate #1); verify is shell commands + grep; commit is git. Moving them to the EM is the minimal change that respects the constraint while preserving the per-task debate and the per-task coder fallback chain.
Step 1 — Plan (Senior subagent dispatch by EM)
Agent(
description: "Senior plan — <task-id>",
subagent_type: "general-purpose",
model: "opus",
prompt: """
repo: /Users/eslam/linkify/<repo-name>
working_branch: <captured at Phase 0 Step 0.5b>
feature_slug: <slug>
coder_family: <codex | gemini | sonnet> # current run state; informs sandbox / permissions decisions in the plan
assigned_engineer: <if task_to_engineer loaded: "<name> (<level>, capacity <X>, skills: <list>, skill_match: <Strong|Partial|Weak>)" — omit this line entirely if no allocations.md was loaded>
You are the SENIOR ENGINEER for task <task-id>. Your sole job in this dispatch is to PRODUCE A PLAN. You do NOT execute, review, or commit — those are downstream steps owned by the EM main thread or other subagents.
<If assigned_engineer is set, include this paragraph; otherwise omit:>
This task is assigned to <name> per docs/<slug>/allocations.md. They are a <level> engineer with <skills>. Their skill match for this task is <skill_match>. Use this context to calibrate plan detail — for example, if Strong match assume domain knowledge and keep the plan concise; if Partial or Weak, include more explicit context in subtasks and Key decisions for the implementer. Do not change the plan's substance based on the engineer — the PLAN is the same; the SHAPE of the plan adapts to their context.
TASK SPEC (from docs/<slug>/engineer-N-tasks.md):
<paste the full task block: id, title, acceptance criteria, TDD red list, interfaces owned/consumed, files to touch>
ENGINEERING STANDARDS (rules floor — non-negotiable):
<paste docs/<slug>/engineering-standards.md verbatim>
PLANNING CONTEXT (excerpt relevant to this task's area):
<paste the relevant section of docs/<slug>/planning.md>
Produce a plan in this exact format and return it as your final output:
## Plan
### Files to create / modify
- <abs path>: <one-line reason>
### Permissions (codex-exec knobs, ignored when coder is gemini or sonnet)
- Sandbox: <read-only | workspace-write | danger-full-access>
- Extra writable roots (--add-dir): <paths, or "none">
- Granular sandbox permissions: <list, or "none">
- Override: <include OVERRIDE: danger-full-access? default "no">
### Subtasks (each ≤30 min)
- [ ] 1. <subtask>
### Key decisions
- <choice> | Why: <reason citing engineering-standards.md>
### TDD red list (from task spec)
- <test name>: <what it asserts>
That's it. Return ONLY the plan. Do NOT dispatch reviewers or coders (you can't — only the EM can). Do NOT commit (the EM does that). Do NOT write any code in this dispatch (the coder does that in Step 4).
"""
)
Step 2 — Review (Reviewer subagent dispatch by EM)
The EM dispatches the same-family reviewer (Hard Gate #10) with the plan from Step 1:
# If current coder is codex:
Agent(
description: "Codex review — <task-id>",
subagent_type: "codex-reviewer",
prompt: "repo: <path>\n\n<plan from Step 1>"
)
# If current coder is gemini:
Agent(
description: "Gemini review — <task-id>",
subagent_type: "gemini-reviewer",
prompt: "repo: <path>\n\n<plan from Step 1>"
)
# If current coder is sonnet:
Agent(
description: "Sonnet review — <task-id>",
subagent_type: "general-purpose",
model: "sonnet",
prompt: """
ROLE: You are the senior engineer reviewing this implementation plan. Be specific — cite file paths. Find real problems, not nits.
For each issue, output exactly one of:
AGREE: <one-line>
CONCERN: <issue> | <suggested change>
QUESTION: <ambiguity>
End with: SUMMARY: <N concerns, M questions>
---
<plan from Step 1>
"""
)
The reviewer returns its critique verbatim. The EM captures it for Step 3.
Step 3 — Debate (EM main thread, inline)
The EM reads every CONCERN: and QUESTION: line from the reviewer's output and produces an updated plan:
- If the reviewer is right → update the plan accordingly (edit the markdown the EM holds in working memory).
- If the reviewer is wrong → write a counter-argument in the EM's internal notes (don't update the plan).
Round 2: if concerns remain after round 1, re-dispatch the SAME reviewer (Step 2) with the updated plan + the EM's round-1 responses appended:
<updated plan>
MY RESPONSES TO ROUND 1:
- Concern: <quote> → My response: <counter-argument>
- ...
After round 2 with unresolved concerns → the EM makes the final call (accept the plan as-is, proceed to Step 4), and notes the unresolved concerns in the per-task NOTES field that will land in the report.
Hard cap: 2 reviewer dispatches per task in the debate phase (Hard Gate #6).
Step 4 — Execute (Coder subagent dispatch by EM)
The EM dispatches the current coder family with the finalized plan:
# If coder is codex:
Agent(
description: "Codex execute — <task-id>",
subagent_type: "codex-executor",
prompt: "repo: <path>\n\nFINALIZED PLAN:\n<plan>"
)
# If coder is gemini:
Agent(
description: "Gemini execute — <task-id>",
subagent_type: "gemini-executor",
prompt: "repo: <path>\n\nFINALIZED PLAN:\n<plan>"
)
# If coder is sonnet:
Agent(
description: "Sonnet execute — <task-id>",
subagent_type: "general-purpose",
model: "sonnet",
prompt: """
ROLE: You are the senior engineer implementing this plan in the repository at <path>. Use the Read/Edit/Write tools to make the changes. Follow the plan precisely. Run any tests called out in the TDD red list and include pass/fail in your output.
For each file you change, output: CHANGED: <path> — <summary>
End with: SUMMARY: <N files changed>
---
<plan>
"""
)
The coder returns stdout (+ for codex/gemini-executor: git status --short and git diff --stat HEAD).
On QUOTA_EXCEEDED: prefix in the coder's return: the EM flips the run state for the rest of the run to the next coder family (Hard Gate #7). Then RESTART from Step 2 with the new coder's reviewer (same-family rule — Hard Gate #10 — the new family deserves to critique the plan with its own rigor lens before executing). Note the trajectory transition in the per-task NOTES.
Step 5 — Verify (EM main thread, inline)
The EM runs:
# Sanity check that changes happened
git status --short
git diff --stat HEAD
# Project check commands (read from engineering-standards.md or CLAUDE.md). Examples:
# Rust: cargo fmt --check && cargo clippy --all-targets -- -D warnings && cargo test
# Node: pnpm install && pnpm typecheck && pnpm test
# Python: ruff check . && pytest
<check command(s)>
Then grep the coder's stdout (captured in Step 4) for silent-denial patterns:
- Codex/Gemini OS-level:
sandbox.*denied,Permission denied,Operation not permitted,EACCES,EPERM,Network is unreachable,ENETUNREACH,Connection refused,ECONNREFUSED,getaddrinfo,Name or service not known,MCP.*error,mcp_server.*denied - Gemini-specific:
QUOTA_EXCEEDED:andAUTH_FAILED:prefixes (the gemini-executor / gemini-reviewer wrappers prepend these) - Sonnet: no stdout to grep — failures surface as
is_error: truefrom the Agent dispatch in Step 4 itself, treat as a silent denial (restart or BLOCK per the rules below)
Outcomes:
- Checks pass with no silent denials → status SHIPPED, proceed to Step 6.
- Checks fail on something trivial (formatting, an obvious typo) → EM fixes inline via Edit, re-runs the check command, proceeds.
- Checks fail materially → ONE restart from Step 2 (re-dispatch reviewer + coder with the failure output appended as a new concern). If second verify also fails → status BLOCKED, do NOT commit, advance to the EM's task-result aggregation.
Step 6 — Commit (EM main thread, inline)
Only if status SHIPPED. The EM runs:
git add <files changed by coder> # parsed from the CHANGED: lines in Step 4's return, OR `git diff --name-only`
git commit -m "<conventional-commit type>(<scope>): <one-line summary of task>
<2-3 line body if task spec warrants — typically just one line>
<If task_to_engineer is loaded for this task ID, prepend this trailer:>
Co-Authored-By: <engineer-name> <<engineer-email>>
Co-Authored-By: <coder trailer>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
"
Coder trailers:
- codex →
codex-cli (gpt-5.5) <noreply@openai.com> - gemini →
gemini-cli (default) <noreply@google.com> - sonnet →
Claude Sonnet 4.6 <noreply@anthropic.com>
Engineer trailer (only when allocations.md was loaded): synthesized from the assigned engineer's name. The em-feature-plan roster schema doesn't capture emails, so synthesize a placeholder: <lowercase-ascii-name>@team.local. Example: Co-Authored-By: Alice <alice@team.local>. For names with spaces (e.g. "Alice Smith"), use alice-smith@team.local. Git requires syntactically-valid emails in trailers; @team.local is a reserved-style placeholder that's syntactically valid and obviously fake. Users who want GitHub-linkable attribution can post-edit the commit (or extend the em-feature-plan roster schema with an email field in a future amendment). The engineer trailer is the first Co-Authored-By so the human attribution leads.
If git_strategy == "single squash commit at end on current branch": do NOT commit; instead, leave the files staged with git add and let Phase 4 do the squash. Engineer attribution in the squash commit is best-effort — list all unique engineers across all squashed tasks as Co-Authored-By trailers (deduplicated by name).
Per-task result (EM-internal structure, used to update the report)
After Steps 1-6 complete for a task, the EM aggregates all the dispatch outputs + its own inline-step decisions into this structure (then Edits the corresponding row in execution-report.md):
TASK: <task-id>
STATUS: SHIPPED | BLOCKED
OWNER: <engineer name if task_to_engineer loaded, else slot label like "engineer-1">
CODER: <codex | gemini | sonnet>
FILES: <count> changed (<comma-separated list>)
TESTS: <pass | fail (N failing)>
DEBATE_ROUNDS: <0 | 1 | 2> # how many reviewer dispatches ran in Step 2
VERIFY_RESTARTS: <0 | 1>
NOTES: <unresolved CONCERNs after round 2, coder trajectory changes, EM's debate notes>
BLOCKER: <reason, if BLOCKED — empty otherwise>
COMMIT_SHA: <sha from `git rev-parse HEAD` after Step 6, if SHIPPED — empty otherwise>
Fallback Chain Semantics
Run state: coder ∈ {codex, gemini, sonnet}, monotonic in degradation order. Initial: codex.
Detection patterns
| Coder | Detection (in subagent return) | Action |
|---|---|---|
| codex | stdout contains You've hit your usage limit or Upgrade to Pro (the codex-executor wrapper prepends QUOTA_EXCEEDED: for clean detection) |
Flip run state to gemini |
| gemini | stdout/stderr contains any of: quota, rate limit, quota exceeded, RESOURCE_EXHAUSTED, 429, daily limit, usage limit (the gemini-executor wrapper prepends QUOTA_EXCEEDED:) |
Flip run state to sonnet |
| sonnet | Agent dispatch returns is_error: true with a rate-limit message |
NO further fallback. Mark the task BLOCKED with reason sonnet quota exhausted, continue with subsequent tasks (they'll also be sonnet and may also fail; that's expected best-effort behavior) |
Run-wide degradation, not per-task probing
- Once a coder family hits quota in task X, the run state flips. All remaining tasks use the new coder. No per-task re-probe.
- Quota windows reset on hour/day boundaries; re-probing wastes API calls. Users who hit quota mid-run can re-invoke the skill in a new session to re-probe from codex.
Mid-task fallback restart
If the coder hits quota during Step 4 of the per-task pipeline (i.e. mid-execution of task X), the senior:
- Flips its local notion of the coder to the next family.
- RESTARTS from Step 2 (review) — because the new family's reviewer has a different rigor lens, the plan deserves a fresh debate cycle.
- The new debate gets ≤ 2 fresh rounds (the round counter resets — the rigor lens has changed).
- NOTES the trajectory transition in its return block.
Coder trajectory in the report
The report's header field Coder trajectory: records the path. Examples:
codex(no degradation)codex → gemini at T-3(degraded once at task T-3)codex → gemini at T-3 → sonnet at T-9(degraded twice)
Phase 3 — Staff-engineer review
ONE subagent call. Reviews the merged diff (not per-task).
Dispatch template
Agent(
description: "Staff engineer — feature review",
subagent_type: "general-purpose",
model: "opus",
prompt: """
repo: /Users/eslam/linkify/<repo-name>
feature_slug: <slug>
You are the STAFF ENGINEER. The EM dispatched you to review every change shipped during this autonomous run, across three lenses:
1. Bugs / Correctness — logical errors, race conditions, missing edge cases, broken invariants, untested paths, security issues
2. Simplification / Clean Code — dead code, premature abstractions, copy-paste, types that should be narrower, functions that should be smaller
3. Codebase Style — adherence to engineering-standards.md, naming, layering, imports, format choices not auto-fixed by linters
…(truncated)