mirror of
https://github.com/garrytan/gstack.git
synced 2026-11-02 15:04:37 +01:00
F2: design-doc discovery reads docs/designs, never a root DESIGN.md
A root DESIGN.md is the design system /design-consultation writes, so preferring it unconditionally reviewed plans against a token file while printing "Design doc found" and never looked at docs/designs/ feature docs. Discovery now considers only docs/designs/*.md as the repo-local design doc (still freshness-compared with the private ~/.gstack copy). The CEO finding fixture seeds its supplied design as a docs/designs doc and its test follows. Co-authored-by: Bit0ps <alex.dreamscaper@gmail.com>
This commit is contained in:
1 parent
e99c1fc7a0
commit
998f116b6d
3 files changed
+44
-10
No files matched your search
@@ -3,8 +3,10 @@
|
||||
*
|
||||
* 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
|
||||
* doc, then lets a repo-local feature doc (docs/designs/*.md) win when
|
||||
* it is at least as fresh. A root DESIGN.md is never a design doc: it is the
|
||||
* design system /design-consultation writes, and preferring it reviewed
|
||||
* plans against a token file while printing "Design doc found" (#2839). 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.
|
||||
*
|
||||
@@ -34,10 +36,8 @@ export const DESIGN_DOC_DISCOVERY_BLOCK = `_LOCALDOC=$(ls -t ~/.gstack/projects/
|
||||
# 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
|
||||
# Only docs/designs/ holds feature design docs; a root DESIGN.md is the design system (#2839).
|
||||
[ -n "$_REPOTOP" ] && _REPODOC=$(ls -t "$_REPOTOP"/docs/designs/*.md 2>/dev/null | head -1)
|
||||
DESIGN="$_LOCALDOC"
|
||||
if [ -n "$_REPODOC" ] && { [ -z "$_LOCALDOC" ] || [ "$_REPODOC" -nt "$_LOCALDOC" ]; }; then
|
||||
DESIGN="$_REPODOC"
|
||||
|
||||
@@ -291,6 +291,36 @@ test.each([
|
||||
expect(pickSuppliedCeoPlanStart({ options })).toBe(expected);
|
||||
});
|
||||
|
||||
describe('F2: design-doc discovery prefers docs/designs over a root DESIGN.md (#2839)', () => {
|
||||
function discover(files: Record<string, string>): string {
|
||||
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'design-doc-f2-'));
|
||||
try {
|
||||
const cwd = path.join(root, 'project');
|
||||
const home = path.join(root, 'home');
|
||||
fs.mkdirSync(cwd); fs.mkdirSync(home);
|
||||
execFileSync('git', ['init', '-q', '-b', 'main'], { cwd, timeout: 30_000 });
|
||||
for (const [rel, body] of Object.entries(files)) {
|
||||
fs.mkdirSync(path.dirname(path.join(cwd, rel)), { recursive: true });
|
||||
fs.writeFileSync(path.join(cwd, rel), body);
|
||||
}
|
||||
// The design system is the newest file, so mtime alone would pick it.
|
||||
if (files['DESIGN.md']) fs.utimesSync(path.join(cwd, 'DESIGN.md'), new Date(), new Date(Date.now() + 60_000));
|
||||
return execFileSync('bash', ['-c', `SLUG=fixture; BRANCH=main; ${DESIGN_DOC_DISCOVERY_BLOCK}`], {
|
||||
cwd, env: { PATH: process.env.PATH!, HOME: home }, encoding: 'utf8', timeout: 10_000,
|
||||
}).replace(fs.realpathSync(cwd), '<repo>').replace(cwd, '<repo>');
|
||||
} finally { fs.rmSync(root, { recursive: true, force: true }); }
|
||||
}
|
||||
|
||||
test('a feature doc in docs/designs/ wins over a newer root DESIGN.md', () => {
|
||||
expect(discover({ 'DESIGN.md': '# Design system\ncolors: {}\n', 'docs/designs/pricing.md': '# Pricing design\n' }))
|
||||
.toBe('Design doc found: <repo>/docs/designs/pricing.md\n');
|
||||
});
|
||||
|
||||
test('a root DESIGN.md alone is a design system, not a design doc', () => {
|
||||
expect(discover({ 'DESIGN.md': '# Design system\n' })).toBe('No design doc found\n');
|
||||
});
|
||||
});
|
||||
|
||||
describe('CEO finding fixture establishes scope before launch', () => {
|
||||
test('a supplied design satisfies actual prerequisite discovery without becoming a branch change', () => {
|
||||
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'ceo-design-seed-'));
|
||||
@@ -304,8 +334,8 @@ describe('CEO finding fixture establishes scope before launch', () => {
|
||||
const output = execFileSync('bash', ['-c', `SLUG=fixture; BRANCH=main; ${DESIGN_DOC_DISCOVERY_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`);
|
||||
expect(execFileSync('git', ['show', 'HEAD:DESIGN.md'], { cwd, encoding: 'utf8', timeout: 30_000 })).toBe(design);
|
||||
expect(output).toBe(`Design doc found: ${path.join(cwd, 'docs', 'designs', 'feature-design.md')}\n`);
|
||||
expect(execFileSync('git', ['show', 'HEAD:docs/designs/feature-design.md'], { cwd, encoding: 'utf8', timeout: 30_000 })).toBe(design);
|
||||
expect(execFileSync('git', ['show', 'HEAD:review-input.md'], { cwd, encoding: 'utf8', timeout: 30_000 })).toBe(plan);
|
||||
expect(execFileSync('git', ['status', '--porcelain'], { cwd, encoding: 'utf8', timeout: 30_000 })).toBe('');
|
||||
expect(execFileSync('git', ['diff', 'origin/main...HEAD'], { cwd, encoding: 'utf8', timeout: 30_000 })).toBe('');
|
||||
|
||||
@@ -59,7 +59,11 @@ export function seedPlanReviewProject(projectDir: string, plan: string, skill: '
|
||||
throw new Error('Plan review fixture requires a fresh private directory');
|
||||
}
|
||||
fs.writeFileSync(path.join(projectDir, 'review-input.md'), plan, { flag: 'wx' });
|
||||
if (design !== undefined) fs.writeFileSync(path.join(projectDir, 'DESIGN.md'), design, { flag: 'wx' });
|
||||
// A feature design doc lives in docs/designs/; a root DESIGN.md is a design system (F2, #2839).
|
||||
if (design !== undefined) {
|
||||
fs.mkdirSync(path.join(projectDir, 'docs', 'designs'), { recursive: true });
|
||||
fs.writeFileSync(path.join(projectDir, 'docs', 'designs', 'feature-design.md'), design, { flag: 'wx' });
|
||||
}
|
||||
fs.writeFileSync(path.join(projectDir, 'README.md'), `# ${skill} fixture\n`, { flag: 'wx' });
|
||||
fs.writeFileSync(path.join(projectDir, 'CLAUDE.md'), [
|
||||
`# ${skill}`, '',
|
||||
@@ -73,7 +77,7 @@ export function seedPlanReviewProject(projectDir: string, plan: string, skill: '
|
||||
].join('\n'), { flag: 'wx' });
|
||||
const git = (args: string[]) => execFileSync('git', args, { cwd: projectDir, stdio: 'pipe', timeout: 10_000 });
|
||||
git(['init', '-b', 'main']);
|
||||
git(['add', 'README.md', 'CLAUDE.md', 'review-input.md', ...(design === undefined ? [] : ['DESIGN.md'])]);
|
||||
git(['add', 'README.md', 'CLAUDE.md', 'review-input.md', ...(design === undefined ? [] : ['docs/designs/feature-design.md'])]);
|
||||
git(['-c', 'user.name=Finding fixture', '-c', 'user.email=fixture@gstack.test', 'commit', '-m', 'Seed review input']);
|
||||
git(['update-ref', 'refs/remotes/origin/main', 'HEAD']);
|
||||
}
|
||||
Reference in new issue
Block a user