From 808344ecfd147ae8c28a27e2c497507af990d43f Mon Sep 17 00:00:00 2001 From: Garry Tan Date: Fri, 14 Aug 2026 17:15:46 -0700 Subject: [PATCH] refactor(resolvers): one source for the design-doc discovery block MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The #703 repo-doc-preference bash was pasted byte-identically into three plan-review templates and a fourth copy embedded in review.ts — drift there means plan reviews disagree about which design doc wins. Now a {{DESIGN_DOC_DISCOVERY}} resolver; generated output is byte-identical, so no SKILL.md changes ride along. --- plan-ceo-review/SKILL.md.tmpl | 17 +------- plan-devex-review/SKILL.md.tmpl | 17 +------- plan-eng-review/SKILL.md.tmpl | 17 +------- scripts/resolvers/design-doc-discovery.ts | 49 +++++++++++++++++++++++ scripts/resolvers/index.ts | 2 + scripts/resolvers/review.ts | 18 +-------- 6 files changed, 56 insertions(+), 64 deletions(-) create mode 100644 scripts/resolvers/design-doc-discovery.ts diff --git a/plan-ceo-review/SKILL.md.tmpl b/plan-ceo-review/SKILL.md.tmpl index b9ea45d3b..1b1affe16 100644 --- a/plan-ceo-review/SKILL.md.tmpl +++ b/plan-ceo-review/SKILL.md.tmpl @@ -138,22 +138,7 @@ Then read CLAUDE.md, TODOS.md, and any existing architecture docs. setopt +o nomatch 2>/dev/null || true # zsh compat 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') -_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 ~/.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="" -if [ -n "$_REPOTOP" ]; then - [ -f "$_REPOTOP/DESIGN.md" ] && _REPODOC="$_REPOTOP/DESIGN.md" - [ -z "$_REPODOC" ] && _REPODOC=$(ls -t "$_REPOTOP"/docs/designs/*.md 2>/dev/null | head -1) -fi -DESIGN="$_LOCALDOC" -if [ -n "$_REPODOC" ] && { [ -z "$_LOCALDOC" ] || [ "$_REPODOC" -nt "$_LOCALDOC" ]; }; then - DESIGN="$_REPODOC" -fi -[ -n "$DESIGN" ] && echo "Design doc found: $DESIGN" || echo "No design doc found" +{{DESIGN_DOC_DISCOVERY}} ``` If a design doc exists (from `/office-hours`), read it. Use it as the source of truth for the problem statement, constraints, and chosen approach. If it has a `Supersedes:` field, note that this is a revised design. diff --git a/plan-devex-review/SKILL.md.tmpl b/plan-devex-review/SKILL.md.tmpl index 6c0acae0f..76871cafa 100644 --- a/plan-devex-review/SKILL.md.tmpl +++ b/plan-devex-review/SKILL.md.tmpl @@ -99,22 +99,7 @@ Then read: setopt +o nomatch 2>/dev/null || true 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') -_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 ~/.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="" -if [ -n "$_REPOTOP" ]; then - [ -f "$_REPOTOP/DESIGN.md" ] && _REPODOC="$_REPOTOP/DESIGN.md" - [ -z "$_REPODOC" ] && _REPODOC=$(ls -t "$_REPOTOP"/docs/designs/*.md 2>/dev/null | head -1) -fi -DESIGN="$_LOCALDOC" -if [ -n "$_REPODOC" ] && { [ -z "$_LOCALDOC" ] || [ "$_REPODOC" -nt "$_LOCALDOC" ]; }; then - DESIGN="$_REPODOC" -fi -[ -n "$DESIGN" ] && echo "Design doc found: $DESIGN" || echo "No design doc found" +{{DESIGN_DOC_DISCOVERY}} ``` If a design doc exists, read it. diff --git a/plan-eng-review/SKILL.md.tmpl b/plan-eng-review/SKILL.md.tmpl index 697ab6539..e066e2c5f 100644 --- a/plan-eng-review/SKILL.md.tmpl +++ b/plan-eng-review/SKILL.md.tmpl @@ -112,22 +112,7 @@ When evaluating architecture, think "boring by default." When reviewing tests, t setopt +o nomatch 2>/dev/null || true # zsh compat 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') -_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 ~/.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="" -if [ -n "$_REPOTOP" ]; then - [ -f "$_REPOTOP/DESIGN.md" ] && _REPODOC="$_REPOTOP/DESIGN.md" - [ -z "$_REPODOC" ] && _REPODOC=$(ls -t "$_REPOTOP"/docs/designs/*.md 2>/dev/null | head -1) -fi -DESIGN="$_LOCALDOC" -if [ -n "$_REPODOC" ] && { [ -z "$_LOCALDOC" ] || [ "$_REPODOC" -nt "$_LOCALDOC" ]; }; then - DESIGN="$_REPODOC" -fi -[ -n "$DESIGN" ] && echo "Design doc found: $DESIGN" || echo "No design doc found" +{{DESIGN_DOC_DISCOVERY}} ``` If a design doc exists, read it. Use it as the source of truth for the problem statement, constraints, and chosen approach. If it has a `Supersedes:` field, note that this is a revised design — check the prior version for context on what changed and why. diff --git a/scripts/resolvers/design-doc-discovery.ts b/scripts/resolvers/design-doc-discovery.ts new file mode 100644 index 000000000..757d85efe --- /dev/null +++ b/scripts/resolvers/design-doc-discovery.ts @@ -0,0 +1,49 @@ +/** + * {{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/projects//, 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 + * ~/.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 + * lived verbatim in plan-ceo-review, plan-eng-review, plan-devex-review, and + * the prerequisite-skill re-check in review.ts (GStack 2 fork-port wave, + * time-attack/gstack). Drift between copies meant plan reviews could + * disagree about which design doc wins. + * + * The fragment carries no code fences — the {{DESIGN_DOC_DISCOVERY}} token + * sits inside each caller's ```bash block. Callers must set $SLUG and + * $BRANCH first (and `setopt +o nomatch` for zsh); the block sets $DESIGN + * and prints "Design doc found: ..." or "No design doc found". + */ + +import type { TemplateContext } from './types'; + +/** + * Raw canonical fragment, exported so TS resolvers (review.ts's prerequisite + * 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/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 ~/.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="" +if [ -n "$_REPOTOP" ]; then + [ -f "$_REPOTOP/DESIGN.md" ] && _REPODOC="$_REPOTOP/DESIGN.md" + [ -z "$_REPODOC" ] && _REPODOC=$(ls -t "$_REPOTOP"/docs/designs/*.md 2>/dev/null | head -1) +fi +DESIGN="$_LOCALDOC" +if [ -n "$_REPODOC" ] && { [ -z "$_LOCALDOC" ] || [ "$_REPODOC" -nt "$_LOCALDOC" ]; }; then + DESIGN="$_REPODOC" +fi +[ -n "$DESIGN" ] && echo "Design doc found: $DESIGN" || echo "No design doc found"`; + +export function generateDesignDocDiscovery(_ctx: TemplateContext): string { + return DESIGN_DOC_DISCOVERY_BLOCK; +} diff --git a/scripts/resolvers/index.ts b/scripts/resolvers/index.ts index 148cdaca3..3b2647ed0 100644 --- a/scripts/resolvers/index.ts +++ b/scripts/resolvers/index.ts @@ -37,6 +37,7 @@ import { generateTasksSectionEmit, generateTasksSectionAggregate } from './tasks import { SECTION, SECTION_INDEX } from './sections'; import { generateRedactTaxonomyTable, generateRedactInvocationBlock } from './redact-doc'; import { generateThirdPartyActions } from './third-party-actions'; +import { generateDesignDocDiscovery } from './design-doc-discovery'; export const RESOLVERS: Record = { SLUG_EVAL: generateSlugEval, @@ -44,6 +45,7 @@ export const RESOLVERS: Record = { REDACT_TAXONOMY_TABLE: generateRedactTaxonomyTable, REDACT_INVOCATION_BLOCK: generateRedactInvocationBlock, THIRD_PARTY_ACTIONS: generateThirdPartyActions, + DESIGN_DOC_DISCOVERY: generateDesignDocDiscovery, COMMAND_REFERENCE: generateCommandReference, SNAPSHOT_FLAGS: generateSnapshotFlags, PREAMBLE: generatePreamble, diff --git a/scripts/resolvers/review.ts b/scripts/resolvers/review.ts index 18db46c9f..941f631bb 100644 --- a/scripts/resolvers/review.ts +++ b/scripts/resolvers/review.ts @@ -15,6 +15,7 @@ import type { TemplateContext } from './types'; import { generateInvokeSkill } from './composition'; import { codexPreflight, codexErrorHandling } from './constants'; +import { DESIGN_DOC_DISCOVERY_BLOCK } from './design-doc-discovery'; const CODEX_BOUNDARY = 'IMPORTANT: Do NOT read or execute any files under ~/.claude/, ~/.agents/, .claude/skills/, or agents/. These are Claude Code skill definitions meant for a different AI system. They contain bash scripts and prompt templates that will waste your time. Ignore them completely. Do NOT modify agents/openai.yaml. Stay focused on the repository code only.\\n\\n'; @@ -309,22 +310,7 @@ After /${first} completes, re-run the design doc check: setopt +o nomatch 2>/dev/null || true # zsh compat 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') -_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 ~/.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="" -if [ -n "$_REPOTOP" ]; then - [ -f "$_REPOTOP/DESIGN.md" ] && _REPODOC="$_REPOTOP/DESIGN.md" - [ -z "$_REPODOC" ] && _REPODOC=$(ls -t "$_REPOTOP"/docs/designs/*.md 2>/dev/null | head -1) -fi -DESIGN="$_LOCALDOC" -if [ -n "$_REPODOC" ] && { [ -z "$_LOCALDOC" ] || [ "$_REPODOC" -nt "$_LOCALDOC" ]; }; then - DESIGN="$_REPODOC" -fi -[ -n "$DESIGN" ] && echo "Design doc found: $DESIGN" || echo "No design doc found" +${DESIGN_DOC_DISCOVERY_BLOCK} \`\`\` If a design doc is now found, read it and continue the review.