with one click
gate-check
// Run the project gate checks and report results. Use after completing any feature, before creating a PR, or to verify project health.
// Run the project gate checks and report results. Use after completing any feature, before creating a PR, or to verify project health.
[HINT] Download the complete skill directory including SKILL.md and all related files
| name | gate-check |
| description | Run the project gate checks and report results. Use after completing any feature, before creating a PR, or to verify project health. |
| argument-hint | [--fix to auto-fix lint issues] |
Run the project's gating checks and report a clear pass/fail for each.
Before running any commands:
docs/patterns.md ยง Tracker Mode Probe). If CLAUDE.md has no ## Task Tracking section, mode is none and tracker-mode hooks skip. If a tracker mode is active:
docs/ticket-binding.md. Decline exits cleanly with zero side effects.updatedAt and warn on mismatch against the value recorded at /implement start; do NOT run bidirectional AC sync resolution here.push_ac_toggle (capability permitting; missing capability degrades with a canonical-shape warning otherwise).
See docs/gate-check-tracker-mode.md for the full tracker-mode flow.Run these deterministic layout-invariant probes in addition to the normal gate:
Filename โ frontmatter convention (strict) โ for every specs/frs/**/*.md, resolve Provider once (same rule as /implement), parse the YAML frontmatter, and compute the expected base name via Provider.filenameFor(spec). Every base name must equal Provider.filenameFor(spec); any mismatch โ GATE FAILED naming the file, the actual basename, and the expected name.
Required frontmatter fields โ every FR file must have the mode-invariant Schema Q keys title, milestone, status, archived_at, tracker, created_at present. Missing a field โ GATE FAILED naming the file and field. The id: key is mode-conditional (required in mode: none, absent in tracker mode) and is enforced by probe 13 identity_mode_conditional, not here.
Stale lock scan โ list every .dpt-locks/<ulid> entry whose branch field names a merged-into-main or deleted branch. Each stale lock โ GATE PASSED WITH NOTES. Offer $ARGUMENTS --cleanup-stale-locks action that deletes them in a single commit.
Plan post-freeze edit scan โ for each specs/plan/<M#>.md with status: active + non-null frozen_at, scan git log --follow for commits to that path authored after frozen_at. Each post-freeze commit โ GATE PASSED WITH NOTES listing the SHA. No auto-revert โ user decides.
Stale release marker scan โ grep specs/requirements.md for markers of the form (in flight โ v<X.Y.Z>) or (planned โ v<X.Y.Z>). For each captured version, check whether CHANGELOG.md already contains a ## [X.Y.Z] header (i.e., that version has shipped). Every match where the CHANGELOG says shipped โ GATE PASSED WITH NOTES listing the stale marker + its line number + the shipped release so the operator can rewrite the overview to past-tense. Warn-only, never GATE FAILED โ prose drift shouldn't block the gate.
Per-milestone heading strip โ grep specs/technical-spec.md and specs/testing-spec.md for ^#{1,3} M\d+ (matches # M<N>:, ## <N>. M<N> โ โฆ, or any other milestone-framed heading). Any match โ GATE FAILED naming the file and line with a pointer to the cross-cutting spec hygiene rule (cross-cutting spec files carry zero per-milestone headings). Per-FR design / per-milestone narrative belongs in specs/frs/<name>.md or specs/plan/<M#>.md, not in the cross-cutting spec files.
Duplicate AC-prefix scan โ call acLint(specsDir) from adapters/_shared/src/ac_lint.ts. It walks every active specs/frs/*.md (excluding archive/), extracts each file's ## Acceptance Criteria section, and counts AC-<prefix>.<N> occurrences per file. Any count > 1 โ GATE FAILED naming the file + AC-<prefix>.<N> pair + occurrence count. The <prefix> is tracker-mode-aware (tracker ID or short-ULID tail), so both prefix shapes are checked by the same probe.
Ticket-state drift โ for every FR file under specs/frs/archive/ whose frontmatter has status: archived and a non-null tracker.<key> binding, resolve Provider once (same rule as /implement) and call Provider.getTicketStatus(<tracker-ref>). Assert the returned status matches the adapter's status_mapping.done canonical name. Every drifted ticket โ GATE FAILED reporting a row with the FR's ULID + tracker ID + observed vs. expected state so the operator can manually transition or rerun /implement. Skipped for mode: none โ LocalProvider.getTicketStatus returns the local-no-tracker sentinel and there's nothing to compare. The check catches the regression where /implement Phase 4 lands a commit + archives FRs but drops releaseLock, stranding the tracker at In Progress after the commit โ the write-path hardening landed earlier, and this probe is the read-side backstop.
Root spec hygiene โ call runRootHygiene(specsDir, pluginJsonPath) from adapters/_shared/src/root_hygiene.ts. It runs two sub-checks on the three root spec files (specs/requirements.md, specs/technical-spec.md, specs/testing-spec.md):
\bM\d+\b tokens, walks up to the containing ##/### heading, skips matches under the allowlist (Shipped milestones / Archived context / Shipped releases / Release notes / Release history), then reports any remaining match that resolves to an existing specs/plan/archive/M<N>.md. Each hit โ GATE FAILED with <file>:<line>: archived milestone M<N> in live-framing (heading "<title>").plugin.json version; parses requirements.md ยง1 Overview for Latest shipped release: vX.Y.Z and optional In-flight milestone: M<N> lines. Declared version must match plugin.json; in-flight milestone (if named) must resolve to a live specs/plan/M<N>.md, not the archive. Each drift โ GATE FAILED naming the specific line + observed vs. expected value. Existence-guarded: when .claude-plugin/plugin.json is absent (the common end-user case โ only Claude Code plugin projects carry that manifest), the version-match arm of this sub-check is skipped and emits an n/a row with the reason "no plugin manifest in this project โ probe skipped". RootHygieneReport.versionFreshnessSkipped carries the n/a marker; the milestone-leakage sub-check (a) still runs unchanged. When the manifest is present (toolkit self-run), this sub-check fires unchanged and gates on drift as before.Enforces the "root specs stay shape-only, current-only" invariant documented in docs/patterns.md ยง Root Spec Hygiene.
CLAUDE.md.template branch_template: hygiene โ existence-guarded: if plugins/dev-process-toolkit/templates/CLAUDE.md.template is absent (the common end-user case โ only the toolkit repo itself carries that path), this probe is skipped and emits an n/a row with the reason "toolkit template not present โ probe skipped". When the template is present (toolkit self-run), grep it for the literal substring branch_template:. Zero matches โ GATE FAILED with the standard hygiene-gate error shape, pointing to the template file and asking the operator to re-add the key documentation. This gate protects /setup step 7c's seeded-key contract: if the template silently loses its branch_template: documentation, downstream projects generated by /setup will have branch automation silently disabled even when the user wants it (the backward-compat reading is "absent โ disabled" โ the template's job is to advertise the key so users know it exists).
Tracker-mode ULID prose hygiene โ tracker mode only (skipped when mode: none). Grep specs/plan/*.md (active only, excluding archive/), the current release section of CHANGELOG.md (content after the topmost ## [X.Y.Z] heading and before the next ## [ heading), and README.md for the pattern fr_[0-9A-HJKMNP-TV-Z]{26} (the full ULID regex). Each hit โ GATE PASSED WITH NOTES listing <file>:<line> so the operator can rewrite the prose to use the tracker ID instead. Warn-only, never GATE FAILED โ pre-existing content shouldn't block merges. Skipped entirely in mode: none (the full 26-char ULID does not appear in user-facing prose there either โ the short-ULID tail is the human-facing form โ but the probe is tracker-mode-scoped to avoid any risk of false-positive chatter on projects that hand-wrote ULIDs into historical docs).
docs/README.md nav contract โ docs-mode only (skipped when readDocsConfig(CLAUDE.md) reports both user_facing_mode and packages_mode false). Call runNavContractProbe(projectRoot) from adapters/_shared/src/docs_nav_contract.ts. The probe (a) reads the ## Docs section to decide whether to run, (b) parses docs/README.md, and (c) asserts exactly four ##-level headings carrying the canonical {#tutorials}, {#how-to}, {#reference}, {#explanation} anchors, each with a relative link resolving to an existing file or directory. Missing anchor, extra ##-level heading, or broken subdirectory reference โ GATE FAILED, one note per violation in file:line โ reason shape, with the NFR-10 remedy:
/gate-check: docs/README.md nav contract violation.
Remedy: docs/README.md must contain exactly four ##-level headings with {#tutorials}, {#how-to}, {#reference}, {#explanation} anchors, each linking to an existing file or directory. Run /docs --full to regenerate the canonical tree.
Context: mode=<docs-mode>, skill=gate-check
Identity mode conditional โ call runIdentityModeConditionalProbe(projectRoot) from adapters/_shared/src/identity_mode_conditional.ts. The probe resolves the CLAUDE.md ## Task Tracking mode once, then walks every active specs/frs/*.md (archive excluded) and enforces the bimodal identity invariant: mode: none requires a valid id: fr_<26-char ULID> line; any tracker mode requires the id: line to be absent. Violations surface as file:line โ reason notes in NFR-10 canonical shape. Severity is error (any violation โ GATE FAILED) โ the prior warn-only severity flipped to error once /spec-write's tracker-mode template stopped emitting id:, removing the regression source. Zero runtime dep on ulid.ts โ the ULID regex is inlined so the probe never pulls the module it's enforcing boundaries around.
Ticket-state drift โ active side โ mirrors probe #8 symmetrically with two carve-outs. For every FR file under specs/frs/*.md (excluding archive/**) whose frontmatter has status: active AND a non-null tracker.<key> binding, resolve Provider once (same rule as /implement / probe #8) and call Provider.getTicketStatus(<tracker-ref>). Then read the FR's milestone plan state via readPlanTaskState(specsDir, milestone) from adapters/_shared/src/plan_task_state.ts and decide via activeTicketDriftPasses(summary, planTaskState, statusMapping, currentUser) from adapters/_shared/src/active_ticket_drift_predicate.ts. The composed predicate passes if (a) the ticket is in the status_mapping.in_progress lane AND assignee == currentUser, OR (b) the single-FR-clean (STE-151) exemption applies โ ticket=done, plan=active, plan has at least one [ ] task remaining, OR (c) the fully-checked-single-FR (STE-180) exemption applies โ ticket=done, plan=active, plan has zero [ ] tasks AND at least one task total. Both exemptions recognise the canonical mid-canonical-chain state where /implement <FR-id> Phase 4 Close transitioned the ticket to Done but the FR file intentionally stays status: active per the milestone-bulk-archive design (skills/implement/SKILL.md ยง Invocation forms). Truth table:
| FR status | Ticket status | Assignee | Plan tasks | Plan status | Outcome |
|---|---|---|---|---|---|
| active | in_progress | currentUser | any | any | pass |
| active | done | any | unchecked > 0 | active | pass (single-FR clean โ STE-151 carve-out) |
| active | done | any | all checked + total > 0 | active | pass + advisory (fully-checked โ STE-180 carve-out) |
| active | done | any | total = 0 | active | fail (empty/malformed plan, strict fallback) |
| active | done | any | any | missing | fail (strict fallback) |
| active | done | any | any | archived | vacuous (probe #27 owns) |
| active | backlog/etc. | any | any | any | fail (drift) |
| active | in_progress | != current | any | any | fail (drift) |
Advisory (STE-180 carve-out only). When the fully-checked branch fires, render exactly this advisory line in the gate-check report (severity = note, NOT GATE FAILED): M<N> plan fully checked but not archived โ run /spec-archive M<N> or /implement M<N> to close. The probe returns { ok: true, advisory: { kind: "milestone_ready_to_close", milestone: "M<N>" } } and the renderer formats it; carve-out provenance is logged via carve_out: fully_checked_milestone (vs. STE-151's carve_out: partial_milestone). One advisory per affected milestone, deduplicated โ if FRs A and B both target M5 and both fire the carve-out, render once mentioning M5.
Every drifted ticket โ GATE FAILED reporting a row with the FR's ULID + tracker ID + observed status vs. expected in_progress + observed assignee vs. expected currentUser so the operator can manually transition, reassign, or rerun /implement Phase 1 step 0.c. The failure-row shape is unchanged across the strict and relaxed branches โ callers see the same row format. Skipped for mode: none โ LocalProvider.getTicketStatus returns the local-no-tracker sentinel and there's nothing to compare. The probe is over observed tracker state, not over call-history: the already-ours claim shape (in_progress + currentUser) passes regardless of whether this session or a prior one called claimLock. Catches the regression where /implement Phase 1 step 0.c is skipped entirely (an early dogfood case had a ticket sitting at Backlog while implementation proceeded), the "forgot bulk archive before /ship-milestone" shape (the M23-class shape: Done ticket + plan with every task checked but total = 0 indicates malformed plan), and symmetrically completes the archive-side ticket-state probe. Both carve-outs strictly weaken the predicate โ every prior failing case where the M23-class drift was real still fails (Phase 1 step 0.c skipped, wrong assignee, missing plan, archived plan vacuous to probe #27).
Guessed tracker-ID scan โ for each specs/frs/*.md (active, non-archive) whose frontmatter has a bound tracker.<key>, parse every AC-<PREFIX>.<N> line (shape from acPrefix). Every <PREFIX> must equal the file's own tracker.<key> value. Mismatch โ GATE FAILED naming the file, the offending prefix used in an AC line, and the expected tracker ID. NFR-10 remedy: "AC prefix does not match the file's bound tracker โ did you draft with a guessed ID? Substitute via the <tracker-id> placeholder convention and re-save." Skipped for mode: none โ short-ULID prefixes are scanned by the existing ac_prefix duplicate-scan suite; the two scopes are mutually exclusive. Catches the regression where an FR is drafted with a guessed tracker number that doesn't match the allocator's return (origin: an early draft session narrated a specific unallocated ID before the Linear allocator responded).
Archive plan-status invariant โ call runArchivePlanStatusProbe(projectRoot) from adapters/_shared/src/archive_plan_status.ts. The probe walks every specs/plan/archive/M*.md and asserts frontmatter status: archived AND a non-null archived_at ISO-8601 string. Any drift โ GATE FAILED with a file:line โ reason note in NFR-10 canonical shape (observed vs. expected). Defense-in-depth โ an iteration-5 audit downgrade confirmed no live code path consumes archived plan status today (every specs/plan/ reader excludes archive/), but /implement Phase 4's plan-status flip prose plus this read-side probe close the door on future drift. Test coverage: tests/gate-check-archive-plan-status.test.ts per the probe-authoring contract.
setup-output-completeness โ call runSetupOutputCompletenessProbe(projectRoot) from adapters/_shared/src/setup_output_completeness.ts. If CLAUDE.md ## Task Tracking declares mode: <tracker> (โ none), .mcp.json MUST exist at project root with the corresponding mcpServers.<adapter> entry. Skipped when mode = none or CLAUDE.md is absent. Catches the smoke-test failure mode where /setup self-aborted on .mcp.json writes and silently moved on. Test coverage: tests/gate-check-setup-output-completeness.test.ts.
claudemd-docs-section-present โ call runClaudeMdDocsSectionProbe(projectRoot) from adapters/_shared/src/claudemd_docs_section.ts. If CLAUDE.md exists, it MUST contain a real (non-commented) ## Docs heading. Vacuous when CLAUDE.md is absent. Sibling probe to existing ## Task Tracking checks; closes the silent feature-drop where /docs becomes a no-op because /setup skipped emitting the section. Test coverage: tests/gate-check-claudemd-docs-section.test.ts.
setup-audit-section-presence โ call runSetupAuditSectionPresenceProbe(projectRoot) from adapters/_shared/src/setup_audit_section_presence.ts. If CLAUDE.md is toolkit-managed (carries the <!-- generated by /dev-process-toolkit:setup --> marker) AND any default-applied step outcome is detectable (branch_template: populated or ## Docs block present), the ## /setup audit section MUST exist. Vacuous on hand-written files (no marker) or fully-interactive runs that emitted no defaults. Test coverage: tests/gate-check-setup-audit-section-presence.test.ts.
bun-zero-match-placeholder โ call runBunZeroMatchPlaceholderProbe(projectRoot) from adapters/_shared/src/bun_zero_match_placeholder.ts. If bun.lock exists AND no *.test.ts file matches outside node_modules AND no source carries the marker comment Bun zero-match workaround, fail. The probe enforces the /setup step 2c scaffolding contract that prevents bun test's zero-match-exit-1 from killing the very first gate-check on a fresh Bun project. Vacuous on non-Bun projects (no bun.lock). Background: examples/bun-typescript.md. Test coverage: tests/gate-check-bun-zero-match-placeholder.test.ts.
task-tracking-canonical-keys โ call runTaskTrackingCanonicalKeysProbe(projectRoot) from adapters/_shared/src/task_tracking_canonical_keys.ts. Parses ## Task Tracking line-by-line; fails if any top-level key is outside the closed set {mode, mcp_server, jira_ac_field, branch_template}. Empty/whitespace lines and ### <Subsection> content (e.g. ### Linear) are scoped out โ tracker-specific metadata (project IDs, team names) belongs under sub-headings, not as Schema L keys. Vacuous when the section is absent (mode: none canonical form). Catches the smoke-test drift where /setup emitted non-canonical linear_* keys at the top level. Migration helper: scripts/migrate-task-tracking-canonical.ts (dry-run only, prints unified diff to stdout). Test coverage: tests/gate-check-task-tracking-canonical-keys.test.ts.
toolkit-bootstrap-committed โ call runToolkitBootstrapCommittedProbe(projectRoot) from adapters/_shared/src/toolkit_bootstrap_committed.ts. If CLAUDE.md is toolkit-managed (carries <!-- generated by /dev-process-toolkit:setup -->) AND the project is a git repository, every toolkit-managed file in the working tree MUST be committed (no ?? or M status in git status --porcelain). Scan-set widened by STE-179: CLAUDE.md, .claude/settings.json, .mcp.json, every active file under specs/ (specs/requirements.md, specs/technical-spec.md, specs/testing-spec.md, specs/plan/M*.md, specs/frs/*.md โ archive excluded). Owner skill by file: CLAUDE.md/settings/mcp โ /setup bootstrap; specs/* โ /spec-write Step 7a; per-FR feature files โ /implement Phase 4. Catches the regression where toolkit-skill outputs leak into a downstream commit. Vacuous when CLAUDE.md is absent, the file is hand-written (no marker), or the project is not a git repo. Test coverage: tests/gate-check-toolkit-bootstrap-committed.test.ts.
traceability-link-validity โ call runTraceabilityLinkValidityProbe(projectRoot) from adapters/_shared/src/traceability_link_validity.ts. Every frs/<id>.md reference (any link form: ](frs/X.md), ](./frs/X.md), bare path) in specs/requirements.md and any specs/plan/<M>.md must resolve to an existing file under specs/frs/<id>.md OR specs/frs/archive/<id>.md. Broken links (e.g. live-path link when the file moved to archive) โ GATE FAILED. Catches the smoke-test failure where /spec-archive moved an FR but the traceability matrix still pointed at the live path. Companion to /spec-archive's "Rewrite traceability links" step (adapters/_shared/src/spec_archive/rewrite_links.ts). Test coverage: tests/gate-check-traceability-link-validity.test.ts.
signature-strategy-honors-setup โ call runSignatureStrategyHonorsSetupProbe(projectRoot) from adapters/_shared/src/signature_strategy_honors_setup.ts. Reads the per-stack preferred signature-extraction strategy recorded by /setup at docs/.dpt-docs-toolchain.json and asserts each non-fallback recording โ typedoc, dart-analyzer, griffe โ still maps to a present toolchain via probeToolchains(). Vacuous when the config file is absent (projects whose /setup runs recorded no preference). Recorded regex-fallback entries skip cleanly โ there is no degradation possible. Recorded preferred tool gone missing โ GATE FAILED with file:line โ reason note plus the standard NFR-10 remedy "Re-install the missing toolchain or re-run /setup to update the recorded preference." Test coverage: tests/gate-check-signature-strategy-honors-setup.test.ts.
task-tracking-workspace-binding-present โ call runTaskTrackingWorkspaceBindingPresentProbe(projectRoot) from adapters/_shared/src/task_tracking_workspace_binding_present.ts. In tracker mode, the ## Task Tracking block MUST carry a populated ### Linear / ### Jira sub-section identifying the workspace binding (Linear: team + project; Jira: project). Vacuous when CLAUDE.md is absent, the section is absent, mode: none is explicit, or a custom (non-linear/non-jira) adapter is active. Sub-section absent OR required key missing OR value empty/whitespace-only โ GATE FAILED with file:line โ reason note plus NFR-10 canonical remedy pointing at scripts/migrate-task-tracking-add-workspace.ts. Catches the silent-landing trap where tickets created without a project field land outside the user's expected project board. Test coverage: tests/gate-check-task-tracking-workspace-binding-present.test.ts.
tracker-project-milestone-attached โ call runTrackerProjectMilestoneAttachedProbe(projectRoot, deps) from adapters/_shared/src/tracker_project_milestone_attached.ts. For each status: active FR with a tracker block, fetch the issue via deps.getIssue(<ticket-id>) (production wires this to mcp__linear__get_issue) and assert the issue's projectMilestone.name byte-equals the canonical local heading (parsed from specs/plan/M<N>.md's H1, anchor stripped). Vacuous when mode: none, the FR is archived, the FR has no tracker block, or the plan file is missing (probe #27 owns that diagnostic). Missing or mismatched binding โ GATE FAILED rendering both names byte-by-byte (em-dash drift visible) plus NFR-10 remedy pointing at /spec-write --rename-milestone M<N> for the rename-on-mismatch escape hatch.
Capability-gap downgrade (STE-194 + STE-214). When the FR's ## Notes section contains a word-bounded match of any of the three milestone-attach capability keys emitted by /spec-write per skills/spec-write/SKILL.md ยง Step 7 โ milestone_attach_skipped_adapter_limit (canonical), milestone_attach_unavailable (deprecated alias per STE-198, honored for one minor-version cycle), or milestone_create_required โ AND the ticket has no projectMilestone binding, the missing-binding outcome routes to ADVISORY (severity = note, NOT GATE FAILED) with the canonical prose milestone-attach skipped โ capability gap declared in FR Notes (<key-found>). Detection rule: case-sensitive, word-bounded match (\b<key>\b) scoped to the FR's ## Notes body โ substring matches (xmilestone_attach_skipped_adapter_limitX) and matches in other sections (## Acceptance Criteria, ## Technical Design) do NOT count. Mismatched bindings still hard-fail; the token only excuses absence, not divergence. Token-present + binding-present is PASS (binding wins; the token is informational). The advisory closes the smoke-driver false-positive surface where Linear-mode runs created tickets in a project with zero milestones by construction. Decision table:
## Notes contains any milestone-attach capability key | projectMilestone set on tracker | Probe #26 outcome |
|---|---|---|
| no | yes | PASS |
| no | no | GATE FAILED |
| yes | yes | PASS (binding wins; token informational) |
| yes | no | ADVISORY (capability-gap downgrade โ prose names the specific key found) |
Recognized key set: milestone_attach_skipped_adapter_limit (canonical), milestone_attach_unavailable (deprecated alias), milestone_create_required.
Skip-reason rendering (STE-238 AC-STE-238.8). When this probe skips for a vacuous cause (mode: none, FR archived, FR has no tracker: block, plan file missing), the rendered skip-reason MUST route through renderProbeSkipReason(...) from adapters/_shared/src/tracker_probe_skip_reason.ts and MUST NOT contain the literal substring require Linear MCP (the F9 LLM-paraphrase regression caught by /conformance-loop iteration 1, 2026-05-07). The helper produces an accurate cause-named line โ mode: none โ skipped โ \mode: none`โฆ; archived/no-tracker FR โ skipped โ active FR has no `tracker:` block (); etc. โ so the operator sees the structural cause, not a vendor-MCP paraphrase. Vendor-neutral by construction: the helper handles Linear and Jira identically. Unit-tested at adapters/_shared/src/tracker_probe_skip_reason.test.ts`.
Test coverage: tests/gate-check-tracker-project-milestone-attached.test.ts.
frontmatter-milestone-not-archived โ call runFrontmatterMilestoneNotArchivedProbe(projectRoot) from adapters/_shared/src/frontmatter_milestone_not_archived.ts. For each status: active FR file under specs/frs/, read frontmatter milestone: and check (a) if <specsDir>/plan/archive/<value>.md exists โ GATE FAILED with collision diagnostic (active FR pointing at archived milestone โ the post-edit catch for the partial-ls bug), (b) if no plan file exists โ GATE FAILED with orphan diagnostic, (c) if milestone: frontmatter missing โ GATE FAILED with malformed diagnostic. Active FRs whose milestone matches an active plan file pass. Archived FRs are vacuous (their milestone naturally matches their archived plan file by construction). Mode-agnostic โ milestone numbers are local-file-system identifiers, not tracker objects. Test coverage: tests/gate-check-frontmatter-milestone-not-archived.test.ts.
plan-verify-line-validity โ call runPlanVerifyLineValidityProbe(projectRoot) from adapters/_shared/src/plan_verify_line_validity.ts. Severity: warning (NotesOnly, never GATE FAILED). Walks every active specs/plan/M*.md (excluding archive/) and inspects verify: lines for path-shaped tokens (must include /) that don't resolve to an existing file in the project tree. Inline-backticked tokens are treated as prose references and skipped (so verify: smoke-test re-run shows no \tests/.placeholder.test.ts` reference leftis vacuous). Each unresolved path โ one note infile:line โ reasonshape with the standard NFR-10 remedy pointing at the cleanup helper atadapters/_shared/src/spec_archive/cleanup_plan_verify_lines.ts. Catches plan-file rot where /implementPhase 4 deleted a file but the verify line lived on. Test coverage:tests/gate-check-plan-verify-line-validity.test.ts`.
Negative-assertion exemptions (STE-217). Verify lines that deliberately reference a deleted/missing path to assert its absence are pre-processed through isNegativeAssertion(line) and skip the path-existence check entirely. Recognized patterns (any-match short-circuits):
returns "No such file or directory" โ POSIX file-not-found error message (case-insensitive on the message)! test -<flag> โ POSIX shell negation prefix at start of line OR after ; / && / || (the test keyword is case-sensitive)--non-existent โ flag substring indicating intentional absencedoes NOT exist โ natural-language negative assertion (case-insensitive on the verb)Example: verify: ls src/missing.ts returns "No such file or directory" is recognized as a negative assertion and produces no advisory. Future negative-assertion shapes are added by amending the isNegativeAssertion recognizer (single source of truth), not by ad-hoc match logic elsewhere.
requirements-md-no-placeholder โ call runRequirementsMdNoPlaceholderProbe(projectRoot) from adapters/_shared/src/requirements_md_no_placeholder.ts. Severity: warning (NotesOnly, never GATE FAILED). Scans specs/requirements.md for surviving placeholder shapes โ the legacy ### FR-N: [Feature Name] heading, the [Feature Name] literal, and the <tracker-id> literal in active content. HTML comments, fenced code blocks, and inline-backticked spans are exempt (those are documentation/example surfaces). Each surviving placeholder โ one note in file:line โ reason shape with the NFR-10 remedy pointing at the cross-cutting-scope rule (per-FR detail belongs in specs/frs/<id>.md, not in requirements.md). Catches the smoke-test failure mode where /setup scaffolded the FR-1 placeholder but the architecture moved per-FR detail to specs/frs/. Vacuous when specs/requirements.md is absent. Test coverage: tests/gate-check-requirements-md-no-placeholder.test.ts.
setup-bootstrap-commit-subject โ call runSetupBootstrapCommitSubjectProbe(projectRoot) from adapters/_shared/src/setup_bootstrap_commit_subject.ts. Scans the most recent chore: bootstrap dev-process-toolkit* commit on the current branch and asserts (a) subject is exactly chore: bootstrap dev-process-toolkit (no parenthesized version suffix), AND (b) body either has no Toolkit: line, OR carries exactly one line matching ^Toolkit: dev-process-toolkit v\d+\.\d+\.\d+$. Backwards-compat carve-out: commits authored before the FR ship date pass vacuously (covers downstream repos whose bootstrap commit predates this FR). Vacuous on non-git repos and when no chore: bootstrap dev-process-toolkit* commit exists. Test coverage: tests/setup-bootstrap-commit-subject.test.ts.
implement-invocation-grammar-doc โ call runImplementInvocationGrammarDocProbe(projectRoot) from adapters/_shared/src/implement_invocation_grammar_doc.ts. Documentation-existence probe. Verifies skills/implement/SKILL.md (when present) carries (a) a ## Invocation forms heading, (b) a comparison table with at least 6 rows (header + separator + one per phase 0โ5), (c) the Phase 5 row contains both silent-skip and runs it literals (case-insensitive). The probe enforces STE-181's documented user-visible contract that /implement <FR-id> and /implement M<N> diverge at Phase 5; missing or under-populated section โ GATE FAILED. Vacuous on projects that don't ship the toolkit's own SKILL.md. Test coverage: tests/gate-check-implement-invocation-grammar-doc.test.ts.
plan-file-single-milestone โ call runPlanFileSingleMilestoneProbe(projectRoot) from adapters/_shared/src/plan_file_single_milestone.ts. Severity: warning (NotesOnly, never GATE FAILED). Walks every specs/plan/M*.md (active + archive) and counts ^## M\d+: headings; >1 โ ADVISORY note in file:1 โ plan file carries N \## M:` headings; expected exactly 1shape. Catches the F3.2 bug from the 2026-05-04 Dart-lib smoke run where/setupcopied the un-trimmed multi-milestone template verbatim intospecs/plan/M1.md. Legacy multi-milestone files exist in user projects (the bug shape we're fixing) โ surfacing them as advisory rather than error gives operators the signal without blocking. /spec-writeupgrades the same condition to refusal on edits. Vacuous on projects with nospecs/plan/. Test coverage: tests/gate-check-plan-file-single-milestone.test.ts`. (STE-197)
plan-task-fr-coverage โ call runPlanTaskFrCoverageProbe(projectRoot) from adapters/_shared/src/plan_task_fr_coverage.ts. Severity: warning (NotesOnly, never GATE FAILED). Walks every active specs/plan/M*.md (excluding archive/), parses each **Tasks:** block, and asserts every [ ] task either has explicit inline link โ STE-NNN to an FR row, or its leading verb-phrase substring-matches a row title; tasks marked [deferred] are exempted. Each unbacked task โ ADVISORY note in file:line โ unchecked task has no backing FR row and no [deferred] marker: <task body> shape. Catches the F3.12 bug where /ship-milestone pre-flight walks FR rows but not task bullets and tags a release for a milestone whose plan still describes uncompleted scope. Vacuous on projects with no specs/plan/. Test coverage: tests/gate-check-plan-task-fr-coverage.test.ts. (STE-201)
mcp-config-shape โ call runMcpConfigShapeProbe(projectRoot) from adapters/_shared/src/mcp_config_shape.ts. Validates .mcp.json entries against Claude Code's MCP server-configuration schema; flags the F6 bug shape "transport": "streamable-http" (canonical replacement: "type": "http"). Severity: routed per file โ toolkit-shipped templates / docs (docs/setup-tracker-mode.md, templates/CLAUDE.md.template, skills/setup/SKILL.md) โ ERROR (we ship the schema; we ship it correctly); user-project .mcp.json โ ADVISORY (don't break existing repos with the legacy shape). Vacuous when neither toolkit files nor .mcp.json carry the bug shape. Test coverage: tests/gate-check-mcp-config-shape.test.ts. (STE-209)
setup-permissions-shape โ call runSetupPermissionsShapeProbe(projectRoot) from adapters/_shared/src/setup_permissions_shape.ts. Severity: advisory only. Walks user-project .claude/settings.json and flags glob-shaped Bash rules (Bash(<cmd> *) patterns); the harness denies any glob-shaped Bash rule when /setup writes the file on a fresh repo (F3b). Each glob โ ADVISORY note file:line โ reason. Migration is operator-driven via /setup --migrate; this probe is the read-side signal. Vacuous when .claude/settings.json is absent. Test coverage: tests/gate-check-mcp-config-shape.test.ts. (STE-209 AC-STE-209.6)
archive-frontmatter-coherent โ call runArchiveFrontmatterCoherentProbe(projectRoot) from adapters/_shared/src/archive_frontmatter_coherent.ts. Severity: error. Walks every specs/frs/archive/*.md and specs/plan/archive/*.md and asserts frontmatter status: archived AND archived_at: populated. Any file in archive/ with status: active or unset archived_at: โ GATE FAILED with file:1 โ reason note. Catches the F11 staging-order bug (an archive commit landing with un-flipped frontmatter because the editor wrote frontmatter BEFORE git mv). Sibling to existing probe #16 archive_plan_status (which walks ONLY plan archives and uses different message text); the two probes overlap on the plan-archive arm but the overlap is intentional defense-in-depth. Vacuous on fresh repos with no archive directories. Test coverage: tests/gate-check-archive-frontmatter-coherent.test.ts. (STE-210 AC-STE-210.4)
cross-cutting-spec-stale-file-refs โ call runCrossCuttingSpecStaleFileRefsProbe(projectRoot) from adapters/_shared/src/cross_cutting_spec_stale_file_refs.ts. Severity: warning (NotesOnly, never GATE FAILED). Walks specs/technical-spec.md and specs/testing-spec.md for path-references inside triple-backtick directory-tree fences whose tokens contain / (full-path tree leaves). Tokens that don't resolve to an existing path on disk surface as ADVISORY rows in file:line โ reason shape. Bare-basename tree leaves (no /) are skipped โ no parent context. Prose mentions outside fences are operator judgment surface and never flagged. Defense-in-depth read-side check for paths that bypass /implement Phase 4b's cross-cutting-spec propagation step (manual deletes, git rm, downstream toolkit consumers). Vacuous when both spec files are absent. Test coverage: tests/gate-check-cross-cutting-spec-stale-file-refs.test.ts. (STE-215 AC-STE-215.5)
auto-approve-marker-in-canonical-spawns โ call runAutoApproveMarkerProbe(projectRoot) from adapters/_shared/src/auto_approve_marker.ts. Severity: error. Globs plugins/dev-process-toolkit/skills/*/SKILL.md and .claude/skills/*/SKILL.md, finds every fenced ```bash block whose body contains a claude -p invocation paired with a heredoc-on-stdin (<<'TAG', <<TAG, <<${VAR}), and asserts the canonical marker line <dpt:auto-approve>v1</dpt:auto-approve> appears on its own line inside that fence. Each missing-marker fence โ GATE FAILED with file:line โ reason note in NFR-10 canonical shape. Non-prompt-bearing < /dev/null snippets (the /gate-check, /spec-review, /simplify reference snippets in /smoke-test SKILL.md) are intentionally out of scope โ those skills carry no operator-approval gate, so a marker would be redundant. Catches the regression where parent skills (/smoke-test, /conformance-loop) silently drop the marker from a documented spawn snippet, leaving downstream claude -p runs to halt at child-skill gates. Vacuous on repos that ship no SKILL.md files. Test coverage: tests/gate-check-auto-approve-marker.test.ts. (STE-226 AC-STE-226.5)
tdd-orchestrator-integrity โ call runTddOrchestratorIntegrityProbe(projectRoot) from adapters/_shared/src/tdd_orchestrator_integrity.ts. Severity: error. Asserts the load-bearing structural invariants of the multi-agent TDD orchestrator (STE-225): (a) the four skill paths exist (skills/tdd/SKILL.md + skills/tdd-write-test/SKILL.md + skills/tdd-implement/SKILL.md + skills/tdd-refactor/SKILL.md); (b) the three child skills carry context: fork; (c) each child's agent: field resolves to an existing agents/*.md; (d) each child carries user-invocable: false; (e) each subagent's tools field excludes Agent (plugin-bundled subagents must not nest-spawn). Probe does not assert specific allowed-tool composition beyond the Agent exclusion or specific prompt phrasing โ content drift is verified by the headless live smoke (AC-STE-225.9), not the probe. Vacuous only on repos with neither a plugins/dev-process-toolkit/skills/ nor a plugins/dev-process-toolkit/agents/ directory (the canonical no-plugin-content shape). When one directory exists but the other is absent, the probe runs and surfaces the asymmetry as violations โ that's intentional, not a bug: a repo with skills/ but no agents/ is a malformed plugin where the asymmetry IS the failure. Test coverage: tests/gate-check-tdd-orchestrator-integrity.test.ts. (STE-225 AC-STE-225.7)
needs_technical_review_consistency โ call runNeedsTechnicalReviewConsistencyProbe(projectRoot) from adapters/_shared/src/needs_technical_review_consistency.ts. Severity: error. Walks every active specs/frs/*.md (archive excluded) and enforces the bidirectional invariant on the needs_technical_review: frontmatter flag: when the flag is set to true, the ## Technical Design and ## Testing body sections MUST contain the canonical placeholder substring ([needs technical review โ run /spec-write โฆ]); when the flag is absent or false, those sections MUST be non-placeholder content (non-empty AND not containing the placeholder substring). Substring match โ not byte-exact โ so future copy edits of the placeholder don't break archived FRs. Each violation surfaces as a file:line โ reason note in NFR-10 canonical shape. Catches the smoke-test failure mode where /spec-write advertises the placeholder convention but lands an FR whose Technical Design / Testing sections drift from the flag value. Vacuous when specs/frs/ is absent. Test coverage: tests/needs-technical-review-consistency-probe.test.ts + tests/gate-check-needs-technical-review.test.ts. (STE-227 AC-STE-227.9)
spec_research_result_shape โ call runSpecResearchResultShapeProbe(projectRoot) from adapters/_shared/src/spec_research_result_shape.ts. Severity: error. Walks every recorded .dpt-locks/**/spec-research-result.txt log produced by the /dev-process-toolkit:spec-research forked skill (parent skills /brainstorm and /spec-write MAY persist the most recent subagent output for inspection). On each block, the probe asserts (a) the literal banner line > [historical reference โ decisions below may be stale; use as background, not authority] is present immediately above the fenced block, (b) the fence opens with \``spec-research-result, (c) exactly three ## headings appear inside the fence in the canonical order (## Related FRs, ## Prior Decisions, ## Reusable ACs / Patterns), and (d) the entire block (banner + opening fence + sections + closing fence) is โค 25 lines. Each violation surfaces as a file:line โ reasonnote in NFR-10 canonical shape. Vacuous when no result log is recorded โ the common no-invocation path. Sibling probe family: colocated with thecommit_producing_skill_branch_gateprobe added in M61. Test coverage:tests/gate-check-spec-research-result-shape.test.ts`. (STE-230 AC-STE-230.12)
requires_input_sentinel_coverage โ call runRequiresInputSentinelCoverageProbe(projectRoot) from adapters/_shared/src/requires_input_sentinel_coverage.ts. Severity: error. Globs plugins/dev-process-toolkit/skills/*/SKILL.md and .claude/skills/*/SKILL.md (matching probe #38's scope). For every skill body carrying a non-comment requires-input: annotation, asserts (a) a requireOrRefuse(...) reference (the canonical helper from adapters/_shared/src/requires_input.ts) AND (b) a relative-path citation of docs/auto-mode-protocol.md. Each missing check surfaces as a separate violation (so the operator sees both gaps in one report) in file:line โ reason shape with NFR-10 remedy. HTML-comment-scoped mentions of requires-input: are stripped before scanning so a doc-only mention does not falsely trigger the probe. Skills carrying no requires-input: annotation are vacuously out of scope. Catches the v2.13.0 incident shape where a requires-input: step bypassed structurally because the skill body did not route through the canonical helper. Test coverage: tests/gate-check-requires-input-sentinel-coverage.test.ts. (STE-232 AC-STE-232.5)
socratic_loop_uses_ask_user_question โ call runSocraticLoopUsesAskUserQuestionProbe(projectRoot) from adapters/_shared/src/socratic_loop_uses_ask_user_question.ts. Severity: error. Globs plugins/dev-process-toolkit/skills/*/SKILL.md and .claude/skills/*/SKILL.md (matching probe #38 / #42 scope). For every skill body that (a) cites Pattern 26 (substring match) OR (b) carries a socratic: true Schema-K frontmatter key, asserts (i) the body references the AskUserQuestion tool primitive AND (ii) the body cites the canonical protocol doc docs/auto-mode-protocol.md. Each missing check surfaces as a separate violation in file:line โ reason shape with NFR-10 remedy. HTML-comment-scoped Pattern-26 mentions are stripped before scanning. Forward-extension hook: any new skill that ships Pattern 26 prose or socratic: true is automatically picked up โ no manual list maintenance. Closes the magpie incident regression class structurally (gist https://gist.github.com/nesquikm/2904e50c7213b6aa392b998d4137f609, 2026-05-07, plugin v2.16.0). Test coverage: tests/gate-check-socratic-loop-uses-ask-user-question.test.ts. (STE-237 AC-STE-237.3)
closing_summary_capability_keys โ call runClosingSummaryCapabilityKeysProbe(projectRoot) from adapters/_shared/src/closing_summary_capability_keys.ts. Severity: error. Globs plugins/dev-process-toolkit/skills/{spec-write,gate-check,smoke-test}/SKILL.md and .claude/skills/{spec-write,gate-check,smoke-test}/SKILL.md. For every capability key in the canonical static map (sourced from /spec-write ยง 7), asserts the owner skill body carries a literal MUST emit \`directive (regex match on the documented directive shape โ backticks required; paraphrased prose like "the closing summary will mention <key>" does NOT satisfy the directive). Each missing directive surfaces as afile:line โ reasonnote with NFR-10 remedy. Modeled on probe #42 / probe #38 shape. Closes the/conformance-loopiteration 1 (2026-05-07, F1+F3+F4) systemic gap where SKILL.md prose specified byte-checkable capability rows but the runtime LLM emitted narrative prose, not the literal tokens. Test coverage:tests/gate-check-closing-summary-capability-keys.test.ts`. (STE-238 AC-STE-238.4)
socratic_first_turn_post_hoc_drift โ call runSocraticFirstTurnPostHocDriftProbe(projectRoot) from adapters/_shared/src/socratic_first_turn_post_hoc_drift.ts. Severity: error. Reads the latest commit on the active branch via git log -1 --format=%H%n%s%n%b. When the subject matches a canonical /spec-write shape (chore(specs): write FR <tracker-id> OR docs(specs): edit cross-cutting specs) AND the body carries NEITHER an audit-row marker (spec_write_draft_default_applied / spec_write_commit_default_applied) NOR a refusal NFR-10 block (Verdict: ... Refused shape โ case-insensitive on the verb), surface socratic_first_turn_post_hoc_drift_violation capability row and GATE FAILED with NFR-10 message naming the commit hash + offending subject. Vacuous on commits not matching the canonical subject patterns (anchored regex โ quoted mentions in revert subjects do not falsely activate). Vacuous on repos with no .git/ or no commits. Catches the F2 silent-commit shape captured by /conformance-loop iter-1 (commit 9b75b4b, 2026-05-08): under claude -p non-tty stdin the model fires AskUserQuestion (passes first-turn), sees "dismissed" responses, self-rationalizes "picked safe defaults" and lands a real commit + tracker writes without operator consent. The legitimate paths always leave one of the two markers in the body โ the audit row when default-apply is exercised, or the refusal NFR-10 block when the operator was surfaced the failure. Test coverage: tests/gate-check-socratic-first-turn-post-hoc-drift.test.ts + adapters/_shared/src/socratic_first_turn_post_hoc_drift.test.ts. (STE-251 AC-STE-251.3)
conformance-loop-bypass-removed โ call runConformanceLoopBypassRemovedProbe(projectRoot) from adapters/_shared/src/conformance_loop_bypass_removed.ts. Severity: error. Globs .claude/skills/{conformance-loop,smoke-test}/SKILL.md, finds every fenced ```bash block whose body contains a claude -p invocation, and asserts none carry --permission-mode bypassPermissions. Each offending fence โ GATE FAILED with file:line โ reason note in NFR-10 canonical shape (cites the offending bypassPermissions line, not the fence start, for actionable diffs). Vacuous on repos that don't ship the project-local .claude/skills/{conformance-loop,smoke-test}/SKILL.md files (toolkit consumers without the smoke harness). Catches the regression where a future edit reintroduces the blanket bypass at any reachable spawn site, defeating the audit-able policy artifact (permissions.allow block in tracked .claude/settings.json). Sibling probe: probe #38 auto-approve-marker-in-canonical-spawns enforces marker presence at the same fences; probe #46 enforces flag absence. Test coverage: tests/gate-check-conformance-loop-bypass-removed.test.ts. (STE-252 AC-STE-252.4)
Full details: docs/layout-reference.md ยง /gate-check.
After all conformance probes complete, render a single roll-up summary line in the canonical shape:
conformance-probes pass: <N>/<N> [<active> active, <vacuous> vacuous]
Where:
<N> is the total probe count (the same numerator and denominator unless a probe failed โ failures route through GATE FAILED above).<active> is the count of probes that ran with non-empty input and exercised at least one assertion against active content.<vacuous> is the count of probes that early-returned because their scope was empty (no active FRs to walk, mode: none skip, specs/ absent, plan file missing, etc.). Vacuous probes are still passing probes โ the count distinguishes them from active passes so the operator can tell at a glance whether the suite is exercising real content.An earlier Jira smoke run caught the previous shape 29/29 (most vacuous post-archive) โ the parenthetical wording was soft and operator-unparseable. The bracketed [N active, M vacuous] form is deterministic; the brackets are the parseable signal a CI step or smoke driver can scan for.
What counts as vacuous post-archive. When the only active FR is archived, the FR-traversal probes (probes that walk active specs/frs/*.md โ e.g., #1 filename-frontmatter convention, #2 required frontmatter, #7 duplicate AC-prefix, #8 ticket-state drift, #14 ticket-state drift active-side, #15 guessed tracker-id scan, #25 task-tracking workspace-binding, #26 tracker-project-milestone-attached, #27 frontmatter-milestone-not-archived) early-return as vacuous because there are no active FRs to walk. The counter must reflect this โ the same probe set that ran fully active before the archive becomes mostly vacuous after, and the summary line should make that visible to the operator.
Every new /gate-check probe ships with a corresponding
tests/gate-check-<slug>.test.ts test file. Self-review refuses a probe
declaration without its test โ a probe advertised in prose without an
integration test can drift from the implementation without detection. The
test must cover both a positive fixture (probe passes clean) and a
negative fixture (probe fires with the documented note shape:
file:line โ reason). Each probe above has a corresponding
tests/gate-check-<slug>.test.ts โ probe #14 (active-side
ticket-state drift) is covered by tests/gate-check-active-ticket-drift.test.ts,
probe #15 (guessed tracker-ID scan) by
tests/gate-check-guessed-tracker-id.test.ts, probe #16
(archive plan-status invariant) by
tests/gate-check-archive-plan-status.test.ts, probes 17โ23
(setup-output-completeness, claudemd-docs-section-present,
setup-audit-section-presence, bun-zero-match-placeholder,
task-tracking-canonical-keys, toolkit-bootstrap-committed,
traceability-link-validity) by their corresponding
tests/gate-check-<slug>.test.ts files, probe #24
(signature-strategy-honors-setup) by
tests/gate-check-signature-strategy-honors-setup.test.ts, probe
#25 (task-tracking-workspace-binding-present) by
tests/gate-check-task-tracking-workspace-binding-present.test.ts,
probe #26 (tracker-project-milestone-attached) by
tests/gate-check-tracker-project-milestone-attached.test.ts,
probe #27 (frontmatter-milestone-not-archived) by
tests/gate-check-frontmatter-milestone-not-archived.test.ts, and
probe #28 (plan-verify-line-validity) by
tests/gate-check-plan-verify-line-validity.test.ts, and
probe #29 (requirements-md-no-placeholder) by
tests/gate-check-requirements-md-no-placeholder.test.ts,
probe #30 (setup-bootstrap-commit-subject) by
tests/setup-bootstrap-commit-subject.test.ts,
probe #31 (implement-invocation-grammar-doc) by
tests/gate-check-implement-invocation-grammar-doc.test.ts,
probes #32 (plan-file-single-milestone, STE-197),
#33 (plan-task-fr-coverage, STE-201),
#34 (mcp-config-shape, STE-209),
#35 (setup-permissions-shape, STE-209),
#36 (archive-frontmatter-coherent, STE-210),
#37 (cross-cutting-spec-stale-file-refs, STE-215), and
#38 (auto-approve-marker-in-canonical-spawns, STE-226), and
#42 (requires-input-sentinel-coverage, STE-232) by their corresponding
tests/gate-check-<slug>.test.ts files (mcp-config-shape and
setup-permissions-shape share tests/gate-check-mcp-config-shape.test.ts;
auto-approve-marker lives at tests/gate-check-auto-approve-marker.test.ts;
requires-input-sentinel-coverage at
tests/gate-check-requires-input-sentinel-coverage.test.ts). Contributors
adding probe 43+ must ship the matching test file in the same commit.
Read the project's CLAUDE.md to find the gate check commands (look for "Key Commands" or "Gating rule" section). If no CLAUDE.md exists, ask the user what commands to run.
Typical commands by stack (use as fallback if CLAUDE.md doesn't specify):
npm run typecheck (or fvm flutter analyze, mypy ., etc.)npm run lint $ARGUMENTS (if $ARGUMENTS contains --fix, add -- --fix)npm run test (or fvm flutter test, pytest, etc.)npm run build (optional โ include if your project has a build step)npm audit (or pip-audit, cargo audit, flutter pub audit). Flag known vulnerabilities. This step is advisory โ failures here produce NOTES, not GATE FAILED, unless your project explicitly gates on audit.For each step:
Cite actual output numbers โ do not report GATE PASSED from memory of a previous run. Run each command fresh and read the result.
If a failure cause is unclear after reading the error output, use /dev-process-toolkit:debug for structured investigation.
After all commands pass, review the changed code. Use git diff against the base branch (e.g., git diff main...HEAD) if on a feature branch, or git diff HEAD~1 if on the main branch. If there are uncommitted changes, include git diff (unstaged) and git diff --cached (staged) as well. Your job here is to find problems, not to praise the work. Approach this as if reviewing someone else's code โ look for what's wrong, not what's right.
Use the canonical review rubric in agents/code-reviewer.md as the source of truth for criteria (quality, security, patterns, stack-specific). Run the review inline โ gate-check must return a verdict in one turn, so do not delegate to the code-reviewer subagent from here. Also check spec compliance: every AC has a corresponding test, and no undocumented behavior has been added (security and spec-compliance concerns are the only critical criteria in gate-check; other concerns are non-critical).
For each criterion, report: OK or CONCERN with specifics. Use the exact shape documented at the bottom of agents/code-reviewer.md (<criterion> โ OK or <criterion> โ CONCERN: file:line โ <reason>).
Never read
specs/frs/archive/orspecs/plan/archive/โ only live spec files count for drift detection.
If specs/ directory exists, check whether the implementation has drifted from the spec:
specs/requirements.md and extract all ACs| AC ID | Status | Location |
|---|---|---|
AC-<tracker-id>.1 | implemented | src/feature.ts:42 |
AC-<tracker-id>.2 | not found | โ |
AC-<other-tracker-id>.1 | implemented | src/service.ts:15 |
potential drift)If specs/ directory does not exist, skip this section silently.
Drift findings do NOT cause GATE FAILED. They appear under GATE PASSED WITH NOTES as informational items for the developer to review.
Combine command results + code review into a final verdict:
Always state what needs fixing if not a clean pass.
Optionally produce a JSON summary alongside the Markdown report so CI pipelines can parse results.
{
"steps": [
{ "step": "typecheck", "status": "pass", "summary": "No type errors" },
{ "step": "test", "status": "pass", "summary": "47 passed, 0 failed" },
{ "step": "code-review", "status": "pass", "summary": "No critical concerns" },
{ "step": "drift-check", "status": "notes", "summary": "2 ACs not found" }
],
"verdict": "GATE PASSED WITH NOTES"
}
The verdict field uses one of: GATE PASSED, GATE PASSED WITH NOTES, GATE FAILED.
If you hear yourself thinking any of these, stop and run the gate anyway:
| Excuse | Reality |
|---|---|
| Should work now | Run the verification |
| I'm confident | Confidence โ evidence |
| Just this once | No exceptions |
| Linter passed | Linter โ compiler / tests |
| Agent said success | Verify independently |
| Partial check is enough | Partial proves nothing |