mirror of
https://github.com/garrytan/gstack.git
synced 2026-11-06 17:04:27 +01:00
W3 T3/T4/E2: design-doc discovery runs bin/gstack-design-doc-find on the state root; drop DESIGN_DOC_HELD rows
This commit is contained in:
1 parent
769ed52eb1
commit
dfb7330a8c
9 files changed
+183
-43
No files matched your search
@@ -224,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/projects/$SLUG/*-design-*.md 2>/dev/null | head -1`
|
||||
- Discover design docs: `~/.claude/skills/gstack/bin/gstack-design-doc-find "$SLUG" "$BRANCH"` (prints the doc path, or nothing)
|
||||
- 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).
|
||||
|
||||
Executable
+50
@@ -0,0 +1,50 @@
|
||||
#!/usr/bin/env bash
|
||||
# gstack-design-doc-find — print the design doc a plan review should read.
|
||||
#
|
||||
# Usage: gstack-design-doc-find <slug> <branch>
|
||||
#
|
||||
# Prints one absolute path, or nothing when no design doc exists. Exit 0 either
|
||||
# way; exit 2 on a usage error.
|
||||
#
|
||||
# Precedence:
|
||||
# 1. newest $GSTACK_STATE_ROOT/projects/<slug>/*-<branch>-design-*.md
|
||||
# 2. else newest $GSTACK_STATE_ROOT/projects/<slug>/*-design-*.md
|
||||
# 3. a repo doc ($TOP/DESIGN.md, else newest $TOP/docs/designs/*.md) wins when
|
||||
# no state-root doc exists or it is newer than the state-root doc.
|
||||
# office-hours writes both copies; the committed copy is what teammates
|
||||
# see, but a stale repo doc never shadows a newer private session.
|
||||
set -u
|
||||
|
||||
if [ "$#" -ne 2 ] || [ -z "$1" ]; then
|
||||
echo "usage: gstack-design-doc-find <slug> <branch>" >&2
|
||||
exit 2
|
||||
fi
|
||||
SLUG=$1
|
||||
BRANCH=$2
|
||||
|
||||
eval "$("$(dirname "$0")/gstack-paths")"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}"
|
||||
|
||||
newest() {
|
||||
ls -t "$@" 2>/dev/null | head -1
|
||||
}
|
||||
|
||||
PROJECT_DIR="$GSTACK_STATE_ROOT/projects/$SLUG"
|
||||
LOCALDOC=$(newest "$PROJECT_DIR"/*-"$BRANCH"-design-*.md)
|
||||
[ -z "$LOCALDOC" ] && LOCALDOC=$(newest "$PROJECT_DIR"/*-design-*.md)
|
||||
|
||||
REPODOC=""
|
||||
TOP=$(git rev-parse --show-toplevel 2>/dev/null || true)
|
||||
if [ -n "$TOP" ]; then
|
||||
if [ -f "$TOP/DESIGN.md" ]; then
|
||||
REPODOC="$TOP/DESIGN.md"
|
||||
else
|
||||
REPODOC=$(newest "$TOP"/docs/designs/*.md)
|
||||
fi
|
||||
fi
|
||||
|
||||
DESIGN="$LOCALDOC"
|
||||
if [ -n "$REPODOC" ] && { [ -z "$LOCALDOC" ] || [ "$REPODOC" -nt "$LOCALDOC" ]; }; then
|
||||
DESIGN="$REPODOC"
|
||||
fi
|
||||
[ -n "$DESIGN" ] && printf '%s\n' "$DESIGN"
|
||||
exit 0
|
||||
@@ -1,47 +1,26 @@
|
||||
/**
|
||||
* {{DESIGN_DOC_DISCOVERY}} — the canonical design-doc discovery block (#703).
|
||||
* {{DESIGN_DOC_DISCOVERY}} — the canonical design-doc discovery block.
|
||||
*
|
||||
* Finds the design doc a plan review should read: newest branch-scoped doc
|
||||
* 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
|
||||
* ~/.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.
|
||||
* One invocation of bin/gstack-design-doc-find, which owns the precedence
|
||||
* (newest branch-scoped doc in the state root's project directory, then the
|
||||
* newest project-scoped doc, then a repo-local DESIGN.md or docs/designs/*.md
|
||||
* when it is at least as fresh) and resolves the state root through
|
||||
* bin/gstack-paths. Plan reviews, autoplan and the prerequisite-skill re-check
|
||||
* in spec-review.ts all render this one block, so they agree on which 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".
|
||||
* $BRANCH first; 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.
|
||||
* Raw canonical fragment, exported so TS resolvers (spec-review.ts's
|
||||
* prerequisite re-check) can interpolate it into their own template strings.
|
||||
*/
|
||||
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
|
||||
export const DESIGN_DOC_DISCOVERY_BLOCK = `DESIGN=$(~/.claude/skills/gstack/bin/gstack-design-doc-find "$SLUG" "$BRANCH")
|
||||
[ -n "$DESIGN" ] && echo "Design doc found: $DESIGN" || echo "No design doc found"`;
|
||||
|
||||
export function generateDesignDocDiscovery(_ctx: TemplateContext): string {
|
||||
|
||||
@@ -23,7 +23,7 @@ function fixture(plan = original) {
|
||||
const env = { PATH: process.env.PATH!, HOME: home, GSTACK_HOME: state, GSTACK_STATE_ROOT: state };
|
||||
const start = () => execFileSync(join(root, 'bin/gstack-skill-start'), ['--skill', 'autoplan'],
|
||||
{ cwd, env, encoding: 'utf8', timeout: 30_000 });
|
||||
const discover = () => execFileSync('bash', ['-c', DESIGN_DOC_DISCOVERY_BLOCK],
|
||||
const discover = () => execFileSync('bash', ['-c', DESIGN_DOC_DISCOVERY_BLOCK.replaceAll('~/.claude/skills/gstack/bin/', `${root}/bin/`)],
|
||||
{ cwd, env: { ...env, SLUG: 'chain-fixture', BRANCH: 'main' }, encoding: 'utf8', timeout: 5000 }).trim();
|
||||
return { cwd, home, state, planFile, start, discover, cleanup: () => rmSync(temp, { recursive: true, force: true }) };
|
||||
}
|
||||
|
||||
@@ -301,7 +301,8 @@ describe('CEO finding fixture establishes scope before launch', () => {
|
||||
const plan = '# Export saved settings\nReview the CSV formatter before implementation.\n';
|
||||
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}`], {
|
||||
const block = DESIGN_DOC_DISCOVERY_BLOCK.replaceAll('~/.claude/skills/gstack/bin/', `${path.resolve(import.meta.dir, '..')}/bin/`);
|
||||
const output = execFileSync('bash', ['-c', `SLUG=fixture; BRANCH=main; ${block}`], {
|
||||
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`);
|
||||
|
||||
@@ -1927,6 +1927,10 @@ 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'));
|
||||
for (const bin of ['gstack-paths', 'gstack-design-doc-find']) {
|
||||
fs.copyFileSync(path.join(ROOT, 'bin', bin), path.join(path.dirname(helper), bin));
|
||||
fs.chmodSync(path.join(path.dirname(helper), bin), 0o755);
|
||||
}
|
||||
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]) {
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
import { afterEach, describe, expect, test } from 'bun:test';
|
||||
import { execFileSync } from 'node:child_process';
|
||||
import * as fs from 'node:fs';
|
||||
import * as os from 'node:os';
|
||||
import * as path from 'node:path';
|
||||
|
||||
const ROOT = path.resolve(import.meta.dir, '..');
|
||||
const BIN = path.join(ROOT, 'bin/gstack-design-doc-find');
|
||||
const temps: string[] = [];
|
||||
|
||||
afterEach(() => {
|
||||
for (const dir of temps.splice(0)) fs.rmSync(dir, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
function fixture() {
|
||||
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'design-doc-find-'));
|
||||
temps.push(dir);
|
||||
const home = path.join(dir, 'home');
|
||||
const repo = path.join(dir, 'repo');
|
||||
fs.mkdirSync(home, { recursive: true });
|
||||
fs.mkdirSync(repo, { recursive: true });
|
||||
execFileSync('git', ['init', '-q', '-b', 'main'], { cwd: repo, timeout: 10_000 });
|
||||
const baseEnv = { PATH: process.env.PATH!, HOME: home, GIT_CEILING_DIRECTORIES: dir };
|
||||
const write = (file: string, ageSeconds: number) => {
|
||||
fs.mkdirSync(path.dirname(file), { recursive: true });
|
||||
fs.writeFileSync(file, '# Design\n');
|
||||
const t = Date.now() / 1000 - ageSeconds;
|
||||
fs.utimesSync(file, t, t);
|
||||
return file;
|
||||
};
|
||||
const find = (env: Record<string, string> = {}, args = ['proj', 'feat-x']) =>
|
||||
execFileSync(BIN, args, { cwd: repo, env: { ...baseEnv, ...env }, encoding: 'utf8', timeout: 10_000 }).trim();
|
||||
return { dir, home, repo, write, find };
|
||||
}
|
||||
|
||||
describe('gstack-design-doc-find', () => {
|
||||
test('default root is ~/.gstack and prints nothing when no doc exists', () => {
|
||||
const f = fixture();
|
||||
expect(f.find()).toBe('');
|
||||
const doc = f.write(path.join(f.home, '.gstack/projects/proj/user-main-design-20260101.md'), 100);
|
||||
expect(f.find()).toBe(doc);
|
||||
});
|
||||
|
||||
test('GSTACK_HOME moves the state root', () => {
|
||||
const f = fixture();
|
||||
f.write(path.join(f.home, '.gstack/projects/proj/user-main-design-a.md'), 10);
|
||||
const custom = path.join(f.dir, 'custom-state');
|
||||
const doc = f.write(path.join(custom, 'projects/proj/user-main-design-b.md'), 100);
|
||||
expect(f.find({ GSTACK_HOME: custom })).toBe(doc);
|
||||
});
|
||||
|
||||
test('a branch-specific doc wins over a newer generic doc', () => {
|
||||
const f = fixture();
|
||||
const branch = f.write(path.join(f.home, '.gstack/projects/proj/user-feat-x-design-1.md'), 500);
|
||||
f.write(path.join(f.home, '.gstack/projects/proj/user-main-design-2.md'), 10);
|
||||
expect(f.find()).toBe(branch);
|
||||
expect(f.find({}, ['proj', 'other'])).toBe(path.join(f.home, '.gstack/projects/proj/user-main-design-2.md'));
|
||||
});
|
||||
|
||||
test('repo DESIGN.md wins only when at least as fresh, and beats docs/designs', () => {
|
||||
const f = fixture();
|
||||
const local = f.write(path.join(f.home, '.gstack/projects/proj/user-feat-x-design-1.md'), 100);
|
||||
const designs = f.write(path.join(f.repo, 'docs/designs/plan.md'), 10);
|
||||
expect(f.find()).toBe(designs);
|
||||
const top = f.write(path.join(f.repo, 'DESIGN.md'), 1000);
|
||||
expect(f.find()).toBe(local);
|
||||
fs.utimesSync(top, Date.now() / 1000, Date.now() / 1000);
|
||||
expect(f.find()).toBe(top);
|
||||
});
|
||||
|
||||
test('a repo doc is used when the state root has none', () => {
|
||||
const f = fixture();
|
||||
const designs = f.write(path.join(f.repo, 'docs/designs/plan.md'), 1000);
|
||||
expect(f.find()).toBe(designs);
|
||||
});
|
||||
|
||||
test('usage errors exit 2', () => {
|
||||
const f = fixture();
|
||||
expect(() => f.find({}, [])).toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('rendered Design Doc Check block', () => {
|
||||
const rendered = fs.readFileSync(path.join(ROOT, 'plan-ceo-review/SKILL.md'), 'utf8');
|
||||
const block = rendered.slice(rendered.indexOf('DESIGN=$(~/.claude/skills/gstack/bin/gstack-design-doc-find'))
|
||||
.split('\n').slice(0, 2).join('\n');
|
||||
|
||||
test('the generated skill renders one helper invocation', () => {
|
||||
expect(block).toContain('gstack-design-doc-find "$SLUG" "$BRANCH"');
|
||||
expect(block).toContain('echo "Design doc found: $DESIGN"');
|
||||
expect(block).toContain('echo "No design doc found"');
|
||||
});
|
||||
|
||||
test('finds an office-hours doc under a custom GSTACK_HOME and keeps the default-root result', () => {
|
||||
const f = fixture();
|
||||
const installed = path.join(f.home, '.claude/skills/gstack/bin');
|
||||
fs.mkdirSync(installed, { recursive: true });
|
||||
for (const bin of ['gstack-design-doc-find', 'gstack-paths', 'gstack-state-root.sh']) {
|
||||
fs.copyFileSync(path.join(ROOT, 'bin', bin), path.join(installed, bin));
|
||||
fs.chmodSync(path.join(installed, bin), 0o755);
|
||||
}
|
||||
const run = (env: Record<string, string>) => execFileSync('bash', ['-c', `SLUG=proj; BRANCH=main; ${block}`],
|
||||
{ cwd: f.repo, env: { PATH: process.env.PATH!, HOME: f.home, GIT_CEILING_DIRECTORIES: f.dir, ...env }, encoding: 'utf8', timeout: 10_000 }).trim();
|
||||
const custom = path.join(f.dir, 'custom-state');
|
||||
const doc = f.write(path.join(custom, 'projects/proj/garry-main-design-20261003-120000.md'), 60);
|
||||
expect(run({ GSTACK_HOME: custom })).toBe(`Design doc found: ${doc}`);
|
||||
expect(run({})).toBe('No design doc found');
|
||||
const legacy = f.write(path.join(f.home, '.gstack/projects/proj/garry-main-design-20261001-120000.md'), 60);
|
||||
expect(run({})).toBe(`Design doc found: ${legacy}`);
|
||||
});
|
||||
});
|
||||
@@ -269,7 +269,7 @@ export const E2E_TOUCHFILES: Record<string, string[]> = {
|
||||
// (--max-concurrency, no retries), so worst-case cost is one pass of
|
||||
// each, sharing the API budget with sibling tests — not the
|
||||
// sequential ~+10min a local read suggests.
|
||||
'plan-mode-no-op': ['bin/gstack-state-root.sh', 'lib/state-root.ts', 'test/fixtures/auto-decide-recommendation-361c.json',
|
||||
'plan-mode-no-op': ['bin/gstack-state-root.sh', 'lib/state-root.ts', 'bin/gstack-design-doc-find', 'scripts/resolvers/design-doc-discovery.ts', 'scripts/resolvers/spec-review.ts', 'plan-devex-review/**', 'test/fixtures/auto-decide-recommendation-361c.json',
|
||||
|
||||
'test/fixtures/auto-decide-target-361c.json',
|
||||
|
||||
@@ -838,7 +838,7 @@ export const E2E_TOUCHFILES: Record<string, string[]> = {
|
||||
'test/fixtures/outside-async-task-m-events.json',
|
||||
'test/fixtures/autoplan-amend-input-77.json',
|
||||
'test/fixtures/autoplan-phase-handoff-6714.json','scripts/resolvers/learnings.ts', 'scripts/resolvers/preamble/generate-completion-status.ts', 'scripts/resolvers/preamble/generate-preamble-bash.ts', 'test/fixtures/plan-scope-recovery-av.json', 'test/fixtures/design-scope-checkpoint-at.json', 'scripts/resolvers/composition.ts', 'autoplan/**', 'codex/**', 'bin/gstack-codex-probe', 'scripts/resolvers/review-dashboard.ts', 'scripts/resolvers/plan-gates.ts', 'scripts/resolvers/spec-review.ts', 'scripts/resolvers/outside-voice-steps.ts', 'scripts/resolvers/review-scope.ts', 'scripts/resolvers/outside-voice.ts', 'scripts/resolvers/design.ts', 'test/skill-e2e-autoplan-dual-voice.test.ts', 'bin/gstack-autoplan-snapshot.ts', 'test/fixtures/autoplan/t-ceo-omitted-obligations.json', 'test/fixtures/autoplan/u-ceo-original-loss.json', 'test/fixtures/autoplan/v-ceo-dangling-references.json',
|
||||
'scripts/resolvers/design-doc-discovery.ts', 'plan-ceo-review/**', 'plan-eng-review/**', 'plan-design-review/**', 'plan-devex-review/**', 'scripts/resolvers/testing.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/resolvers/tasks-section.ts', 'test/helpers/autoplan-phase-observer.ts', 'scripts/resolve-codex-generation-model.ts', 'scripts/resolvers/constants.ts'],
|
||||
'scripts/resolvers/design-doc-discovery.ts', 'bin/gstack-design-doc-find', 'plan-ceo-review/**', 'plan-eng-review/**', 'plan-design-review/**', 'plan-devex-review/**', 'scripts/resolvers/testing.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/resolvers/tasks-section.ts', 'test/helpers/autoplan-phase-observer.ts', 'scripts/resolve-codex-generation-model.ts', 'scripts/resolvers/constants.ts'],
|
||||
|
||||
// Multi-provider benchmark adapters — live API smoke against real claude/codex/gemini CLIs
|
||||
'benchmark-providers-live': ['bin/gstack-model-benchmark', 'test/helpers/providers/**', 'test/helpers/benchmark-runner.ts', 'test/helpers/pricing.ts', 'test/skill-e2e-benchmark-providers.test.ts'],
|
||||
|
||||
@@ -169,10 +169,8 @@ 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: 5 of 5 runs failed with it, 6 of 6 passed without); 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 },
|
||||
@@ -183,9 +181,6 @@ 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 },
|
||||
];
|
||||
|
||||
Reference in new issue
Block a user