Revert design-doc discovery state-root conversion: it made plan-eng-review ask its scope gate in prose (plan-mode no-op eval)

This commit is contained in:
garrytan committed 2026-10-03 04:32:31 +00:00
1 parent 35d4efdc01
commit fbc0c15aaf
9 files changed
+16 -19

No files matched your search

+1 -2
View File
@@ -39,7 +39,6 @@ allowed-tools:
```bash
setopt +o nomatch 2>/dev/null || true # zsh compat
eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}"
SLUG=$(~/.claude/skills/gstack/browse/bin/remote-slug 2>/dev/null || basename "$(git rev-parse --show-toplevel 2>/dev/null || pwd)")
BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null | tr '/' '-' || echo 'no-branch')
{{DESIGN_DOC_DISCOVERY}}
@@ -225,7 +224,7 @@ Re-run: copy RESTORE_PATH's bytes to SOURCE_PLAN, then /autoplan.
### Step 2: Read context
- Read CLAUDE.md, TODOS.md, git log -30, git diff against the base branch --stat
- Discover design docs: `ls -t "$GSTACK_STATE_ROOT"/projects/$SLUG/*-design-*.md 2>/dev/null | head -1`, resolving the state root and slug as the Design Doc Check block does
- Discover design docs: `ls -t ~/.gstack/projects/$SLUG/*-design-*.md 2>/dev/null | head -1`
- Detect UI scope: grep the plan for view/rendering terms (component, screen, form,
button, modal, layout, dashboard, sidebar, nav, dialog). Require 2+ matches. Exclude
false positives ("page" alone, "UI" in acronyms).
-1
View File
@@ -112,7 +112,6 @@ Then read CLAUDE.md, TODOS.md, and any existing architecture docs.
**Design doc check:**
```bash
setopt +o nomatch 2>/dev/null || true # zsh compat
eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}"
SLUG=$(~/.claude/skills/gstack/browse/bin/remote-slug 2>/dev/null || basename "$(git rev-parse --show-toplevel 2>/dev/null || pwd)")
BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null | tr '/' '-' || echo 'no-branch')
{{DESIGN_DOC_DISCOVERY}}
-1
View File
@@ -106,7 +106,6 @@ No background exploration before those questions; record unknowns for later.
**Design doc check:**
```bash
setopt +o nomatch 2>/dev/null || true
eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}"
SLUG=$(~/.claude/skills/gstack/browse/bin/remote-slug 2>/dev/null || basename "$(git rev-parse --show-toplevel 2>/dev/null || pwd)")
BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null | tr '/' '-' || echo 'no-branch')
{{DESIGN_DOC_DISCOVERY}}
-1
View File
@@ -126,7 +126,6 @@ Apply throughout, not as extra checks:
### Design Doc Check
```bash
setopt +o nomatch 2>/dev/null || true # zsh compat
eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}"
if _REVIEW_SLUG=$(~/.claude/skills/gstack/bin/gstack-slug); then
eval "$_REVIEW_SLUG"
{{DESIGN_DOC_DISCOVERY}}
+6 -7
View File
@@ -2,10 +2,10 @@
* {{DESIGN_DOC_DISCOVERY}} — the canonical design-doc discovery block (#703).
*
* Finds the design doc a plan review should read: newest branch-scoped doc
* under $GSTACK_STATE_ROOT/projects/<slug>/, falling back to newest project-scoped
* under ~/.gstack/projects/<slug>/, falling back to newest project-scoped
* doc, then lets a repo-local doc (DESIGN.md or docs/designs/*.md) win when
* it is at least as fresh. office-hours dual-writes docs/designs/ alongside
* the state root, and the committed copy is what teammates see — but a stale old
* ~/.gstack, and the committed copy is what teammates see — but a stale old
* repo doc must never shadow a newer private session.
*
* Single source of truth for the freshness-preference logic that previously
@@ -16,8 +16,7 @@
*
* The fragment carries no code fences — the {{DESIGN_DOC_DISCOVERY}} token
* sits inside each caller's ```bash block. Callers must set $SLUG and
* $BRANCH and resolve $GSTACK_STATE_ROOT through the guarded gstack-paths
* eval first (and `setopt +o nomatch` for zsh); the block sets $DESIGN
* $BRANCH first (and `setopt +o nomatch` for zsh); the block sets $DESIGN
* and prints "Design doc found: ..." or "No design doc found".
*/
@@ -28,10 +27,10 @@ import type { TemplateContext } from './types';
* re-check) can interpolate it into their own template strings instead of
* embedding a drifting copy.
*/
export const DESIGN_DOC_DISCOVERY_BLOCK = `_LOCALDOC=$(ls -t "$GSTACK_STATE_ROOT"/projects/$SLUG/*-$BRANCH-design-*.md 2>/dev/null | head -1)
[ -z "$_LOCALDOC" ] && _LOCALDOC=$(ls -t "$GSTACK_STATE_ROOT"/projects/$SLUG/*-design-*.md 2>/dev/null | head -1)
export const DESIGN_DOC_DISCOVERY_BLOCK = `_LOCALDOC=$(ls -t ~/.gstack/projects/$SLUG/*-$BRANCH-design-*.md 2>/dev/null | head -1)
[ -z "$_LOCALDOC" ] && _LOCALDOC=$(ls -t ~/.gstack/projects/$SLUG/*-design-*.md 2>/dev/null | head -1)
# Repo-local docs win when at least as fresh (#703): office-hours dual-writes
# docs/designs/ alongside the state root, and the committed copy is what teammates
# docs/designs/ alongside ~/.gstack, and the committed copy is what teammates
# see. A stale old repo doc never shadows a newer private session.
_REPOTOP=$(git rev-parse --show-toplevel 2>/dev/null || echo "")
_REPODOC=""
-1
View File
@@ -258,7 +258,6 @@ Read the resulting doc if found; otherwise continue the standard review.
Do not rerun the preamble or re-offer the prerequisite.` : `After /${first} completes, re-run the design doc check:
\`\`\`bash
setopt +o nomatch 2>/dev/null || true # zsh compat
eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "\${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}"
SLUG=$(~/.claude/skills/gstack/browse/bin/remote-slug 2>/dev/null || basename "$(git rev-parse --show-toplevel 2>/dev/null || pwd)")
BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null | tr '/' '-' || echo 'no-branch')
${DESIGN_DOC_DISCOVERY_BLOCK}
+1 -1
View File
@@ -302,7 +302,7 @@ describe('CEO finding fixture establishes scope before launch', () => {
const design = '# Settings export design\n\n## Problem\nOperators need saved settings in a spreadsheet for offline comparison.\n\n## Approach\nReuse the settings API and escape commas, quotes, and newlines in a CSV formatter.\n';
seedCeoFindingProject(cwd, plan, design);
const output = execFileSync('bash', ['-c', `SLUG=fixture; BRANCH=main; ${DESIGN_DOC_DISCOVERY_BLOCK}`], {
cwd, env: { PATH: process.env.PATH!, HOME: home, GSTACK_STATE_ROOT: path.join(root, 'state') }, encoding: 'utf8', timeout: 10_000,
cwd, env: { PATH: process.env.PATH!, HOME: home }, encoding: 'utf8', timeout: 10_000,
});
expect(output).toBe(`Design doc found: ${path.join(cwd, 'DESIGN.md')}\n`);
expect(execFileSync('git', ['show', 'HEAD:DESIGN.md'], { cwd, encoding: 'utf8', timeout: 30_000 })).toBe(design);
+3 -5
View File
@@ -1905,15 +1905,13 @@ describe('BENEFITS_FROM resolver', () => {
fs.chmodSync(helper, 0o755);
fs.copyFileSync(path.join(ROOT, 'bin/gstack-state-root.sh'), path.join(path.dirname(helper), 'gstack-state-root.sh'));
fs.copyFileSync(path.join(ROOT, 'bin/gstack-remote-identity.sh'), path.join(path.dirname(helper), 'gstack-remote-identity.sh'));
fs.copyFileSync(path.join(ROOT, 'bin/gstack-paths'), path.join(path.dirname(helper), 'gstack-paths'));
fs.chmodSync(path.join(path.dirname(helper), 'gstack-paths'), 0o755);
const expected = path.join(dir, 'state/projects/canonical-override/session-unknown-design-current.md');
const wrong = path.join(dir, 'state/projects/project/session-unknown-design-wrong.md');
const expected = path.join(home, '.gstack/projects/canonical-override/session-unknown-design-current.md');
const wrong = path.join(home, '.gstack/projects/project/session-unknown-design-wrong.md');
for (const file of [expected, wrong]) {
fs.mkdirSync(path.dirname(file), {recursive: true});
fs.writeFileSync(file, '# Design fixture\n');
}
const env = {...process.env, HOME: home, GSTACK_HOME: path.join(dir, 'state'), GSTACK_STATE_ROOT: path.join(dir, 'state'),
const env = {...process.env, HOME: home, GSTACK_HOME: path.join(home, '.gstack'),
GSTACK_PROJECT_SLUG: 'canonical-override', GIT_CEILING_DIRECTORIES: dir};
for (const command of [initial, recheck]) {
expect(command).toContain('if _REVIEW_SLUG=$(~/.claude/skills/gstack/bin/gstack-slug); then');
+5
View File
@@ -169,8 +169,10 @@ const RATCHET_ALLOWLIST = JSON.parse(read('test/state-root-ratchet.allowlist.jso
const readinessEvalReason = RATCHET_ALLOWLIST.find(entry => entry.path === 'land-and-deploy/sections/readiness-gate.md.tmpl')!.reason;
const DESCRIPTION = 'user-facing description of where the data lives; no command reads or writes this literal';
const SOURCE_COMMENT = 'source comment, not rendered into any skill';
const DESIGN_DOC_HELD = 'held at the literal path: converting design-doc discovery to $GSTACK_STATE_ROOT made plan-eng-review ask its scope gate in prose instead of AskUserQuestion (plan-mode no-op eval, 4 of 4 runs); see TODOS.md';
const STATE_ROOT_ALLOWLIST: Array<{ file: string; match: string; reason: string }> = [
{ file: 'autoplan/SKILL.md.tmpl', match: 'ls -t ~/.gstack/projects/$SLUG/*-design-*.md', reason: DESIGN_DOC_HELD },
{ file: 'autoplan/SKILL.md.tmpl', match: 'test plan on disk at ~/.gstack/projects/$SLUG/', reason: DESCRIPTION },
{ file: 'careful/SKILL.md.tmpl', match: '`~/.gstack/projects/<slug>/careful-patterns.txt` (per-project)', reason: DESCRIPTION },
{ file: 'design-shotgun/SKILL.md.tmpl', match: 'v1 schema at `~/.gstack/projects/$SLUG/taste-profile.json`', reason: DESCRIPTION },
@@ -181,6 +183,9 @@ const STATE_ROOT_ALLOWLIST: Array<{ file: string; match: string; reason: string
{ file: 'plan-ceo-review/sections/review-sections.md.tmpl', match: 'Keep in `~/.gstack/projects/` only (local, personal reference)', reason: DESCRIPTION },
{ file: 'plan-tune/SKILL.md.tmpl', match: 'Logs stay local (`~/.gstack/projects/<slug>/question-log.jsonl`)', reason: DESCRIPTION },
{ file: 'plan-tune/SKILL.md.tmpl', match: '`~/.gstack/projects/<slug>/question-log.jsonl` — nothing leaves your', reason: DESCRIPTION },
{ file: 'scripts/resolvers/design-doc-discovery.ts', match: '* under ~/.gstack/projects/<slug>/, falling back', reason: SOURCE_COMMENT },
{ file: 'scripts/resolvers/design-doc-discovery.ts', match: '_LOCALDOC=$(ls -t ~/.gstack/projects/$SLUG/*-$BRANCH-design-*.md', reason: DESIGN_DOC_HELD },
{ file: 'scripts/resolvers/design-doc-discovery.ts', match: '_LOCALDOC=$(ls -t ~/.gstack/projects/$SLUG/*-design-*.md', reason: DESIGN_DOC_HELD },
{ file: 'scripts/resolvers/learnings.ts', match: '* Learnings are stored per-project at ~/.gstack/projects/{slug}/learnings.jsonl', reason: SOURCE_COMMENT },
{ file: 'scripts/resolvers/preamble.ts', match: 'local JSONL append to ~/.gstack/analytics/ (inline, inspectable)', reason: SOURCE_COMMENT },
];