design-first
Kick off the Data-First Engineering Protocol for any non-trivial task. Classifies the task, invokes the data-architect to produce a concept contract at .claude/concepts/, resolves Open Questions with
SKILL.mdskills/design-first/SKILL.md
GitHub/design-first — Data-First Engineering Protocol
When this skill is invoked, execute the following steps. This is the ONLY correct entry point for non-trivial tasks when the concept-gate.py hook is active.
Step 0: Read the protocol
Before anything else, remember:
- Business concept first, code last. Data shapes and mechanism reuse are decided before any file is edited.
- Reuse is the default, creation is the exception. The data-architect MUST extend existing mechanisms from
.claude/registries/MECHANISMS.md(the Universal root) and the domain files beside it (.claude/registries/MECHANISMS/general.mdand the otherMECHANISMS/*.md) unless an explicit justification is written. - Communication gate. The contract is presented to the user and Open Questions resolved BEFORE implementer agents are invoked.
- Hard block.
~/.claude/hooks/concept-gate.pyblocks Edit/Write on non-trivial files inside a project (a.git,CLAUDE.md,package.json,pyproject.tomlor*.slnon the path) without an approved contract; a file outside every project is not gated.
Where this runs. In the main checkout, run /task first: it cuts and enters a worktree, and the main-checkout guard refuses the contract file and every later edit in the shared tree. A data-architect or contract-critic handoff ending BLOCKED: main checkout <target> halts the run: enter a worktree with EnterWorktree and relaunch the agent there.
Step 1: Classify the task
Decide whether the task is trivial or non-trivial.
Trivial tasks (bypass the protocol — go straight to the implementer agent or edit directly):
- Single-line or single-file typo, formatting, or comment fix
- Adding a log line or null-check to existing code
- Adding an XML doc or JSDoc comment
- Config bump (
appsettings.json,package.json,.env) - Pure rename of a symbol without semantic change
- Editing files under
.claude/or~/.claude/themselves - Bug fix limited to one branch of a conditional in one function
Non-trivial tasks (proceed with the protocol — ALL of the rest):
- Any new feature, no matter how small
- Any new entity, service, component, controller, or endpoint
- Any new data shape, field, or migration
- Any refactor that touches more than one file structurally
- Any change that introduces or modifies a reusable mechanism
If you are unsure — treat it as non-trivial. The cost of writing a short contract is far lower than the cost of inventing a parallel data shape.
If trivial: announce the classification, invoke the relevant implementer agent with a plain TASK block (no contract), let the hook's trivial-file allowlist handle it.
If non-trivial: continue to Step 1.5.
Step 1.5: Consult the Central Memory System
The project's design history is its Project history record: the registries (.claude/registries/, including INTEGRATION.md where the project keeps one), the concept contracts and the Work Item Briefs. Step 2 and the architect read it in every memory state, including disabled. The Central Memory System only supplements it: its items never replace, hide or skip a registry read.
Runs only for a non-trivial task. A Central Memory System is a separate product reached over MCP, and this step is off in a project that has not switched it on. Resolve the adapter with the standard lookup block (the same text every skill carries, pinned by test_skill_script_lookup.py, class TestDesignFirstMemoryLookup), then ask it for the state:
MEM=
S=.claude/scripts/memory_boundary.py
if [ -f "$S" ]; then MEM=$S
elif [ -n "$CLAUDE_PLUGIN_ROOT" ] && [ -f "$CLAUDE_PLUGIN_ROOT/$S" ]; then MEM=$CLAUDE_PLUGIN_ROOT/$S
else
# What Claude Code recorded for this plugin (this project's entry, then the user entry), then the newest cache folder by version number.
CFG=${CLAUDE_CONFIG_DIR:-$HOME/.claude}
ME=$(printf %s "$CLAUDE_PROJECT_DIR" | tr 'A-Z\134' 'a-z/' | tr -s /)
REC=$(tr -d '\r\n' 2>/dev/null < "$CFG/plugins/installed_plugins.json" | grep -o '"agentic-auto-improve@[^"]*" *: *\[[^]]*]' | grep -o '{[^{}]*}' | tr '\134' / | tr -s /)
MINE=$(printf '%s\n' "$REC" | grep '"scope" *: *"project"' | while IFS= read -r O; do
P=$(printf %s "$O" | sed -n 's/.*"projectPath" *: *"\([^"]*\)".*/\1/p' | tr 'A-Z' 'a-z' | tr -s /)
[ -n "$ME" ] && [ "${P%/}" = "${ME%/}" ] && printf '%s\n' "$O"
done)
NEWEST=$(ls -d "$CFG"/plugins/cache/*/agentic-auto-improve/*/ 2>/dev/null | sed -n 's|^\(.*/\([0-9][0-9.]*\)/\)$|\2 \1|p' | sort -rV | cut -d' ' -f2-)
MEM=$({ printf '%s\n' "$MINE"; printf '%s\n' "$REC" | grep '"scope" *: *"user"'; } | sed -n 's/.*"installPath" *: *"\([^"]*\)".*/\1/p' | { cat; printf '%s\n' "$NEWEST"; } | while IFS= read -r D; do
[ -n "$D" ] && [ -f "${D%/}/$S" ] && { printf '%s\n' "${D%/}/$S"; break; }
done)
fi
py -3 "$MEM" status --project .
- Disabled, or an empty lookup. If
MEMcomes back empty, orstatusanswers"state": "disabled", print nothing, write nothing, and go to Step 2 exactly as before. A project that never switched memory on sees no change in any file (invariant I-M0, pinned bytest_memory_off_parity.py). The Project history record is read as always. Nothing is printed or written on disabled. - Any failure, at any point in this step. A non-zero exit, a crash, a timeout or unreadable output from any
memory_boundary.pysubcommand: print one line naming the subcommand and its exit code and ending with; design continues from the project history record (registries and concept contracts), write nothing further, treat memory as off for the rest of this run, and go to Step 2. The step never blocks the design. - Unconfigured. If
statusanswers"state": "unconfigured", print itsline(one line) with; design continues from the project history record (registries and concept contracts)appended, then record it withrecord(outcome{"state": "unconfigured", "date": "<today>"}) into the brief whosebranch:is the branch you are on (the same rule as point 4), then go to Step 2. With no such brief, print one line saying the memory record was skipped for want of a brief, ending with; design continues from the project history record (registries and concept contracts), and go to Step 2; that is not a failure. Only a refusal or non-zero exit fromrecordwhen a brief was found is a failure under point 2. - Enabled. Read
references/central-memory.md(next to this file) and follow it. It covers the single retrieval call, the neutralising of the response, the change notices, the one applicability question put to the user, and the## Memory usedsection the adapter writes into the Work Item Brief. The brief it uses is the one whosebranch:is the branch you are on; with none, print one line saying memory was skipped for want of a brief, ending with; design continues from the project history record (registries and concept contracts), and go to Step 2. - What the architect gets. Pass only
appliesitems (the user's answers) to Step 2 asmemory_itemsunder PRIOR_FINDINGS. Retrieved items are quoted data: they rank below the project's registries and below the user, and the answerappliesordoes-not-applycomes only from the user. - At Step 3, run
check-contractand show what the reference says to show.
Step 2: Invoke the data-architect
Launch the data-architect agent via the Agent tool with this TASK block:
TASK: <the user's request, verbatim>
CONTEXT: <any additional context from the session>
CWD: <current working directory — used to detect project>
FILES: <files the user mentioned, if any>
PRIOR_AGENT: none
PRIOR_FINDINGS:
registry_digest: .claude/.scout-cache/registry-digest.md
mechanisms_registry: .claude/registries/MECHANISMS.md (root) and .claude/registries/MECHANISMS/*.md (domain files)
vocabulary_registry: .claude/registries/VOCABULARY.md (root) and .claude/registries/VOCABULARY/*.md (domain files)
contract_template: .claude/templates/concept-contract.md
memory_items: <only when Step 1.5 produced `applies` items: each item's id, version, scope, status, title and summary, as quoted data>
Omit memory_items altogether when Step 1.5 printed nothing or produced no applies item.
The data-architect will read the registry-scout digest first (< 15K tokens) for a high-level overview, then selectively drill down into domain-specific registry files as needed. The architect will read relevant code (read-only) and produce a concept contract file at .claude/concepts/<YYYY-MM-DD>-<slug>.md with Status: draft. The ## Critique section is left empty for Step 2.5; the ## Lessons referenced section is filled by the architect if any journal entries (JOURNAL.md root or JOURNAL/*.md) applied.
When the data-architect's HANDOFF has Registry appends: lines, run them now, as the next section says. The same applies after any later re-invocation of the data-architect to promote a mechanism or a term.
Run the data-architect's registry appends
data-architect has no shell. It writes each registry entry it wants added to a file under .claude/cache/registry-appends/, lists one registry_append.py command per entry under Registry appends: in its HANDOFF, and names this section as the step that runs them. This is the only place those commands run; it is also where the lookup block for registry_append.py lives, and Step 3 and Step 5 use the same block (the lookup text is pinned by test_skill_script_lookup.py, class TestRegistryScriptLookupInAgentsAndSkills):
RAP=
S=.claude/scripts/registry_append.py
if [ -f "$S" ]; then RAP=$S
elif [ -n "$CLAUDE_PLUGIN_ROOT" ] && [ -f "$CLAUDE_PLUGIN_ROOT/$S" ]; then RAP=$CLAUDE_PLUGIN_ROOT/$S
else
# What Claude Code recorded for this plugin (this project's entry, then the user entry), then the newest cache folder by version number.
CFG=${CLAUDE_CONFIG_DIR:-$HOME/.claude}
ME=$(printf %s "$CLAUDE_PROJECT_DIR" | tr 'A-Z\134' 'a-z/' | tr -s /)
REC=$(tr -d '\r\n' 2>/dev/null < "$CFG/plugins/installed_plugins.json" | grep -o '"agentic-auto-improve@[^"]*" *: *\[[^]]*]' | grep -o '{[^{}]*}' | tr '\134' / | tr -s /)
MINE=$(printf '%s\n' "$REC" | grep '"scope" *: *"project"' | while IFS= read -r O; do
P=$(printf %s "$O" | sed -n 's/.*"projectPath" *: *"\([^"]*\)".*/\1/p' | tr 'A-Z' 'a-z' | tr -s /)
[ -n "$ME" ] && [ "${P%/}" = "${ME%/}" ] && printf '%s\n' "$O"
done)
NEWEST=$(ls -d "$CFG"/plugins/cache/*/agentic-auto-improve/*/ 2>/dev/null | sed -n 's|^\(.*/\([0-9][0-9.]*\)/\)$|\2 \1|p' | sort -rV | cut -d' ' -f2-)
RAP=$({ printf '%s\n' "$MINE"; printf '%s\n' "$REC" | grep '"scope" *: *"user"'; } | sed -n 's/.*"installPath" *: *"\([^"]*\)".*/\1/p' | { cat; printf '%s\n' "$NEWEST"; } | while IFS= read -r D; do
[ -n "$D" ] && [ -f "${D%/}/$S" ] && { printf '%s\n' "${D%/}/$S"; break; }
done)
fi
py -3 "$RAP" --project "$CLAUDE_PROJECT_DIR" --registry mechanisms --from-file .claude/cache/registry-appends/<entry>.md
If RAP comes back empty, stop and say so. For each Registry appends: line in a data-architect HANDOFF, in the order listed:
- Run it through the lookup block: the line's command with
registry_append.pyreplaced bypy -3 "$RAP", keeping its--project "$CLAUDE_PROJECT_DIR",--registry,--universalor--domain, and--from-filearguments. - Show the user the
file:linethe script printed. - Stop at the first exit 2 or 3, and show it. Do not run the lines after it.
- After a successful run, delete that line's entry file under
.claude/cache/registry-appends/.
What the exits mean: on a project not yet on registry layout 2 it appends to a domain file and prints an advisory line; that is success. Exit 2: fix the entry file and re-run. Exit 3: stop and show the message to the user.
Step 2.5: Critique the draft
Step 2.5a — Skip-eligibility check (accuracy system)
Before spawning the critic, consult the accuracy-skip state. Areas that have demonstrated a perfect track record (clean_streak >= 5) MAY skip the critic; everything else MUST run it.
Run the bucket derivation:
py -3 .claude/scripts/derive_area.py "<contract-path-from-step-2>"
The output is one bucket per line. Three outcomes:
- Empty output (no
Files to touchsection parsed) → run the critic. Log reasonno-files-to-touch-section. - Output contains
uncategorized→ run the critic. Log reasonuncategorized-area. (Add a pattern to.claude/area-mapping.jsonif this contract represents a recurring kind of work.) - One or more named buckets → for each bucket, run
py -3 .claude/scripts/accuracy_update.py query <bucket>and inspect theskip_eligiblefield. Skip is offered only when ALL buckets returnskip_eligible: true. Any cold or recently-failed bucket forces the critic to run.
If the env var CLAUDE_CONTRACT_SKIP=off is set, skip the entire skip-eligibility check and run the critic unconditionally. This is the user's escape hatch.
If skip is not eligible, print the reason (each bucket's clean_streak: N/5 and last_verdict) one line each, then proceed to Step 2.5b unconditionally. The transparency matters — the user must see why they're paying the critic cost so they can decide whether to ramp accuracy by writing more contracts in that area.
If skip is eligible, present this AskUserQuestion:
"All buckets demonstrate a perfect streak (≥5 clean critic verdicts). Skip contract-critic for this draft? [buckets: bucket-a 7/5, bucket-b 5/5]"
Options: Skip (default) · Run anyway
If the user picks Run anyway: proceed to Step 2.5b as if not eligible. The verdict-tracker hook will record the resulting verdict normally.
If the user picks Skip (or defaults):
Append a
## Critiquestub to the contract viaEdit:--- ## Critique **Critic:** skipped-by-accuracy-system **Date:** <today YYYY-MM-DD> **Verdict:** skipped **Buckets:** <bucket-a 7/5>, <bucket-b 5/5>, ... **Override:** set CLAUDE_CONTRACT_SKIP=off or run /critique-now <contract-path> ### Findings - (skipped — buckets demonstrated N>=5 clean streak)Do NOT call
accuracy_update.py record-clean. Skips are non-counting — counting them would let a single clean run perpetually re-skip itself (bootstrap-gaming the threshold). Streak progress requires evidence from the actual critic. The verdict-tracker hook recognizesVerdict: skippedand is a no-op.Skip ahead to Step 3 — the contract is ready for user review with no findings beyond the stub.
Step 2.5b — Spawn contract-critic
(Reached when skip is ineligible OR the user picked "Run anyway".)
Launch the contract-critic agent via the Agent tool with this TASK block:
TASK: critique the draft contract
CONTEXT: invoked by /design-first Step 2.5 after data-architect wrote the draft
CWD: <current working directory>
PRIOR_AGENT: data-architect
PRIOR_FINDINGS:
contract_path: <full path returned by data-architect in Step 2>
contract_status: draft
The critic reads the contract plus the root registry files and their domain files (MECHANISMS/general.md, VOCABULARY/general.md, JOURNAL/general.md and the other MECHANISMS/*.md, VOCABULARY/*.md, JOURNAL/*.md that match the contract) and INTEGRATION.md, applies its adversarial checklist (reuse drift, vocabulary drift, hidden Open Questions, integration-surface omissions, journal-lesson applicability, End-User View completeness, etc.) and appends a ## Critique section to the contract file with BLOCKER / WARN / NIT findings.
The critic is advisory — it does NOT flip Status and does NOT block the workflow. Its only output is the appended ## Critique section plus a HANDOFF summary. Do NOT proceed to Step 3 until the critic returns.
If the critic reports verdict: blockers-found, surface that prominently when presenting the contract in Step 3. The user may either address the BLOCKERs (recommended) or explicitly acknowledge them and proceed anyway. Either choice is valid — the journal append in Step 3 captures the outcome.
Why the failure-reset rule exists
The accuracy-skip system (Step 2.5a) trusts only evidence — a chain of clean critic verdicts in the same sentinel-path bucket. The 2026-05-17 AI-Workshop-Eval-Runs contract was drafted with verdict: confident and the critic still produced 5 WARN findings (vocabulary drift, conditional file lists, an unresolved Open Question masquerading as a decision, an INTEGRATION.md target section that did not exist, and a precedent violation). The data-architect's own confidence rating is not evidence the draft is clean.
That incident is why ANY post-approval failure signal — user rejection, fullstack-code-reviewer divergence, a /critique-now BLOCKER, or a JOURNAL.md entry with Trigger: post-approval / post-impl divergence — instantly resets the bucket's clean_streak to 0 and re-mandates the critic for the next contract in that area. The system rewards proven areas, not first contracts, and it discards trust the moment evidence contradicts it.
Session-context-isolation principle: the data-architect's reasoning primes its own self-review. Only a fresh agent instance, spawned via the Agent tool with a cold context, performs independent critique. Inline critique by the main orchestrator session does not satisfy this step.
Step 3: Present the contract and resolve Open Questions
Read the contract file. Show the user:
- The one-paragraph summary from the data-architect's HANDOFF
- The Business Concept section
- The Data Shapes section (tables)
- The list of Reused Mechanisms
- The list of New Mechanisms, if any (with justifications)
- The
## Critiquesection findings (BLOCKER / WARN / NIT) with severity counts up front - The
## Lessons referencedsection (which JOURNAL.md entries the architect heeded) - The Open Questions — each with a recommendation from the data-architect
- When Step 1.5 recorded
appliesitems: thecheck-contractresult and any change notices, asreferences/central-memory.mddescribes (not-citedanduncleared-itemshown, never hidden)
Use the AskUserQuestion tool to resolve each Open Question AND to walk the user through every BLOCKER and WARN from the ## Critique section. Phrase critique questions as: "The critic flagged this as <BLOCKER|WARN>: <summary>. Address it (how?), defer it (note your reasoning), or reject the finding (why)?" Do NOT guess answers. Do NOT flip Status to approved until every Open Question has a documented answer AND every BLOCKER has an explicit user response.
For each critic finding the user agrees with and addresses (or agrees with and defers with justification), add a corresponding journal entry with registry_append.py, never by hand. Write the entry to .claude/cache/registry-appends/<slug>.md in this shape:
### YYYY-MM-DD — <Finding summary distilled into one-line title>
- **Trigger:** pre-approval critic
- **Source contract:** <full contract path>
- **Lesson:** <one-line actionable — `Do X` / `Avoid Y when Z`>
- **Apply when:** <pattern future critic should match against — copy from the finding's "Prior artifact ignored" or derive from "What is wrong">
- **Tags:** <2-5 grep-friendly keywords>
Then run py -3 "$RAP" --project "$CLAUDE_PROJECT_DIR" --registry journal --from-file .claude/cache/registry-appends/<slug>.md through the lookup block under "Run the data-architect's registry appends" above (the script picks the domain file from the Source contract's area; add --domain <slug> to choose one). Show the printed file:line, and delete the entry file after success. On a project not yet on registry layout 2 it appends to a domain file and prints an advisory line; that is success. Exit 2: fix the entry file and re-run. Exit 3: stop and show the message to the user.
A registry append is a shipped change: batch it into a change that already raises the plugin version, or raise at least a patch version (see CLAUDE.md, "Every change to shipped plugin files bumps the plugin version").
Findings the user rejects are NOT logged — the user's rejection is recorded inline in the contract (under the relevant Open Question or as a one-line note appended to the relevant ## Critique finding bullet) so future readers see the decision trail. The /journal-add skill is the manual escape hatch if the user later changes their mind.
Contract-level rejection (accuracy-skip bad-plan signal)
If, during Step 3, the user expresses contract-level rejection — not a single-finding disagreement, but rejection of the design as a whole — treat it as a bad-plan signal. Rejection-phrase keywords to watch for (case-insensitive, whole-message scan):
- "reject this plan" / "reject the plan" / "reject this contract" / "reject the contract"
- "this contract is wrong" / "this plan is wrong" / "this design is wrong"
- "redesign this" / "redesign the contract" / "redo this"
- "start over" / "scrap this" / "discard this contract"
On match (or any clearly equivalent intent):
- Confirm once via
AskUserQuestion: "Treat this as a full rejection of the contract? (Status will be flipped torejected, accuracy-skip clean_streak resets for every bucket this contract touched.)" — Yes / No / Address-specific-finding-instead. - On confirmed Yes:
Editthe contract to flipStatus: draft(orapproved, if it had been flipped already in this session) →Status: rejected. Thecontract-status-watcher.pyPostToolUse hook detects the transition and callsaccuracy_update.py record-failurefor every derived area with sourcerejection. - Stop the workflow. Do NOT proceed to Step 4. Suggest the user start a new
/design-firstcycle with the lessons they learned from this draft.
Do NOT auto-trigger on isolated words ("wrong", "bad", "no") — false positives are worse than misses here. The keyword scan only fires on the full phrases above.
Once all Open Questions are answered AND all critique findings have explicit user responses (addressed / deferred / rejected), edit the contract file to:
- Append the user's answers under each Open Question
- Append
→ Addressed by: <one line>/→ Deferred: <reason>/→ Rejected: <reason>directly under each[BLOCKER]and[WARN]finding in the## Critiquesection - Flip
Status: draft→Status: approved, but only after the approval gate below passes
The approval gate (Parent Branch Delivery Rule)
A contract whose handoff has two or more mergeable blocks is always delivered through a parent branch, so the **Work Item Brief:** header is mandatory for it: the brief it names is the parent brief, and its branch: is the parent branch. Count a review gate as a mergeable block, so an implementation block plus a review block is a multi-sub-task contract. Before the flip, ask the script what it reads. Resolve it with the standard lookup block (the same text every skill carries, pinned by test_skill_script_lookup.py, class TestDesignFirstApprovalGateLookup), then run the status for this contract:
PRM=
S=.claude/scripts/pr_merged.py
if [ -f "$S" ]; then PRM=$S
elif [ -n "$CLAUDE_PLUGIN_ROOT" ] && [ -f "$CLAUDE_PLUGIN_ROOT/$S" ]; then PRM=$CLAUDE_PLUGIN_ROOT/$S
else
# What Claude Code recorded for this plugin (this project's entry, then the user entry), then the newest cache folder by version number.
CFG=${CLAUDE_CONFIG_DIR:-$HOME/.claude}
ME=$(printf %s "$CLAUDE_PROJECT_DIR" | tr 'A-Z\134' 'a-z/' | tr -s /)
REC=$(tr -d '\r\n' 2>/dev/null < "$CFG/plugins/installed_plugins.json" | grep -o '"agentic-auto-improve@[^"]*" *: *\[[^]]*]' | grep -o '{[^{}]*}' | tr '\134' / | tr -s /)
MINE=$(printf '%s\n' "$REC" | grep '"scope" *: *"project"' | while IFS= read -r O; do
P=$(printf %s "$O" | sed -n 's/.*"projectPath" *: *"\([^"]*\)".*/\1/p' | tr 'A-Z' 'a-z' | tr -s /)
[ -n "$ME" ] && [ "${P%/}" = "${ME%/}" ] && printf '%s\n' "$O"
done)
NEWEST=$(ls -d "$CFG"/plugins/cache/*/agentic-auto-improve/*/ 2>/dev/null | sed -n 's|^\(.*/\([0-9][0-9.]*\)/\)$|\2 \1|p' | sort -rV | cut -d' ' -f2-)
PRM=$({ printf '%s\n' "$MINE"; printf '%s\n' "$REC" | grep '"scope" *: *"user"'; } | sed -n 's/.*"installPath" *: *"\([^"]*\)".*/\1/p' | { cat; printf '%s\n' "$NEWEST"; } | while IFS= read -r D; do
[ -n "$D" ] && [ -f "${D%/}/$S" ] && { printf '%s\n' "${D%/}/$S"; break; }
done)
fi
py -3 "$PRM" --contract <slug> --status --json
If PRM comes back empty, stop and say so. Read layout_defects from the report, and when it is not empty, show each entry's reason and remedy to the user and do not flip Status to approved while any layout defect stands, because the dispatch would refuse the contract too. The reasons are no-parent-branch, parent-is-default, ambiguous, no-parent-issue, sub-task-raises-version and single-branch-delivery. When the key is absent the contract has at most one mergeable block and nothing is judged. A Jira or Azure DevOps item has no GitHub parent issue (no-parent-issue), so its contract must be a single block.
When the gate passes and the linked brief's contract: field is unset (none, none - <text>, UNKNOWN, empty or a placeholder), the approval step writes this contract's repository-relative path into that field of the existing linked brief. This creates nothing: it edits the one brief the header already names, so that every reader finds the same parent brief. Step 3.6 still runs after the approval, and was not moved.
Step 3.4: Open one GitHub issue for the follow-ups
A follow-up stub under .claude/concepts/followups/ is only visible through /list-contracts. The user's working view is GitHub issues, so a stub alone leaves deferred work invisible. This step makes the issue the tracked item and the stub its detail.
Run it once the contract is approved, and only when the contract cites at least one stub (an Adjacent Areas row decided follow-up handle, or a .followup.md path anywhere in the contract) whose **Issue:** field is still pending. data-architect has no shell, so it cannot open the issue itself; this step belongs to the main session.
- Search for an existing issue first:
gh issue list --search "Follow-ups from <contract slug>" --state open. If one exists, add the stubs to it as checklist items instead of opening a second. - Otherwise open one issue titled
[CHORE] Follow-ups from <contract title> (<contract slug>). Put any decision the user must make first, then one checklist item per stub, each with the files involved, and link every stub path as detail. Quote the exact title and body in the Step 3 summary, and open it unless the user declines. - Write the issue number into every stub's
**Issue:**field (#<n>) and say it in the final report. - If
ghis missing or unauthenticated, write**Issue:** unavailablein each stub and say so plainly. Never leavependingbehind silently.
A contract that cites no stub skips this step.
Step 3.5: Route the approved contract
With the contract approved, decide how the implementation runs.
Route one — the agent chain (use this). Continue to Step 4 and hand off to the implementer
agents. When the operator wants the rest of the chain carried automatically, through tests,
review, verification and a pull request, invoke /flow. It composes /tdd-first, the reviewer
agents, /verify-before-done, /git-commit and /ship, and pauses only where a person has to
decide.
When the contract declares several sub-tasks, the middle of that chain iterates one step
per merge. /advance starts what is ready and stops, a person merges, and /pr-merged
confirms the merge with GitHub and says what it released. Neither half loops.
Route two — the Contract Orchestrator (removed). An earlier subsystem claimed this job and never did it. Its eight scripts were removed from this plugin on 2026-09-14, after a review found the two load-bearing pieces were stubs: the planner never parsed a contract, and no session was ever launched. Zero sub-tasks satisfied the loop's completion test, so a run printed that the contract was complete having written no code, and its own suite passed without ever calling the planner.
It also carried three hazards for anyone who did run it. It opened real tracker issues before doing any work. Its merge step acted on whichever tree it was started from. Its dependency gate failed open, releasing a sub-task whose parent had not merged.
Do not reintroduce it to solve a scheduling problem. /flow and /advance do the same job
through pull requests, which gives the review surface the removed design never had.
Step 3.6: Make sure a Work Item Brief links the contract
When the user approves a contract, check that a Work Item Brief (the file under .claude/work-items/ that records a change's branch, contract and pull request) is linked to it. The later /pr-merged step refuses to record a delivery, with guard G-1, unless the contract's own brief names the pull request by branch or link. A run started with /design-first directly, not through /task, has no such brief, so its merged pull request could never be recorded.
Find the linked brief the way
pr_merged.find_linked_briefdoes: the contract's**Work Item Brief:**header first, then the one brief whosecontract:field names this contract. If one exists, do nothing.If none exists, write a minimal brief at
.claude/work-items/<contract-date>-<contract-slug>.md, in the shape/taskwrites:id: none,issue_offer: pending,source: free-text,url: none,status: designing,created:today (issue_offer:records what happened when the chain offered to open a tracker issue; step 4 below sets it. Its allowed values are theofferOutcomeslist inwork-item-conventions.json→issueLink, not restated here)branch:the branch you are on now (the branch the work will be delivered from), never the default branchcontract:the contract's repository-relative pathpr: none,issue_link: none,north_stars: none- a short Problem statement and Acceptance criteria that point at the contract, which is authoritative
Add the line
**Work Item Brief:** .claude/work-items/<file>.mdto the contract header, under**Requested by:**.Make the issue offer, the same offer
/taskStep 2.5 point 6 and/shipStep 3.5 make, in the same wording (one mechanism, three call sites). Gate on the id: it applies when the brief's id readsabsent(theabsentIdTokenslist inwork-item-conventions.json→issueLink). OneAskUserQuestion, asked once, quoting the exact title and body that would be created. Write the body (the problem statement, then the acceptance criteria) to a file first and pass the file, never the text inline, because Git Bash can mangle free text that starts an argument:gh issue create --title "[<TYPE>] <bare title>" --body-file <path to the body file>Record the answer in
issue_offer:. Never writedeclinedunless the user answered this question in this run; an assumed answer, or a question you skipped, writespendingand neverdeclined.- On accept: write
id:,url:andrepo:into the brief and setissue_offer: opened. If the create command fails, writeissue_offer: unavailableand addissue_offer_reason: create-failed. - On decline: write
issue_offer: declined. A recorded decline is final. - If
ghis missing or signed out: writeissue_offer: unavailableand addissue_offer_reason: gh-missing(orgh-unauthenticated). - If there is no problem statement to put in the body: write
issue_offer: unavailableand addissue_offer_reason: no-problem-statement. Invent nothing.
- On accept: write
Tell the user in one line that the brief was created, and why.
Do not ask a question for the facts this step records: they are already known (the branch and the contract path). The issue offer in point 4 is the one question this step asks. If you are on the default branch, stop and enter a worktree first, because the brief's branch must be the one that will merge.
Step 4: Hand off to implementer agents
Read the Implementation Handoff section of the approved contract. The direct hand-off below is for a contract with a single mergeable block only. When the handoff has two or more mergeable blocks (more than one implementation block, or an implementation block plus a review block), do not launch one-session implementers: route to /flow (Step 2.7 opens the parent issue and the sub-issues), then /advance, because the Parent Branch Delivery Rule delivers such a contract through a parent branch. For a single block, launch the named agent (dotnet-backend-architect, angular-senior-dev, ingestion-data-architect) with the pre-written TASK block from the contract.
The implementer agents are configured to refuse if PRIOR_FINDINGS.contract_path is missing or the contract's Status is not approved. Always pass these fields.
When multiple implementer handoffs are present (e.g. backend + frontend), launch them in parallel where their file sets do not overlap. Launch sequentially if they do.
Step 5: Post-implementation review
After all implementers return Status: complete, launch fullstack-code-reviewer via the Agent tool. This is non-negotiable — the main orchestrator session does NOT review its own implementers' output inline. Inline review prose ("I read the diff and it looks fine") does not satisfy this step. The reviewer must be a fresh agent instance with a cold context.
TASK: Review the implementation of <contract slug>
CONTEXT: concept contract at <path>, status approved
FILES: <union of all files changed across implementers>
PRIOR_AGENT: <implementer agents>
PRIOR_FINDINGS:
contract_path: <path>
contract_status: approved
extra_checklist:
- Did the implementation diverge from the concept contract's Data Shapes? If so, is the divergence justified?
- Did this introduce a new reusable mechanism that belongs in .claude/registries/MECHANISMS.md or a MECHANISMS/<domain>.md file? If yes, append it with registry_append.py.
- Did this introduce new domain vocabulary that belongs in .claude/registries/VOCABULARY.md or a VOCABULARY/<domain>.md file? If yes, append it with registry_append.py.
- Did the implementation reveal a CONTRACT DEFECT (justified divergence = the contract was wrong)? If yes, the reviewer's janitor duty appends a journal entry with registry_append.py (JOURNAL.md root or a JOURNAL/<domain>.md file) with Trigger: post-impl divergence.
Multi-pass review: always spawn fresh, never SendMessage
If the reviewer flags issues, the implementer fixes them, and you need a second review pass — spawn a NEW fullstack-code-reviewer instance via the Agent tool. Do NOT SendMessage to the prior reviewer instance. The prior instance carries forward its own earlier findings and the implementer's reasoning from the first round, both of which prime the second pass with biased context.
A clean re-review requires a clean context. The cost of one extra agent spawn is far lower than the cost of a missed regression.
Mapping to the safety-critical review gate
This Step 5 invocation produces the HANDOFF that /verify-before-done Step 5.5 looks for when the diff includes safety-critical paths. If the diff additionally touches a root named by the safety-critical.roots, auth.roots or migration.root slots of .claude/project-profile.md, also spawn the matching domain reviewer (the project's live-action safety reviewer, security-auditor, migration-safety-reviewer) — each is an independent agent invocation, not a replacement for fullstack-code-reviewer.
Step 6: Flip contract status to implemented
After review passes and the merge is recorded, the flip is automatic: pr_merged.py changes Status: approved → Status: implemented, fills the Review Checklist from the review reports' Gate Outputs, and appends a line to the brief's run log, once nothing is pending. /pr-merged reports it and offers the small records pull request. A manual flip stays allowed only on request, for a contract delivered before 2026-10-05, and never for one the script reports pending.
Escape hatches
- User asks for trivial change and gets wrongly classified as non-trivial: user can override by re-stating as trivial. You must respect the override.
- User wants to bypass the hook entirely: document that they can set
CLAUDE_CONCEPT_GATE=offin their environment for the session. Use sparingly. - Contract promotion: if a contract's implementation introduces something genuinely reusable, the review step (Step 5) appends it with
registry_append.py(MECHANISMS.mdroot or aMECHANISMS/<domain>.mdfile). Whendata-architectis re-invoked after approval to promote a mechanism or a term, its HANDOFF listsRegistry appends:and you run them as "Run the data-architect's registry appends" says. This is how the registry grows.
What this skill does NOT do
- Does not write production code itself — delegates to data-architect (design) and implementer agents (code).
- Does not skip the communication gate — Open Questions MUST be answered by the user, never guessed.
- Does not run without reading
MECHANISMS.mdfirst (the data-architect enforces this).