Imported from DiologIR/diolog-plugins (
plugins/feature-spec-pipeline/skills/work/SKILL.md). Install upstream withnpx skills add DiologIR/diolog-plugins --skill work. Copyright stays with the author.
Feature Spec Worker (markdown specs)
Implement a planned feature spec inside an isolated git worktree, driven by dynamic ultracode workflows, and leave the branch local for human review — no remote PR.
You run in your current session as the orchestrator, using Read/Write/Edit/Glob/Grep/Bash and the Workflow tool. You use no issue-tracker MCP (Diolog Tasks or otherwise) and invoke no Agent SDK script. The spec markdown file replaces a tracker issue + comment thread: it carries the original feature description and the triage assumptions/answers, and you append your completion progress to it at the end.
Inputs
- A spec id (
DIO-0001). It should already have an implementation plan atdocs/plans/plan-<ID>.mdand a spec atdocs/specs/spec-<ID>.md(produced by/triageand/plan). If the plan file is missing, work from the spec's feature description + triage answers and flag the absence in the final progress section.
Throughout: <ID> is the uppercase id (e.g. DIO-0001, used in file names and the commit Resolves line); <id> is its lowercase form (e.g. dio-0001, used in the branch name).
Setup (do this before any phase)
- Determine the integration branch first — do NOT assume
staging. The base + rebase target is the branch this repo actually merges into. Detect it, don't hardcode: preferorigin/stagingif it exists, else the repo's default branch (git remote show origin | sed -n 's/.*HEAD branch: //p'— usuallymain, sometimesdevelop). Call itINT(e.g.origin/stagingororigin/main). A skill that hardcodesorigin/stagingin a repo that has no staging will silently branch from a stale localmainand skip the rebase — the classic "based on a month-old base, re-created an already-merged module" failure. Then create the worktree from the freshly-fetched tip ofINT:
(Ifgit fetch origin # refresh ALL integration refs, not just one git worktree add .worktrees/<ID> -b ai/<id> "$INT".worktrees/<ID>already exists, reuse it — but first confirm its base is not stale:git -C .worktrees/<ID> merge-base --is-ancestor "$INT" HEAD || echo "STALE BASE — rebase in Phase C".) LetWT` = the absolute path to that worktree. - Read the plan and spec from the main repo at the absolute
docs/plans/plan-<ID>.mdanddocs/specs/spec-<ID>.md(the worktree is branched fromINTand won't contain the untracked docs — read them from the main working tree). The plan is the build source of truth; the spec's## Feature description+ the latest## Triagesection (Assumptions and any human answers/edits) are the requirement source of truth. Human answers are authoritative decisions. - Mark the spec In Progress. In the main working tree, set the spec header
Status: In Progress(andLast updated), and update the ledger row's Status toIn Progress. - Do all implementation file edits and git commands inside the worktree (
WT) — use absolute paths orgit -C "$WT". When you spawn workflow subagents, give each the absolute worktree path and an explicit, disjoint file scope so their reads/writes/commands target this worktree and never collide. (The spec/plan/ledger docs are NOT code — leave them in the main working tree; don't commit them onto the feature branch.)
How you must run this — ultracode dynamic workflows
You are the orchestrator, not a single-pass implementer. Drive the work as a sequence of dynamic workflows, staying in control between phases (read each phase's result, then launch the next). Fanning subagents across the slices of a large plan, with review-and-fix loops, is the whole point.
A large, multi-slice plan is the expected input — decompose it across workflows and deliver it in full. Do NOT bail merely because the plan is large; size is what this approach exists to handle. Deliver every task and subfeature the plan asks for; a subfeature is dropped or deferred to a follow-up only when it has a genuine external (non-internal) dependency — an unbuilt upstream system, a missing credential/contract, or a product decision that is the human's to make. Anything you can resolve yourself (an internal dependency: it's fiddly, ambiguous, large, or lower-priority, but the code and analogues can settle it) you build now — never punt it for size, complexity, or "a human might prefer to decide." Stop the whole run only on genuine missing information — a real external dependency — that makes safe implementation impossible; then append a blocker note naming that dependency, build everything it does not block, and stop only the blocked slice. Never ship partial or stubbed work (CLAUDE.md guardrails).
Workflow fan-out limits (avoid throttling) — apply to EVERY phase below
When you use the Workflow tool to fan out subagents:
- Cap each wave at ≤4 concurrent agents. Batch a larger fan-out into sequential waves of ≤4 (e.g. process 14 slices as four waves; review 12 findings as three waves). Firing ~10+ agents at once trips a server-side rate limit ("temporarily limiting requests — not your usage limit") that fails most of the wave. In the workflow script, chunk the items and
awaiteach smallparallel(...)batch before the next — do not pass all items to oneparallel(). - Retry transient failures. If an agent's result is an "API Error / Rate limited / temporarily limiting requests" string (or
null), re-run it in a later small batch; never treat it as a real result or finding. - Prefer plain-text returns for long, file-reading subagents. Schema-forced agents that read many files often finish without emitting the structured output. Have each reader/reviewer return a fixed-shape markdown fragment, and reserve any
schemafor the single synthesis/aggregation step.
Run these phases in order; none may be skipped, and carry the work all the way through to Phase F — do not stop after implementation. Every phase A–F must actually run; the spec is not done until Phase F has completed.
Conformance checks (after Phase B and Phase C — not after every phase)
After B and after C, and before starting the next phase, check that phase's output against BOTH: (1) the implementation plan at docs/plans/plan-<ID>.md, and (2) the spec (docs/specs/spec-<ID>.md) — the original feature description + every triage answer (human corrections, assumptions). The question is narrow — did this phase drift from, drop, or half-build a stated requirement? — and it is answered with evidence (the clause, the file:line, the gate you actually ran), then fixed before advancing. Do NOT add re-read passes after A, E, or F: Phase A ends by producing its own checklist, Phase D is the comprehensive review, and Phase E re-verifies exactly the findings it fixed. Stacking further same-author re-reads adds cost without recall — current models self-check unprompted, and Anthropic's Opus 5 guidance is explicit that carried-over verification instructions cause over-verification. The tokens NOT spent re-reading here are what pay for the browser/measurement evidence Phase D requires. references/model-and-effort.md §6 draws the oracle-vs-re-read line in full.
A green gate is necessary but not sufficient — never mistake it for "it works." Typecheck plus a passing test suite do NOT prove a surface behaves correctly: a test that stubs the unit under test hides exactly the runtime breakage it appears to cover (this is the single most common way a broken feature ships past this review with an all-green gate). Therefore: (1) treat every new critical path — a persisted read/write round-trip, a sanitiser, an auth/scope/visibility check, an external adapter, a served page or endpoint — as unverified until the REAL (un-stubbed) path is exercised, whether by a test that runs the real code or a manual invocation; if the only way to show it works is to run it, run it. (2) A test that mocks the very thing it claims to verify does not count as coverage for that thing — flag it and add a real-path test. (3) Never report a gate as passed that you did not actually run — and an unverified critical path is a blocker, not a caveat (see Phase D).
Phase A — Understand & specify (workflow)
Fan out parallel reader subagents — one per plan slice / subsystem (backend module, schemas, chat orchestrator, settings, UI, etc.). Each reads the relevant existing code in WT, the plan steps it owns, and the spec requirements it must satisfy, and returns: exact files to create/modify, interfaces/contracts, the closest existing analogue, and the feature acceptance checks it fulfils. Synthesize into ONE dependency-ordered build spec: the ordered slice list, each slice's file set (disjoint across any slices run in parallel), and the requirements it covers. The build spec must exist before any code is written.
Produce the acceptance checklist up front, as part of the build spec — do NOT defer it to Phase D. Enumerate, as an explicit numbered checklist, every Acceptance Criterion, every Constraint & Decision, and every triage Assumption from the plan + spec (this is the Clause table, built now); and for every new user-facing capability, list the UI→producer wire it must complete end-to-end (this is the Reachability table, built now). Assign each checklist row to the slice that owns it, so each Phase B slice implements against explicit, verifiable success criteria (Karpathy "goal-driven") and ticks its rows. Building this list before code — rather than re-deriving it at review time — is what stops a requirement being silently dropped; a requirement re-derived independently at Phase D and again by a later /gap-fix produces two different lists, and the item on neither is exactly the "large missing requirement" gap-fix later surfaces. Carry this same checklist into Phase D and re-audit against it (do not regenerate a different one).
Phase B — Implement (workflow)
Build from the spec in dependency order. Parallelize ONLY file-disjoint slices — never two subagents editing the same file at once. Order: backend schemas/module/service/resolver first; pnpm graphql:codegen after schema changes; BFF + frontend after the GraphQL/BFF contracts exist. After each wave, a gate subagent runs the scoped pnpm typecheck / pnpm graphql:codegen / pnpm validate:graphql (the reference product's gates — substitute the target repo's own typecheck/codegen/validate commands) and reports failures; the next wave does not start until the gate is green. Production code only — no mocks, stubs, placeholders, or fallbacks. Commit the implementation in WT (stage only files you created/modified — never git add .).
The default executor for a plan-scoped slice is the Codex CLI — gpt-5.6-sol at medium reasoning effort — with the spec and plan as its context. The plan has already made the decisions; the executor types. You stay the orchestrator: you own the phases, the gates, the reviews, and every judgment call.
codex exec -C "$WT" -m gpt-5.6-sol -c model_reasoning_effort="medium" \
-s workspace-write --dangerously-bypass-hook-trust \
-o "$WT/.codex/last-<slice>.md" "<prompt>" < /dev/null
Four rules make this safe, and references/codex-cli.md (role R3) carries them in full — read it before the first invocation:
- Pass the spec and plan as ABSOLUTE main-tree paths. The docs are untracked and live in the main working tree; the worktree is branched from
INTand does not contain them. Under-C "$WT"a relativedocs/specs/spec-<ID>.mdresolves inside the worktree, finds nothing, and Codex quietly builds from the task description alone — a run that looks successful and is grounded in nothing. Use the same absolute paths in the prompt and in the hook harness, and have the run report one distinctive fact from the plan (its tier, its step count, this slice's file list) so you can confirm the read actually landed. - Install the re-context harness first, and verify it. Codex auto-compacts on a long slice, and a summarised spec is a diluted spec — the exact mechanism by which a long build drifts off its requirements. Generate the two hooks in
$WT/.codex/(aPostCompacthook that drops a flag, aPostToolUsehook that sees the flag and re-emitsspec-<ID>.md+plan-<ID>.mdverbatim as injected context) so the authoritative documents are back in context within one tool call of every compaction — as text Codex did not have to choose to re-read. The generator, the wire format (the emitted field ishookEventName, camelCase —hook_event_namefails the payload silently), and the pre-flight self-test are in the reference. Watch each run forhook: PostCompact … Completed; aFailedthere means nothing was injected and the run is drifting. Keep$WT/.codex/out of the commit. The prompt contract also carries an explicit "after any compaction, re-orient to the spec and plan" instruction — belt and braces, because this is the failure mode that silently wastes a whole slice. - Delegate only what the plan already decided. The never-delegate list in ship-fleet's
references/cursor-composer.md§"What to delegate" holds unchanged — no architecture or data-model decisions, no security-sensitive code (auth, secret custody, webhook signature verification, tenancy/authz boundaries, payment), no maker≠checker or idempotency logic, no provenance-honesty judgment, no contract-version changes, no cross-cutting refactors, no conflict resolution, no design work, nothing marked "investigate". One coherent plan step per invocation: many small runs beat one sprawling session — cheaper retries, cleaner verification, far less compaction. - Codex typed it; that is not the same as verified.
workspace-writehas no network, so you run the gates. Read the whole diff (revert out-of-scope hunks), run the repo gates, judge against spec/plan, and hold the slice to the self-certification bar below. One retry with the failure quoted; a second failure means you write the slice yourself and logcodex: reverted.
Before every invocation, check the repo opt-out — grep CLAUDE.md / AGENTS.md / ORCHESTRATOR.md for ANTHROPIC-ONLY, NO EXTERNAL MODEL CLIS, or external-model-clis: off. A hit means Claude writes the code and you log codex: opted out (<file>) → claude. It is a per-invocation check because it is the only kill-switch that can stop a run already in flight, and because delegating implementation is also egress: the executor transmits the spec, the plan, and every file it opens to OpenAI. If the Codex lane is unavailable for any other reason, the slice likewise routes back to Claude — never to another cheap lane, never dropped or deferred because the executor was down. Log the fallback (codex: unavailable → claude) so the accounting stays honest.
Each slice self-certifies before it reports done — "I edited these files" is not done. Every implementer subagent returns, for its slice: the checklist row(s) it satisfied at file:line, the real (non-test) caller that reaches its new code, and — for any critical seam it touched (a persisted read/write round-trip, an auth/scope/visibility gate, a sanitiser, an external adapter, a served page/endpoint) — the real-path exercise it actually ran and the observed result. A slice with an unwired seam or an un-exercised critical path is not done; it does not advance the wave, and you do not defer it to Phase D. This front-loads the checks that Phase D otherwise finds late — the point is that the fix happens where it's cheap.
Exercise the real path test-first, not after (Karpathy "goal-driven"). For a critical seam, write the real-path check first — an integration test that runs the un-stubbed code, or a recorded manual invocation — watch it fail, then implement until it passes. A check written after the code tends to encode the bug it was meant to catch; a green typecheck is not evidence the seam runs, so run it. A test that stubs the unit under test is not coverage for that unit (this is the single most common way runtime-broken code ships green).
Build surgically and simply (Karpathy "surgical" + "simplicity"). Each subagent touches only the files in its scope, matches the surrounding style, and does not drive-by refactor, reformat, or "improve" adjacent code — every changed line must trace to a checklist row. Write the minimum code that satisfies the row: no speculative abstraction, config, or flexibility the spec didn't ask for; no error handling for impossible states (if 200 lines could be 50, write 50). This matters more under fan-out — N subagents each over-building or restyling its slice yields a bloated, conflict-prone diff that buries the real change and slows every downstream gate and review.
Wire-through gate (runs alongside typecheck after the FRONTEND/BFF wave) — a green typecheck does NOT prove a new endpoint is reachable. For every new backend endpoint, exported client/BFF function, and action-seam field this slice added, grep the diff for a real, non-test caller: a new API route with no BFF caller, a client function whose only references are its own definition + a test, or an action-seam field (onX/actions.x) the host never supplies is dead-on-arrival — fail the wave and wire it before proceeding, do not defer it to Phase D. This is the single most common way a "complete" backend ships with an inert UI: the frontend still calls the old stub, or the new action was added to the type but never populated in the host, and it all type-checks because the seam is optional (actions?.x?.() ?? fallback).
Affected-test sweep (mandatory, mechanical — run it with the wire-through gate). Grep the repo's test trees (e2e/, *.spec.*, *.test.*) for every route, component name, user-visible string, and behaviour this branch changes or inverts. Every hit is in scope: update it to the new contract and RUN it. A spec that asserts the behaviour you are removing is part of your diff — leaving it red, or fixme'd asserting the old world, is shipping a broken test. For every behavioural requirement, produce the regression-discrimination proof: the updated/added test shown failing against the pre-change code and passing after — record both shas for the progress note's Tests row.
Phase C — Rebase onto the integration branch
git -C "$WT" fetch origin, then rebase ai/<id> onto the current tip of INT (the integration branch you resolved in Setup — origin/staging or the repo's real default) and resolve every conflict faithfully — integrate both sides, never drop existing work or your own. This phase is mandatory even when Setup looked clean: a branch built on a stale base will silently duplicate work that landed on INT in the meantime (a re-created module, a second copy of a shipped collection) — the rebase is where that collision surfaces, so never skip it because "it compiles." Verify the base was current: if git merge-base --is-ancestor "$INT" HEAD was false before the rebase, call that out. Re-run the typecheck/build gate to confirm the integration compiles. Do NOT push.
Phase D — Acceptance review vs the spec (workflow)
Ground the review in two oracles BEFORE you fan out, and emit BOTH as tables — "I reviewed it" is not falsifiable; a filled table is. The biggest class of shipped defects is a clause the plan already stated and the review never checked, or a capability whose backend exists but whose UI never calls it. Start from the acceptance checklist you built in Phase A — do not regenerate a different list — and fill in each row's satisfying file:line; the completeness critic (below) is what catches any clause Phase A itself missed. On a resumed or multi-session run, rebuild the checklist from the FULL spec + plan (every clause, every kind/variant on the branch), never from a previous session's progress note — scoping the audit to "what this run built" is exactly how a whole vertical ships unreachable or a spec-promised mechanism (a review tier, a measurement, a guard) quietly disappears across sessions. Emit both tables (they go into the Phase F Progress note verbatim, and the In Review transition is blocked until every row is satisfied — a shallow "found N findings" with no tables is not a completed Phase D):
- Clause table (fill in the Phase A checklist, do not rebuild it — this is not optional). For every Acceptance Criterion, Constraint & Decision, and triage Assumption on the carried-forward checklist, name the exact
file:linethat satisfies it — or file a finding at that clause's severity. A clause with no code, or code that only partly honours it, is the finding: the give-away words to verify literally are ones like "enforced server-side", "Owner-only", "hash-only + one-time reveal", "de-duped per list", "re-validated on the way out", "setsList-Unsubscribe", "separate publish vs schedule step", "never fabricated / honest degradation", "propose-never-apply", "maker ≠ checker". These are the invariants an implementer most often half-builds; the plan handed you the checklist. When the plan carries a Parity inventory (a replacement/parallel path for an existing flow), every keep/port row in it is a clause: verify the new path actually preserves that guard/reconciliation/metering behaviour at a namedfile:line— the old path having it is not evidence the new one does. - Reachability table (the antidote to a backend that's built but never called). For EVERY new user-facing capability, trace and name the
file:lineat each hop: UI entry point → host action/handler → BFF/client fn → API route → producer method → back. A row with a missing hop (an API route with no BFF caller, a client fn whose only references are itself + a test, an action-seam field the host never populates) is an automatic Critical, not a Medium — it is a dead-on-arrival feature that type-checks. Do not accept "the endpoint exists" as coverage; require the caller. Reachability covers every contract arm delivered, not just the headline flow: every enum value, artifact/job kind, switch case, and schema variant the branch adds gets its own row, traced to a real in-product producer that can actually emit/trigger it (akind:'deck'apply rail whose only trigger is an external test harness has a missing hop). A variant deliberately built ahead of its entry point must be declared producer-less/deferred in the progress note — never silently counted as "delivered". - Adapt the dimensions to THIS repo's real invariants. The dimension list below is a default; its specifics (the
promptscollection, the AI gateway, GraphQL codegen, MNPI, citation-tag stripping) are specific to one reference product (an IR app), shown only as a worked example. For any target repo, replace them with that repo's own load-bearing invariants, read from itsCLAUDE.md+ the plan's Constraints & Decisions. (E.g. a studio whose DNA is "agents propose, never apply · maker≠checker · honest degradation, never fabricated · secrets in the Vault · outbound only via the send choke point · every tenant read/write company-scoped" must be audited against those words — not GraphQL/MNPI.) Reviewing the wrong invariant list is exactly how a Critical governance bypass sails past an all-green Phase D.
Fan out parallel reviewer subagents auditing the implemented worktree code against the spec's original feature description + every triage answer (especially human corrections and any UI amendment), the plan, and any UI mocks. One reviewer per dimension (substitute the repo's real invariants per the note above): (1) requirement completeness — every functional + UI requirement AND every plan Acceptance Criterion fully implemented, not partial or stubbed; (2) correctness — bugs, data flow, edge cases; (3) guardrails — no mocks/stubs/fallbacks; the repo's AI/prompt/secret rules; auth/BFF patterns; every authorization, ownership, attribution, and governance-gate decision enforced server-side at READ and WRITE (never on a client-supplied value); (4) UI fidelity — copy, badge labels, states, design rules vs the mocks; (5) security — visibility leakage, multi-company/tenant isolation, secrets, injection + untrusted input; (6) simplicity & surgical diff (Karpathy) — no speculative abstraction, dead scaffolding, unrequested config/flexibility, or bloat (200 lines where 50 would do); no drive-by refactor, restyle, or edit to code outside the slice's scope; every changed line traces to a spec/plan clause. Each reviewer returns findings tagged Critical / High / Medium / Low with file:line and the exact spec/plan/mock clause violated (for dimension 6, the bloat/out-of-scope change). Then adversarially verify — aimed, not blanket. Independent verifier subagents confirm a finding is real against the actual code; that is a precision filter, so spend it where a false positive is expensive: every Critical, every finding whose fix is a structural change, and anything that would reverse a locked spec decision. A 1:1 verifier on every Low spends a whole agent to avoid a cheap edit, and current review passes already run at high precision — so trimming the blanket pass is safe, trimming the Criticals is not. Never tell a reviewer to be conservative or to report only serious findings: that instruction is followed literally and lowers recall. Report everything, then filter here.
A prior "gap-fix" / self-review commit on the branch does not certify the code — run Phase D in full regardless. (The pattern that most often survives a self-review is a governance/authorization bypass, because the author trusts their own attribution.)
Exercise, don't just read — the miss-classes that survive a code-read + an all-green gate. These are the defects that repeatedly ship past this review because they type-check and pass a stubbed test suite. For each one the review must exercise the real path (call the endpoint, render the page, round-trip a real persisted doc, feed a hostile input), not merely read the code. An unverified critical path is a BLOCKER, not a finding: the status does not advance and no behavioural claim about that path may appear in any progress note. Claiming verification is environmentally impossible requires (1) the exact failing command and its output, and (2) a second, independent probe agreeing — a which <tool> miss is not evidence of "no browser" while the app answers HTTP and browser tools sit in your tool list. Record the blocker WITH its dissolution condition ("blocked until the branch is served"), and re-test the moment the condition clears — merging to the served branch clears it. A blocker that survives a context compaction must be re-verified before it is restated. Two further evidence rules: any visual clause ("wrong font", "off the boundary", "hidden behind") closes only on a getComputedStyle / getBoundingClientRect / elementFromPoint measurement from the rendered page — never a class string or "typecheck clean" (never derive a rendered fact from source; overrides get silently discarded); and any clause of the form "X is written / ingested / scheduled / sent" closes on the spec-validation bar — name the producer at file:line, then show a stored row / fired job / received message from a real run, or classify it AUTHORED/MOCK and file the finding. Use the repo's browser tooling (playwright / a browser MCP, per its CLAUDE.md) with the serving ladder: serve the worktree; else verify on the merged stack before posting behavioural claims; else the browser MCP. Check every miss-class explicitly:
- Compile-clean but runtime-broken — a boundary that type-checks yet throws at runtime: a persisted-doc read parsed against a schema whose types don't match what the store actually returns (e.g. a DB
Datevs a contract ISO string, anObjectIdvs a string), an identifier crossing a module boundary consumed under the wrong convention (an auth-provider subject likeauth0|…fed to an ObjectId cast; oneuserIdfield holding two formats depending on which path wrote it — verify each consumer's expected format against a REAL value from the producer), an env/config assumption, a "can't be null" that is. → Round-trip a real write→read and parse it; don't trust the types at the ORM/wire boundary. - Schema-parse-only "verification" of adapters/converters — a structural
safeParseon an adapter's output proves shape, not meaning: dropped theme/token resolution, unresolved references (accent1passed through as a literal CSS color), wrong units/scaling, and lost required-by-the-renderer context all parse green and render broken. → For every adapter/converter, assert semantic output values (resolved colors/labels/geometry/amounts) from a REAL production-shaped fixture, and where a renderer exists, render the adapted output and look at it. - Inert / not wired end-to-end — a UI affordance with no working handler, a form/picker with no submit path, a backend module no caller reaches, a client still calling the OLD stub instead of the new endpoint (dead-on-arrival backend), or a URL/link/header/callback emitted to an external party (an email
List-Unsubscribe, a redirect, a webhook, a formaction, a public share/verification link) that resolves to no route or the wrong host. → Trace every new user-facing capability from its UI entry point → BFF → API producer → back, and follow every emitted URL/endpoint to a live handler; grep for a new symbol whose only callers are itself or tests, and for a schema field / column / config key that is declared but never written or read — a dead field is almost always a half-built requirement (the "add via API key" that no path ever mints). - Mis-wired to the WRONG action — reachable, type-clean, semantically inverted. A handler whose name/label implies one effect but whose body calls a different endpoint: a
scheduleaction wired topublish, asaveDraftthat ignores the selected target and writes the first one, apreviewthat commits, acancelthat confirms. It passes typecheck and even "works" (something happens), so it survives a shallow read. → For every irreversible or external-egress action (publish/send/post/delete/pay), trace its name to the endpoint it actually invokes and confirm they match; aschedule→publishinversion is a Critical because it fires live, unintended egress. Also confirm a mutation targets the object the user is acting on (the composed/selected id), not a hardcoded[0]/first-in-list. - Optional-callback seams hide unwired code from the typechecker. A host seam like
actions?.x?.() ?? sampleFallback(orprops.onX?.(),data ?? SAMPLE) makes an unwired action both type-check and silently render sample/local behaviour — so the feature looks alive in the showcase and passes every gate while being inert in production. A green typecheck is therefore no evidence an optional action is wired. → For each optional seam, grep that the host actually provides the callback / real data; the fallback existing is not the feature working. - Hardcoded / fabricated data behind a real-looking UI — a component rendering a literal sample array, a success toast/return not backed by an actual call, a "ready/available/connected" state keyed off the wrong signal (an unrelated env var) instead of the real producer's presence, or a literal/constant returned inside a
live/successenvelope (a fixed score/count/status presented as measured). This is where the "no mocks/stubs/fallbacks" guardrail is most often violated silently — an honest-degradation contract requires an unmeasured value to be absent or explicitly sample-tagged, never a plausible constant. → Grep the diff for inline sample arrays in components, fabricated success returns, literals insidesource:"live"/ok responses, and honest-degradation gates tied to the wrong condition. - Untrusted-input surface not adversarially tested — anything served to the public, rendering/sanitising untrusted or model-generated content, or crossing a trust boundary (auth/scope, SSRF, injection). Assume any regex-based sanitiser is bypassable. → Feed known payloads (XSS handler variants incl. slash-delimited
<svg/onload=…>, CSS@import/url(https://…)beacons,javascript:/data:schemes, SSRF hosts, cross-tenant/scope-escalation) and confirm they are neutralised; prefer a fail-closed design (reject to a trusted default) over trusting the filter. - Client-asserted identity, authority, or gate-state — an authorization, ownership, attribution, or governance-gate decision (maker≠checker / four-eyes, "acting as", a role / plan / Owner gate, tenant/company, or an acknowledgement / consent flag) keyed off a request-supplied value — a body/query/header field like
approver,userId,role,companyId, orackFlag— instead of the authenticated principal or server-derived state. It type-checks, passes tests, and is the classic self-approve / privilege-escalation / spoofed-attribution bypass; it also survives a self-review because the author trusts their own client. → Grep the diff for identity/role/tenant/flag fields read from the request and then used in a comparison, an authZ branch, a "who approved" attribution, or a gate; require each to use the session's id/role, be fail-closed (unset ⇒ deny), and match the plan's privilege level (Owner-only vs any-member), not merely "some check exists". - Wrong-target / silently-capped mutation — a write that acts on a client-supplied target (a
listId/ownerIdin the body) rather than the persisted object's own bound field, or that silently truncates (a.limit(N)/ page cap) without returning a count /cappedsignal. → Confirm a mutation's target and audience come from the stored object, and that any cap surfacestotal/cappedrather than dropping data quietly. - Wrong-zone / boundary-value logic — time reasoned in UTC or the server-local zone instead of the participant's real IANA zone; DST gap/overlap; off-by-one; money/rounding; pagination/limit caps. → Test with a value that differs across the boundary (a non-UTC zone, a DST transition, an empty/oversized input), never just the happy path.
- Decision-reversal without the guard — a change that quietly reverses a locked spec decision (a zero-retention/read-only default, a visibility rule, a propose-never-apply gate) without the compensating opt-in/gate that decision requires.
Scale the review to the surface's trust level: public, security, persistence, and external-integration surfaces get more reviewers and a second verification pass. Criticals cluster — when the first pass finds any Critical, run another full audit round before trusting the result (a single shallow pass that reports "a handful of findings" on a large, high-trust surface is itself a red flag).
Run the completeness critic as the last reviewer — and run it OUT OF FAMILY, on the Codex CLI (gpt-5.6-sol, max effort, read-only). Its only job is to attack the audit itself: which acceptance-checklist rows were never matched to a file:line (or matched to one that doesn't actually satisfy the clause when you read it), which reachability hop was never traced, which critical seam was reviewed by reading but never exercised, which contract arm has no in-product producer, which dimension quietly returned "nothing" on a large surface?
This step is deliberately not a Claude subagent. Every other reviewer in Phase D is Claude auditing Claude's own build, and the critic exists precisely to catch what that family's blind spot lets through — an author-judged oracle is how a whole family's misses ship green. So it runs on a different model family:
codex exec -C "$WT" -m gpt-5.6-sol -c model_reasoning_effort="max" \
-s read-only -o /tmp/codex-critic-<ID>.md "<prompt>" < /dev/null
Give it the audit's own artifacts — it cannot critique an audit it cannot see: write the filled Clause + Reachability tables and the findings list to a temp file and name that file, the spec, and the plan in the prompt, all as absolute paths (with -C "$WT", a relative docs/specs/… resolves inside the worktree and finds nothing — the docs live in the main tree). The verbatim prompt (R2), the availability check, and the fallback are in references/codex-cli.md; follow it rather than improvising the invocation.
Its output is not a finding list — it is the seed for the next audit round: every item goes back through the reviewers, never straight into "resolved". A clean audit that the critic can poke a hole in is not clean; it is under-enumerated, and under-enumeration is exactly what a later /gap-fix converts into a "large missing requirement." A clean pass here is only meaningful because it came from outside the family that did the audit.
Bound it (perl -e 'alarm shift @ARGV; exec @ARGV' 600 codex exec …) and verify the wire (grep -qx "reasoning effort: max" on the captured log) — an over-scoped max run burns its turn budget and writes nothing, and a dropped effort flag silently inherits the user's config default.
If the Codex lane is unavailable or the repo opted out (no binary, not logged in, usage/rate limit, empty output file, deadline fired, repeated errors, or an ANTHROPIC-ONLY / NO EXTERNAL MODEL CLIS marker in CLAUDE.md/AGENTS.md/ORCHESTRATOR.md — re-checked per invocation, since that grep is the only kill-switch that reaches a run already in flight), fall back to a Claude strong-model completeness critic subagent with the same prompt and record it in the Phase F progress note. An in-family critic of in-family work is weaker evidence and the reader deserves to know which they got; an opted-out repo is a correct run, not a degraded one. Availability and the opt-out are the only licensed skips — skipping the critic outright is not an option.
Phase E — Resolve findings (workflow)
Fix every confirmed finding at all severity levels (Critical → Low), test-first where the finding is a bug (write the failing check that reproduces it, then make it pass — Karpathy "goal-driven"), and surgically (the fix touches only what the finding names; resist "while I'm here" cleanup). A mechanically-specified fix may go to the Codex executor on the same terms as Phase B (gpt-5.6-sol at medium, the re-context harness, the verify-fix loop) — but a finding whose diagnosis is the hard part, and anything on the never-delegate list (security, governance gates, identity/attribution, conflict resolution), you fix yourself. Parallelize file-disjoint fixes; serialize overlapping ones. Re-gate with typecheck / lint / validate, and re-run the specific evidence checks (measurement / exercised request / test) for each fixed row. Then run one targeted re-audit pass over the fixed items plus the out-of-family critic's seed items — not a fresh full Phase D, and not a loop that runs until reviewers go quiet: repeated same-author audit rounds add cost without recall (see the conformance-check note above), and the budget belongs on the real-path evidence. The one exception: when the first pass found Criticals on a large, high-trust surface (public / security / persistence / external integration), Criticals cluster — run one more full audit round there before trusting the result. Document any Low you intentionally defer.
Phase F — Finalize
Actually run the full gates (pnpm validate:all, pnpm validate:graphql, pnpm typecheck, pnpm lint — or the target repo's own equivalents, scoped sensibly) — never infer a pass. If a gate genuinely cannot run in the environment, record it as an explicit, prominent caveat in the progress note (a skipped gate is a known risk, never an implied pass — "typecheck + 15 stubbed tests passed" is not a substitute for the full gate). Commit any outstanding fixes in WT. Do NOT push and do NOT open a PR — the branch stays local in the worktree for human review. Append a completion progress section to the spec in the main working tree (docs/specs/spec-<ID>.md):
## Progress — <YYYY-MM-DD>
**Implementation Complete (local branch — no PR)**
**Summary:** <1-2 sentences on what was built>
**Branch:** `ai/<id>` (local, rebased on `<INT>` — the integration branch, e.g. `origin/staging` or `origin/main`; not pushed; worktree: .worktrees/<ID>)
**Built by slice:**
- <slice>: <files / what changed>
**Rebase:** <clean, or conflicts resolved in: file list; note if the base was stale>
**Reachability (every new capability reaches its producer):**
| Capability | UI entry | Host action | BFF/client | API route | Producer | Wired? |
|---|---|---|---|---|---|---|
| <capability> | `file:line` | `file:line` | `file:line` | `file:line` | `file:line` | ✅ / ✗ |
**Clause coverage (every Acceptance Criterion / Constraint / Assumption):**
| Clause | Kind | Evidence | Status |
|---|---|---|---|
| <clause> | static / visual / behavioural | see evidence rule | ✅ / ✗ |
**Tests:** <spec ids updated/added — each red@<sha-before> → green@<sha-after>> · existing specs asserting the old behaviour: <none found (patterns searched: …) | list, all updated + run>
**Acceptance review:** <N findings — Critical/High/Medium/Low counts> found and resolved.<any deferred Low items, with reason>
**Implementation assumptions:** <any ambiguity the spec/plan didn't pin down that you resolved yourself, one line each with the call you made — so a human can catch a wrong one; "none" if the spec fully determined it. Do not bury a silent pick (Karpathy "surface assumptions").>
**Dropped or changed vs spec/plan:** <every spec/plan-promised mechanism that was NOT delivered or was replaced — name the promise, what shipped instead, and why; "none" only if literally nothing was dropped or substituted. An undisclosed drop discovered later is a finding against this run, not a simplification.>
**Gates:** validate:all / validate:graphql / typecheck / lint / affected tests — <pass/fail (actually run, per Phase F)>
**Codex lane:** critic: <N seed items — M became confirmed findings> · exec: <N tasks, M retries, K reverted> — or `unavailable → claude` with the reason, per `references/codex-cli.md`
**Reviewing models:** <the wire-verified model per review gate — so REVIEWER ≥ WRITER is checkable from the artifact>
The evidence rule (what a Clause row may cite): a STATIC clause (naming, schema shape, copy in source, a config value) may close on file:line. A VISUAL clause closes only on a pasted measurement — getComputedStyle / getBoundingClientRect values, or a screenshot path — from the rendered page. A BEHAVIOURAL clause closes only on an exercised request→response (verbatim status + body fragment) or a named test shown red→green. "In the code and typecheck clean" is never evidence for a visual or behavioural clause. There is no partial status: a row without admissible evidence is ✗, and there is no "flagged rather than claimed" category — a row you cannot close is a blocker note, not a caveat.
Keep the note to its shape. The progress section is tables, counts, assumptions, drops, and gate results — the evidence a reader needs to trust or reject the run. It is not a narrative of the phases. Written output drifts long by default; the sections above are the budget, not a starting point (references/model-and-effort.md §7). Caveats propagate: every blocker/✗ in this note must appear verbatim in any later summary or merge record — a closing claim may never be stronger than the evidence table beneath it.
Reconcile the plan's ## Acceptance Criteria checkboxes (the plan file lives in the main working tree): tick each - [ ] you actually verified in this run; every box left unticked must appear in the progress note — as a blocker, a documented deferral, or a "Dropped or changed" row. A spec cannot read In Review-toward-done while its plan carries unticked, unmentioned AC boxes; that silent divergence is how "Done (Merged)" and "every AC unchecked" end up true at once.
Gate on the tables: do not set In Review while any Reachability or Clause row is not ✅. A row you cannot close is a blocker note naming the row, and the status stays put. A completed Phase F carries both tables filled; "N findings resolved" without them is an incomplete review, not a done spec.
Then set the spec header Status: In Review (and Last updated), and update the ledger row's Status to In Review. (Skip the status change only if already In Review or further downstream.)
Commit convention
<type>(<scope>): <summary under 72 chars> (types: feat, fix, refactor, chore, docs, test, perf), a short body, a Resolves <ID> line, and Co-Authored-By: Claude (AI Assistant) <noreply@anthropic.com>. Stage only files you created or modified — never git add ..
Guidelines
- Follow the target project's CLAUDE.md. Production-ready code only.
- Deliver what was asked, at the scope intended. Make routine judgment calls yourself; if the spec seems mistaken or a better approach exists, say so in a sentence and continue with the task as asked rather than quietly narrowing, widening, or transforming it. A change reaching beyond the spec's surfaces (a shared component, a global utility) is disclosed as its own line item in the progress note.
- Do NOT push the branch and do NOT open a PR. The work stays local in the worktree, committed and rebased on the integration branch (
INT), for human review. The only writes outside the worktree are the spec status + progress section and the ledger row, in the main working tree. - Every phase (A–F) is mandatory and must run to completion through Phase F; do not skip the spec phase, the rebase, the acceptance review, or the fix-resolution pass. Do not finalize (Phase F) until A–E have completed, the B/C conformance checks passed, and every clause row carries admissible evidence.
- Do NOT block on plan size — decompose and deliver. Block only on a genuine external (non-internal) dependency — a real missing input (unbuilt upstream system, missing credential/contract, or a human-only product decision) that makes safe implementation impossible: append a blocker note naming it, deliver everything it does not block, do NOT change status to
In Review, and stop only the blocked slice. A gap you can resolve from the codebase, analogues, or the safer default is internal — resolve it and keep building; never defer a subfeature for size, complexity, ambiguity, or priority. Never ship partial or stubbed work to dodge a block. - Model routing (cost note, made concrete) — and effort is the other half of it.
references/model-and-effort.mdis canonical: model sets the capability class,effortsets how much work happens inside it, an agent spawned without an explicit effort runs athigh, andlowis the level built for subagents. Two rules that change routing decisions: step effort down before you step model down (a strong model atlowstays in its capability class, so it keeps REVIEWER ≥ WRITER intact where a model downgrade would not); and hold a given agent's effort constant, because changing it forfeits the prompt-cache prefix. Heavy fan-out on the strongest model burns your interactive allowance fast — route each lane where theWorkflowtool allows a per-agent model override. Readers and gate-runners (Phase A readers, the typecheck/codegen/lint gate subagents) → the cheapest model (haiku). Phase D evidence lenses (UI fidelity, the clause table, the reachability table) and the adversarial finding-verifiers → a mid model (sonnet). Never downgrade: Phase A build-spec/acceptance-checklist synthesis, Phase C rebase-conflict resolution, and the security / guardrails / client-asserted-identity review lenses — these stay on the strongest model. The completeness critic doesn't downgrade either; it moves sideways, out of family, to Codexgpt-5.6-solatmaxeffort (Phase D). The invariant that makes the downgrades safe: REVIEWER ≥ WRITER — for every artifact, the strongest reviewer is at least as strong as the strongest model that wrote it. Mechanical Phase B/E implementation slices go to the Codex executor (gpt-5.6-solatmedium, the default — see Phase B andreferences/codex-cli.md); the older cheap-executor CLI lanes (Cursor composer-2.5 / glm-5.2 via zero) remain available for the same class of work under the same verify-fix loop, per-lane revert-rate kill-switch, and fail-back rule — their criteria and invocations live in the ship-fleet skill'sreferences/cursor-composer.md; follow it, don't re-derive it here. Any executor lane failing for any reason routes the work back to Claude, never to a sibling cheap lane and never silently skipped. - Sync note:
diolog-tasks-pipeline/skills/tasks-workeris this skill's Diolog-Tasks twin (tracker issue instead of spec file,origin/stagingfixed). This file carries the canonical phase text — when a phase here evolves, port the change there in its compact form.references/codex-cli.mdis the pipeline-wide canonical Codex reference (the triage and plan gates point at it too) — change it here, not in a copy.
References
references/model-and-effort.md— pipeline-wide canonical: the model × effort lane calibration (per-lane effort levels, effort-before-model, thexhigh+ 64k pairing, hold-effort-constant, never hardcode a dated model id), the oracle-checks-vs-re-reading distinction that says which verification still earns its tokens, and the length budget for every written artifact.triage,plan,gap-fix,tasks-*,ship-feature, andship-fleetall point here.references/codex-cli.md— the Codexgpt-5.6-sollane in full: the availability/auth check, the three roles (R1 spec/plan review atmax, R2 completeness critic atmax, R3 implementation executor atmedium), the verbatim prompt contracts, the post-compaction re-context hook harness + its self-test, the verify-fix loop, the fallback policy, and the accounting format. Read R3 before the first Phase B delegation and R2 before closing Phase D.