fix: design briefs and approval feedback travel as agent-written files

Design briefs went into $D variants/generate/check as a double-quoted
argument, and approval feedback was spliced into a single-quoted echo of JSON,
so a quote, backtick or $(...) in either broke the command or ran. The agent
now writes each brief into a private .gstack/tmp file and the block passes
--brief "$(cat "$BRIEF_FILE")" (the CLI's --brief-file expects a JSON
brief, so plain text uses --brief). variants and the later check reuse the
same brief file. approved.json is built with jq -n from --arg values and
--rawfile feedback, so the feedback is data. The office-hours mockup flow
creates its brief and feedback files in one block.

design-approval-flow now writes the feedback file (with quotes, backticks and
$(...) in it) instead of substituting <FEEDBACK>, and checks the file is
removed after the save.

Co-authored-by: dgrant <dgrant@users.noreply.github.com>
This commit is contained in:
garrytananddgrant committed 2026-10-06 20:57:02 +00:00
1 parent e399cf69e2
commit 7637f604a4
13 files changed
+228 -77

No files matched your search

+10 -3
View File
@@ -22,7 +22,8 @@ function bashBlockAfter(marker: string): string {
}
const BOARD = bashBlockAfter('<!-- design:board -->');
const APPROVAL = SKILL.slice(SKILL.indexOf('**Save the approved choice.**')).match(/```bash\n([\s\S]*?)```/)![1];
// The save block follows the feedback-file block.
const APPROVAL = SKILL.slice(SKILL.indexOf('**Save the approved choice.**')).match(/```bash\n(_IMG=[\s\S]*?)```/)![1];
let dir: string;
let designDir: string;
@@ -50,7 +51,12 @@ afterEach(() => { fs.rmSync(dir, { recursive: true, force: true }); });
const sh = (script: string) => spawnSync('bash', ['-c', script], { cwd: dir, env, encoding: 'utf8', timeout: 60_000 });
const variants = () => JSON.parse(sh('"$D" variants --brief "home page" --count 2 --output-dir "$_DESIGN_DIR/"').stdout);
const approve = (letter: string) => sh(APPROVAL.replaceAll('<VARIANT>', letter).replaceAll('<FEEDBACK>', 'calmer').replaceAll('<SCREEN>', 'home'));
// CEO-12: the feedback travels in an agent-written file under the project's .gstack/tmp, never in the command.
const approve = (letter: string) => {
fs.mkdirSync(path.join(dir, '.gstack', 'tmp'), { recursive: true });
fs.writeFileSync(path.join(dir, '.gstack', 'tmp', 'feedback.fixture'), 'calmer "quoted" `not run` $(not run)\n');
return sh(APPROVAL.replaceAll('<VARIANT>', letter).replaceAll('<feedback-file-name>', 'feedback.fixture').replaceAll('<SCREEN>', 'home'));
};
// Value: protects=round two's approval comes from round two's board, never round one's stale feedback.json or the newest-file listing;
// fails_when=the board block stops archiving feedback.json or approval maps letters through the directory; why_new=eng feedback-binding; seam=none
@@ -73,7 +79,8 @@ test('round two board archives round one Submit and approval maps to the bumped
expect(approved.status, approved.stderr).toBe(0);
expect(approved.stdout).toContain(`APPROVED_IMAGE: ${path.join(designDir, 'variant-A-2.png')}`);
const record = JSON.parse(fs.readFileSync(path.join(designDir, 'approved.json'), 'utf8'));
expect(record).toMatchObject({ approved_variant: 'A', approved_path: 'variant-A-2.png', screen: 'home', feedback: 'calmer' });
expect(record).toMatchObject({ approved_variant: 'A', approved_path: 'variant-A-2.png', screen: 'home', feedback: 'calmer "quoted" `not run` $(not run)' });
expect(fs.existsSync(path.join(dir, '.gstack', 'tmp', 'feedback.fixture'))).toBe(false);
const missing = approve('C');
expect(missing.stdout).toContain('NO_BOARD_IMAGE');
+2
View File
@@ -161,4 +161,6 @@ export const IDENTIFIER_PLACEHOLDERS: Record<string, IdentifierGrammar> = {
// A3 group 1 (Aside prompts, design briefs, design approval feedback): mktemp basenames.
'<prompt-file-name>': { grammar: ID, what: 'basename of the mktemp Aside prompt file printed by the previous block' },
'<brief-file-name>': { grammar: ID, what: 'basename of the mktemp design brief file printed by the free-text block' },
'<feedback-file-name>': { grammar: ID, what: 'basename of the mktemp design feedback file printed by the previous block' },
};