v1.26.4.0 fix: GSTACK REVIEW REPORT delete-then-append (no more mid-file leftovers) (#1335)

* fix: GSTACK REVIEW REPORT delete-then-append flow

Replaces contradictory "replace it entirely" + "always last section / move
if mid-file" bullets in scripts/resolvers/review.ts with a single
delete-then-append rule. Adds Read-tool verification step so the agent
self-checks before continuing.

Affected SKILL.md files (regenerated): plan-ceo-review, plan-design-review,
plan-devex-review, plan-eng-review, codex, devex-review.

* test: static template assertions for delete-then-append + revert autoplan E2E shape

5 new static tests in test/gen-skill-docs.test.ts (4 plan-review SKILL.md
files + 1 source resolver) verify the new prompt language is present and
the old contradictory bullets are absent. Synthetic regression check
confirmed all 5 fail when the prompt fix is reverted.

The autoplan E2E (skill-e2e-autoplan-auto-mode.test.ts) reverts to its
original AUQ-blocked-gate-surface shape. The mid-file regression scenario
the plan briefly proposed isn't reachable in the current PTY harness because
--disallowedTools AskUserQuestion makes autoplan bail at the Phase 1
premise gate before any review-write code path runs. Static prompt-text
verification covers the load-bearing change.

* chore: bump version and changelog (v1.26.4.0)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Garry Tan
2026-05-05 21:18:35 -07:00
committed by GitHub
parent db9447c333
commit 19e699ab9b
12 changed files with 244 additions and 65 deletions
+26
View File
@@ -1,5 +1,31 @@
# Changelog
## [1.26.4.0] - 2026-05-05
## **`/autoplan` review reports now reliably land at the bottom of the plan, even when an older copy lives mid-file.**
The `## GSTACK REVIEW REPORT` section had a write rule that contradicted itself: one bullet said "replace it entirely (in place)" while another said "always last section, move if mid-file." When the agent inherited a plan whose prior `/autoplan` run had landed before user-added sections, the in-place replace path won and the new report stayed mid-file. The user opened ExitPlanMode, saw their plan with no review at the bottom, and had to ask twice. Single delete-then-append rule now, with a Read-tool verification step before the next instruction runs.
### What you can now do
- **Run `/autoplan` against a plan that already has a stale `## GSTACK REVIEW REPORT` mid-file and trust the new report ends up at the bottom.** The instruction in `scripts/resolvers/review.ts` (which feeds `/plan-ceo-review`, `/plan-eng-review`, `/plan-design-review`, `/plan-devex-review`, `/codex`, `/devex-review`) now reads as one rule: search for any existing report section, delete it wherever it lives, append a fresh report at end of file, verify with the Read tool that the report is the last `##` heading. No more contradiction for the agent to reconcile.
### What gets safer
- **Five static template assertions in `test/gen-skill-docs.test.ts` lock the prompt change against drift.** Each plan-review SKILL.md (4 of them) plus the source resolver are checked for the new "delete-then-append flow" / "never mid-file" / "Do NOT replace the section in place" markers AND the absence of the old "replace it** entirely using the Edit tool" / "If it was found mid-file, move it" bullets. Synthetic regression check confirmed: all 5 fail when the prompt is reverted, all 5 pass when restored. The tests are bound to the change, not to incidentally green output.
### Itemized changes
#### Changed
- `scripts/resolvers/review.ts` — "Write to the plan file" subsection rewritten. Old contradictory pair ("replace it entirely" vs "always last / move if mid-file") collapsed into a single 4-step delete-then-append flow with explicit verification.
- All 6 generated SKILL.md files refreshed to carry the new instruction: `plan-ceo-review`, `plan-design-review`, `plan-devex-review`, `plan-eng-review`, `codex`, `devex-review`.
#### Added
- `test/gen-skill-docs.test.ts` — new `GSTACK REVIEW REPORT delete-then-append flow` describe block: 4 SKILL.md target tests + 1 source resolver test. Static, deterministic, free.
#### For contributors
- The `/autoplan` E2E approach attempted in the plan was dropped after a paid run revealed that `--disallowedTools AskUserQuestion` makes autoplan bail at the Phase 1 premise gate via the plan-file fallback. The PTY harness can't drive autoplan through its review phases without auto-progression of AskUserQuestions. The static prompt-text test catches the load-bearing change without needing that infrastructure.
## [1.26.3.0] - 2026-05-03
## **`/sync-gbrain` keeps your brain current and teaches the agent when to use it.**
+1 -1
View File
@@ -1 +1 @@
1.26.3.0
1.26.4.0
+23 -9
View File
@@ -1047,15 +1047,29 @@ Below the table, add these lines (omit any that are empty/not applicable):
file you are allowed to edit in plan mode. The plan file review report is part of the
plan's living status.
- Search the plan file for a \`## GSTACK REVIEW REPORT\` section **anywhere** in the file
(not just at the end — content may have been added after it).
- If found, **replace it** entirely using the Edit tool. Match from \`## GSTACK REVIEW REPORT\`
through either the next \`## \` heading or end of file, whichever comes first. This ensures
content added after the report section is preserved, not eaten. If the Edit fails
(e.g., concurrent edit changed the content), re-read the plan file and retry once.
- If no such section exists, **append it** to the end of the plan file.
- Always place it as the very last section in the plan file. If it was found mid-file,
move it: delete the old location and append at the end.
The report must always be the LAST section of the plan file — never mid-file.
Use a single delete-then-append flow:
1. Read the plan file (Read tool) to see its full current content. Search the read
output for a \`## GSTACK REVIEW REPORT\` heading anywhere in the file.
2. If found, use the Edit tool to DELETE the entire existing section. Match from
\`## GSTACK REVIEW REPORT\` through either the next \`## \` heading or end of
file, whichever comes first. Replace with the empty string. This applies
regardless of where the section currently lives — mid-file deletion is
intentional, not a special case. If the Edit fails (e.g., concurrent edit
changed the content), re-read the plan file and retry once.
3. After the delete (or skipped, if no section existed), append the new
\`## GSTACK REVIEW REPORT\` section at the END of the file. Use the Edit
tool to match the file's current last paragraph and add the section after it,
or use Write to re-emit the whole file with the section at the end.
4. Verify with the Read tool that \`## GSTACK REVIEW REPORT\` is the last
\`## \` heading in the file before continuing. If it isn't, repeat steps
2-3 once.
Do NOT replace the section in place. The "replace mid-file" path is what allowed
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.
---
+23 -9
View File
@@ -1153,15 +1153,29 @@ Below the table, add these lines (omit any that are empty/not applicable):
file you are allowed to edit in plan mode. The plan file review report is part of the
plan's living status.
- Search the plan file for a \`## GSTACK REVIEW REPORT\` section **anywhere** in the file
(not just at the end — content may have been added after it).
- If found, **replace it** entirely using the Edit tool. Match from \`## GSTACK REVIEW REPORT\`
through either the next \`## \` heading or end of file, whichever comes first. This ensures
content added after the report section is preserved, not eaten. If the Edit fails
(e.g., concurrent edit changed the content), re-read the plan file and retry once.
- If no such section exists, **append it** to the end of the plan file.
- Always place it as the very last section in the plan file. If it was found mid-file,
move it: delete the old location and append at the end.
The report must always be the LAST section of the plan file — never mid-file.
Use a single delete-then-append flow:
1. Read the plan file (Read tool) to see its full current content. Search the read
output for a \`## GSTACK REVIEW REPORT\` heading anywhere in the file.
2. If found, use the Edit tool to DELETE the entire existing section. Match from
\`## GSTACK REVIEW REPORT\` through either the next \`## \` heading or end of
file, whichever comes first. Replace with the empty string. This applies
regardless of where the section currently lives — mid-file deletion is
intentional, not a special case. If the Edit fails (e.g., concurrent edit
changed the content), re-read the plan file and retry once.
3. After the delete (or skipped, if no section existed), append the new
\`## GSTACK REVIEW REPORT\` section at the END of the file. Use the Edit
tool to match the file's current last paragraph and add the section after it,
or use Write to re-emit the whole file with the section at the end.
4. Verify with the Read tool that \`## GSTACK REVIEW REPORT\` is the last
\`## \` heading in the file before continuing. If it isn't, repeat steps
2-3 once.
Do NOT replace the section in place. The "replace mid-file" path is what allowed
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.
## Capture Learnings
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "gstack",
"version": "1.26.3.0",
"version": "1.26.4.0",
"description": "Garry's Stack — Claude Code skills + fast headless browser. One repo, one install, entire AI engineering workflow.",
"license": "MIT",
"type": "module",
+23 -9
View File
@@ -1958,15 +1958,29 @@ Below the table, add these lines (omit any that are empty/not applicable):
file you are allowed to edit in plan mode. The plan file review report is part of the
plan's living status.
- Search the plan file for a \`## GSTACK REVIEW REPORT\` section **anywhere** in the file
(not just at the end — content may have been added after it).
- If found, **replace it** entirely using the Edit tool. Match from \`## GSTACK REVIEW REPORT\`
through either the next \`## \` heading or end of file, whichever comes first. This ensures
content added after the report section is preserved, not eaten. If the Edit fails
(e.g., concurrent edit changed the content), re-read the plan file and retry once.
- If no such section exists, **append it** to the end of the plan file.
- Always place it as the very last section in the plan file. If it was found mid-file,
move it: delete the old location and append at the end.
The report must always be the LAST section of the plan file — never mid-file.
Use a single delete-then-append flow:
1. Read the plan file (Read tool) to see its full current content. Search the read
output for a \`## GSTACK REVIEW REPORT\` heading anywhere in the file.
2. If found, use the Edit tool to DELETE the entire existing section. Match from
\`## GSTACK REVIEW REPORT\` through either the next \`## \` heading or end of
file, whichever comes first. Replace with the empty string. This applies
regardless of where the section currently lives — mid-file deletion is
intentional, not a special case. If the Edit fails (e.g., concurrent edit
changed the content), re-read the plan file and retry once.
3. After the delete (or skipped, if no section existed), append the new
\`## GSTACK REVIEW REPORT\` section at the END of the file. Use the Edit
tool to match the file's current last paragraph and add the section after it,
or use Write to re-emit the whole file with the section at the end.
4. Verify with the Read tool that \`## GSTACK REVIEW REPORT\` is the last
\`## \` heading in the file before continuing. If it isn't, repeat steps
2-3 once.
Do NOT replace the section in place. The "replace mid-file" path is what allowed
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.
## Next Steps — Review Chaining
+23 -9
View File
@@ -1722,15 +1722,29 @@ Below the table, add these lines (omit any that are empty/not applicable):
file you are allowed to edit in plan mode. The plan file review report is part of the
plan's living status.
- Search the plan file for a \`## GSTACK REVIEW REPORT\` section **anywhere** in the file
(not just at the end — content may have been added after it).
- If found, **replace it** entirely using the Edit tool. Match from \`## GSTACK REVIEW REPORT\`
through either the next \`## \` heading or end of file, whichever comes first. This ensures
content added after the report section is preserved, not eaten. If the Edit fails
(e.g., concurrent edit changed the content), re-read the plan file and retry once.
- If no such section exists, **append it** to the end of the plan file.
- Always place it as the very last section in the plan file. If it was found mid-file,
move it: delete the old location and append at the end.
The report must always be the LAST section of the plan file — never mid-file.
Use a single delete-then-append flow:
1. Read the plan file (Read tool) to see its full current content. Search the read
output for a \`## GSTACK REVIEW REPORT\` heading anywhere in the file.
2. If found, use the Edit tool to DELETE the entire existing section. Match from
\`## GSTACK REVIEW REPORT\` through either the next \`## \` heading or end of
file, whichever comes first. Replace with the empty string. This applies
regardless of where the section currently lives — mid-file deletion is
intentional, not a special case. If the Edit fails (e.g., concurrent edit
changed the content), re-read the plan file and retry once.
3. After the delete (or skipped, if no section existed), append the new
\`## GSTACK REVIEW REPORT\` section at the END of the file. Use the Edit
tool to match the file's current last paragraph and add the section after it,
or use Write to re-emit the whole file with the section at the end.
4. Verify with the Read tool that \`## GSTACK REVIEW REPORT\` is the last
\`## \` heading in the file before continuing. If it isn't, repeat steps
2-3 once.
Do NOT replace the section in place. The "replace mid-file" path is what allowed
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.
## Capture Learnings
+23 -9
View File
@@ -1926,15 +1926,29 @@ Below the table, add these lines (omit any that are empty/not applicable):
file you are allowed to edit in plan mode. The plan file review report is part of the
plan's living status.
- Search the plan file for a \`## GSTACK REVIEW REPORT\` section **anywhere** in the file
(not just at the end — content may have been added after it).
- If found, **replace it** entirely using the Edit tool. Match from \`## GSTACK REVIEW REPORT\`
through either the next \`## \` heading or end of file, whichever comes first. This ensures
content added after the report section is preserved, not eaten. If the Edit fails
(e.g., concurrent edit changed the content), re-read the plan file and retry once.
- If no such section exists, **append it** to the end of the plan file.
- Always place it as the very last section in the plan file. If it was found mid-file,
move it: delete the old location and append at the end.
The report must always be the LAST section of the plan file — never mid-file.
Use a single delete-then-append flow:
1. Read the plan file (Read tool) to see its full current content. Search the read
output for a \`## GSTACK REVIEW REPORT\` heading anywhere in the file.
2. If found, use the Edit tool to DELETE the entire existing section. Match from
\`## GSTACK REVIEW REPORT\` through either the next \`## \` heading or end of
file, whichever comes first. Replace with the empty string. This applies
regardless of where the section currently lives — mid-file deletion is
intentional, not a special case. If the Edit fails (e.g., concurrent edit
changed the content), re-read the plan file and retry once.
3. After the delete (or skipped, if no section existed), append the new
\`## GSTACK REVIEW REPORT\` section at the END of the file. Use the Edit
tool to match the file's current last paragraph and add the section after it,
or use Write to re-emit the whole file with the section at the end.
4. Verify with the Read tool that \`## GSTACK REVIEW REPORT\` is the last
\`## \` heading in the file before continuing. If it isn't, repeat steps
2-3 once.
Do NOT replace the section in place. The "replace mid-file" path is what allowed
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.
## Capture Learnings
+23 -9
View File
@@ -1537,15 +1537,29 @@ Below the table, add these lines (omit any that are empty/not applicable):
file you are allowed to edit in plan mode. The plan file review report is part of the
plan's living status.
- Search the plan file for a \`## GSTACK REVIEW REPORT\` section **anywhere** in the file
(not just at the end — content may have been added after it).
- If found, **replace it** entirely using the Edit tool. Match from \`## GSTACK REVIEW REPORT\`
through either the next \`## \` heading or end of file, whichever comes first. This ensures
content added after the report section is preserved, not eaten. If the Edit fails
(e.g., concurrent edit changed the content), re-read the plan file and retry once.
- If no such section exists, **append it** to the end of the plan file.
- Always place it as the very last section in the plan file. If it was found mid-file,
move it: delete the old location and append at the end.
The report must always be the LAST section of the plan file — never mid-file.
Use a single delete-then-append flow:
1. Read the plan file (Read tool) to see its full current content. Search the read
output for a \`## GSTACK REVIEW REPORT\` heading anywhere in the file.
2. If found, use the Edit tool to DELETE the entire existing section. Match from
\`## GSTACK REVIEW REPORT\` through either the next \`## \` heading or end of
file, whichever comes first. Replace with the empty string. This applies
regardless of where the section currently lives — mid-file deletion is
intentional, not a special case. If the Edit fails (e.g., concurrent edit
changed the content), re-read the plan file and retry once.
3. After the delete (or skipped, if no section existed), append the new
\`## GSTACK REVIEW REPORT\` section at the END of the file. Use the Edit
tool to match the file's current last paragraph and add the section after it,
or use Write to re-emit the whole file with the section at the end.
4. Verify with the Read tool that \`## GSTACK REVIEW REPORT\` is the last
\`## \` heading in the file before continuing. If it isn't, repeat steps
2-3 once.
Do NOT replace the section in place. The "replace mid-file" path is what allowed
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.
## Capture Learnings
+23 -9
View File
@@ -133,15 +133,29 @@ Below the table, add these lines (omit any that are empty/not applicable):
file you are allowed to edit in plan mode. The plan file review report is part of the
plan's living status.
- Search the plan file for a \\\`## GSTACK REVIEW REPORT\\\` section **anywhere** in the file
(not just at the end content may have been added after it).
- If found, **replace it** entirely using the Edit tool. Match from \\\`## GSTACK REVIEW REPORT\\\`
through either the next \\\`## \\\` heading or end of file, whichever comes first. This ensures
content added after the report section is preserved, not eaten. If the Edit fails
(e.g., concurrent edit changed the content), re-read the plan file and retry once.
- If no such section exists, **append it** to the end of the plan file.
- Always place it as the very last section in the plan file. If it was found mid-file,
move it: delete the old location and append at the end.`;
The report must always be the LAST section of the plan file never mid-file.
Use a single delete-then-append flow:
1. Read the plan file (Read tool) to see its full current content. Search the read
output for a \\\`## GSTACK REVIEW REPORT\\\` heading anywhere in the file.
2. If found, use the Edit tool to DELETE the entire existing section. Match from
\\\`## GSTACK REVIEW REPORT\\\` through either the next \\\`## \\\` heading or end of
file, whichever comes first. Replace with the empty string. This applies
regardless of where the section currently lives mid-file deletion is
intentional, not a special case. If the Edit fails (e.g., concurrent edit
changed the content), re-read the plan file and retry once.
3. After the delete (or skipped, if no section existed), append the new
\\\`## GSTACK REVIEW REPORT\\\` section at the END of the file. Use the Edit
tool to match the file's current last paragraph and add the section after it,
or use Write to re-emit the whole file with the section at the end.
4. Verify with the Read tool that \\\`## GSTACK REVIEW REPORT\\\` is the last
\\\`## \\\` heading in the file before continuing. If it isn't, repeat steps
2-3 once.
Do NOT replace the section in place. The "replace mid-file" path is what allowed
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.`;
}
export function generateSpecReviewLoop(_ctx: TemplateContext): string {
+45
View File
@@ -2977,3 +2977,48 @@ describe('plan-mode-info resolver (handshake-replacement)', () => {
expect(between).toContain('Do NOT proceed to Step 0D or 0F until the user responds to 0C-bis');
});
});
// GSTACK REVIEW REPORT report-at-bottom contract — verifies the prompt-text
// fix in scripts/resolvers/review.ts (the load-bearing change for the
// "report not at bottom of plan in plan mode" bug). The bug is in the
// prompt's contradictory write-flow instructions, not in observable
// runtime behavior we can cheaply gate in CI. Verifying the prompt text
// directly is the deterministic equivalent of the regression test the
// PTY harness can't reliably drive (autoplan needs auto-progression of
// AskUserQuestions to reach the report-write step, which the harness
// doesn't support today).
describe('GSTACK REVIEW REPORT delete-then-append flow', () => {
const PLAN_REVIEW_SKILLS = [
'plan-ceo-review',
'plan-design-review',
'plan-devex-review',
'plan-eng-review',
];
for (const skill of PLAN_REVIEW_SKILLS) {
test(`${skill}/SKILL.md prescribes delete-then-append, not in-place replace`, () => {
const content = fs.readFileSync(path.join(ROOT, skill, 'SKILL.md'), 'utf-8');
// The new (correct) instruction must be present.
expect(content).toContain('delete-then-append flow');
expect(content).toContain('never mid-file');
expect(content).toContain('Do NOT replace the section in place');
// The old contradictory bullets must be gone. The signature phrase
// from the buggy prompt was 'replace it entirely using the Edit tool'
// which is what allowed mid-file reports to stay mid-file.
expect(content).not.toContain('replace it** entirely using the Edit tool');
expect(content).not.toContain('If it was found mid-file, move it');
});
}
test('scripts/resolvers/review.ts source has the rewritten flow', () => {
const src = fs.readFileSync(path.join(ROOT, 'scripts', 'resolvers', 'review.ts'), 'utf-8');
expect(src).toContain('delete-then-append flow');
expect(src).toContain('never mid-file');
expect(src).toContain('Do NOT replace the section in place');
// Old contradictory bullets are gone from the source resolver.
expect(src).not.toContain('replace it** entirely using the Edit tool');
expect(src).not.toContain('If it was found mid-file, move it');
});
});
+10
View File
@@ -18,6 +18,16 @@
* Filename keeps `auto-mode` for branch-history continuity. Auto-mode (the
* AUTO_DECIDE preamble path when QUESTION_TUNING=true) is a related but
* distinct silencing mechanism; both share the same fix surface.
*
* Note on report-at-bottom contract: the GSTACK REVIEW REPORT delete-then-
* append flow lives in `scripts/resolvers/review.ts` and is exercised when
* reviews actually run. The PTY harness can't drive autoplan through its
* review phases without auto-progression of AUQs (see runPlanSkillCounting),
* and `--disallowedTools AskUserQuestion` makes autoplan bail at the
* premise gate via the plan-file fallback before any review runs. The
* report-at-bottom prompt change is verified statically in
* `test/gen-skill-docs.test.ts` instead that's the load-bearing
* verification for the contradictory-prompt fix.
*/
import { describe, test, expect } from 'bun:test';