feat: add DESIGN_SHOTGUN_LOOP resolver + fix design artifact paths

Adds generateDesignShotgunLoop() resolver for the shared comparison board
feedback loop (serve via HTTP, handle regenerate/remix, AskUserQuestion
fallback, feedback confirmation). Registered as {{DESIGN_SHOTGUN_LOOP}}.

Fixes generateDesignMockup() to use ~/.gstack/projects/$SLUG/designs/
instead of /tmp/ and docs/designs/. Replaces broken $B goto file:// +
$B eval polling with $D compare --serve (HTTP-based, stdout feedback).

Adds CRITICAL PATH RULE guardrail to DESIGN_SETUP: design artifacts must
go to ~/.gstack/projects/$SLUG/designs/, never .context/ or /tmp/.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Garry Tan 2026-03-27 08:23:46 -06:00
parent a0af98c1ef
commit 432e20f89f
No known key found for this signature in database
GPG Key ID: C1F69E85C74EFE1D
2 changed files with 112 additions and 65 deletions

View File

@ -757,9 +757,15 @@ If \`DESIGN_READY\`: the design binary is available for visual mockup generation
Commands: Commands:
- \`$D generate --brief "..." --output /path.png\` — generate a single mockup - \`$D generate --brief "..." --output /path.png\` — generate a single mockup
- \`$D variants --brief "..." --count 3 --output-dir /path/\` — generate N style variants - \`$D variants --brief "..." --count 3 --output-dir /path/\` — generate N style variants
- \`$D compare --images "a.png,b.png,c.png" --output /path/board.html\` — comparison board - \`$D compare --images "a.png,b.png,c.png" --output /path/board.html --serve\` — comparison board + HTTP server
- \`$D serve --html /path/board.html\` — serve comparison board and collect feedback via HTTP
- \`$D check --image /path.png --brief "..."\` — vision quality gate - \`$D check --image /path.png --brief "..."\` — vision quality gate
- \`$D iterate --session /path/session.json --feedback "..." --output /path.png\` — iterate`; - \`$D iterate --session /path/session.json --feedback "..." --output /path.png\` — iterate
**CRITICAL PATH RULE:** All design artifacts (mockups, comparison boards, approved.json)
MUST be saved to \`~/.gstack/projects/$SLUG/designs/\`, NEVER to \`.context/\`,
\`docs/designs/\`, \`/tmp/\`, or any project-local directory. Design artifacts are USER
data, not project files. They persist across branches, conversations, and workspaces.`;
} }
export function generateDesignMockup(ctx: TemplateContext): string { export function generateDesignMockup(ctx: TemplateContext): string {
@ -780,85 +786,125 @@ D=""
Generating visual mockups of the proposed design... (say "skip" if you don't need visuals) Generating visual mockups of the proposed design... (say "skip" if you don't need visuals)
**Step 1: Construct the design brief** **Step 1: Set up the design directory**
\`\`\`bash
eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)"
_DESIGN_DIR=~/.gstack/projects/$SLUG/designs/mockup-$(date +%Y%m%d)
mkdir -p "$_DESIGN_DIR"
echo "DESIGN_DIR: $_DESIGN_DIR"
\`\`\`
**Step 2: Construct the design brief**
Read DESIGN.md if it exists use it to constrain the visual style. If no DESIGN.md, Read DESIGN.md if it exists use it to constrain the visual style. If no DESIGN.md,
explore wide across diverse directions. explore wide across diverse directions.
Assemble a structured brief as a JSON file: **Step 3: Generate 3 variants**
\`\`\`bash
cat > /tmp/gstack-design-brief.json << 'BRIEF_EOF'
{
"goal": "<what this screen/page does>",
"audience": "<who uses it>",
"style": "<visual style from DESIGN.md or from the user's description>",
"elements": ["<list>", "<of>", "<key UI elements>"],
"constraints": "<any size/layout constraints>",
"screenType": "<desktop-dashboard|mobile-app|landing-page|settings|etc>"
}
BRIEF_EOF
\`\`\`
**Step 2: Generate 3 variants**
\`\`\`bash \`\`\`bash
$D variants --brief-file /tmp/gstack-design-brief.json --count 3 --output-dir /tmp/gstack-mockups/ $D variants --brief "<assembled brief>" --count 3 --output-dir "$_DESIGN_DIR/"
\`\`\` \`\`\`
This generates 3 style variations of the same brief (~40 seconds total). This generates 3 style variations of the same brief (~40 seconds total).
**Step 3: Show the comparison board** **Step 4: Show variants inline, then open comparison board**
Show each variant to the user inline first (read the PNGs with Read tool), then
create and serve the comparison board:
\`\`\`bash \`\`\`bash
$D compare --images "/tmp/gstack-mockups/variant-A.png,/tmp/gstack-mockups/variant-B.png,/tmp/gstack-mockups/variant-C.png" --output /tmp/gstack-design-board.html $D compare --images "$_DESIGN_DIR/variant-A.png,$_DESIGN_DIR/variant-B.png,$_DESIGN_DIR/variant-C.png" --output "$_DESIGN_DIR/design-board.html" --serve
\`\`\` \`\`\`
Open the comparison board in headed Chrome for user review: This opens the board in the user's default browser and blocks until feedback is
received. Read stdout for the structured JSON result. No polling needed.
If \`$D serve\` is not available or fails, fall back to AskUserQuestion:
"I've opened the design board. Which variant do you prefer? Any feedback?"
**Step 5: Handle feedback**
If the JSON contains \`"regenerated": true\`:
1. Read \`regenerateAction\` (or \`remixSpec\` for remix requests)
2. Generate new variants with \`$D iterate\` or \`$D variants\` using updated brief
3. Create new board with \`$D compare\`
4. POST the new HTML to the running server via \`curl -X POST http://localhost:PORT/api/reload -H 'Content-Type: application/json' -d '{"html":"$_DESIGN_DIR/design-board.html"}'\`
(parse the port from stderr: look for \`SERVE_STARTED: port=XXXXX\`)
5. Board auto-refreshes in the same tab
If \`"regenerated": false\`: proceed with the approved variant.
**Step 6: Save approved choice**
\`\`\`bash \`\`\`bash
$B goto file:///tmp/gstack-design-board.html echo '{"approved_variant":"<VARIANT>","feedback":"<FEEDBACK>","date":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","screen":"mockup","branch":"'$(git branch --show-current 2>/dev/null)'"}' > "$_DESIGN_DIR/approved.json"
\`\`\` \`\`\`
Tell the user: "I've generated 3 design directions and opened them in Chrome. Reference the saved mockup in the design doc or plan.`;
Pick your favorite, rate the others, and click Submit when you're done." }
**Step 4: Poll for user feedback** export function generateDesignShotgunLoop(_ctx: TemplateContext): string {
return `### Comparison Board + Feedback Loop
Poll the page for the user's submission:
Create the comparison board and serve it over HTTP:
\`\`\`bash
$B eval document.getElementById('status').textContent \`\`\`bash
\`\`\` $D compare --images "$_DESIGN_DIR/variant-A.png,$_DESIGN_DIR/variant-B.png,$_DESIGN_DIR/variant-C.png" --output "$_DESIGN_DIR/design-board.html" --serve
\`\`\`
- If empty: user hasn't submitted yet. Wait 10 seconds and poll again.
- If "submitted": read the feedback. This command generates the board HTML, starts an HTTP server on a random port,
- If "regenerate": user wants new variants. Read the regeneration request, and opens it in the user's default browser. It blocks until the user submits
generate new variants with the updated brief, and refresh the comparison board. feedback. The feedback JSON is printed to stdout.
When status is "submitted", read the structured feedback: **Reading the result:**
\`\`\`bash The agent reads stdout. The JSON has this shape:
$B eval document.getElementById('feedback-result').textContent \`\`\`json
\`\`\` {
"preferred": "A",
This returns JSON with the user's preferred variant, star ratings, comments, "ratings": { "A": 4, "B": 3, "C": 2 },
and overall direction. "comments": { "A": "Love the spacing" },
"overall": "Go with A, bigger CTA",
**Step 5: Save approved mockup** "regenerated": false
}
Copy the user's preferred variant to \`docs/designs/\` (create if needed): \`\`\`
\`\`\`bash **If \`"regenerated": true\`:**
mkdir -p docs/designs 1. Read \`regenerateAction\` from the JSON (\`"different"\`, \`"match"\`, \`"more_like_B"\`,
cp /tmp/gstack-mockups/variant-<PREFERRED>.png docs/designs/<skill>-<description>-$(date +%Y%m%d).png \`"remix"\`, or custom text)
\`\`\` 2. If \`regenerateAction\` is \`"remix"\`, read \`remixSpec\` (e.g. \`{"layout":"A","colors":"B"}\`)
3. Generate new variants with \`$D iterate\` or \`$D variants\` using updated brief
Reference the saved mockup in the design doc or plan. 4. Create new board: \`$D compare --images "..." --output "$_DESIGN_DIR/design-board.html"\`
5. Reload the running server: parse the port from stderr (\`SERVE_STARTED: port=XXXXX\`),
**Step 6: Generate HTML wireframe** then POST the new HTML:
\`curl -s -X POST http://localhost:PORT/api/reload -H 'Content-Type: application/json' -d '{"html":"$_DESIGN_DIR/design-board.html"}'\`
After the mockup is approved, generate an HTML wireframe matching the approved 6. The board auto-refreshes in the same browser tab. Wait for the next stdout line.
direction using the existing DESIGN_SKETCH approach. The wireframe is what the 7. Repeat until \`"regenerated": false\`.
agent implements from the mockup is what the human approved.`;
**If \`"regenerated": false\`:**
1. Read \`preferred\`, \`ratings\`, \`comments\`, \`overall\` from the JSON
2. Proceed with the approved variant
**If \`$D serve\` fails or times out:** Fall back to AskUserQuestion:
"I've opened the design board. Which variant do you prefer? Any feedback?"
**After receiving feedback (any path):** Output a clear summary confirming
what was understood:
"Here's what I understood from your feedback:
PREFERRED: Variant [X]
RATINGS: [list]
YOUR NOTES: [comments]
DIRECTION: [overall]
Is this right?"
Use AskUserQuestion to verify before proceeding.
**Save the approved choice:**
\`\`\`bash
echo '{"approved_variant":"<V>","feedback":"<FB>","date":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","screen":"<SCREEN>","branch":"'$(git branch --show-current 2>/dev/null)'"}' > "$_DESIGN_DIR/approved.json"
\`\`\``;
} }

View File

@ -9,7 +9,7 @@ import type { TemplateContext } from './types';
import { generatePreamble } from './preamble'; import { generatePreamble } from './preamble';
import { generateTestFailureTriage } from './preamble'; import { generateTestFailureTriage } from './preamble';
import { generateCommandReference, generateSnapshotFlags, generateBrowseSetup } from './browse'; import { generateCommandReference, generateSnapshotFlags, generateBrowseSetup } from './browse';
import { generateDesignMethodology, generateDesignHardRules, generateDesignOutsideVoices, generateDesignReviewLite, generateDesignSketch, generateDesignSetup, generateDesignMockup } from './design'; import { generateDesignMethodology, generateDesignHardRules, generateDesignOutsideVoices, generateDesignReviewLite, generateDesignSketch, generateDesignSetup, generateDesignMockup, generateDesignShotgunLoop } from './design';
import { generateTestBootstrap, generateTestCoverageAuditPlan, generateTestCoverageAuditShip, generateTestCoverageAuditReview } from './testing'; import { generateTestBootstrap, generateTestCoverageAuditPlan, generateTestCoverageAuditShip, generateTestCoverageAuditReview } from './testing';
import { generateReviewDashboard, generatePlanFileReviewReport, generateSpecReviewLoop, generateBenefitsFrom, generateCodexSecondOpinion, generateAdversarialStep, generateCodexPlanReview, generatePlanCompletionAuditShip, generatePlanCompletionAuditReview, generatePlanVerificationExec } from './review'; import { generateReviewDashboard, generatePlanFileReviewReport, generateSpecReviewLoop, generateBenefitsFrom, generateCodexSecondOpinion, generateAdversarialStep, generateCodexPlanReview, generatePlanCompletionAuditShip, generatePlanCompletionAuditReview, generatePlanVerificationExec } from './review';
import { generateSlugEval, generateSlugSetup, generateBaseBranchDetect, generateDeployBootstrap, generateQAMethodology, generateCoAuthorTrailer } from './utility'; import { generateSlugEval, generateSlugSetup, generateBaseBranchDetect, generateDeployBootstrap, generateQAMethodology, generateCoAuthorTrailer } from './utility';
@ -38,6 +38,7 @@ export const RESOLVERS: Record<string, (ctx: TemplateContext) => string> = {
DESIGN_SKETCH: generateDesignSketch, DESIGN_SKETCH: generateDesignSketch,
DESIGN_SETUP: generateDesignSetup, DESIGN_SETUP: generateDesignSetup,
DESIGN_MOCKUP: generateDesignMockup, DESIGN_MOCKUP: generateDesignMockup,
DESIGN_SHOTGUN_LOOP: generateDesignShotgunLoop,
BENEFITS_FROM: generateBenefitsFrom, BENEFITS_FROM: generateBenefitsFrom,
CODEX_SECOND_OPINION: generateCodexSecondOpinion, CODEX_SECOND_OPINION: generateCodexSecondOpinion,
ADVERSARIAL_STEP: generateAdversarialStep, ADVERSARIAL_STEP: generateAdversarialStep,