com um clique
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.
Implement a feature or fix end-to-end. Analyzes the request, builds in TDD order, runs gate checks, self-reviews with bounded loops, and reports for human approval before committing.
Guide the user through writing or completing spec files (requirements, technical spec, testing spec, plan). Use after /setup to fill in specs before implementation, or to update existing specs.
Drive `/smoke-test` against both trackers in parallel and aggregate the per-tracker findings files into one deduplicated report. Default `capture-only` mode honors `/smoke-test`'s "Capture, don't fix" rule unchanged. Opt-in `--auto-fix` mode walks the deduplicated high-severity findings list and dispatches `/dev-process-toolkit:spec-write` + `/dev-process-toolkit:implement` per finding, then re-iterates until termination. Project-local skill, not plugin.
Review implementation against specs to find deviations, missing features, or inconsistencies. Delegates to the `spec-review-audit` fork + `spec-reviewer` subagent.
Bundle the Release Checklist + /docs --commit --full into one atomic, human-approved release commit. Reads specs/plan/M<N>.md, bumps the four release files, regenerates docs, prompts once for approval, commits on `y`, does not push.
Manually archive user-selected FRs or milestones by `git mv` into `specs/frs/archive/` (and optionally `specs/plan/archive/`) with a diff approval gate. Accepts ULID, tracker ID/URL, or `M<N>`. Escape hatch for /implement Phase 4 auto-archival gaps.
| 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, created_at present. The id and tracker keys are mode-conditional (required in mode: none and tracker mode respectively, absent in the other), enforced by probe #13 identity_mode_conditional. Missing a field → GATE FAILED naming the file and field.
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>). 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.
Tolerance wrapper integration (STE-305). The comparison against the adapter's status_mapping.done canonical name is routed through withTolerance(...) from adapters/_shared/src/tracker_tolerance.ts rather than a bare strict-equality check. The wrapper maps the observed tracker label to its canonical role via specs/tracker-config.yaml roles:; when the mapped role equals the expected role (done) the probe passes, when the mapped role differs the probe surfaces genuine drift and GATE FAILED with the row shape above. Under non-tty (claude -p) execution the wrapper additionally routes any non-fatal label-shape advisory through the closing-summary capability token tracker_status_advisory_non_tty — the row is rendered as ADVISORY (not GATE FAILED) so harness runs are not blocked by transient label-shape signals while the byte-checkable capability key preserves audit traceability.
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) and symmetrically completes the archive-side ticket-state probe. Both carve-outs strictly weaken the predicate — every prior failing case where the drift was real still fails (Phase 1 step 0.c skipped, wrong assignee, missing plan, archived plan vacuous to probe #27).
Tolerance wrapper integration (STE-305). The in_progress-lane membership check is routed through withTolerance(...) from adapters/_shared/src/tracker_tolerance.ts rather than a bare strict-equality match against status_mapping.in_progress. The wrapper maps the observed tracker label to its canonical role via specs/tracker-config.yaml roles:; when the mapped role equals in_progress the lane check passes, and when the mapped role does not match the expected role the probe surfaces genuine drift and GATE FAILED with the row shape above (mismatch in mapped role is preserved as a hard failure — the wrapper relaxes label-shape brittleness, not the semantic predicate). Under non-tty (claude -p) execution the wrapper additionally routes any non-fatal label-shape advisory through the closing-summary capability token tracker_status_advisory_non_tty — the row is rendered as ADVISORY (not GATE FAILED) so harness runs are not blocked by transient label-shape signals while the byte-checkable capability key preserves audit traceability.
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>) and verify the milestone landed on the ticket. The verification surface is adapter-aware, selected by deps.milestoneBinding (wired in production from the active adapter's milestone_binding: Schema M frontmatter; defaults to object when absent): the object binding (Linear; mcp__linear__get_issue) asserts the issue's projectMilestone.name byte-equals the canonical local heading (parsed from specs/plan/M<N>.md's H1, anchor stripped); the label binding (Jira; mcp__atlassian__getJiraIssue) asserts the issue's labels array contains milestone-<M-token> (the leading M\d+ of that same canonical heading) — labels are Jira's create-on-write milestone surface, since the atlassian MCP exposes no milestone object. 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 (object: name mismatch; label: the expected milestone-<M-token> absent) → GATE FAILED rendering the offending names/labels 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`.
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.
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.
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.
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.
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. Probe #37 (cross-cutting-spec-stale-file-refs) only fires on path tokens inside fenced directory-tree blocks in technical-spec.md / testing-spec.md; bare-prose path mentions outside fences are operator judgment surface and never flagged. 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)
spec_write_first_turn_drift_scan — call runSpecWriteFirstTurnDriftScanProbe(projectRoot) from adapters/_shared/src/spec_write_first_turn_drift_scan.ts. Severity: error. Globs ONLY plugins/dev-process-toolkit/skills/spec-write/SKILL.md (single-file scope; the regression is uniquely /spec-write's, not a cross-skill issue) and scans for six literal forbidden alternate-trigger paraphrases of the Pattern-26 first-turn contract: "pre-baked args allow", "verbose <command-args> permits", "autonomous-mode permits skip", "marker absence implies", "first AskUserQuestion may be deferred", "Socratic loop is optional under". Each occurrence → GATE FAILED with file:line:column — reason note in NFR-10 canonical shape, naming the matched phrase and the rephrase remedy. Sibling probe shape to STE-226's auto_approve_marker.ts and STE-262's spec_write_alternate_trigger_scan.ts; literal substring match (no regex) so future SKILL.md copy edits don't accidentally regress detection. Vacuous when /spec-write SKILL.md is absent (downstream toolkit consumers without the plugin's own skills tree). Closes the /conformance-loop iter-1 (2026-05-10) F1 reproducer where /spec-write's diluting-prose surface (§ 0a Resolver entry + § 0b FR creation path) read to the LLM as license to skip the Socratic loop entirely under the autonomous-mode reminder — same prose-only failure shape STE-220 → STE-226 → STE-237 each hit before structural / byte-checkable hardening landed. Test coverage: tests/gate-check-spec-write-first-turn-drift-scan.test.ts. (STE-270 AC-STE-270.2)
spec_write_marker_alternate_trigger_scan — call runSpecWriteAlternateTriggerScanProbe(projectRoot) from adapters/_shared/src/spec_write_alternate_trigger_scan.ts. Severity: error. Globs ONLY plugins/dev-process-toolkit/skills/spec-write/SKILL.md and scans for six literal forbidden alternate-trigger paraphrases of the STE-226 marker contract: "Auto Mode", "work without stopping", "imputed approval", "autonomous-mode reminder", "inferred approval", "non-interactive inference". Each occurrence → GATE FAILED with file:line:column — reason note in NFR-10 canonical shape, naming the matched phrase and the rephrase remedy. Negation-context carve-out: lines containing any of "single deterministic" (canonical contract anchor at gate sites), "are removed" / "is removed" (legacy-removal historical reference), "regardless of" (disclaimer phrasing), or "NOT acceptable" (explicit forbidden-trigger marker) are excluded — those signatures only appear in legitimate negation/historical context, not as positive alternate triggers. Sibling probe shape to STE-226's auto_approve_marker.ts and STE-270's spec_write_first_turn_drift_scan.ts. Vacuous when /spec-write SKILL.md is absent. Closes the /conformance-loop iter-1 (2026-05-08) F1 cross-tracker reproducer where /spec-write's draft + commit gates auto-applied silently on claude -p runs whose prompt body lacked the marker, conflating the harness's autonomous-mode reminder with the byte-checkable STE-226 marker. Third attempt at this gate (STE-213 → STE-220 → STE-226 → STE-262); structural enforcement at runtime + this source-level probe close the prose-only LLM-inference path. Test coverage: tests/gate-check-spec-write-marker-alternate-trigger-scan.test.ts. (STE-262 AC-STE-262.4 + AC-STE-262.7)
tracker_local_reconciliation_drift — call runTrackerLocalReconciliationDriftProbe(projectRoot, { provider }) from adapters/_shared/src/tracker_local_reconciliation_drift.ts. Severity tiered: info (no drift), warning (any drift — tracker-orphan / unbound local FR / milestone-mismatch), error (hard FR-id collisions — same tracker ID bound by two or more local files OR local FR's tracker: value points at an FR id absent from the tracker's active set). Wraps reconcileTrackerLocal from adapters/_shared/src/reconcile_tracker_local.ts and adds a local-side scan for the duplicate-binding shape the helper cannot see by construction (both files carry a binding, so neither is an orphan). Mode-aware via the helper: LocalProvider (mode: 'none') reconciles to three empty lists and the probe surfaces info/zero-violations on every local-only project. Read-side: never writes, never throws on missing specs/ directories. Closes the 2026-05-13 partial-scan trap shape where a milestone existed on the tracker but had no local plan file. Test coverage: tests/gate-check-tracker-local-reconciliation-drift.test.ts. (STE-284 AC-STE-284.4)
Capability-key roll-up (STE-284 AC-STE-284.5). Probe #49 surfaces three byte-checkable capability keys into the closing summary so the operator sees the reconciliation outcome without scrolling probe output. Owner skill: /spec-write (preamble reconcile is the canonical emission site); /gate-check mirrors the keys here so the conformance probe at #44 (closing_summary_capability_keys) and the future cross-skill audit can find them in either surface. The plain-language rendering tracks /spec-write § 7's static map row of the same name:
tracker_local_reconciled — emitted whenever the preamble auto-import path reconciled ≥ 1 FR or milestone from the tracker into local files. Render: tracker → local reconciliation imported N FR(s) and M milestone(s) — see specs/frs/<id>.md and specs/plan/<M>.md for the imported content.tracker_local_orphan_local — emitted per offending entity when a local FR file carries no tracker: binding. Render: local FR <id> has no tracker binding — run /spec-write <id> --push-to-tracker to sync, or remove the local file if abandoned.milestone_local_orphan — emitted per offending entity when a local specs/plan/M<N>.md has no matching tracker milestone (the partial-scan trap inverse). Render: local milestone M<N> has no tracker milestone — auto-create via Provider.attachProjectMilestone(M<N>), or remove if abandoned.The three keys MUST appear verbatim in the closing summary; paraphrased prose is insufficient (STE-220 lesson). Vacuous in mode: none.
tdd_spec_reviewer_subagent_invariants — call runTddSpecReviewerInvariantsProbe(projectRoot) from adapters/_shared/src/tdd_spec_reviewer_invariants.ts. Severity: error. Asserts the byte-checkable frontmatter invariants for the AUDIT-stage subagent + child skill: (a) agents/tdd-spec-reviewer.md exists with tools: Read, Grep, Glob (read-only — Write/Edit/Bash/Agent excluded), maxTurns: 8, model: sonnet; (b) skills/tdd-spec-review/SKILL.md exists with context: fork, agent: tdd-spec-reviewer, user-invocable: false, and (when allowed-tools: is present) Agent excluded. Mirrors probe #39 tdd_orchestrator_integrity shape against the new fourth stage; both probes share frontmatter parsing + violation-rendering primitives from adapters/_shared/src/tdd_probe_helpers.ts. Vacuous only on repos with neither plugins/dev-process-toolkit/skills/ nor plugins/dev-process-toolkit/agents/. Test coverage: tests/gate-check-tdd-spec-reviewer-invariants.test.ts. (AC-STE-296.8)
deps_researcher_subagent_invariants — call runDepsResearcherInvariantsProbe(projectRoot) from adapters/_shared/src/deps_researcher_invariants.ts. Severity: error. Asserts the byte-checkable frontmatter invariants for the deps-research subagent + child skill: (a) agents/deps-researcher.md exists with tools: Read, Grep, Glob (read-only — Write/Edit/Bash/Agent excluded) and model: haiku; (b) skills/deps-research/SKILL.md exists with context: fork, agent: deps-researcher, user-invocable: false, and (when allowed-tools: is present) Agent excluded. Mirrors probe #50 tdd_spec_reviewer_subagent_invariants shape (STE-296 AC.8) against the deps-research fork; both probes share frontmatter parsing + violation-rendering primitives from adapters/_shared/src/tdd_probe_helpers.ts. Vacuous only on repos with neither plugins/dev-process-toolkit/skills/ nor plugins/dev-process-toolkit/agents/. Test coverage: tests/gate-check-deps-researcher-invariants.test.ts. (STE-301 AC-STE-301.15)
tracker_config_shape — call runTrackerConfigShapeProbe(projectRoot) from adapters/_shared/src/tracker_config_shape.ts. Severity: error. Byte-checks specs/tracker-config.yaml shape when the file exists: tracker_key: matches the active adapter's name: field (resolved via readAdapterName(...) from adapters/<mode>.md, where <mode> is declared by CLAUDE.md ## Task Tracking mode:) per STE-321 AC.1 — the hard-coded linear/jira allowlist is gone, custom-adapter authors are first-class; statuses: is a non-empty list of verbatim tracker labels (AC.2); and roles: declares exactly the canonical four-role enum {initial, in_progress, in_review, done} with every value present in statuses: (AC.3 + AC.4). Schema authority is validateTrackerConfig from adapters/_shared/src/tracker_config.ts — the same validator readTrackerConfig calls — so the probe and the runtime loader can never diverge. Each violation surfaces as a specs/tracker-config.yaml:<line> — <reason> note in NFR-10 canonical shape (Refusing: / Remedy: / Context:). Vacuous when specs/tracker-config.yaml is absent (FR2 owns creation) OR CLAUDE.md ## Task Tracking declares mode: none (short-circuits before the file is read — even a malformed file is inert under mode: none). Test coverage: tests/gate-check-tracker-config-shape.test.ts.
tracker_tolerance_wrapper_present — call runTrackerToleranceWrapperPresentProbe(projectRoot) from adapters/_shared/src/tracker_tolerance_wrapper_present.ts. Severity: error. Defense-in-depth byte-check that plugins/dev-process-toolkit/adapters/_shared/src/tracker_tolerance.ts exists AND exports withTolerance (matched as export function withTolerance or export const withTolerance). If a future refactor accidentally drops the wrapper or renames the symbol, the relaxed status-mapping probes would silently lose tolerance — this probe is the structural fuse that catches that regression at gate-check time. Each violation surfaces as a plugins/dev-process-toolkit/adapters/_shared/src/tracker_tolerance.ts:1 — <reason> note in NFR-10 canonical shape (Refusing: / Remedy: / Context:). Vacuous when plugins/dev-process-toolkit/adapters/_shared/src/ is absent (non-toolkit projects do not ship the shared adapter layer). Test coverage: tests/gate-check-tracker-tolerance-wrapper-present.test.ts.
audit_fix_loop_pattern_invariants — call runAuditFixLoopPatternInvariantsProbe(projectRoot) from adapters/_shared/src/audit_fix_loop_pattern.ts. Severity: error. Asserts byte-checkable invariants on every canonical audit-fix loop declared in the AUDIT_FIX_LOOP_CANONICAL_LOOPS allowlist exported from the same module. The allowlist ships with the /tdd audit-fork pair only at FR ship (tdd-spec-review::tdd-spec-reviewer); STE-308 appends the /spec-review audit-fork entry. The RED/GREEN/REFACTOR /tdd forks (tdd-write-test, tdd-implement, tdd-refactor) are deliberately excluded — they are the action half of /tdd's loop and need Write/Edit/Bash; their orchestrator-level invariants are already enforced by probe #39 tdd_orchestrator_integrity. For each allowlist entry the probe asserts (a) the child skill file exists at plugins/dev-process-toolkit/skills/<child>/SKILL.md and carries context: fork, user-invocable: false, and an agent: resolving to an existing plugins/dev-process-toolkit/agents/<name>.md; (b) the subagent declares tools: Read, Grep, Glob only (Write/Edit/Bash/Agent excluded); (c) the child's allowed-tools: (when present) excludes Agent. Generalises probes #39 / #50 / #51 to a single allowlist-driven scan so new audit-fix loops added downstream are picked up without bespoke probe code. Vacuous on repos with neither plugins/dev-process-toolkit/skills/ nor plugins/dev-process-toolkit/agents/. Test coverage: tests/gate-check-audit-fix-loop-pattern-invariants.test.ts. (STE-307)
not_a_trigger_anchor_present — call runNotATriggerAnchorPresentProbe(projectRoot) from adapters/_shared/src/not_a_trigger_anchor_present.ts. Severity: error. Asserts a § Rules NOT-a-trigger anchor lands in BOTH plugins/dev-process-toolkit/skills/spec-write/SKILL.md AND plugins/dev-process-toolkit/skills/setup/SKILL.md. The anchor's byte-checkable contract is that every literal phrase in NOT_A_TRIGGER_REQUIRED_PHRASES ("work without stopping", "autonomous-mode", "standing instruction", <command-args>, claude -p) appears in the file at or after the ## Rules heading, alongside a literal reference to adapters/_shared/src/check_marker_runtime.ts as the SOLE byte-checkable evaluation path. Each violation surfaces as a <skill SKILL.md>:<line>:<column> — <reason> note in NFR-10 canonical shape (Refusing: / Remedy: / Context:). Vacuous when neither SKILL.md ships. Test coverage: tests/gate-check-not-a-trigger-anchor-present.test.ts.
marker_helper_invoked_per_gate — call runMarkerHelperInvokedPerGateProbe(projectRoot, { sessionLogPath }) from adapters/_shared/src/marker_helper_invoked_per_gate.ts. Severity: error. Scans the NDJSON Bash-tool transcript of /spec-write and /setup runs (via the stream-json session-log capture path) and refuses with NFR-10 if any marker-gated decision token (spec_write_draft_default_applied, spec_write_commit_default_applied, branch_gate_default_applied, or any setup_socratic_first_turn_* token) surfaces in an assistant text block without a preceding Bash invocation of bun run plugins/dev-process-toolkit/adapters/_shared/src/check_marker_runtime.ts in the same session log. Refusal events (RequiresInputRefusedError text) are not gate decisions — the probe ignores them. Each violation surfaces in NFR-10 canonical shape (Refusing: / Remedy: / Context:). Vacuous when no sessionLogPath is supplied OR the path points at a missing file (downstream toolkit consumers without the capture path shouldn't see false-positive violations). Test coverage: tests/gate-check-marker-helper-invoked-per-gate.test.ts.
public_surface_count_drift — call runPublicSurfaceCountDriftProbe(projectRoot) from adapters/_shared/src/public_surface_count_drift.ts with PROBE_ID = "public_surface_count_drift". Severity: error. Asserts the documented skill / agent / probe count tokens in README.md and CLAUDE.md match the actual on-disk counts. Walks three input files (README.md, CLAUDE.md, skills/gate-check/SKILL.md) and the on-disk plugins/dev-process-toolkit/skills/*/ + agents/*.md trees to compute observed counts; any documented value that disagrees with the corresponding observed value surfaces as an NFR-10 canonical refusal (<file>:<line>:<column> — <reason> with Refusing: / Remedy: / Context: sub-lines). Pattern list lives as a const at the top of the probe module so contributors editing public surfaces see the contract. Vacuous when none of the three input files exists (non-toolkit projects do not ship them). Test coverage: tests/gate-check-public-surface-count-drift.test.ts.
cross_skill_contract_drift — call runCrossSkillContractDriftProbe(projectRoot) from adapters/_shared/src/cross_skill_contract_drift.ts with PROBE_ID = "cross_skill_contract_drift". Severity: error. Walks the active-surface glob plugins/dev-process-toolkit/{skills,docs,agents}/**/*.md plus plugins/dev-process-toolkit/README.md (archive directories excluded) and scans each line for the literal forbidden substrings exported as FORBIDDEN_SUBSTRINGS at the top of the probe module — see the module source for the authoritative list. Closes three already-shipped cross-skill contract drifts — A2 (/tdd 4-stage AUDIT vocabulary per STE-296 M77), A6 (2-tier ticket-binding resolver per v1.21.0), and A14 (a phantom probe reference deleted from agents/deps-researcher.md). Each match surfaces as an NFR-10 canonical refusal (<file>:<line>:<column> — <reason> with Refusing: / Remedy: / Context: sub-lines). Literal substring match — no regex — so future SKILL.md copy edits don't accidentally regress detection. Vacuous when plugins/dev-process-toolkit/ is absent (downstream toolkit consumers without the plugin's own docs). Test coverage: tests/gate-check-cross-skill-contract-drift.test.ts.
disable_model_invocation_allowlist — call runDisableModelInvocationAllowlistProbe(projectRoot) from adapters/_shared/src/disable_model_invocation_allowlist.ts with PROBE_ID = "disable_model_invocation_allowlist". Severity: error. Walks every plugins/dev-process-toolkit/skills/*/SKILL.md frontmatter and refuses if disable-model-invocation: true is declared on any skill not in the canonical allowlist exported as DISABLE_MODEL_INVOCATION_ALLOWLIST (currently ["setup"] only — /setup is the bootstrap entry point and must not be auto-invoked mid-session). Composable skills like /ship-milestone and /spec-archive must remain model-invocable; surfacing the flag on them would silently break orchestration chains (/implement → /spec-archive, release pipelines). Each violation surfaces as an NFR-10 canonical refusal (<file>:<line>:<column> — <reason> with Refusing: / Remedy: / Context: sub-lines). Canonical-allowlist shape mirrors closing_summary_capability_keys (STE-238 AC.4). Vacuous when plugins/dev-process-toolkit/skills/ is absent (downstream toolkit consumers without the plugin's own skills tree). Test coverage: tests/m84-ste-324-low-polish-bundle.test.ts (AC-STE-324.8).
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 60+ 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 |