diff --git a/CHANGELOG.md b/CHANGELOG.md index 8417d6a33..cf89b49b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,51 @@ # Changelog +## [1.39.1.0] - 2026-05-15 + +## **Plan-mode reviews now enforce a blocking ExitPlanMode gate.** +## **The review report can no longer go missing without breaking the contract.** + +`/plan-eng-review`, `/plan-ceo-review`, `/plan-design-review`, `/plan-devex-review`, and `/codex review` now end with an EXIT PLAN MODE GATE (BLOCKING) section. Before calling ExitPlanMode, the model runs a four-item checklist: read the plan file, confirm the last `## ` heading is `## GSTACK REVIEW REPORT`, verify the report has a Runs/Status/Findings table + VERDICT line, and confirm `gstack-review-log` + `gstack-review-read` ran. Failing the checklist and exiting plan mode anyway is framed as a contract violation, not a soft permission to defer. The structural property ("review report is the file's terminal heading") is what makes the gate immune to "I wrote some review prose into the plan body" self-deception. A regression test in `test/gen-skill-docs.test.ts` strips fenced code blocks and asserts the gate is the terminal `## ` heading in all four plan-* review SKILL.md files. + +### The numbers that matter + +Source: `bun test test/gen-skill-docs.test.ts` — 389 cases, all green in ~1.5s. Manual verification via `awk` confirms the gate is the LAST `## ` heading in the regenerated SKILL.md for each plan-* review skill, and present mid-file in codex's Step 2A (where it's review-mode-scoped per design). + +| Surface | Before | After | +|---|---|---| +| ExitPlanMode discipline in plan-* reviews | Soft `## Plan Status Footer` injected at TOP of skill via preamble: "if the plan file lacks `## GSTACK REVIEW REPORT`, run `gstack-review-read` and append... PLAN MODE EXCEPTION — always allowed." Permission grant, not a precondition. Sat ~3000 lines above ExitPlanMode in the skill prompt. | Terminal `## EXIT PLAN MODE GATE (BLOCKING)` injected at EOF of every plan-* review skill: 4-item self-check with explicit "contract violation" framing for the failure mode. Last thing the model reads before ExitPlanMode. | +| Preamble footer in operational skills (`/ship`, `/qa`, `/review`, `/health`) | Same enforcement text as plan-mode skills — review-report rules bled into skills that have no review report | Neutral forward reference: "Plan-review skills include the EXIT PLAN MODE GATE at the end; this footer is a no-op for operational skills." No imposed rules where they can't apply. | +| Regression protection | None — gate placement could silently regress on any future template edit | `bun test test/gen-skill-docs.test.ts` asserts gate is terminal `## ` heading in 4 plan-* skills (with fenced-code-block stripping) and present in codex via `toContain`. | + +Cross-model review by Codex (`/codex` consult mode) caught six pre-merge factual issues the eng review missed: insertion line numbers were not terminal positions, the test regex would false-match `## ` lines inside fenced code blocks, the existing `REVIEW_SKILLS` constant in the test file was missing `plan-devex-review`, the preamble retoning bled review-report rules into operational skills, gate check 4 conflicted with `PLAN_FILE_REVIEW_REPORT`'s "skip silently if no plan file" escape clause, and the implementation sequence wasn't explicit enough to prevent bisect-broken commits. All six folded in before push. + +### What this means for plan reviews + +When the model finishes a plan-* review and is about to exit plan mode, it reads a blocking checklist that reframes ExitPlanMode as a precondition-bearing call, not a free termination. The plan ships with its review report attached as the file's terminal heading, every time. If the user has been bitten by "approved a plan only to discover the review report was never written" before, that failure mode is gone. + +### Itemized changes + +#### Added + +- `generateExitPlanModeGate` resolver in `scripts/resolvers/review.ts:161` — emits the 4-item blocking checklist with "contract violation" framing. Single source of truth for the gate text. +- `EXIT_PLAN_MODE_GATE` placeholder registered in `scripts/resolvers/index.ts:42`. Appended at EOF of `plan-eng-review/SKILL.md.tmpl`, `plan-ceo-review/SKILL.md.tmpl`, `plan-design-review/SKILL.md.tmpl`, `plan-devex-review/SKILL.md.tmpl`. Inserted into `codex/SKILL.md.tmpl` after `{{PLAN_FILE_REVIEW_REPORT}}` in Step 2A (mid-file by design — Step 2B/2C are not plan-touching modes). +- `test/gen-skill-docs.test.ts:3097` — new `EXIT PLAN MODE GATE placement` describe block. Strips fenced code blocks before matching `## ` headings (a naive regex would false-match the `## GSTACK REVIEW REPORT` example inside `PLAN_FILE_REVIEW_REPORT`'s fenced markdown block). Uses a fresh skill list — not the upstream `REVIEW_SKILLS` constant which only has 3 entries and would silently miss plan-devex-review. + +#### Changed + +- `scripts/resolvers/preamble/generate-completion-status.ts:82` — `## Plan Status Footer` retoned from enforcement language ("if the plan file lacks `## GSTACK REVIEW REPORT`, run `gstack-review-read`... PLAN MODE EXCEPTION — always allowed") to neutral forward reference ("plan-review skills include the EXIT PLAN MODE GATE at the end; this footer is a no-op for operational skills"). Avoids review-report rules bleeding into `/ship`, `/qa`, `/review`, `/health`, etc. +- `test/gen-skill-docs.test.ts:1093` — updated existing "Plan status footer in preamble" assertion to match the new neutral wording. Now also asserts the absence of "NO REVIEWS YET" to lock in the no-bleed property. +- `test/fixtures/golden/{claude,codex,factory}-ship-SKILL.md` — golden baselines updated to capture the new preamble wording. The ship skill's body did not change; only the inherited preamble footer. + +#### Fixed + +- `package.json` build script — three `{ git rev-parse HEAD 2>/dev/null || true; }` brace groups (Bun-Windows-hostile) regressed during the v1.38.0.0 merge resolution; replaced with `( ... )` subshells to match the v1.38.0.0 invariant. Caught by Windows CI's `build-script-shell-compat` test on PR #1512. + +#### For contributors + +- The implementation sequence is load-bearing: resolver → index → templates → preamble → `bun run gen:skill-docs` → tests. Adding the test before regeneration fails on missing gate; regenerating before the resolver edits produces no-op output. Bisectable commits should respect this order. +- The codex gate is intentionally NOT terminal in `codex/SKILL.md`. Codex has three modes (review/challenge/consult) and only review mode writes to plan files. The gate's check-2 ("last heading is GSTACK REVIEW REPORT") short-circuits cleanly when no plan file is in context, so non-plan codex invocations are unaffected. + ## [1.39.0.0] - 2026-05-14 ## **`buildFetchHandler` ships. Embedders compose overlay routes on top of** @@ -31,6 +77,7 @@ gbrowser v0.6.0.0 (phoenix overlay) can now ship. Phoenix imports `buildFetchHan ### Itemized changes #### Added + - `buildFetchHandler(cfg: ServerConfig): ServerHandle` in `browse/src/server.ts`. - `beforeRoute` hook wiring in the request handler, with a security warning JSDoc for overlay authors. - 14 factory contract tests in `browse/test/server-factory.test.ts` (covers ServerHandle shape, auth wiring, validation throws, hook semantics across both surfaces, and registry idempotency / mismatch-throw). diff --git a/SKILL.md b/SKILL.md index 1a61ac96f..c6441014c 100644 --- a/SKILL.md +++ b/SKILL.md @@ -473,9 +473,7 @@ Replace `SKILL_NAME`, `OUTCOME`, and `USED_BROWSE` before running. ## Plan Status Footer -In plan mode before ExitPlanMode: if the plan file lacks `## GSTACK REVIEW REPORT`, run `~/.claude/skills/gstack/bin/gstack-review-read` and append the standard runs/status/findings table. With `NO_REVIEWS` or empty, append a 5-row placeholder with verdict "NO REVIEWS YET — run `/autoplan`". If a richer report exists, skip. - -PLAN MODE EXCEPTION — always allowed (it's the plan file). +Skills that run plan reviews (`/plan-*-review`, `/codex review`) include the EXIT PLAN MODE GATE blocking checklist at the end of the skill, which verifies the plan file ends with `## GSTACK REVIEW REPORT` before ExitPlanMode is called. Skills that don't run plan reviews (operational skills like `/ship`, `/qa`, `/review`) typically don't operate in plan mode and have no review report to verify; this footer is a no-op for them. Writing the plan file is the one edit allowed in plan mode. If `PROACTIVE` is `false`: do NOT proactively invoke or suggest other gstack skills during this session. Only run skills the user explicitly invokes. This preference persists across diff --git a/VERSION b/VERSION index db98c026f..57fdbd724 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.39.0.0 +1.39.1.0 diff --git a/autoplan/SKILL.md b/autoplan/SKILL.md index 462cd9dc0..a39b60bbd 100644 --- a/autoplan/SKILL.md +++ b/autoplan/SKILL.md @@ -766,9 +766,7 @@ Replace `SKILL_NAME`, `OUTCOME`, and `USED_BROWSE` before running. ## Plan Status Footer -In plan mode before ExitPlanMode: if the plan file lacks `## GSTACK REVIEW REPORT`, run `~/.claude/skills/gstack/bin/gstack-review-read` and append the standard runs/status/findings table. With `NO_REVIEWS` or empty, append a 5-row placeholder with verdict "NO REVIEWS YET — run `/autoplan`". If a richer report exists, skip. - -PLAN MODE EXCEPTION — always allowed (it's the plan file). +Skills that run plan reviews (`/plan-*-review`, `/codex review`) include the EXIT PLAN MODE GATE blocking checklist at the end of the skill, which verifies the plan file ends with `## GSTACK REVIEW REPORT` before ExitPlanMode is called. Skills that don't run plan reviews (operational skills like `/ship`, `/qa`, `/review`) typically don't operate in plan mode and have no review report to verify; this footer is a no-op for them. Writing the plan file is the one edit allowed in plan mode. ## Step 0: Detect platform and base branch diff --git a/benchmark-models/SKILL.md b/benchmark-models/SKILL.md index 5e5e6bd66..47050855b 100644 --- a/benchmark-models/SKILL.md +++ b/benchmark-models/SKILL.md @@ -475,9 +475,7 @@ Replace `SKILL_NAME`, `OUTCOME`, and `USED_BROWSE` before running. ## Plan Status Footer -In plan mode before ExitPlanMode: if the plan file lacks `## GSTACK REVIEW REPORT`, run `~/.claude/skills/gstack/bin/gstack-review-read` and append the standard runs/status/findings table. With `NO_REVIEWS` or empty, append a 5-row placeholder with verdict "NO REVIEWS YET — run `/autoplan`". If a richer report exists, skip. - -PLAN MODE EXCEPTION — always allowed (it's the plan file). +Skills that run plan reviews (`/plan-*-review`, `/codex review`) include the EXIT PLAN MODE GATE blocking checklist at the end of the skill, which verifies the plan file ends with `## GSTACK REVIEW REPORT` before ExitPlanMode is called. Skills that don't run plan reviews (operational skills like `/ship`, `/qa`, `/review`) typically don't operate in plan mode and have no review report to verify; this footer is a no-op for them. Writing the plan file is the one edit allowed in plan mode. # /benchmark-models — Cross-Model Skill Benchmark diff --git a/benchmark/SKILL.md b/benchmark/SKILL.md index 46934ba3d..b6dc81373 100644 --- a/benchmark/SKILL.md +++ b/benchmark/SKILL.md @@ -475,9 +475,7 @@ Replace `SKILL_NAME`, `OUTCOME`, and `USED_BROWSE` before running. ## Plan Status Footer -In plan mode before ExitPlanMode: if the plan file lacks `## GSTACK REVIEW REPORT`, run `~/.claude/skills/gstack/bin/gstack-review-read` and append the standard runs/status/findings table. With `NO_REVIEWS` or empty, append a 5-row placeholder with verdict "NO REVIEWS YET — run `/autoplan`". If a richer report exists, skip. - -PLAN MODE EXCEPTION — always allowed (it's the plan file). +Skills that run plan reviews (`/plan-*-review`, `/codex review`) include the EXIT PLAN MODE GATE blocking checklist at the end of the skill, which verifies the plan file ends with `## GSTACK REVIEW REPORT` before ExitPlanMode is called. Skills that don't run plan reviews (operational skills like `/ship`, `/qa`, `/review`) typically don't operate in plan mode and have no review report to verify; this footer is a no-op for them. Writing the plan file is the one edit allowed in plan mode. ## SETUP (run this check BEFORE any browse command) diff --git a/browse/SKILL.md b/browse/SKILL.md index 1d544756c..6a4f5c269 100644 --- a/browse/SKILL.md +++ b/browse/SKILL.md @@ -474,9 +474,7 @@ Replace `SKILL_NAME`, `OUTCOME`, and `USED_BROWSE` before running. ## Plan Status Footer -In plan mode before ExitPlanMode: if the plan file lacks `## GSTACK REVIEW REPORT`, run `~/.claude/skills/gstack/bin/gstack-review-read` and append the standard runs/status/findings table. With `NO_REVIEWS` or empty, append a 5-row placeholder with verdict "NO REVIEWS YET — run `/autoplan`". If a richer report exists, skip. - -PLAN MODE EXCEPTION — always allowed (it's the plan file). +Skills that run plan reviews (`/plan-*-review`, `/codex review`) include the EXIT PLAN MODE GATE blocking checklist at the end of the skill, which verifies the plan file ends with `## GSTACK REVIEW REPORT` before ExitPlanMode is called. Skills that don't run plan reviews (operational skills like `/ship`, `/qa`, `/review`) typically don't operate in plan mode and have no review report to verify; this footer is a no-op for them. Writing the plan file is the one edit allowed in plan mode. # browse: QA Testing & Dogfooding diff --git a/canary/SKILL.md b/canary/SKILL.md index a211c386a..1ba6ecec7 100644 --- a/canary/SKILL.md +++ b/canary/SKILL.md @@ -740,9 +740,7 @@ Replace `SKILL_NAME`, `OUTCOME`, and `USED_BROWSE` before running. ## Plan Status Footer -In plan mode before ExitPlanMode: if the plan file lacks `## GSTACK REVIEW REPORT`, run `~/.claude/skills/gstack/bin/gstack-review-read` and append the standard runs/status/findings table. With `NO_REVIEWS` or empty, append a 5-row placeholder with verdict "NO REVIEWS YET — run `/autoplan`". If a richer report exists, skip. - -PLAN MODE EXCEPTION — always allowed (it's the plan file). +Skills that run plan reviews (`/plan-*-review`, `/codex review`) include the EXIT PLAN MODE GATE blocking checklist at the end of the skill, which verifies the plan file ends with `## GSTACK REVIEW REPORT` before ExitPlanMode is called. Skills that don't run plan reviews (operational skills like `/ship`, `/qa`, `/review`) typically don't operate in plan mode and have no review report to verify; this footer is a no-op for them. Writing the plan file is the one edit allowed in plan mode. ## SETUP (run this check BEFORE any browse command) diff --git a/codex/SKILL.md b/codex/SKILL.md index f6b507697..edf4075f2 100644 --- a/codex/SKILL.md +++ b/codex/SKILL.md @@ -760,9 +760,7 @@ Replace `SKILL_NAME`, `OUTCOME`, and `USED_BROWSE` before running. ## Plan Status Footer -In plan mode before ExitPlanMode: if the plan file lacks `## GSTACK REVIEW REPORT`, run `~/.claude/skills/gstack/bin/gstack-review-read` and append the standard runs/status/findings table. With `NO_REVIEWS` or empty, append a 5-row placeholder with verdict "NO REVIEWS YET — run `/autoplan`". If a richer report exists, skip. - -PLAN MODE EXCEPTION — always allowed (it's the plan file). +Skills that run plan reviews (`/plan-*-review`, `/codex review`) include the EXIT PLAN MODE GATE blocking checklist at the end of the skill, which verifies the plan file ends with `## GSTACK REVIEW REPORT` before ExitPlanMode is called. Skills that don't run plan reviews (operational skills like `/ship`, `/qa`, `/review`) typically don't operate in plan mode and have no review report to verify; this footer is a no-op for them. Writing the plan file is the one edit allowed in plan mode. ## Step 0: Detect platform and base branch @@ -1153,6 +1151,31 @@ prior versions to leave the report mid-file when an older report already lived there — the user then sees a plan whose review report is not at the bottom and (correctly) rejects it. +## EXIT PLAN MODE GATE (BLOCKING) + +Before calling ExitPlanMode, run this self-check. If any item fails, do the +missing work — do NOT call ExitPlanMode: + +1. Read the plan file with the Read tool (after your most recent write to it). +2. Confirm the LAST `## ` heading in the file is `## GSTACK REVIEW REPORT`. + In-body prose that mentions "outside voice", "codex findings", or similar + does NOT count — only the structured `## GSTACK REVIEW REPORT` section + satisfies this check. +3. Confirm the report contains: a Runs / Status / Findings table, a VERDICT + line, and absorbs CODEX / CROSS-MODEL / UNRESOLVED lines if applicable. +4. If a plan file is in context for this skill invocation: confirm + `gstack-review-log` was called and `gstack-review-read` was run at least + once. If no plan file is in context (e.g. `/codex consult` against a + diff with no plan), this check short-circuits — checks 1-3 already + short-circuit when no plan file exists. + +Failing this gate and calling ExitPlanMode anyway is a contract violation — +the user will see a plan whose review report is missing or stale, and will +(correctly) reject it. Self-deception failure mode to watch for: feeling +"done" after writing review prose into the plan body. The body prose is not +the report. The report is a separate, structured, table-bearing section that +must be the file's terminal heading. + --- ## Step 2B: Challenge (Adversarial) Mode diff --git a/codex/SKILL.md.tmpl b/codex/SKILL.md.tmpl index ab2a405f8..329e93c4f 100644 --- a/codex/SKILL.md.tmpl +++ b/codex/SKILL.md.tmpl @@ -295,6 +295,8 @@ rm -f "$TMPERR" {{PLAN_FILE_REVIEW_REPORT}} +{{EXIT_PLAN_MODE_GATE}} + --- ## Step 2B: Challenge (Adversarial) Mode diff --git a/context-restore/SKILL.md b/context-restore/SKILL.md index 4f0cd70eb..92eb1cdd1 100644 --- a/context-restore/SKILL.md +++ b/context-restore/SKILL.md @@ -744,9 +744,7 @@ Replace `SKILL_NAME`, `OUTCOME`, and `USED_BROWSE` before running. ## Plan Status Footer -In plan mode before ExitPlanMode: if the plan file lacks `## GSTACK REVIEW REPORT`, run `~/.claude/skills/gstack/bin/gstack-review-read` and append the standard runs/status/findings table. With `NO_REVIEWS` or empty, append a 5-row placeholder with verdict "NO REVIEWS YET — run `/autoplan`". If a richer report exists, skip. - -PLAN MODE EXCEPTION — always allowed (it's the plan file). +Skills that run plan reviews (`/plan-*-review`, `/codex review`) include the EXIT PLAN MODE GATE blocking checklist at the end of the skill, which verifies the plan file ends with `## GSTACK REVIEW REPORT` before ExitPlanMode is called. Skills that don't run plan reviews (operational skills like `/ship`, `/qa`, `/review`) typically don't operate in plan mode and have no review report to verify; this footer is a no-op for them. Writing the plan file is the one edit allowed in plan mode. # /context-restore — Restore Saved Working Context diff --git a/context-save/SKILL.md b/context-save/SKILL.md index b083b039f..5a7b0d60e 100644 --- a/context-save/SKILL.md +++ b/context-save/SKILL.md @@ -744,9 +744,7 @@ Replace `SKILL_NAME`, `OUTCOME`, and `USED_BROWSE` before running. ## Plan Status Footer -In plan mode before ExitPlanMode: if the plan file lacks `## GSTACK REVIEW REPORT`, run `~/.claude/skills/gstack/bin/gstack-review-read` and append the standard runs/status/findings table. With `NO_REVIEWS` or empty, append a 5-row placeholder with verdict "NO REVIEWS YET — run `/autoplan`". If a richer report exists, skip. - -PLAN MODE EXCEPTION — always allowed (it's the plan file). +Skills that run plan reviews (`/plan-*-review`, `/codex review`) include the EXIT PLAN MODE GATE blocking checklist at the end of the skill, which verifies the plan file ends with `## GSTACK REVIEW REPORT` before ExitPlanMode is called. Skills that don't run plan reviews (operational skills like `/ship`, `/qa`, `/review`) typically don't operate in plan mode and have no review report to verify; this footer is a no-op for them. Writing the plan file is the one edit allowed in plan mode. # /context-save — Save Working Context diff --git a/cso/SKILL.md b/cso/SKILL.md index fe12df74e..70d8105e7 100644 --- a/cso/SKILL.md +++ b/cso/SKILL.md @@ -745,9 +745,7 @@ Replace `SKILL_NAME`, `OUTCOME`, and `USED_BROWSE` before running. ## Plan Status Footer -In plan mode before ExitPlanMode: if the plan file lacks `## GSTACK REVIEW REPORT`, run `~/.claude/skills/gstack/bin/gstack-review-read` and append the standard runs/status/findings table. With `NO_REVIEWS` or empty, append a 5-row placeholder with verdict "NO REVIEWS YET — run `/autoplan`". If a richer report exists, skip. - -PLAN MODE EXCEPTION — always allowed (it's the plan file). +Skills that run plan reviews (`/plan-*-review`, `/codex review`) include the EXIT PLAN MODE GATE blocking checklist at the end of the skill, which verifies the plan file ends with `## GSTACK REVIEW REPORT` before ExitPlanMode is called. Skills that don't run plan reviews (operational skills like `/ship`, `/qa`, `/review`) typically don't operate in plan mode and have no review report to verify; this footer is a no-op for them. Writing the plan file is the one edit allowed in plan mode. diff --git a/design-consultation/SKILL.md b/design-consultation/SKILL.md index ed4d3811b..00a5f0f2e 100644 --- a/design-consultation/SKILL.md +++ b/design-consultation/SKILL.md @@ -786,9 +786,7 @@ Replace `SKILL_NAME`, `OUTCOME`, and `USED_BROWSE` before running. ## Plan Status Footer -In plan mode before ExitPlanMode: if the plan file lacks `## GSTACK REVIEW REPORT`, run `~/.claude/skills/gstack/bin/gstack-review-read` and append the standard runs/status/findings table. With `NO_REVIEWS` or empty, append a 5-row placeholder with verdict "NO REVIEWS YET — run `/autoplan`". If a richer report exists, skip. - -PLAN MODE EXCEPTION — always allowed (it's the plan file). +Skills that run plan reviews (`/plan-*-review`, `/codex review`) include the EXIT PLAN MODE GATE blocking checklist at the end of the skill, which verifies the plan file ends with `## GSTACK REVIEW REPORT` before ExitPlanMode is called. Skills that don't run plan reviews (operational skills like `/ship`, `/qa`, `/review`) typically don't operate in plan mode and have no review report to verify; this footer is a no-op for them. Writing the plan file is the one edit allowed in plan mode. # /design-consultation: Your Design System, Built Together diff --git a/design-html/SKILL.md b/design-html/SKILL.md index 2337af721..5c92f7703 100644 --- a/design-html/SKILL.md +++ b/design-html/SKILL.md @@ -747,9 +747,7 @@ Replace `SKILL_NAME`, `OUTCOME`, and `USED_BROWSE` before running. ## Plan Status Footer -In plan mode before ExitPlanMode: if the plan file lacks `## GSTACK REVIEW REPORT`, run `~/.claude/skills/gstack/bin/gstack-review-read` and append the standard runs/status/findings table. With `NO_REVIEWS` or empty, append a 5-row placeholder with verdict "NO REVIEWS YET — run `/autoplan`". If a richer report exists, skip. - -PLAN MODE EXCEPTION — always allowed (it's the plan file). +Skills that run plan reviews (`/plan-*-review`, `/codex review`) include the EXIT PLAN MODE GATE blocking checklist at the end of the skill, which verifies the plan file ends with `## GSTACK REVIEW REPORT` before ExitPlanMode is called. Skills that don't run plan reviews (operational skills like `/ship`, `/qa`, `/review`) typically don't operate in plan mode and have no review report to verify; this footer is a no-op for them. Writing the plan file is the one edit allowed in plan mode. # /design-html: Pretext-Native HTML Engine diff --git a/design-review/SKILL.md b/design-review/SKILL.md index d17c07678..91603dd2e 100644 --- a/design-review/SKILL.md +++ b/design-review/SKILL.md @@ -763,9 +763,7 @@ Replace `SKILL_NAME`, `OUTCOME`, and `USED_BROWSE` before running. ## Plan Status Footer -In plan mode before ExitPlanMode: if the plan file lacks `## GSTACK REVIEW REPORT`, run `~/.claude/skills/gstack/bin/gstack-review-read` and append the standard runs/status/findings table. With `NO_REVIEWS` or empty, append a 5-row placeholder with verdict "NO REVIEWS YET — run `/autoplan`". If a richer report exists, skip. - -PLAN MODE EXCEPTION — always allowed (it's the plan file). +Skills that run plan reviews (`/plan-*-review`, `/codex review`) include the EXIT PLAN MODE GATE blocking checklist at the end of the skill, which verifies the plan file ends with `## GSTACK REVIEW REPORT` before ExitPlanMode is called. Skills that don't run plan reviews (operational skills like `/ship`, `/qa`, `/review`) typically don't operate in plan mode and have no review report to verify; this footer is a no-op for them. Writing the plan file is the one edit allowed in plan mode. diff --git a/design-shotgun/SKILL.md b/design-shotgun/SKILL.md index 2f8ac7abb..178416ba2 100644 --- a/design-shotgun/SKILL.md +++ b/design-shotgun/SKILL.md @@ -762,9 +762,7 @@ Replace `SKILL_NAME`, `OUTCOME`, and `USED_BROWSE` before running. ## Plan Status Footer -In plan mode before ExitPlanMode: if the plan file lacks `## GSTACK REVIEW REPORT`, run `~/.claude/skills/gstack/bin/gstack-review-read` and append the standard runs/status/findings table. With `NO_REVIEWS` or empty, append a 5-row placeholder with verdict "NO REVIEWS YET — run `/autoplan`". If a richer report exists, skip. - -PLAN MODE EXCEPTION — always allowed (it's the plan file). +Skills that run plan reviews (`/plan-*-review`, `/codex review`) include the EXIT PLAN MODE GATE blocking checklist at the end of the skill, which verifies the plan file ends with `## GSTACK REVIEW REPORT` before ExitPlanMode is called. Skills that don't run plan reviews (operational skills like `/ship`, `/qa`, `/review`) typically don't operate in plan mode and have no review report to verify; this footer is a no-op for them. Writing the plan file is the one edit allowed in plan mode. # /design-shotgun: Visual Design Exploration diff --git a/devex-review/SKILL.md b/devex-review/SKILL.md index fd8dbf908..49d5ae212 100644 --- a/devex-review/SKILL.md +++ b/devex-review/SKILL.md @@ -763,9 +763,7 @@ Replace `SKILL_NAME`, `OUTCOME`, and `USED_BROWSE` before running. ## Plan Status Footer -In plan mode before ExitPlanMode: if the plan file lacks `## GSTACK REVIEW REPORT`, run `~/.claude/skills/gstack/bin/gstack-review-read` and append the standard runs/status/findings table. With `NO_REVIEWS` or empty, append a 5-row placeholder with verdict "NO REVIEWS YET — run `/autoplan`". If a richer report exists, skip. - -PLAN MODE EXCEPTION — always allowed (it's the plan file). +Skills that run plan reviews (`/plan-*-review`, `/codex review`) include the EXIT PLAN MODE GATE blocking checklist at the end of the skill, which verifies the plan file ends with `## GSTACK REVIEW REPORT` before ExitPlanMode is called. Skills that don't run plan reviews (operational skills like `/ship`, `/qa`, `/review`) typically don't operate in plan mode and have no review report to verify; this footer is a no-op for them. Writing the plan file is the one edit allowed in plan mode. ## Step 0: Detect platform and base branch diff --git a/document-generate/SKILL.md b/document-generate/SKILL.md index d9e0ddeb8..e6cf9965d 100644 --- a/document-generate/SKILL.md +++ b/document-generate/SKILL.md @@ -747,9 +747,7 @@ Replace `SKILL_NAME`, `OUTCOME`, and `USED_BROWSE` before running. ## Plan Status Footer -In plan mode before ExitPlanMode: if the plan file lacks `## GSTACK REVIEW REPORT`, run `~/.claude/skills/gstack/bin/gstack-review-read` and append the standard runs/status/findings table. With `NO_REVIEWS` or empty, append a 5-row placeholder with verdict "NO REVIEWS YET — run `/autoplan`". If a richer report exists, skip. - -PLAN MODE EXCEPTION — always allowed (it's the plan file). +Skills that run plan reviews (`/plan-*-review`, `/codex review`) include the EXIT PLAN MODE GATE blocking checklist at the end of the skill, which verifies the plan file ends with `## GSTACK REVIEW REPORT` before ExitPlanMode is called. Skills that don't run plan reviews (operational skills like `/ship`, `/qa`, `/review`) typically don't operate in plan mode and have no review report to verify; this footer is a no-op for them. Writing the plan file is the one edit allowed in plan mode. ## Step 0: Detect platform and base branch diff --git a/document-release/SKILL.md b/document-release/SKILL.md index 24d48aaaa..b49f4e89b 100644 --- a/document-release/SKILL.md +++ b/document-release/SKILL.md @@ -744,9 +744,7 @@ Replace `SKILL_NAME`, `OUTCOME`, and `USED_BROWSE` before running. ## Plan Status Footer -In plan mode before ExitPlanMode: if the plan file lacks `## GSTACK REVIEW REPORT`, run `~/.claude/skills/gstack/bin/gstack-review-read` and append the standard runs/status/findings table. With `NO_REVIEWS` or empty, append a 5-row placeholder with verdict "NO REVIEWS YET — run `/autoplan`". If a richer report exists, skip. - -PLAN MODE EXCEPTION — always allowed (it's the plan file). +Skills that run plan reviews (`/plan-*-review`, `/codex review`) include the EXIT PLAN MODE GATE blocking checklist at the end of the skill, which verifies the plan file ends with `## GSTACK REVIEW REPORT` before ExitPlanMode is called. Skills that don't run plan reviews (operational skills like `/ship`, `/qa`, `/review`) typically don't operate in plan mode and have no review report to verify; this footer is a no-op for them. Writing the plan file is the one edit allowed in plan mode. ## Step 0: Detect platform and base branch diff --git a/health/SKILL.md b/health/SKILL.md index b5471c0e8..396c980b2 100644 --- a/health/SKILL.md +++ b/health/SKILL.md @@ -742,9 +742,7 @@ Replace `SKILL_NAME`, `OUTCOME`, and `USED_BROWSE` before running. ## Plan Status Footer -In plan mode before ExitPlanMode: if the plan file lacks `## GSTACK REVIEW REPORT`, run `~/.claude/skills/gstack/bin/gstack-review-read` and append the standard runs/status/findings table. With `NO_REVIEWS` or empty, append a 5-row placeholder with verdict "NO REVIEWS YET — run `/autoplan`". If a richer report exists, skip. - -PLAN MODE EXCEPTION — always allowed (it's the plan file). +Skills that run plan reviews (`/plan-*-review`, `/codex review`) include the EXIT PLAN MODE GATE blocking checklist at the end of the skill, which verifies the plan file ends with `## GSTACK REVIEW REPORT` before ExitPlanMode is called. Skills that don't run plan reviews (operational skills like `/ship`, `/qa`, `/review`) typically don't operate in plan mode and have no review report to verify; this footer is a no-op for them. Writing the plan file is the one edit allowed in plan mode. # /health -- Code Quality Dashboard diff --git a/investigate/SKILL.md b/investigate/SKILL.md index 9e2b23f0f..b7780c1c4 100644 --- a/investigate/SKILL.md +++ b/investigate/SKILL.md @@ -781,9 +781,7 @@ Replace `SKILL_NAME`, `OUTCOME`, and `USED_BROWSE` before running. ## Plan Status Footer -In plan mode before ExitPlanMode: if the plan file lacks `## GSTACK REVIEW REPORT`, run `~/.claude/skills/gstack/bin/gstack-review-read` and append the standard runs/status/findings table. With `NO_REVIEWS` or empty, append a 5-row placeholder with verdict "NO REVIEWS YET — run `/autoplan`". If a richer report exists, skip. - -PLAN MODE EXCEPTION — always allowed (it's the plan file). +Skills that run plan reviews (`/plan-*-review`, `/codex review`) include the EXIT PLAN MODE GATE blocking checklist at the end of the skill, which verifies the plan file ends with `## GSTACK REVIEW REPORT` before ExitPlanMode is called. Skills that don't run plan reviews (operational skills like `/ship`, `/qa`, `/review`) typically don't operate in plan mode and have no review report to verify; this footer is a no-op for them. Writing the plan file is the one edit allowed in plan mode. # Systematic Debugging diff --git a/land-and-deploy/SKILL.md b/land-and-deploy/SKILL.md index 1c19c98b0..b58ec2316 100644 --- a/land-and-deploy/SKILL.md +++ b/land-and-deploy/SKILL.md @@ -757,9 +757,7 @@ Replace `SKILL_NAME`, `OUTCOME`, and `USED_BROWSE` before running. ## Plan Status Footer -In plan mode before ExitPlanMode: if the plan file lacks `## GSTACK REVIEW REPORT`, run `~/.claude/skills/gstack/bin/gstack-review-read` and append the standard runs/status/findings table. With `NO_REVIEWS` or empty, append a 5-row placeholder with verdict "NO REVIEWS YET — run `/autoplan`". If a richer report exists, skip. - -PLAN MODE EXCEPTION — always allowed (it's the plan file). +Skills that run plan reviews (`/plan-*-review`, `/codex review`) include the EXIT PLAN MODE GATE blocking checklist at the end of the skill, which verifies the plan file ends with `## GSTACK REVIEW REPORT` before ExitPlanMode is called. Skills that don't run plan reviews (operational skills like `/ship`, `/qa`, `/review`) typically don't operate in plan mode and have no review report to verify; this footer is a no-op for them. Writing the plan file is the one edit allowed in plan mode. ## SETUP (run this check BEFORE any browse command) diff --git a/landing-report/SKILL.md b/landing-report/SKILL.md index e14817cfa..be8aed5e1 100644 --- a/landing-report/SKILL.md +++ b/landing-report/SKILL.md @@ -758,9 +758,7 @@ Replace `SKILL_NAME`, `OUTCOME`, and `USED_BROWSE` before running. ## Plan Status Footer -In plan mode before ExitPlanMode: if the plan file lacks `## GSTACK REVIEW REPORT`, run `~/.claude/skills/gstack/bin/gstack-review-read` and append the standard runs/status/findings table. With `NO_REVIEWS` or empty, append a 5-row placeholder with verdict "NO REVIEWS YET — run `/autoplan`". If a richer report exists, skip. - -PLAN MODE EXCEPTION — always allowed (it's the plan file). +Skills that run plan reviews (`/plan-*-review`, `/codex review`) include the EXIT PLAN MODE GATE blocking checklist at the end of the skill, which verifies the plan file ends with `## GSTACK REVIEW REPORT` before ExitPlanMode is called. Skills that don't run plan reviews (operational skills like `/ship`, `/qa`, `/review`) typically don't operate in plan mode and have no review report to verify; this footer is a no-op for them. Writing the plan file is the one edit allowed in plan mode. --- diff --git a/learn/SKILL.md b/learn/SKILL.md index 899ad42c1..3599115b8 100644 --- a/learn/SKILL.md +++ b/learn/SKILL.md @@ -742,9 +742,7 @@ Replace `SKILL_NAME`, `OUTCOME`, and `USED_BROWSE` before running. ## Plan Status Footer -In plan mode before ExitPlanMode: if the plan file lacks `## GSTACK REVIEW REPORT`, run `~/.claude/skills/gstack/bin/gstack-review-read` and append the standard runs/status/findings table. With `NO_REVIEWS` or empty, append a 5-row placeholder with verdict "NO REVIEWS YET — run `/autoplan`". If a richer report exists, skip. - -PLAN MODE EXCEPTION — always allowed (it's the plan file). +Skills that run plan reviews (`/plan-*-review`, `/codex review`) include the EXIT PLAN MODE GATE blocking checklist at the end of the skill, which verifies the plan file ends with `## GSTACK REVIEW REPORT` before ExitPlanMode is called. Skills that don't run plan reviews (operational skills like `/ship`, `/qa`, `/review`) typically don't operate in plan mode and have no review report to verify; this footer is a no-op for them. Writing the plan file is the one edit allowed in plan mode. # Project Learnings Manager diff --git a/make-pdf/SKILL.md b/make-pdf/SKILL.md index 927b637d9..045e31516 100644 --- a/make-pdf/SKILL.md +++ b/make-pdf/SKILL.md @@ -510,9 +510,7 @@ Replace `SKILL_NAME`, `OUTCOME`, and `USED_BROWSE` before running. ## Plan Status Footer -In plan mode before ExitPlanMode: if the plan file lacks `## GSTACK REVIEW REPORT`, run `~/.claude/skills/gstack/bin/gstack-review-read` and append the standard runs/status/findings table. With `NO_REVIEWS` or empty, append a 5-row placeholder with verdict "NO REVIEWS YET — run `/autoplan`". If a richer report exists, skip. - -PLAN MODE EXCEPTION — always allowed (it's the plan file). +Skills that run plan reviews (`/plan-*-review`, `/codex review`) include the EXIT PLAN MODE GATE blocking checklist at the end of the skill, which verifies the plan file ends with `## GSTACK REVIEW REPORT` before ExitPlanMode is called. Skills that don't run plan reviews (operational skills like `/ship`, `/qa`, `/review`) typically don't operate in plan mode and have no review report to verify; this footer is a no-op for them. Writing the plan file is the one edit allowed in plan mode. # make-pdf: publication-quality PDFs from markdown diff --git a/office-hours/SKILL.md b/office-hours/SKILL.md index 6170f0e5f..c4acb9ea8 100644 --- a/office-hours/SKILL.md +++ b/office-hours/SKILL.md @@ -795,9 +795,7 @@ Replace `SKILL_NAME`, `OUTCOME`, and `USED_BROWSE` before running. ## Plan Status Footer -In plan mode before ExitPlanMode: if the plan file lacks `## GSTACK REVIEW REPORT`, run `~/.claude/skills/gstack/bin/gstack-review-read` and append the standard runs/status/findings table. With `NO_REVIEWS` or empty, append a 5-row placeholder with verdict "NO REVIEWS YET — run `/autoplan`". If a richer report exists, skip. - -PLAN MODE EXCEPTION — always allowed (it's the plan file). +Skills that run plan reviews (`/plan-*-review`, `/codex review`) include the EXIT PLAN MODE GATE blocking checklist at the end of the skill, which verifies the plan file ends with `## GSTACK REVIEW REPORT` before ExitPlanMode is called. Skills that don't run plan reviews (operational skills like `/ship`, `/qa`, `/review`) typically don't operate in plan mode and have no review report to verify; this footer is a no-op for them. Writing the plan file is the one edit allowed in plan mode. ## SETUP (run this check BEFORE any browse command) diff --git a/open-gstack-browser/SKILL.md b/open-gstack-browser/SKILL.md index b510d9d7b..8b4b0c493 100644 --- a/open-gstack-browser/SKILL.md +++ b/open-gstack-browser/SKILL.md @@ -757,9 +757,7 @@ Replace `SKILL_NAME`, `OUTCOME`, and `USED_BROWSE` before running. ## Plan Status Footer -In plan mode before ExitPlanMode: if the plan file lacks `## GSTACK REVIEW REPORT`, run `~/.claude/skills/gstack/bin/gstack-review-read` and append the standard runs/status/findings table. With `NO_REVIEWS` or empty, append a 5-row placeholder with verdict "NO REVIEWS YET — run `/autoplan`". If a richer report exists, skip. - -PLAN MODE EXCEPTION — always allowed (it's the plan file). +Skills that run plan reviews (`/plan-*-review`, `/codex review`) include the EXIT PLAN MODE GATE blocking checklist at the end of the skill, which verifies the plan file ends with `## GSTACK REVIEW REPORT` before ExitPlanMode is called. Skills that don't run plan reviews (operational skills like `/ship`, `/qa`, `/review`) typically don't operate in plan mode and have no review report to verify; this footer is a no-op for them. Writing the plan file is the one edit allowed in plan mode. # /open-gstack-browser — Launch GStack Browser diff --git a/package.json b/package.json index 9c869f84f..601eb963c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gstack", - "version": "1.39.0.0", + "version": "1.39.1.0", "description": "Garry's Stack — Claude Code skills + fast headless browser. One repo, one install, entire AI engineering workflow.", "license": "MIT", "type": "module", diff --git a/pair-agent/SKILL.md b/pair-agent/SKILL.md index 8ddaf5e1a..dd7a51ecd 100644 --- a/pair-agent/SKILL.md +++ b/pair-agent/SKILL.md @@ -758,9 +758,7 @@ Replace `SKILL_NAME`, `OUTCOME`, and `USED_BROWSE` before running. ## Plan Status Footer -In plan mode before ExitPlanMode: if the plan file lacks `## GSTACK REVIEW REPORT`, run `~/.claude/skills/gstack/bin/gstack-review-read` and append the standard runs/status/findings table. With `NO_REVIEWS` or empty, append a 5-row placeholder with verdict "NO REVIEWS YET — run `/autoplan`". If a richer report exists, skip. - -PLAN MODE EXCEPTION — always allowed (it's the plan file). +Skills that run plan reviews (`/plan-*-review`, `/codex review`) include the EXIT PLAN MODE GATE blocking checklist at the end of the skill, which verifies the plan file ends with `## GSTACK REVIEW REPORT` before ExitPlanMode is called. Skills that don't run plan reviews (operational skills like `/ship`, `/qa`, `/review`) typically don't operate in plan mode and have no review report to verify; this footer is a no-op for them. Writing the plan file is the one edit allowed in plan mode. # /pair-agent — Share Your Browser With Another AI Agent diff --git a/plan-ceo-review/SKILL.md b/plan-ceo-review/SKILL.md index 227db949b..91c1cfc79 100644 --- a/plan-ceo-review/SKILL.md +++ b/plan-ceo-review/SKILL.md @@ -789,9 +789,7 @@ Replace `SKILL_NAME`, `OUTCOME`, and `USED_BROWSE` before running. ## Plan Status Footer -In plan mode before ExitPlanMode: if the plan file lacks `## GSTACK REVIEW REPORT`, run `~/.claude/skills/gstack/bin/gstack-review-read` and append the standard runs/status/findings table. With `NO_REVIEWS` or empty, append a 5-row placeholder with verdict "NO REVIEWS YET — run `/autoplan`". If a richer report exists, skip. - -PLAN MODE EXCEPTION — always allowed (it's the plan file). +Skills that run plan reviews (`/plan-*-review`, `/codex review`) include the EXIT PLAN MODE GATE blocking checklist at the end of the skill, which verifies the plan file ends with `## GSTACK REVIEW REPORT` before ExitPlanMode is called. Skills that don't run plan reviews (operational skills like `/ship`, `/qa`, `/review`) typically don't operate in plan mode and have no review report to verify; this footer is a no-op for them. Writing the plan file is the one edit allowed in plan mode. ## Step 0: Detect platform and base branch @@ -2198,3 +2196,28 @@ already knows. A good test: would this insight save time in a future session? If │ (Sec 11) │ UI review │ detected │ detected │ │ └─────────────┴──────────────┴──────────────┴──────────────┴────────────────────┘ ``` + +## EXIT PLAN MODE GATE (BLOCKING) + +Before calling ExitPlanMode, run this self-check. If any item fails, do the +missing work — do NOT call ExitPlanMode: + +1. Read the plan file with the Read tool (after your most recent write to it). +2. Confirm the LAST `## ` heading in the file is `## GSTACK REVIEW REPORT`. + In-body prose that mentions "outside voice", "codex findings", or similar + does NOT count — only the structured `## GSTACK REVIEW REPORT` section + satisfies this check. +3. Confirm the report contains: a Runs / Status / Findings table, a VERDICT + line, and absorbs CODEX / CROSS-MODEL / UNRESOLVED lines if applicable. +4. If a plan file is in context for this skill invocation: confirm + `gstack-review-log` was called and `gstack-review-read` was run at least + once. If no plan file is in context (e.g. `/codex consult` against a + diff with no plan), this check short-circuits — checks 1-3 already + short-circuit when no plan file exists. + +Failing this gate and calling ExitPlanMode anyway is a contract violation — +the user will see a plan whose review report is missing or stale, and will +(correctly) reject it. Self-deception failure mode to watch for: feeling +"done" after writing review prose into the plan body. The body prose is not +the report. The report is a separate, structured, table-bearing section that +must be the file's terminal heading. diff --git a/plan-ceo-review/SKILL.md.tmpl b/plan-ceo-review/SKILL.md.tmpl index 6069ff2b1..4e4861d62 100644 --- a/plan-ceo-review/SKILL.md.tmpl +++ b/plan-ceo-review/SKILL.md.tmpl @@ -892,3 +892,5 @@ If promoted, copy the CEO plan content to `docs/designs/{FEATURE}.md` (create th │ (Sec 11) │ UI review │ detected │ detected │ │ └─────────────┴──────────────┴──────────────┴──────────────┴────────────────────┘ ``` + +{{EXIT_PLAN_MODE_GATE}} diff --git a/plan-design-review/SKILL.md b/plan-design-review/SKILL.md index dc2f2136a..580268767 100644 --- a/plan-design-review/SKILL.md +++ b/plan-design-review/SKILL.md @@ -762,9 +762,7 @@ Replace `SKILL_NAME`, `OUTCOME`, and `USED_BROWSE` before running. ## Plan Status Footer -In plan mode before ExitPlanMode: if the plan file lacks `## GSTACK REVIEW REPORT`, run `~/.claude/skills/gstack/bin/gstack-review-read` and append the standard runs/status/findings table. With `NO_REVIEWS` or empty, append a 5-row placeholder with verdict "NO REVIEWS YET — run `/autoplan`". If a richer report exists, skip. - -PLAN MODE EXCEPTION — always allowed (it's the plan file). +Skills that run plan reviews (`/plan-*-review`, `/codex review`) include the EXIT PLAN MODE GATE blocking checklist at the end of the skill, which verifies the plan file ends with `## GSTACK REVIEW REPORT` before ExitPlanMode is called. Skills that don't run plan reviews (operational skills like `/ship`, `/qa`, `/review`) typically don't operate in plan mode and have no review report to verify; this footer is a no-op for them. Writing the plan file is the one edit allowed in plan mode. ## Step 0: Detect platform and base branch @@ -1918,3 +1916,28 @@ Use AskUserQuestion to present the next step. Include only applicable options: * One sentence max per option. * After each pass, pause and wait for feedback. * Rate before and after each pass for scannability. + +## EXIT PLAN MODE GATE (BLOCKING) + +Before calling ExitPlanMode, run this self-check. If any item fails, do the +missing work — do NOT call ExitPlanMode: + +1. Read the plan file with the Read tool (after your most recent write to it). +2. Confirm the LAST `## ` heading in the file is `## GSTACK REVIEW REPORT`. + In-body prose that mentions "outside voice", "codex findings", or similar + does NOT count — only the structured `## GSTACK REVIEW REPORT` section + satisfies this check. +3. Confirm the report contains: a Runs / Status / Findings table, a VERDICT + line, and absorbs CODEX / CROSS-MODEL / UNRESOLVED lines if applicable. +4. If a plan file is in context for this skill invocation: confirm + `gstack-review-log` was called and `gstack-review-read` was run at least + once. If no plan file is in context (e.g. `/codex consult` against a + diff with no plan), this check short-circuits — checks 1-3 already + short-circuit when no plan file exists. + +Failing this gate and calling ExitPlanMode anyway is a contract violation — +the user will see a plan whose review report is missing or stale, and will +(correctly) reject it. Self-deception failure mode to watch for: feeling +"done" after writing review prose into the plan body. The body prose is not +the report. The report is a separate, structured, table-bearing section that +must be the file's terminal heading. diff --git a/plan-design-review/SKILL.md.tmpl b/plan-design-review/SKILL.md.tmpl index 3c05c76a9..7ff17284f 100644 --- a/plan-design-review/SKILL.md.tmpl +++ b/plan-design-review/SKILL.md.tmpl @@ -477,3 +477,5 @@ Use AskUserQuestion to present the next step. Include only applicable options: * One sentence max per option. * After each pass, pause and wait for feedback. * Rate before and after each pass for scannability. + +{{EXIT_PLAN_MODE_GATE}} diff --git a/plan-devex-review/SKILL.md b/plan-devex-review/SKILL.md index d1421a59a..29014b4a4 100644 --- a/plan-devex-review/SKILL.md +++ b/plan-devex-review/SKILL.md @@ -766,9 +766,7 @@ Replace `SKILL_NAME`, `OUTCOME`, and `USED_BROWSE` before running. ## Plan Status Footer -In plan mode before ExitPlanMode: if the plan file lacks `## GSTACK REVIEW REPORT`, run `~/.claude/skills/gstack/bin/gstack-review-read` and append the standard runs/status/findings table. With `NO_REVIEWS` or empty, append a 5-row placeholder with verdict "NO REVIEWS YET — run `/autoplan`". If a richer report exists, skip. - -PLAN MODE EXCEPTION — always allowed (it's the plan file). +Skills that run plan reviews (`/plan-*-review`, `/codex review`) include the EXIT PLAN MODE GATE blocking checklist at the end of the skill, which verifies the plan file ends with `## GSTACK REVIEW REPORT` before ExitPlanMode is called. Skills that don't run plan reviews (operational skills like `/ship`, `/qa`, `/review`) typically don't operate in plan mode and have no review report to verify; this footer is a no-op for them. Writing the plan file is the one edit allowed in plan mode. ## Step 0: Detect platform and base branch @@ -2119,3 +2117,28 @@ Outside voice| Recommended | Recommended | Skip * One sentence max per option. * After each pass, pause and wait for feedback before moving on. * Rate before and after each pass for scannability. + +## EXIT PLAN MODE GATE (BLOCKING) + +Before calling ExitPlanMode, run this self-check. If any item fails, do the +missing work — do NOT call ExitPlanMode: + +1. Read the plan file with the Read tool (after your most recent write to it). +2. Confirm the LAST `## ` heading in the file is `## GSTACK REVIEW REPORT`. + In-body prose that mentions "outside voice", "codex findings", or similar + does NOT count — only the structured `## GSTACK REVIEW REPORT` section + satisfies this check. +3. Confirm the report contains: a Runs / Status / Findings table, a VERDICT + line, and absorbs CODEX / CROSS-MODEL / UNRESOLVED lines if applicable. +4. If a plan file is in context for this skill invocation: confirm + `gstack-review-log` was called and `gstack-review-read` was run at least + once. If no plan file is in context (e.g. `/codex consult` against a + diff with no plan), this check short-circuits — checks 1-3 already + short-circuit when no plan file exists. + +Failing this gate and calling ExitPlanMode anyway is a contract violation — +the user will see a plan whose review report is missing or stale, and will +(correctly) reject it. Self-deception failure mode to watch for: feeling +"done" after writing review prose into the plan body. The body prose is not +the report. The report is a separate, structured, table-bearing section that +must be the file's terminal heading. diff --git a/plan-devex-review/SKILL.md.tmpl b/plan-devex-review/SKILL.md.tmpl index f95b8835f..e40f05b52 100644 --- a/plan-devex-review/SKILL.md.tmpl +++ b/plan-devex-review/SKILL.md.tmpl @@ -829,3 +829,5 @@ Outside voice| Recommended | Recommended | Skip * One sentence max per option. * After each pass, pause and wait for feedback before moving on. * Rate before and after each pass for scannability. + +{{EXIT_PLAN_MODE_GATE}} diff --git a/plan-eng-review/SKILL.md b/plan-eng-review/SKILL.md index 9ba4def39..1dbc3c96e 100644 --- a/plan-eng-review/SKILL.md +++ b/plan-eng-review/SKILL.md @@ -764,9 +764,7 @@ Replace `SKILL_NAME`, `OUTCOME`, and `USED_BROWSE` before running. ## Plan Status Footer -In plan mode before ExitPlanMode: if the plan file lacks `## GSTACK REVIEW REPORT`, run `~/.claude/skills/gstack/bin/gstack-review-read` and append the standard runs/status/findings table. With `NO_REVIEWS` or empty, append a 5-row placeholder with verdict "NO REVIEWS YET — run `/autoplan`". If a richer report exists, skip. - -PLAN MODE EXCEPTION — always allowed (it's the plan file). +Skills that run plan reviews (`/plan-*-review`, `/codex review`) include the EXIT PLAN MODE GATE blocking checklist at the end of the skill, which verifies the plan file ends with `## GSTACK REVIEW REPORT` before ExitPlanMode is called. Skills that don't run plan reviews (operational skills like `/ship`, `/qa`, `/review`) typically don't operate in plan mode and have no review report to verify; this footer is a no-op for them. Writing the plan file is the one edit allowed in plan mode. @@ -1725,3 +1723,28 @@ Use AskUserQuestion with only the applicable options: ## Unresolved decisions If the user does not respond to an AskUserQuestion or interrupts to move on, note which decisions were left unresolved. At the end of the review, list these as "Unresolved decisions that may bite you later" — never silently default to an option. + +## EXIT PLAN MODE GATE (BLOCKING) + +Before calling ExitPlanMode, run this self-check. If any item fails, do the +missing work — do NOT call ExitPlanMode: + +1. Read the plan file with the Read tool (after your most recent write to it). +2. Confirm the LAST `## ` heading in the file is `## GSTACK REVIEW REPORT`. + In-body prose that mentions "outside voice", "codex findings", or similar + does NOT count — only the structured `## GSTACK REVIEW REPORT` section + satisfies this check. +3. Confirm the report contains: a Runs / Status / Findings table, a VERDICT + line, and absorbs CODEX / CROSS-MODEL / UNRESOLVED lines if applicable. +4. If a plan file is in context for this skill invocation: confirm + `gstack-review-log` was called and `gstack-review-read` was run at least + once. If no plan file is in context (e.g. `/codex consult` against a + diff with no plan), this check short-circuits — checks 1-3 already + short-circuit when no plan file exists. + +Failing this gate and calling ExitPlanMode anyway is a contract violation — +the user will see a plan whose review report is missing or stale, and will +(correctly) reject it. Self-deception failure mode to watch for: feeling +"done" after writing review prose into the plan body. The body prose is not +the report. The report is a separate, structured, table-bearing section that +must be the file's terminal heading. diff --git a/plan-eng-review/SKILL.md.tmpl b/plan-eng-review/SKILL.md.tmpl index fea0ea328..8a167c14b 100644 --- a/plan-eng-review/SKILL.md.tmpl +++ b/plan-eng-review/SKILL.md.tmpl @@ -340,3 +340,5 @@ Use AskUserQuestion with only the applicable options: ## Unresolved decisions If the user does not respond to an AskUserQuestion or interrupts to move on, note which decisions were left unresolved. At the end of the review, list these as "Unresolved decisions that may bite you later" — never silently default to an option. + +{{EXIT_PLAN_MODE_GATE}} diff --git a/plan-tune/SKILL.md b/plan-tune/SKILL.md index 471fbe517..c575ef4f4 100644 --- a/plan-tune/SKILL.md +++ b/plan-tune/SKILL.md @@ -753,9 +753,7 @@ Replace `SKILL_NAME`, `OUTCOME`, and `USED_BROWSE` before running. ## Plan Status Footer -In plan mode before ExitPlanMode: if the plan file lacks `## GSTACK REVIEW REPORT`, run `~/.claude/skills/gstack/bin/gstack-review-read` and append the standard runs/status/findings table. With `NO_REVIEWS` or empty, append a 5-row placeholder with verdict "NO REVIEWS YET — run `/autoplan`". If a richer report exists, skip. - -PLAN MODE EXCEPTION — always allowed (it's the plan file). +Skills that run plan reviews (`/plan-*-review`, `/codex review`) include the EXIT PLAN MODE GATE blocking checklist at the end of the skill, which verifies the plan file ends with `## GSTACK REVIEW REPORT` before ExitPlanMode is called. Skills that don't run plan reviews (operational skills like `/ship`, `/qa`, `/review`) typically don't operate in plan mode and have no review report to verify; this footer is a no-op for them. Writing the plan file is the one edit allowed in plan mode. # /plan-tune — Question Tuning + Developer Profile (v1 observational) diff --git a/qa-only/SKILL.md b/qa-only/SKILL.md index 77dcc4d23..3e95cb032 100644 --- a/qa-only/SKILL.md +++ b/qa-only/SKILL.md @@ -759,9 +759,7 @@ Replace `SKILL_NAME`, `OUTCOME`, and `USED_BROWSE` before running. ## Plan Status Footer -In plan mode before ExitPlanMode: if the plan file lacks `## GSTACK REVIEW REPORT`, run `~/.claude/skills/gstack/bin/gstack-review-read` and append the standard runs/status/findings table. With `NO_REVIEWS` or empty, append a 5-row placeholder with verdict "NO REVIEWS YET — run `/autoplan`". If a richer report exists, skip. - -PLAN MODE EXCEPTION — always allowed (it's the plan file). +Skills that run plan reviews (`/plan-*-review`, `/codex review`) include the EXIT PLAN MODE GATE blocking checklist at the end of the skill, which verifies the plan file ends with `## GSTACK REVIEW REPORT` before ExitPlanMode is called. Skills that don't run plan reviews (operational skills like `/ship`, `/qa`, `/review`) typically don't operate in plan mode and have no review report to verify; this footer is a no-op for them. Writing the plan file is the one edit allowed in plan mode. # /qa-only: Report-Only QA Testing diff --git a/qa/SKILL.md b/qa/SKILL.md index 0b56e53e2..aec716f95 100644 --- a/qa/SKILL.md +++ b/qa/SKILL.md @@ -765,9 +765,7 @@ Replace `SKILL_NAME`, `OUTCOME`, and `USED_BROWSE` before running. ## Plan Status Footer -In plan mode before ExitPlanMode: if the plan file lacks `## GSTACK REVIEW REPORT`, run `~/.claude/skills/gstack/bin/gstack-review-read` and append the standard runs/status/findings table. With `NO_REVIEWS` or empty, append a 5-row placeholder with verdict "NO REVIEWS YET — run `/autoplan`". If a richer report exists, skip. - -PLAN MODE EXCEPTION — always allowed (it's the plan file). +Skills that run plan reviews (`/plan-*-review`, `/codex review`) include the EXIT PLAN MODE GATE blocking checklist at the end of the skill, which verifies the plan file ends with `## GSTACK REVIEW REPORT` before ExitPlanMode is called. Skills that don't run plan reviews (operational skills like `/ship`, `/qa`, `/review`) typically don't operate in plan mode and have no review report to verify; this footer is a no-op for them. Writing the plan file is the one edit allowed in plan mode. ## Step 0: Detect platform and base branch diff --git a/retro/SKILL.md b/retro/SKILL.md index 2d2684afe..92d58f7b8 100644 --- a/retro/SKILL.md +++ b/retro/SKILL.md @@ -759,9 +759,7 @@ Replace `SKILL_NAME`, `OUTCOME`, and `USED_BROWSE` before running. ## Plan Status Footer -In plan mode before ExitPlanMode: if the plan file lacks `## GSTACK REVIEW REPORT`, run `~/.claude/skills/gstack/bin/gstack-review-read` and append the standard runs/status/findings table. With `NO_REVIEWS` or empty, append a 5-row placeholder with verdict "NO REVIEWS YET — run `/autoplan`". If a richer report exists, skip. - -PLAN MODE EXCEPTION — always allowed (it's the plan file). +Skills that run plan reviews (`/plan-*-review`, `/codex review`) include the EXIT PLAN MODE GATE blocking checklist at the end of the skill, which verifies the plan file ends with `## GSTACK REVIEW REPORT` before ExitPlanMode is called. Skills that don't run plan reviews (operational skills like `/ship`, `/qa`, `/review`) typically don't operate in plan mode and have no review report to verify; this footer is a no-op for them. Writing the plan file is the one edit allowed in plan mode. ## Step 0: Detect platform and base branch diff --git a/review/SKILL.md b/review/SKILL.md index 4d134d175..88378396a 100644 --- a/review/SKILL.md +++ b/review/SKILL.md @@ -762,9 +762,7 @@ Replace `SKILL_NAME`, `OUTCOME`, and `USED_BROWSE` before running. ## Plan Status Footer -In plan mode before ExitPlanMode: if the plan file lacks `## GSTACK REVIEW REPORT`, run `~/.claude/skills/gstack/bin/gstack-review-read` and append the standard runs/status/findings table. With `NO_REVIEWS` or empty, append a 5-row placeholder with verdict "NO REVIEWS YET — run `/autoplan`". If a richer report exists, skip. - -PLAN MODE EXCEPTION — always allowed (it's the plan file). +Skills that run plan reviews (`/plan-*-review`, `/codex review`) include the EXIT PLAN MODE GATE blocking checklist at the end of the skill, which verifies the plan file ends with `## GSTACK REVIEW REPORT` before ExitPlanMode is called. Skills that don't run plan reviews (operational skills like `/ship`, `/qa`, `/review`) typically don't operate in plan mode and have no review report to verify; this footer is a no-op for them. Writing the plan file is the one edit allowed in plan mode. ## Step 0: Detect platform and base branch diff --git a/scrape/SKILL.md b/scrape/SKILL.md index b255abe08..7fb04d3f6 100644 --- a/scrape/SKILL.md +++ b/scrape/SKILL.md @@ -758,9 +758,7 @@ Replace `SKILL_NAME`, `OUTCOME`, and `USED_BROWSE` before running. ## Plan Status Footer -In plan mode before ExitPlanMode: if the plan file lacks `## GSTACK REVIEW REPORT`, run `~/.claude/skills/gstack/bin/gstack-review-read` and append the standard runs/status/findings table. With `NO_REVIEWS` or empty, append a 5-row placeholder with verdict "NO REVIEWS YET — run `/autoplan`". If a richer report exists, skip. - -PLAN MODE EXCEPTION — always allowed (it's the plan file). +Skills that run plan reviews (`/plan-*-review`, `/codex review`) include the EXIT PLAN MODE GATE blocking checklist at the end of the skill, which verifies the plan file ends with `## GSTACK REVIEW REPORT` before ExitPlanMode is called. Skills that don't run plan reviews (operational skills like `/ship`, `/qa`, `/review`) typically don't operate in plan mode and have no review report to verify; this footer is a no-op for them. Writing the plan file is the one edit allowed in plan mode. # /scrape — pull data from a page diff --git a/scripts/resolvers/index.ts b/scripts/resolvers/index.ts index 78d592b0e..c5cbd0445 100644 --- a/scripts/resolvers/index.ts +++ b/scripts/resolvers/index.ts @@ -11,7 +11,7 @@ import { generateTestFailureTriage } from './preamble'; import { generateCommandReference, generateSnapshotFlags, generateBrowseSetup } from './browse'; import { generateDesignMethodology, generateDesignHardRules, generateDesignOutsideVoices, generateDesignReviewLite, generateDesignSketch, generateDesignSetup, generateDesignMockup, generateDesignShotgunLoop, generateTasteProfile, generateUXPrinciples } from './design'; import { generateTestBootstrap, generateTestCoverageAuditPlan, generateTestCoverageAuditShip, generateTestCoverageAuditReview } from './testing'; -import { generateReviewDashboard, generatePlanFileReviewReport, generateAntiShortcutClause, generateSpecReviewLoop, generateBenefitsFrom, generateCodexSecondOpinion, generateAdversarialStep, generateCodexPlanReview, generatePlanCompletionAuditShip, generatePlanCompletionAuditReview, generatePlanVerificationExec, generateScopeDrift, generateCrossReviewDedup } from './review'; +import { generateReviewDashboard, generatePlanFileReviewReport, generateExitPlanModeGate, generateAntiShortcutClause, generateSpecReviewLoop, generateBenefitsFrom, generateCodexSecondOpinion, generateAdversarialStep, generateCodexPlanReview, generatePlanCompletionAuditShip, generatePlanCompletionAuditReview, generatePlanVerificationExec, generateScopeDrift, generateCrossReviewDedup } from './review'; import { generateSlugEval, generateSlugSetup, generateBaseBranchDetect, generateDeployBootstrap, generateQAMethodology, generateCoAuthorTrailer, generateChangelogWorkflow } from './utility'; import { generateLearningsSearch, generateLearningsLog } from './learnings'; import { generateConfidenceCalibration } from './confidence'; @@ -40,6 +40,7 @@ export const RESOLVERS: Record = { DESIGN_REVIEW_LITE: generateDesignReviewLite, REVIEW_DASHBOARD: generateReviewDashboard, PLAN_FILE_REVIEW_REPORT: generatePlanFileReviewReport, + EXIT_PLAN_MODE_GATE: generateExitPlanModeGate, ANTI_SHORTCUT_CLAUSE: generateAntiShortcutClause, TEST_BOOTSTRAP: generateTestBootstrap, TEST_COVERAGE_AUDIT_PLAN: generateTestCoverageAuditPlan, diff --git a/scripts/resolvers/preamble/generate-completion-status.ts b/scripts/resolvers/preamble/generate-completion-status.ts index 21c0bd5e7..0c50da1c7 100644 --- a/scripts/resolvers/preamble/generate-completion-status.ts +++ b/scripts/resolvers/preamble/generate-completion-status.ts @@ -81,7 +81,5 @@ Replace \`SKILL_NAME\`, \`OUTCOME\`, and \`USED_BROWSE\` before running. ## Plan Status Footer -In plan mode before ExitPlanMode: if the plan file lacks \`## GSTACK REVIEW REPORT\`, run \`~/.claude/skills/gstack/bin/gstack-review-read\` and append the standard runs/status/findings table. With \`NO_REVIEWS\` or empty, append a 5-row placeholder with verdict "NO REVIEWS YET — run \`/autoplan\`". If a richer report exists, skip. - -PLAN MODE EXCEPTION — always allowed (it's the plan file).`; +Skills that run plan reviews (\`/plan-*-review\`, \`/codex review\`) include the EXIT PLAN MODE GATE blocking checklist at the end of the skill, which verifies the plan file ends with \`## GSTACK REVIEW REPORT\` before ExitPlanMode is called. Skills that don't run plan reviews (operational skills like \`/ship\`, \`/qa\`, \`/review\`) typically don't operate in plan mode and have no review report to verify; this footer is a no-op for them. Writing the plan file is the one edit allowed in plan mode.`; } diff --git a/scripts/resolvers/review.ts b/scripts/resolvers/review.ts index 263767d69..3b9e2999d 100644 --- a/scripts/resolvers/review.ts +++ b/scripts/resolvers/review.ts @@ -158,6 +158,33 @@ there — the user then sees a plan whose review report is not at the bottom and (correctly) rejects it.`; } +export function generateExitPlanModeGate(_ctx: TemplateContext): string { + return `## EXIT PLAN MODE GATE (BLOCKING) + +Before calling ExitPlanMode, run this self-check. If any item fails, do the +missing work — do NOT call ExitPlanMode: + +1. Read the plan file with the Read tool (after your most recent write to it). +2. Confirm the LAST \`## \` heading in the file is \`## GSTACK REVIEW REPORT\`. + In-body prose that mentions "outside voice", "codex findings", or similar + does NOT count — only the structured \`## GSTACK REVIEW REPORT\` section + satisfies this check. +3. Confirm the report contains: a Runs / Status / Findings table, a VERDICT + line, and absorbs CODEX / CROSS-MODEL / UNRESOLVED lines if applicable. +4. If a plan file is in context for this skill invocation: confirm + \`gstack-review-log\` was called and \`gstack-review-read\` was run at least + once. If no plan file is in context (e.g. \`/codex consult\` against a + diff with no plan), this check short-circuits — checks 1-3 already + short-circuit when no plan file exists. + +Failing this gate and calling ExitPlanMode anyway is a contract violation — +the user will see a plan whose review report is missing or stale, and will +(correctly) reject it. Self-deception failure mode to watch for: feeling +"done" after writing review prose into the plan body. The body prose is not +the report. The report is a separate, structured, table-bearing section that +must be the file's terminal heading.`; +} + export function generateAntiShortcutClause(_ctx: TemplateContext): string { return `**Anti-shortcut clause:** The plan file is the OUTPUT of the interactive review, not a substitute for it. Writing every finding into one plan write and calling ExitPlanMode without firing AskUserQuestion is the precise failure mode of the May 2026 transcript bug — the model explored, found issues, and dumped them into a deliverable rather than walking the user through them. If you have ANY non-trivial finding in any review section, the path from finding to ExitPlanMode goes THROUGH AskUserQuestion. Zero findings in every section is the only path to ExitPlanMode that bypasses AskUserQuestion. If you find yourself wanting to write a plan with findings before asking, stop and call AskUserQuestion now — that's the bug, recognize it.`; } diff --git a/setup-browser-cookies/SKILL.md b/setup-browser-cookies/SKILL.md index 4e46c0b25..8b80fd58b 100644 --- a/setup-browser-cookies/SKILL.md +++ b/setup-browser-cookies/SKILL.md @@ -471,9 +471,7 @@ Replace `SKILL_NAME`, `OUTCOME`, and `USED_BROWSE` before running. ## Plan Status Footer -In plan mode before ExitPlanMode: if the plan file lacks `## GSTACK REVIEW REPORT`, run `~/.claude/skills/gstack/bin/gstack-review-read` and append the standard runs/status/findings table. With `NO_REVIEWS` or empty, append a 5-row placeholder with verdict "NO REVIEWS YET — run `/autoplan`". If a richer report exists, skip. - -PLAN MODE EXCEPTION — always allowed (it's the plan file). +Skills that run plan reviews (`/plan-*-review`, `/codex review`) include the EXIT PLAN MODE GATE blocking checklist at the end of the skill, which verifies the plan file ends with `## GSTACK REVIEW REPORT` before ExitPlanMode is called. Skills that don't run plan reviews (operational skills like `/ship`, `/qa`, `/review`) typically don't operate in plan mode and have no review report to verify; this footer is a no-op for them. Writing the plan file is the one edit allowed in plan mode. # Setup Browser Cookies diff --git a/setup-deploy/SKILL.md b/setup-deploy/SKILL.md index 2731365c5..0e09cc491 100644 --- a/setup-deploy/SKILL.md +++ b/setup-deploy/SKILL.md @@ -743,9 +743,7 @@ Replace `SKILL_NAME`, `OUTCOME`, and `USED_BROWSE` before running. ## Plan Status Footer -In plan mode before ExitPlanMode: if the plan file lacks `## GSTACK REVIEW REPORT`, run `~/.claude/skills/gstack/bin/gstack-review-read` and append the standard runs/status/findings table. With `NO_REVIEWS` or empty, append a 5-row placeholder with verdict "NO REVIEWS YET — run `/autoplan`". If a richer report exists, skip. - -PLAN MODE EXCEPTION — always allowed (it's the plan file). +Skills that run plan reviews (`/plan-*-review`, `/codex review`) include the EXIT PLAN MODE GATE blocking checklist at the end of the skill, which verifies the plan file ends with `## GSTACK REVIEW REPORT` before ExitPlanMode is called. Skills that don't run plan reviews (operational skills like `/ship`, `/qa`, `/review`) typically don't operate in plan mode and have no review report to verify; this footer is a no-op for them. Writing the plan file is the one edit allowed in plan mode. # /setup-deploy — Configure Deployment for gstack diff --git a/setup-gbrain/SKILL.md b/setup-gbrain/SKILL.md index c1abd775c..a31b7de7a 100644 --- a/setup-gbrain/SKILL.md +++ b/setup-gbrain/SKILL.md @@ -744,9 +744,7 @@ Replace `SKILL_NAME`, `OUTCOME`, and `USED_BROWSE` before running. ## Plan Status Footer -In plan mode before ExitPlanMode: if the plan file lacks `## GSTACK REVIEW REPORT`, run `~/.claude/skills/gstack/bin/gstack-review-read` and append the standard runs/status/findings table. With `NO_REVIEWS` or empty, append a 5-row placeholder with verdict "NO REVIEWS YET — run `/autoplan`". If a richer report exists, skip. - -PLAN MODE EXCEPTION — always allowed (it's the plan file). +Skills that run plan reviews (`/plan-*-review`, `/codex review`) include the EXIT PLAN MODE GATE blocking checklist at the end of the skill, which verifies the plan file ends with `## GSTACK REVIEW REPORT` before ExitPlanMode is called. Skills that don't run plan reviews (operational skills like `/ship`, `/qa`, `/review`) typically don't operate in plan mode and have no review report to verify; this footer is a no-op for them. Writing the plan file is the one edit allowed in plan mode. # /setup-gbrain — Coding-Agent Onboarding for gbrain diff --git a/ship/SKILL.md b/ship/SKILL.md index 25119fb39..dcab2bdda 100644 --- a/ship/SKILL.md +++ b/ship/SKILL.md @@ -763,9 +763,7 @@ Replace `SKILL_NAME`, `OUTCOME`, and `USED_BROWSE` before running. ## Plan Status Footer -In plan mode before ExitPlanMode: if the plan file lacks `## GSTACK REVIEW REPORT`, run `~/.claude/skills/gstack/bin/gstack-review-read` and append the standard runs/status/findings table. With `NO_REVIEWS` or empty, append a 5-row placeholder with verdict "NO REVIEWS YET — run `/autoplan`". If a richer report exists, skip. - -PLAN MODE EXCEPTION — always allowed (it's the plan file). +Skills that run plan reviews (`/plan-*-review`, `/codex review`) include the EXIT PLAN MODE GATE blocking checklist at the end of the skill, which verifies the plan file ends with `## GSTACK REVIEW REPORT` before ExitPlanMode is called. Skills that don't run plan reviews (operational skills like `/ship`, `/qa`, `/review`) typically don't operate in plan mode and have no review report to verify; this footer is a no-op for them. Writing the plan file is the one edit allowed in plan mode. ## Step 0: Detect platform and base branch diff --git a/skillify/SKILL.md b/skillify/SKILL.md index 503f8262b..afef0e3a1 100644 --- a/skillify/SKILL.md +++ b/skillify/SKILL.md @@ -759,9 +759,7 @@ Replace `SKILL_NAME`, `OUTCOME`, and `USED_BROWSE` before running. ## Plan Status Footer -In plan mode before ExitPlanMode: if the plan file lacks `## GSTACK REVIEW REPORT`, run `~/.claude/skills/gstack/bin/gstack-review-read` and append the standard runs/status/findings table. With `NO_REVIEWS` or empty, append a 5-row placeholder with verdict "NO REVIEWS YET — run `/autoplan`". If a richer report exists, skip. - -PLAN MODE EXCEPTION — always allowed (it's the plan file). +Skills that run plan reviews (`/plan-*-review`, `/codex review`) include the EXIT PLAN MODE GATE blocking checklist at the end of the skill, which verifies the plan file ends with `## GSTACK REVIEW REPORT` before ExitPlanMode is called. Skills that don't run plan reviews (operational skills like `/ship`, `/qa`, `/review`) typically don't operate in plan mode and have no review report to verify; this footer is a no-op for them. Writing the plan file is the one edit allowed in plan mode. # /skillify — codify the last scrape into a permanent skill diff --git a/sync-gbrain/SKILL.md b/sync-gbrain/SKILL.md index 8dba77386..f7b9b5230 100644 --- a/sync-gbrain/SKILL.md +++ b/sync-gbrain/SKILL.md @@ -744,9 +744,7 @@ Replace `SKILL_NAME`, `OUTCOME`, and `USED_BROWSE` before running. ## Plan Status Footer -In plan mode before ExitPlanMode: if the plan file lacks `## GSTACK REVIEW REPORT`, run `~/.claude/skills/gstack/bin/gstack-review-read` and append the standard runs/status/findings table. With `NO_REVIEWS` or empty, append a 5-row placeholder with verdict "NO REVIEWS YET — run `/autoplan`". If a richer report exists, skip. - -PLAN MODE EXCEPTION — always allowed (it's the plan file). +Skills that run plan reviews (`/plan-*-review`, `/codex review`) include the EXIT PLAN MODE GATE blocking checklist at the end of the skill, which verifies the plan file ends with `## GSTACK REVIEW REPORT` before ExitPlanMode is called. Skills that don't run plan reviews (operational skills like `/ship`, `/qa`, `/review`) typically don't operate in plan mode and have no review report to verify; this footer is a no-op for them. Writing the plan file is the one edit allowed in plan mode. # /sync-gbrain — Keep gbrain current and teach the agent to use it diff --git a/test/fixtures/golden/claude-ship-SKILL.md b/test/fixtures/golden/claude-ship-SKILL.md index 25119fb39..dcab2bdda 100644 --- a/test/fixtures/golden/claude-ship-SKILL.md +++ b/test/fixtures/golden/claude-ship-SKILL.md @@ -763,9 +763,7 @@ Replace `SKILL_NAME`, `OUTCOME`, and `USED_BROWSE` before running. ## Plan Status Footer -In plan mode before ExitPlanMode: if the plan file lacks `## GSTACK REVIEW REPORT`, run `~/.claude/skills/gstack/bin/gstack-review-read` and append the standard runs/status/findings table. With `NO_REVIEWS` or empty, append a 5-row placeholder with verdict "NO REVIEWS YET — run `/autoplan`". If a richer report exists, skip. - -PLAN MODE EXCEPTION — always allowed (it's the plan file). +Skills that run plan reviews (`/plan-*-review`, `/codex review`) include the EXIT PLAN MODE GATE blocking checklist at the end of the skill, which verifies the plan file ends with `## GSTACK REVIEW REPORT` before ExitPlanMode is called. Skills that don't run plan reviews (operational skills like `/ship`, `/qa`, `/review`) typically don't operate in plan mode and have no review report to verify; this footer is a no-op for them. Writing the plan file is the one edit allowed in plan mode. ## Step 0: Detect platform and base branch diff --git a/test/fixtures/golden/codex-ship-SKILL.md b/test/fixtures/golden/codex-ship-SKILL.md index 7770a8906..58bf20a0d 100644 --- a/test/fixtures/golden/codex-ship-SKILL.md +++ b/test/fixtures/golden/codex-ship-SKILL.md @@ -752,9 +752,7 @@ Replace `SKILL_NAME`, `OUTCOME`, and `USED_BROWSE` before running. ## Plan Status Footer -In plan mode before ExitPlanMode: if the plan file lacks `## GSTACK REVIEW REPORT`, run `$GSTACK_ROOT/bin/gstack-review-read` and append the standard runs/status/findings table. With `NO_REVIEWS` or empty, append a 5-row placeholder with verdict "NO REVIEWS YET — run `/autoplan`". If a richer report exists, skip. - -PLAN MODE EXCEPTION — always allowed (it's the plan file). +Skills that run plan reviews (`/plan-*-review`, `/codex review`) include the EXIT PLAN MODE GATE blocking checklist at the end of the skill, which verifies the plan file ends with `## GSTACK REVIEW REPORT` before ExitPlanMode is called. Skills that don't run plan reviews (operational skills like `/ship`, `/qa`, `/review`) typically don't operate in plan mode and have no review report to verify; this footer is a no-op for them. Writing the plan file is the one edit allowed in plan mode. ## Step 0: Detect platform and base branch diff --git a/test/fixtures/golden/factory-ship-SKILL.md b/test/fixtures/golden/factory-ship-SKILL.md index baae7421d..e71f38883 100644 --- a/test/fixtures/golden/factory-ship-SKILL.md +++ b/test/fixtures/golden/factory-ship-SKILL.md @@ -754,9 +754,7 @@ Replace `SKILL_NAME`, `OUTCOME`, and `USED_BROWSE` before running. ## Plan Status Footer -In plan mode before ExitPlanMode: if the plan file lacks `## GSTACK REVIEW REPORT`, run `$GSTACK_ROOT/bin/gstack-review-read` and append the standard runs/status/findings table. With `NO_REVIEWS` or empty, append a 5-row placeholder with verdict "NO REVIEWS YET — run `/autoplan`". If a richer report exists, skip. - -PLAN MODE EXCEPTION — always allowed (it's the plan file). +Skills that run plan reviews (`/plan-*-review`, `/codex review`) include the EXIT PLAN MODE GATE blocking checklist at the end of the skill, which verifies the plan file ends with `## GSTACK REVIEW REPORT` before ExitPlanMode is called. Skills that don't run plan reviews (operational skills like `/ship`, `/qa`, `/review`) typically don't operate in plan mode and have no review report to verify; this footer is a no-op for them. Writing the plan file is the one edit allowed in plan mode. ## Step 0: Detect platform and base branch diff --git a/test/gen-skill-docs.test.ts b/test/gen-skill-docs.test.ts index 309fd7e4b..8e6b8b486 100644 --- a/test/gen-skill-docs.test.ts +++ b/test/gen-skill-docs.test.ts @@ -1090,14 +1090,16 @@ describe('Retro plan completion section', () => { // --- Plan status footer in preamble --- describe('Plan status footer in preamble', () => { - test('preamble contains plan status footer', () => { + test('preamble contains plan status footer as neutral forward reference to EXIT PLAN MODE GATE', () => { // Read any skill that uses PREAMBLE const content = fs.readFileSync(path.join(ROOT, 'office-hours', 'SKILL.md'), 'utf-8'); expect(content).toContain('Plan Status Footer'); expect(content).toContain('GSTACK REVIEW REPORT'); - expect(content).toContain('gstack-review-read'); expect(content).toContain('ExitPlanMode'); - expect(content).toContain('NO REVIEWS YET'); + expect(content).toContain('EXIT PLAN MODE GATE'); + // The preamble must NOT impose review-report rules on operational skills + // that have no review report. It's a forward reference, not enforcement. + expect(content).not.toContain('NO REVIEWS YET'); }); }); @@ -3096,3 +3098,30 @@ describe('LEARNINGS_SEARCH resolver: query parameter', () => { } }); }); + +describe('EXIT PLAN MODE GATE placement', () => { + // Fresh skill list — do NOT reuse REVIEW_SKILLS upstream (3 entries, missing plan-devex). + const planSkills = ['plan-eng-review', 'plan-ceo-review', 'plan-design-review', 'plan-devex-review']; + + // Strip fenced code blocks before matching headings — PLAN_FILE_REVIEW_REPORT + // already contains `## GSTACK REVIEW REPORT` inside a markdown example fence, + // and the gate text itself shows `## GSTACK REVIEW REPORT` inside a fence too. + const stripFences = (md: string) => md.replace(/```[\s\S]*?```/g, ''); + + test('gate is the terminal ## heading in every plan-* review SKILL.md', () => { + for (const skill of planSkills) { + const md = fs.readFileSync(path.join(ROOT, skill, 'SKILL.md'), 'utf-8'); + const stripped = stripFences(md); + const headings = [...stripped.matchAll(/^## .+$/gm)].map(m => m[0]); + const lastH2 = headings.at(-1); + expect(lastH2, `${skill}/SKILL.md last ## heading (fences stripped)`).toBe('## EXIT PLAN MODE GATE (BLOCKING)'); + expect(md, `${skill}/SKILL.md gate body`).toContain('Failing this gate and calling ExitPlanMode anyway is a contract violation'); + } + }); + + test('codex/SKILL.md contains gate (mid-file per D5; Step 2B/2C follow)', () => { + const codex = fs.readFileSync(path.join(ROOT, 'codex', 'SKILL.md'), 'utf-8'); + expect(codex).toContain('## EXIT PLAN MODE GATE (BLOCKING)'); + expect(codex).toContain('Failing this gate and calling ExitPlanMode anyway is a contract violation'); + }); +});