You are operating the goal-chain skill. A chain is a linear ordered list of goal slugs that execute one after another, gated by judge approval at each step.
Modes
The skill operates in one of three modes determined by args and state:
- Start mode —
argsis a non-empty path to a chain file. Begin a new chain. - Advance mode — invoked by the judge skill after approve. No args. Move cursor forward.
- Status mode —
args == "status"or chain exists and user asks plainly. Show progress.
Start mode
Triggered by /goal-chain "<path/to/chain.md>".
1. Parse chain file
The chain file is a markdown document with optional frontmatter and an ordered list of slugs. Accepted formats:
---
name: my-chain-name
---
1. first-goal-slug
2. second-goal-slug
3. third-goal-slug
Or with bullets:
- first-goal-slug
- second-goal-slug
Extract slugs from numbered list items or bullets. Trim whitespace. Strip any trailing comments after #.
2. Validate every slug has a contract
For each slug, verify .claude/goals/<slug>/contract.md exists. If any are missing, list them and stop. Tell the user to run /goal-prep for each missing slug, or remove them from the chain file.
3. Refuse to overlap
If .claude/goals/active.json shows an active goal, or .claude/goals/chain.json exists with status active, refuse and tell the user to /goal-clear first.
4. Write chain.json
Per the canonical chain.json shape (see goal.md "Canonical state shapes"):
{
"name": "<from frontmatter or filename>",
"slugs": ["...", "..."],
"cursor": 0,
"status": "active",
"started_at": "<ISO8601>",
"completed_at": null,
"source_file": "<absolute path to chain file>",
"link_approvals": []
}
link_approvals starts empty and accumulates one entry per judge-approved link as the chain advances.
5. Activate the first slug
Write .claude/goals/active.json per the canonical active shape with chain populated:
{
"slug": "<slugs[0]>",
"activated_at": "<ISO8601>",
"chain": "<chain name>"
}
Initialize that goal's state.json per the canonical state.json shape (status=active, rejection_count=0, started_at=now, started_at_commit=git rev-parse HEAD, started_at_dirty_paths=git status --porcelain, chain_step=1). Append to its log.md:
## <ISO8601> — activated (chain step 1/<N>)
Chain: <name>. Starting first goal: <slug>.
6. Spawn the executor subagent
v0.3+: chains run their per-goal execution in a fresh-context subagent instead of the main conversation. Main context only orchestrates — spawning executor, spawning judge, applying verdict, advancing cursor. This is the load-bearing change that lets a chain run autonomously across many goals without main-context aging out.
Use the Agent tool with subagent_type: general-purpose. Pass a self-contained prompt:
- The full
contract.md— verbatim. - The full
log.md— verbatim (so the executor sees the activation entry + any prior checkpoints if this is a re-spawn after judge rejection). - Chain context — chain name, current cursor position, total slugs, the prior link's approval timestamp.
- Repo state —
git rev-parse HEAD,git status --porcelain(first 20 lines). - The directive — verbatim, in this exact format:
You are the goalkeeper EXECUTOR SUBAGENT for goal `<slug>` (chain step <N>/<total>).
Your job: read the contract above, execute every Definition-of-Done item, run
the validator at the end, and return a structured summary. You operate in a
fresh context — you have no conversation history beyond this prompt. The
contract is your spec; do not improvise outside it.
Execution loop:
1. Read the contract carefully. Identify the implementation work required.
2. Do the work. Edit/write files as needed. Follow the contract's non-goals
and anti-placeholder rule strictly.
3. Append checkpoint entries to `.claude/goals/<slug>/log.md` as you go
(one per logical sub-task or every ~5 file edits — whichever is more
natural for the work).
4. Run the validator: `<validator.command>`. Capture stdout+stderr.
5. If validator FAILS: diagnose, fix, re-run. Repeat up to a reasonable
attempt budget (~3-5 inner attempts). If still failing, document why
in the log and return with status=blocked.
6. If validator PASSES: append a "validator passed" entry to the log,
update `.claude/goals/<slug>/state.json` with last_validator_result=pass
and last_checkpoint_at, then return.
DO NOT spawn the judge yourself. The chain orchestrator handles that step.
DO NOT advance the chain cursor. DO NOT modify chain.json, active.json, or
the goals_completed list. DO NOT activate the next goal. Just do the work
for THIS goal and return.
Return ONCE with this structured output:
STATUS: validator_pass | validator_fail | blocked | needs_clarification
SUMMARY:
<3-8 sentences describing what you did: files touched, decisions made, any
non-obvious tradeoffs, anything the judge should pay particular attention to>
VALIDATOR_OUTPUT_TAIL:
<last ~40 lines of the validator's stdout+stderr — let the judge see the
actual pass/fail signal directly>
FILES_CHANGED:
<bulleted list of paths modified relative to repo root, plus brief one-line
note per file about what changed>
BLOCKERS: (only if status != validator_pass)
<specific reason: which DoD item, which file, what's missing or wrong>
7. Receive executor return, spawn judge
When the executor subagent returns its structured summary:
-
STATUS = blocked or needs_clarification — append a
## <ISO8601> — executor blockedentry to the goal's log with the BLOCKERS verbatim. Setstate.status = needs_human. Pause chain (do NOT abort). Tell the user: "Executor subagent surfaced a blocker on<slug>: <one-line>. See.claude/goals/<slug>/log.md. Resolve and run/goal-resumeto re-spawn the executor." -
STATUS = validator_fail — same as blocked, but the surface message names the failing validator path so the user knows it's a test/lint issue specifically.
-
STATUS = validator_pass — spawn the judge per the
goal-judgeSKILL. Pass the executor's SUMMARY + VALIDATOR_OUTPUT_TAIL + FILES_CHANGED as part of the judge brief. The judge returns approve or reject.
8. Apply judge verdict
-
APPROVE: advance per existing Advance mode (mark state.status=done, append link_approval, increment cursor, branch on end-of-chain or next goal).
-
REJECT: the judge wrote a fix-list to the goal's log. Increment
state.rejection_count. If undermax_rejections, re-spawn the executor subagent with the original contract + log (which now includes the judge's fix-list) + an additional directive: "Address the judge's fix-list from the most recent## <ISO8601> — judge rejectedblock. Then proceed per the standard execution loop." Loop back to step 7. -
REJECT exceeding max_rejections: set
state.status = needs_human, do NOT advance, surface to user.
Advance mode
Triggered by the goal-judge skill after an approve verdict, when chain.json exists and contains the approved slug at the cursor.
Atomic write order matters. Follow these steps in this order so an interruption leaves a recoverable state (see "Recovery from interrupted advance" below):
1. Read chain.json and validate
Read cursor and slugs. Confirm slugs[cursor] matches the just-approved slug. If mismatch, abort and tell the user — chain state is corrupt; manual recovery needed.
2. Mark current goal done (and record link approval)
The just-approved goal: set state.status = done in its state.json. Leave its files in place (do NOT archive — chain artifacts stay for review).
The judge skill on approve already appended {"slug": <approved>, "approved_at": <ISO>} to chain.json.link_approvals before handing off. Verify the entry is present; if missing, append it now (defensive — handles the case where cha