From 4cd4d11cb0abfbc203de41ca2d288152160fb533 Mon Sep 17 00:00:00 2001 From: Garry Tan Date: Sun, 22 Mar 2026 20:22:23 -0700 Subject: [PATCH] =?UTF-8?q?feat:=20design=20outside=20voices=20=E2=80=94?= =?UTF-8?q?=20cross-model=20design=20critique=20(v0.11.3.0)=20(#347)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(gen-skill-docs): add design outside voices + hard rules resolvers Add generateDesignOutsideVoices() — parallel Codex + Claude subagent dispatch for cross-model design critique with litmus scorecard synthesis. Branches per skillName (plan-design-review, design-review, design-consultation) with task-specific reasoning effort (high for analytical, medium for creative). Add generateDesignHardRules() — OpenAI Frontend Skill hard rules + gstack AI slop blacklist unified into one shared block with classifier step (landing page vs app UI vs hybrid). Extract AI_SLOP_BLACKLIST constant from inline prose in generateDesignMethodology() for DRY. Extend generateDesignReviewLite() with lightweight Codex block. Extend generateDesignSketch() with outside voices opt-in after wireframe. Source: OpenAI "Designing Delightful Frontends with GPT-5.4" (Mar 2026) Co-Authored-By: Claude Opus 4.6 (1M context) * feat(design skills): add outside voices + hard rules to all design templates Insert {{DESIGN_OUTSIDE_VOICES}} in plan-design-review (between Step 0D and Pass 1), design-review (between Phase 6 and Phase 7), and design-consultation (between Phase 2 and Phase 3). Insert {{DESIGN_HARD_RULES}} in plan-design-review Pass 4 and design-review Phase 3 checklist. DESIGN_REVIEW_LITE in /ship and /review now includes a Codex design voice block with litmus checks. DESIGN_SKETCH in /office-hours now includes outside voices opt-in after wireframe approval. Regenerated all SKILL.md files (both Claude and Codex hosts). Co-Authored-By: Claude Opus 4.6 (1M context) * test: add resolver tests + touchfiles for design outside voices Add 18 test cases across 4 new describe blocks: - DESIGN_OUTSIDE_VOICES: host guard, skillName branching, reasoning effort - DESIGN_HARD_RULES: classifier, 3 rule sets, slop blacklist, OpenAI criteria - DESIGN_SKETCH extended: outside voices step, original wireframe preserved - DESIGN_REVIEW_LITE extended: Codex block, codex host exclusion Update touchfiles: add scripts/gen-skill-docs.ts to design skill E2E test dependencies for accurate diff-based test selection. Co-Authored-By: Claude Opus 4.6 (1M context) * chore: bump version and changelog (v0.11.3.0) Design outside voices — parallel Codex + Claude subagent for cross-model design critique with litmus scorecard synthesis. OpenAI hard rules + gstack slop blacklist unified. Classifier for landing page vs app UI. Co-Authored-By: Claude Opus 4.6 (1M context) * fix: generate .agents/ on demand in tests (not checked in since v0.11.2.0) .agents/ is gitignored since v0.11.2.0 — tests that read Codex-host SKILL.md files now generate them on demand via `bun run gen-skill-docs.ts --host codex` before reading. Fixes test failures on fresh clones. Co-Authored-By: Claude Opus 4.6 (1M context) --------- Co-authored-by: Claude Opus 4.6 (1M context) --- CHANGELOG.md | 10 + TODOS.md | 24 ++ VERSION | 2 +- design-consultation/SKILL.md | 65 ++++++ design-consultation/SKILL.md.tmpl | 2 + design-review/SKILL.md | 150 +++++++++++++ design-review/SKILL.md.tmpl | 4 + office-hours/SKILL.md | 29 +++ plan-design-review/SKILL.md | 176 +++++++++++++++ plan-design-review/SKILL.md.tmpl | 4 + review/SKILL.md | 22 ++ scripts/gen-skill-docs.ts | 362 ++++++++++++++++++++++++++++-- ship/SKILL.md | 22 ++ test/gen-skill-docs.test.ts | 127 +++++++++++ test/helpers/touchfiles.ts | 14 +- test/skill-validation.test.ts | 9 + 16 files changed, 999 insertions(+), 23 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a2fccbb727120cc33c8f6958180413d92d1ad8a1..68758c31ab780cd63b5029999c7a879386711f88 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,15 @@ # Changelog +## [0.11.3.0] - 2026-03-23 — Design Outside Voices + +### Added + +- **Every design review now gets a second opinion.** `/plan-design-review`, `/design-review`, and `/design-consultation` dispatch both Codex (OpenAI) and a fresh Claude subagent in parallel to independently evaluate your design — then synthesize findings with a litmus scorecard showing where they agree and disagree. Cross-model agreement = high confidence; disagreement = investigate. +- **OpenAI's design hard rules baked in.** 7 hard rejection criteria, 7 litmus checks, and a landing-page vs app-UI classifier from OpenAI's "Designing Delightful Frontends" framework — merged with gstack's existing 10-item AI slop blacklist. Your design gets evaluated against the same rules OpenAI recommends for their own models. +- **Codex design voice in every PR.** The lightweight design review that runs in `/ship` and `/review` now includes a Codex design check when frontend files change — automatic, no opt-in needed. +- **Outside voices in /office-hours brainstorming.** After wireframe sketches, you can now get Codex + Claude subagent design perspectives on your approaches before committing to a direction. +- **AI slop blacklist extracted as shared constant.** The 10 anti-patterns (purple gradients, 3-column icon grids, centered everything, etc.) are now defined once and shared across all design skills. Easier to maintain, impossible to drift. + ## [0.11.2.0] - 2026-03-22 — Codex Just Works ### Fixed diff --git a/TODOS.md b/TODOS.md index 388792d64ae2e1941c42f4aa507e0f3ed1bcb1c9..f30f5550e1355d755253a281ad215b46b0ef73a0 100644 --- a/TODOS.md +++ b/TODOS.md @@ -432,6 +432,30 @@ Shipped: Default model changed to Sonnet for structure tests (~30), Opus retaine Shipped as v0.5.0 on main. Includes `/plan-design-review` (report-only design audit), `/qa-design-review` (audit + fix loop), and `/design-consultation` (interactive DESIGN.md creation). `{{DESIGN_METHODOLOGY}}` resolver provides shared 80-item design audit checklist. +### Design outside voices in /plan-eng-review + +**What:** Extend the parallel dual-voice pattern (Codex + Claude subagent) to /plan-eng-review's architecture review section. + +**Why:** The design beachhead (v0.11.3.0) proves cross-model consensus works for subjective reviews. Architecture reviews have similar subjectivity in tradeoff decisions. + +**Context:** Depends on learnings from the design beachhead. If the litmus scorecard format proves useful, adapt it for architecture dimensions (coupling, scaling, reversibility). + +**Effort:** S +**Priority:** P3 +**Depends on:** Design outside voices shipped (v0.11.3.0) + +### Outside voices in /qa visual regression detection + +**What:** Add Codex design voice to /qa for detecting visual regressions during bug-fix verification. + +**Why:** When fixing bugs, the fix can introduce visual regressions that code-level checks miss. Codex could flag "the fix broke the responsive layout" during re-test. + +**Context:** Depends on /qa having design awareness. Currently /qa focuses on functional testing. + +**Effort:** M +**Priority:** P3 +**Depends on:** Design outside voices shipped (v0.11.3.0) + ## Document-Release ### Auto-invoke /document-release from /ship — SHIPPED diff --git a/VERSION b/VERSION index ac9987cf8c0e52d02defc8eebf04d68c425b5714..6d425833e4501f8b112765f98516db4d2e449e82 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.11.2.0 +0.11.3.0 diff --git a/design-consultation/SKILL.md b/design-consultation/SKILL.md index 183913c030051c4012f4b5e79febfa8c3f096b04..d4cf72b23293d1c7ead47e2e15c1985b3820c80d 100644 --- a/design-consultation/SKILL.md +++ b/design-consultation/SKILL.md @@ -423,6 +423,71 @@ If the user said no research, skip entirely and proceed to Phase 3 using your bu --- +## Design Outside Voices (parallel) + +Use AskUserQuestion: +> "Want outside design voices? Codex evaluates against OpenAI's design hard rules + litmus checks; Claude subagent does an independent design direction proposal." +> +> A) Yes — run outside design voices +> B) No — proceed without + +If user chooses B, skip this step and continue. + +**Check Codex availability:** +```bash +which codex 2>/dev/null && echo "CODEX_AVAILABLE" || echo "CODEX_NOT_AVAILABLE" +``` + +**If Codex is available**, launch both voices simultaneously: + +1. **Codex design voice** (via Bash): +```bash +TMPERR_DESIGN=$(mktemp /tmp/codex-design-XXXXXXXX) +codex exec "Given this product context, propose a complete design direction: +- Visual thesis: one sentence describing mood, material, and energy +- Typography: specific font names (not defaults — no Inter/Roboto/Arial/system) + hex colors +- Color system: CSS variables for background, surface, primary text, muted text, accent +- Layout: composition-first, not component-first. First viewport as poster, not document +- Differentiation: 2 deliberate departures from category norms +- Anti-slop: no purple gradients, no 3-column icon grids, no centered everything, no decorative blobs + +Be opinionated. Be specific. Do not hedge. This is YOUR design direction — own it." -s read-only -c 'model_reasoning_effort="medium"' --enable web_search_cached 2>"$TMPERR_DESIGN" +``` +Use a 5-minute timeout (`timeout: 300000`). After the command completes, read stderr: +```bash +cat "$TMPERR_DESIGN" && rm -f "$TMPERR_DESIGN" +``` + +2. **Claude design subagent** (via Agent tool): +Dispatch a subagent with this prompt: +"Given this product context, propose a design direction that would SURPRISE. What would the cool indie studio do that the enterprise UI team wouldn't? +- Propose an aesthetic direction, typography stack (specific font names), color palette (hex values) +- 2 deliberate departures from category norms +- What emotional reaction should the user have in the first 3 seconds? + +Be bold. Be specific. No hedging." + +**Error handling (all non-blocking):** +- **Auth failure:** If stderr contains "auth", "login", "unauthorized", or "API key": "Codex authentication failed. Run `codex login` to authenticate." +- **Timeout:** "Codex timed out after 5 minutes." +- **Empty response:** "Codex returned no response." +- On any Codex error: proceed with Claude subagent output only, tagged `[single-model]`. +- If Claude subagent also fails: "Outside voices unavailable — continuing with primary review." + +Present Codex output under a `CODEX SAYS (design direction):` header. +Present subagent output under a `CLAUDE SUBAGENT (design direction):` header. + +**Synthesis:** Claude main references both Codex and subagent proposals in the Phase 3 proposal. Present: +- Areas of agreement between all three voices (Claude main + Codex + subagent) +- Genuine divergences as creative alternatives for the user to choose from +- "Codex and I agree on X. Codex suggested Y where I'm proposing Z — here's why..." + +**Log the result:** +```bash +~/.claude/skills/gstack/bin/gstack-review-log '{"skill":"design-outside-voices","timestamp":"'"$(date -u +%Y-%m-%dT%H:%M:%SZ)"'","status":"STATUS","source":"SOURCE","commit":"'"$(git rev-parse --short HEAD)"'"}' +``` +Replace STATUS with "clean" or "issues_found", SOURCE with "codex+subagent", "codex-only", "subagent-only", or "unavailable". + ## Phase 3: The Complete Proposal This is the soul of the skill. Propose EVERYTHING as one coherent package. diff --git a/design-consultation/SKILL.md.tmpl b/design-consultation/SKILL.md.tmpl index ed9a4efaead07ba28f6c9068ad9fe0dd3a288e9e..87c191df129ec059e19148f00a11972f070904f5 100644 --- a/design-consultation/SKILL.md.tmpl +++ b/design-consultation/SKILL.md.tmpl @@ -131,6 +131,8 @@ If the user said no research, skip entirely and proceed to Phase 3 using your bu --- +{{DESIGN_OUTSIDE_VOICES}} + ## Phase 3: The Complete Proposal This is the soul of the skill. Propose EVERYTHING as one coherent package. diff --git a/design-review/SKILL.md b/design-review/SKILL.md index 34c7b5438013929e286ad9e90d5d850301596f3e..ae8fa1bdb3cb14dc15a93601c423721f669e9cc3 100644 --- a/design-review/SKILL.md +++ b/design-review/SKILL.md @@ -856,6 +856,75 @@ Tie everything to user goals and product objectives. Always suggest specific imp 10. **Depth over breadth.** 5-10 well-documented findings with screenshots and specific suggestions > 20 vague observations. 11. **Show screenshots to the user.** After every `$B screenshot`, `$B snapshot -a -o`, or `$B responsive` command, use the Read tool on the output file(s) so the user can see them inline. For `responsive` (3 files), Read all three. This is critical — without it, screenshots are invisible to the user. +### Design Hard Rules + +**Classifier — determine rule set before evaluating:** +- **MARKETING/LANDING PAGE** (hero-driven, brand-forward, conversion-focused) → apply Landing Page Rules +- **APP UI** (workspace-driven, data-dense, task-focused: dashboards, admin, settings) → apply App UI Rules +- **HYBRID** (marketing shell with app-like sections) → apply Landing Page Rules to hero/marketing sections, App UI Rules to functional sections + +**Hard rejection criteria** (instant-fail patterns — flag if ANY apply): +1. Generic SaaS card grid as first impression +2. Beautiful image with weak brand +3. Strong headline with no clear action +4. Busy imagery behind text +5. Sections repeating same mood statement +6. Carousel with no narrative purpose +7. App UI made of stacked cards instead of layout + +**Litmus checks** (answer YES/NO for each — used for cross-model consensus scoring): +1. Brand/product unmistakable in first screen? +2. One strong visual anchor present? +3. Page understandable by scanning headlines only? +4. Each section has one job? +5. Are cards actually necessary? +6. Does motion improve hierarchy or atmosphere? +7. Would design feel premium with all decorative shadows removed? + +**Landing page rules** (apply when classifier = MARKETING/LANDING): +- First viewport reads as one composition, not a dashboard +- Brand-first hierarchy: brand > headline > body > CTA +- Typography: expressive, purposeful — no default stacks (Inter, Roboto, Arial, system) +- No flat single-color backgrounds — use gradients, images, subtle patterns +- Hero: full-bleed, edge-to-edge, no inset/tiled/rounded variants +- Hero budget: brand, one headline, one supporting sentence, one CTA group, one image +- No cards in hero. Cards only when card IS the interaction +- One job per section: one purpose, one headline, one short supporting sentence +- Motion: 2-3 intentional motions minimum (entrance, scroll-linked, hover/reveal) +- Color: define CSS variables, avoid purple-on-white defaults, one accent color default +- Copy: product language not design commentary. "If deleting 30% improves it, keep deleting" +- Beautiful defaults: composition-first, brand as loudest text, two typefaces max, cardless by default, first viewport as poster not document + +**App UI rules** (apply when classifier = APP UI): +- Calm surface hierarchy, strong typography, few colors +- Dense but readable, minimal chrome +- Organize: primary workspace, navigation, secondary context, one accent +- Avoid: dashboard-card mosaics, thick borders, decorative gradients, ornamental icons +- Copy: utility language — orientation, status, action. Not mood/brand/aspiration +- Cards only when card IS the interaction +- Section headings state what area is or what user can do ("Selected KPIs", "Plan status") + +**Universal rules** (apply to ALL types): +- Define CSS variables for color system +- No default font stacks (Inter, Roboto, Arial, system) +- One job per section +- "If deleting 30% of the copy improves it, keep deleting" +- Cards earn their existence — no decorative card grids + +**AI Slop blacklist** (the 10 patterns that scream "AI-generated"): +1. Purple/violet/indigo gradient backgrounds or blue-to-purple color schemes +2. **The 3-column feature grid:** icon-in-colored-circle + bold title + 2-line description, repeated 3x symmetrically. THE most recognizable AI layout. +3. Icons in colored circles as section decoration (SaaS starter template look) +4. Centered everything (`text-align: center` on all headings, descriptions, cards) +5. Uniform bubbly border-radius on every element (same large radius on everything) +6. Decorative blobs, floating circles, wavy SVG dividers (if a section feels empty, it needs better content, not decoration) +7. Emoji as design elements (rockets in headings, emoji as bullet points) +8. Colored left-border on cards (`border-left: 3px solid `) +9. Generic hero copy ("Welcome to [X]", "Unlock the power of...", "Your all-in-one solution for...") +10. Cookie-cutter section rhythm (hero → 3 features → testimonials → pricing → CTA, every section same height) + +Source: [OpenAI "Designing Delightful Frontends with GPT-5.4"](https://developers.openai.com/blog/designing-delightful-frontends-with-gpt-5-4) (Mar 2026) + gstack design methodology. + Record baseline design score and AI slop score at end of Phase 6. --- @@ -879,6 +948,87 @@ Record baseline design score and AI slop score at end of Phase 6. --- +## Design Outside Voices (parallel) + +**Automatic:** Outside voices run automatically when Codex is available. No opt-in needed. + +**Check Codex availability:** +```bash +which codex 2>/dev/null && echo "CODEX_AVAILABLE" || echo "CODEX_NOT_AVAILABLE" +``` + +**If Codex is available**, launch both voices simultaneously: + +1. **Codex design voice** (via Bash): +```bash +TMPERR_DESIGN=$(mktemp /tmp/codex-design-XXXXXXXX) +codex exec "Review the frontend source code in this repo. Evaluate against these design hard rules: +- Spacing: systematic (design tokens / CSS variables) or magic numbers? +- Typography: expressive purposeful fonts or default stacks? +- Color: CSS variables with defined system, or hardcoded hex scattered? +- Responsive: breakpoints defined? calc(100svh - header) for heroes? Mobile tested? +- A11y: ARIA landmarks, alt text, contrast ratios, 44px touch targets? +- Motion: 2-3 intentional animations, or zero / ornamental only? +- Cards: used only when card IS the interaction? No decorative card grids? + +First classify as MARKETING/LANDING PAGE vs APP UI vs HYBRID, then apply matching rules. + +LITMUS CHECKS — answer YES/NO: +1. Brand/product unmistakable in first screen? +2. One strong visual anchor present? +3. Page understandable by scanning headlines only? +4. Each section has one job? +5. Are cards actually necessary? +6. Does motion improve hierarchy or atmosphere? +7. Would design feel premium with all decorative shadows removed? + +HARD REJECTION — flag if ANY apply: +1. Generic SaaS card grid as first impression +2. Beautiful image with weak brand +3. Strong headline with no clear action +4. Busy imagery behind text +5. Sections repeating same mood statement +6. Carousel with no narrative purpose +7. App UI made of stacked cards instead of layout + +Be specific. Reference file:line for every finding." -s read-only -c 'model_reasoning_effort="high"' --enable web_search_cached 2>"$TMPERR_DESIGN" +``` +Use a 5-minute timeout (`timeout: 300000`). After the command completes, read stderr: +```bash +cat "$TMPERR_DESIGN" && rm -f "$TMPERR_DESIGN" +``` + +2. **Claude design subagent** (via Agent tool): +Dispatch a subagent with this prompt: +"Review the frontend source code in this repo. You are an independent senior product designer doing a source-code design audit. Focus on CONSISTENCY PATTERNS across files rather than individual violations: +- Are spacing values systematic across the codebase? +- Is there ONE color system or scattered approaches? +- Do responsive breakpoints follow a consistent set? +- Is the accessibility approach consistent or spotty? + +For each finding: what's wrong, severity (critical/high/medium), and the file:line." + +**Error handling (all non-blocking):** +- **Auth failure:** If stderr contains "auth", "login", "unauthorized", or "API key": "Codex authentication failed. Run `codex login` to authenticate." +- **Timeout:** "Codex timed out after 5 minutes." +- **Empty response:** "Codex returned no response." +- On any Codex error: proceed with Claude subagent output only, tagged `[single-model]`. +- If Claude subagent also fails: "Outside voices unavailable — continuing with primary review." + +Present Codex output under a `CODEX SAYS (design source audit):` header. +Present subagent output under a `CLAUDE SUBAGENT (design consistency):` header. + +**Synthesis — Litmus scorecard:** + +Use the same scorecard format as /plan-design-review (shown above). Fill in from both outputs. +Merge findings into the triage with `[codex]` / `[subagent]` / `[cross-model]` tags. + +**Log the result:** +```bash +~/.claude/skills/gstack/bin/gstack-review-log '{"skill":"design-outside-voices","timestamp":"'"$(date -u +%Y-%m-%dT%H:%M:%SZ)"'","status":"STATUS","source":"SOURCE","commit":"'"$(git rev-parse --short HEAD)"'"}' +``` +Replace STATUS with "clean" or "issues_found", SOURCE with "codex+subagent", "codex-only", "subagent-only", or "unavailable". + ## Phase 7: Triage Sort all discovered findings by impact, then decide which to fix: diff --git a/design-review/SKILL.md.tmpl b/design-review/SKILL.md.tmpl index 84119c26860cee2622ed39622f4f08501be7d420..e54332fa8b6ecb63fb95027a1b87169656046b52 100644 --- a/design-review/SKILL.md.tmpl +++ b/design-review/SKILL.md.tmpl @@ -84,6 +84,8 @@ mkdir -p "$REPORT_DIR/screenshots" {{DESIGN_METHODOLOGY}} +{{DESIGN_HARD_RULES}} + Record baseline design score and AI slop score at end of Phase 6. --- @@ -107,6 +109,8 @@ Record baseline design score and AI slop score at end of Phase 6. --- +{{DESIGN_OUTSIDE_VOICES}} + ## Phase 7: Triage Sort all discovered findings by impact, then decide which to fix: diff --git a/office-hours/SKILL.md b/office-hours/SKILL.md index 47a841a70c5c8ad787fab1715de1b8aaa59ae09b..ab69f1b6a976df44276df1da0eef7ce542eff9cf 100644 --- a/office-hours/SKILL.md +++ b/office-hours/SKILL.md @@ -731,6 +731,35 @@ Reference the wireframe screenshot in the design doc's "Recommended Approach" se The screenshot file at `/tmp/gstack-sketch.png` can be referenced by downstream skills (`/plan-design-review`, `/design-review`) to see what was originally envisioned. +**Step 6: Outside design voices** (optional) + +After the wireframe is approved, offer outside design perspectives: + +```bash +which codex 2>/dev/null && echo "CODEX_AVAILABLE" || echo "CODEX_NOT_AVAILABLE" +``` + +If Codex is available, use AskUserQuestion: +> "Want outside design perspectives on the chosen approach? Codex proposes a visual thesis, content plan, and interaction ideas. A Claude subagent proposes an alternative aesthetic direction." +> +> A) Yes — get outside design voices +> B) No — proceed without + +If user chooses A, launch both voices simultaneously: + +1. **Codex** (via Bash, `model_reasoning_effort="medium"`): +```bash +TMPERR_SKETCH=$(mktemp /tmp/codex-sketch-XXXXXXXX) +codex exec "For this product approach, provide: a visual thesis (one sentence — mood, material, energy), a content plan (hero → support → detail → CTA), and 2 interaction ideas that change page feel. Apply beautiful defaults: composition-first, brand-first, cardless, poster not document. Be opinionated." -s read-only -c 'model_reasoning_effort="medium"' --enable web_search_cached 2>"$TMPERR_SKETCH" +``` +Use a 5-minute timeout (`timeout: 300000`). After completion: `cat "$TMPERR_SKETCH" && rm -f "$TMPERR_SKETCH"` + +2. **Claude subagent** (via Agent tool): +"For this product approach, what design direction would you recommend? What aesthetic, typography, and interaction patterns fit? What would make this approach feel inevitable to the user? Be specific — font names, hex colors, spacing values." + +Present Codex output under `CODEX SAYS (design sketch):` and subagent output under `CLAUDE SUBAGENT (design direction):`. +Error handling: all non-blocking. On failure, skip and continue. + --- ## Phase 4.5: Founder Signal Synthesis diff --git a/plan-design-review/SKILL.md b/plan-design-review/SKILL.md index afd81ff09c9bca786d390e3b7526dae4e9b2a329..fb0bbfa2fede5c6e78f046de3928ff775ff6e1bf 100644 --- a/plan-design-review/SKILL.md +++ b/plan-design-review/SKILL.md @@ -420,6 +420,113 @@ AskUserQuestion: "I've rated this plan {N}/10 on design completeness. The bigges **STOP.** Do NOT proceed until user responds. +## Design Outside Voices (parallel) + +Use AskUserQuestion: +> "Want outside design voices before the detailed review? Codex evaluates against OpenAI's design hard rules + litmus checks; Claude subagent does an independent completeness review." +> +> A) Yes — run outside design voices +> B) No — proceed without + +If user chooses B, skip this step and continue. + +**Check Codex availability:** +```bash +which codex 2>/dev/null && echo "CODEX_AVAILABLE" || echo "CODEX_NOT_AVAILABLE" +``` + +**If Codex is available**, launch both voices simultaneously: + +1. **Codex design voice** (via Bash): +```bash +TMPERR_DESIGN=$(mktemp /tmp/codex-design-XXXXXXXX) +codex exec "Read the plan file at [plan-file-path]. Evaluate this plan's UI/UX design against these criteria. + +HARD REJECTION — flag if ANY apply: +1. Generic SaaS card grid as first impression +2. Beautiful image with weak brand +3. Strong headline with no clear action +4. Busy imagery behind text +5. Sections repeating same mood statement +6. Carousel with no narrative purpose +7. App UI made of stacked cards instead of layout + +LITMUS CHECKS — answer YES or NO for each: +1. Brand/product unmistakable in first screen? +2. One strong visual anchor present? +3. Page understandable by scanning headlines only? +4. Each section has one job? +5. Are cards actually necessary? +6. Does motion improve hierarchy or atmosphere? +7. Would design feel premium with all decorative shadows removed? + +HARD RULES — first classify as MARKETING/LANDING PAGE vs APP UI vs HYBRID, then flag violations of the matching rule set: +- MARKETING: First viewport as one composition, brand-first hierarchy, full-bleed hero, 2-3 intentional motions, composition-first layout +- APP UI: Calm surface hierarchy, dense but readable, utility language, minimal chrome +- UNIVERSAL: CSS variables for colors, no default font stacks, one job per section, cards earn existence + +For each finding: what's wrong, what will happen if it ships unresolved, and the specific fix. Be opinionated. No hedging." -s read-only -c 'model_reasoning_effort="high"' --enable web_search_cached 2>"$TMPERR_DESIGN" +``` +Use a 5-minute timeout (`timeout: 300000`). After the command completes, read stderr: +```bash +cat "$TMPERR_DESIGN" && rm -f "$TMPERR_DESIGN" +``` + +2. **Claude design subagent** (via Agent tool): +Dispatch a subagent with this prompt: +"Read the plan file at [plan-file-path]. You are an independent senior product designer reviewing this plan. You have NOT seen any prior review. Evaluate: + +1. Information hierarchy: what does the user see first, second, third? Is it right? +2. Missing states: loading, empty, error, success, partial — which are unspecified? +3. User journey: what's the emotional arc? Where does it break? +4. Specificity: does the plan describe SPECIFIC UI ("48px Söhne Bold header, #1a1a1a on white") or generic patterns ("clean modern card-based layout")? +5. What design decisions will haunt the implementer if left ambiguous? + +For each finding: what's wrong, severity (critical/high/medium), and the fix." + +**Error handling (all non-blocking):** +- **Auth failure:** If stderr contains "auth", "login", "unauthorized", or "API key": "Codex authentication failed. Run `codex login` to authenticate." +- **Timeout:** "Codex timed out after 5 minutes." +- **Empty response:** "Codex returned no response." +- On any Codex error: proceed with Claude subagent output only, tagged `[single-model]`. +- If Claude subagent also fails: "Outside voices unavailable — continuing with primary review." + +Present Codex output under a `CODEX SAYS (design critique):` header. +Present subagent output under a `CLAUDE SUBAGENT (design completeness):` header. + +**Synthesis — Litmus scorecard:** + +``` +DESIGN OUTSIDE VOICES — LITMUS SCORECARD: +═══════════════════════════════════════════════════════════════ + Check Claude Codex Consensus + ─────────────────────────────────────── ─────── ─────── ───────── + 1. Brand unmistakable in first screen? — — — + 2. One strong visual anchor? — — — + 3. Scannable by headlines only? — — — + 4. Each section has one job? — — — + 5. Cards actually necessary? — — — + 6. Motion improves hierarchy? — — — + 7. Premium without decorative shadows? — — — + ─────────────────────────────────────── ─────── ─────── ───────── + Hard rejections triggered: — — — +═══════════════════════════════════════════════════════════════ +``` + +Fill in each cell from the Codex and subagent outputs. CONFIRMED = both agree. DISAGREE = models differ. NOT SPEC'D = not enough info to evaluate. + +**Pass integration (respects existing 7-pass contract):** +- Hard rejections → raised as the FIRST items in Pass 1, tagged `[HARD REJECTION]` +- Litmus DISAGREE items → raised in the relevant pass with both perspectives +- Litmus CONFIRMED failures → pre-loaded as known issues in the relevant pass +- Passes can skip discovery and go straight to fixing for pre-identified issues + +**Log the result:** +```bash +~/.claude/skills/gstack/bin/gstack-review-log '{"skill":"design-outside-voices","timestamp":"'"$(date -u +%Y-%m-%dT%H:%M:%SZ)"'","status":"STATUS","source":"SOURCE","commit":"'"$(git rev-parse --short HEAD)"'"}' +``` +Replace STATUS with "clean" or "issues_found", SOURCE with "codex+subagent", "codex-only", "subagent-only", or "unavailable". + ## The 0-10 Rating Method For each design section, rate the plan 0-10 on that dimension. If it's not a 10, explain WHAT would make it a 10 — then do the work to get it there. @@ -468,6 +575,75 @@ Apply time-horizon design: 5-sec visceral, 5-min behavioral, 5-year reflective. ### Pass 4: AI Slop Risk Rate 0-10: Does the plan describe specific, intentional UI — or generic patterns? FIX TO 10: Rewrite vague UI descriptions with specific alternatives. + +### Design Hard Rules + +**Classifier — determine rule set before evaluating:** +- **MARKETING/LANDING PAGE** (hero-driven, brand-forward, conversion-focused) → apply Landing Page Rules +- **APP UI** (workspace-driven, data-dense, task-focused: dashboards, admin, settings) → apply App UI Rules +- **HYBRID** (marketing shell with app-like sections) → apply Landing Page Rules to hero/marketing sections, App UI Rules to functional sections + +**Hard rejection criteria** (instant-fail patterns — flag if ANY apply): +1. Generic SaaS card grid as first impression +2. Beautiful image with weak brand +3. Strong headline with no clear action +4. Busy imagery behind text +5. Sections repeating same mood statement +6. Carousel with no narrative purpose +7. App UI made of stacked cards instead of layout + +**Litmus checks** (answer YES/NO for each — used for cross-model consensus scoring): +1. Brand/product unmistakable in first screen? +2. One strong visual anchor present? +3. Page understandable by scanning headlines only? +4. Each section has one job? +5. Are cards actually necessary? +6. Does motion improve hierarchy or atmosphere? +7. Would design feel premium with all decorative shadows removed? + +**Landing page rules** (apply when classifier = MARKETING/LANDING): +- First viewport reads as one composition, not a dashboard +- Brand-first hierarchy: brand > headline > body > CTA +- Typography: expressive, purposeful — no default stacks (Inter, Roboto, Arial, system) +- No flat single-color backgrounds — use gradients, images, subtle patterns +- Hero: full-bleed, edge-to-edge, no inset/tiled/rounded variants +- Hero budget: brand, one headline, one supporting sentence, one CTA group, one image +- No cards in hero. Cards only when card IS the interaction +- One job per section: one purpose, one headline, one short supporting sentence +- Motion: 2-3 intentional motions minimum (entrance, scroll-linked, hover/reveal) +- Color: define CSS variables, avoid purple-on-white defaults, one accent color default +- Copy: product language not design commentary. "If deleting 30% improves it, keep deleting" +- Beautiful defaults: composition-first, brand as loudest text, two typefaces max, cardless by default, first viewport as poster not document + +**App UI rules** (apply when classifier = APP UI): +- Calm surface hierarchy, strong typography, few colors +- Dense but readable, minimal chrome +- Organize: primary workspace, navigation, secondary context, one accent +- Avoid: dashboard-card mosaics, thick borders, decorative gradients, ornamental icons +- Copy: utility language — orientation, status, action. Not mood/brand/aspiration +- Cards only when card IS the interaction +- Section headings state what area is or what user can do ("Selected KPIs", "Plan status") + +**Universal rules** (apply to ALL types): +- Define CSS variables for color system +- No default font stacks (Inter, Roboto, Arial, system) +- One job per section +- "If deleting 30% of the copy improves it, keep deleting" +- Cards earn their existence — no decorative card grids + +**AI Slop blacklist** (the 10 patterns that scream "AI-generated"): +1. Purple/violet/indigo gradient backgrounds or blue-to-purple color schemes +2. **The 3-column feature grid:** icon-in-colored-circle + bold title + 2-line description, repeated 3x symmetrically. THE most recognizable AI layout. +3. Icons in colored circles as section decoration (SaaS starter template look) +4. Centered everything (`text-align: center` on all headings, descriptions, cards) +5. Uniform bubbly border-radius on every element (same large radius on everything) +6. Decorative blobs, floating circles, wavy SVG dividers (if a section feels empty, it needs better content, not decoration) +7. Emoji as design elements (rockets in headings, emoji as bullet points) +8. Colored left-border on cards (`border-left: 3px solid `) +9. Generic hero copy ("Welcome to [X]", "Unlock the power of...", "Your all-in-one solution for...") +10. Cookie-cutter section rhythm (hero → 3 features → testimonials → pricing → CTA, every section same height) + +Source: [OpenAI "Designing Delightful Frontends with GPT-5.4"](https://developers.openai.com/blog/designing-delightful-frontends-with-gpt-5-4) (Mar 2026) + gstack design methodology. - "Cards with icons" → what differentiates these from every SaaS template? - "Hero section" → what makes this hero feel like THIS product? - "Clean, modern UI" → meaningless. Replace with actual design decisions. diff --git a/plan-design-review/SKILL.md.tmpl b/plan-design-review/SKILL.md.tmpl index 46e5b6f1986ebbb819cb295b8a612f13e110bc4f..30e5b41415dad1f964a7954a07c6e1b6da80b6d5 100644 --- a/plan-design-review/SKILL.md.tmpl +++ b/plan-design-review/SKILL.md.tmpl @@ -128,6 +128,8 @@ AskUserQuestion: "I've rated this plan {N}/10 on design completeness. The bigges **STOP.** Do NOT proceed until user responds. +{{DESIGN_OUTSIDE_VOICES}} + ## The 0-10 Rating Method For each design section, rate the plan 0-10 on that dimension. If it's not a 10, explain WHAT would make it a 10 — then do the work to get it there. @@ -176,6 +178,8 @@ Apply time-horizon design: 5-sec visceral, 5-min behavioral, 5-year reflective. ### Pass 4: AI Slop Risk Rate 0-10: Does the plan describe specific, intentional UI — or generic patterns? FIX TO 10: Rewrite vague UI descriptions with specific alternatives. + +{{DESIGN_HARD_RULES}} - "Cards with icons" → what differentiates these from every SaaS template? - "Hero section" → what makes this hero feel like THIS product? - "Clean, modern UI" → meaningless. Replace with actual design decisions. diff --git a/review/SKILL.md b/review/SKILL.md index 41207076d388c049838994939a034b1ce912c0b3..862e78e1b5eaa065a5fe6f10978a9451a6ab7297 100644 --- a/review/SKILL.md +++ b/review/SKILL.md @@ -447,6 +447,28 @@ source <(~/.claude/skills/gstack/bin/gstack-diff-scope 2>/dev/null) Substitute: TIMESTAMP = ISO 8601 datetime, STATUS = "clean" if 0 findings or "issues_found", N = total findings, M = auto-fixed count, COMMIT = output of `git rev-parse --short HEAD`. +7. **Codex design voice** (optional, automatic if available): + +```bash +which codex 2>/dev/null && echo "CODEX_AVAILABLE" || echo "CODEX_NOT_AVAILABLE" +``` + +If Codex is available, run a lightweight design check on the diff: + +```bash +TMPERR_DRL=$(mktemp /tmp/codex-drl-XXXXXXXX) +codex exec "Review the git diff on this branch. Run 7 litmus checks (YES/NO each): 1. Brand/product unmistakable in first screen? 2. One strong visual anchor present? 3. Page understandable by scanning headlines only? 4. Each section has one job? 5. Are cards actually necessary? 6. Does motion improve hierarchy or atmosphere? 7. Would design feel premium with all decorative shadows removed? Flag any hard rejections: 1. Generic SaaS card grid as first impression 2. Beautiful image with weak brand 3. Strong headline with no clear action 4. Busy imagery behind text 5. Sections repeating same mood statement 6. Carousel with no narrative purpose 7. App UI made of stacked cards instead of layout 5 most important design findings only. Reference file:line." -s read-only -c 'model_reasoning_effort="high"' --enable web_search_cached 2>"$TMPERR_DRL" +``` + +Use a 5-minute timeout (`timeout: 300000`). After the command completes, read stderr: +```bash +cat "$TMPERR_DRL" && rm -f "$TMPERR_DRL" +``` + +**Error handling:** All errors are non-blocking. On auth failure, timeout, or empty response — skip with a brief note and continue. + +Present Codex output under a `CODEX (design):` header, merged with the checklist findings above. + Include any design findings alongside the findings from Step 4. They follow the same Fix-First flow in Step 5 — AUTO-FIX for mechanical CSS fixes, ASK for everything else. --- diff --git a/scripts/gen-skill-docs.ts b/scripts/gen-skill-docs.ts index 00b6263b4de9ef22959bfcabba010c3033eb2499..364db1feee5eccdcfedef4b1bfe1e47ec0e2acee 100644 --- a/scripts/gen-skill-docs.ts +++ b/scripts/gen-skill-docs.ts @@ -60,6 +60,44 @@ interface TemplateContext { paths: HostPaths; } +// ─── Shared Design Constants ──────────────────────────────── + +/** gstack's 10 AI slop anti-patterns — shared between DESIGN_METHODOLOGY and DESIGN_HARD_RULES */ +const AI_SLOP_BLACKLIST = [ + 'Purple/violet/indigo gradient backgrounds or blue-to-purple color schemes', + '**The 3-column feature grid:** icon-in-colored-circle + bold title + 2-line description, repeated 3x symmetrically. THE most recognizable AI layout.', + 'Icons in colored circles as section decoration (SaaS starter template look)', + 'Centered everything (`text-align: center` on all headings, descriptions, cards)', + 'Uniform bubbly border-radius on every element (same large radius on everything)', + 'Decorative blobs, floating circles, wavy SVG dividers (if a section feels empty, it needs better content, not decoration)', + 'Emoji as design elements (rockets in headings, emoji as bullet points)', + 'Colored left-border on cards (`border-left: 3px solid `)', + 'Generic hero copy ("Welcome to [X]", "Unlock the power of...", "Your all-in-one solution for...")', + 'Cookie-cutter section rhythm (hero → 3 features → testimonials → pricing → CTA, every section same height)', +]; + +/** OpenAI hard rejection criteria (from "Designing Delightful Frontends with GPT-5.4", Mar 2026) */ +const OPENAI_HARD_REJECTIONS = [ + 'Generic SaaS card grid as first impression', + 'Beautiful image with weak brand', + 'Strong headline with no clear action', + 'Busy imagery behind text', + 'Sections repeating same mood statement', + 'Carousel with no narrative purpose', + 'App UI made of stacked cards instead of layout', +]; + +/** OpenAI litmus checks — 7 yes/no tests for cross-model consensus scoring */ +const OPENAI_LITMUS_CHECKS = [ + 'Brand/product unmistakable in first screen?', + 'One strong visual anchor present?', + 'Page understandable by scanning headlines only?', + 'Each section has one job?', + 'Are cards actually necessary?', + 'Does motion improve hierarchy or atmosphere?', + 'Would design feel premium with all decorative shadows removed?', +]; + // ─── Placeholder Resolvers ────────────────────────────────── function generateCommandReference(_ctx: TemplateContext): string { @@ -876,13 +914,40 @@ Minimum 0 per category. 12. **Never refuse to use the browser.** When the user invokes /qa or /qa-only, they are requesting browser-based testing. Never suggest evals, unit tests, or other alternatives as a substitute. Even if the diff appears to have no UI changes, backend changes affect app behavior — always open the browser and test.`; } -function generateDesignReviewLite(_ctx: TemplateContext): string { +function generateDesignReviewLite(ctx: TemplateContext): string { + const litmusList = OPENAI_LITMUS_CHECKS.map((item, i) => `${i + 1}. ${item}`).join(' '); + const rejectionList = OPENAI_HARD_REJECTIONS.map((item, i) => `${i + 1}. ${item}`).join(' '); + // Codex block only for Claude host + const codexBlock = ctx.host === 'codex' ? '' : ` + +7. **Codex design voice** (optional, automatic if available): + +\`\`\`bash +which codex 2>/dev/null && echo "CODEX_AVAILABLE" || echo "CODEX_NOT_AVAILABLE" +\`\`\` + +If Codex is available, run a lightweight design check on the diff: + +\`\`\`bash +TMPERR_DRL=$(mktemp /tmp/codex-drl-XXXXXXXX) +codex exec "Review the git diff on this branch. Run 7 litmus checks (YES/NO each): ${litmusList} Flag any hard rejections: ${rejectionList} 5 most important design findings only. Reference file:line." -s read-only -c 'model_reasoning_effort="high"' --enable web_search_cached 2>"$TMPERR_DRL" +\`\`\` + +Use a 5-minute timeout (\`timeout: 300000\`). After the command completes, read stderr: +\`\`\`bash +cat "$TMPERR_DRL" && rm -f "$TMPERR_DRL" +\`\`\` + +**Error handling:** All errors are non-blocking. On auth failure, timeout, or empty response — skip with a brief note and continue. + +Present Codex output under a \`CODEX (design):\` header, merged with the checklist findings above.`; + return `## Design Review (conditional, diff-scoped) Check if the diff touches frontend files using \`gstack-diff-scope\`: \`\`\`bash -source <(~/.claude/skills/gstack/bin/gstack-diff-scope 2>/dev/null) +source <(${ctx.paths.binDir}/gstack-diff-scope 2>/dev/null) \`\`\` **If \`SCOPE_FRONTEND=false\`:** Skip design review silently. No output. @@ -905,10 +970,10 @@ source <(~/.claude/skills/gstack/bin/gstack-diff-scope 2>/dev/null) 6. **Log the result** for the Review Readiness Dashboard: \`\`\`bash -~/.claude/skills/gstack/bin/gstack-review-log '{"skill":"design-review-lite","timestamp":"TIMESTAMP","status":"STATUS","findings":N,"auto_fixed":M,"commit":"COMMIT"}' +${ctx.paths.binDir}/gstack-review-log '{"skill":"design-review-lite","timestamp":"TIMESTAMP","status":"STATUS","findings":N,"auto_fixed":M,"commit":"COMMIT"}' \`\`\` -Substitute: TIMESTAMP = ISO 8601 datetime, STATUS = "clean" if 0 findings or "issues_found", N = total findings, M = auto-fixed count, COMMIT = output of \`git rev-parse --short HEAD\`.`; +Substitute: TIMESTAMP = ISO 8601 datetime, STATUS = "clean" if 0 findings or "issues_found", N = total findings, M = auto-fixed count, COMMIT = output of \`git rev-parse --short HEAD\`.${codexBlock}`; } // NOTE: design-checklist.md is a subset of this methodology for code-level detection. @@ -1105,16 +1170,7 @@ Apply these at each page. Each finding gets an impact rating (high/medium/polish The test: would a human designer at a respected studio ever ship this? -- Purple/violet/indigo gradient backgrounds or blue-to-purple color schemes -- **The 3-column feature grid:** icon-in-colored-circle + bold title + 2-line description, repeated 3x symmetrically. THE most recognizable AI layout. -- Icons in colored circles as section decoration (SaaS starter template look) -- Centered everything (\`text-align: center\` on all headings, descriptions, cards) -- Uniform bubbly border-radius on every element (same large radius on everything) -- Decorative blobs, floating circles, wavy SVG dividers (if a section feels empty, it needs better content, not decoration) -- Emoji as design elements (rockets in headings, emoji as bullet points) -- Colored left-border on cards (\`border-left: 3px solid \`) -- Generic hero copy ("Welcome to [X]", "Unlock the power of...", "Your all-in-one solution for...") -- Cookie-cutter section rhythm (hero → 3 features → testimonials → pricing → CTA, every section same height) +${AI_SLOP_BLACKLIST.map(item => `- ${item}`).join('\n')} **10. Performance as Design** (6 items) - LCP < 2.0s (web apps), < 1.5s (informational sites) @@ -2029,7 +2085,36 @@ If they approve or say "good enough," proceed. Reference the wireframe screenshot in the design doc's "Recommended Approach" section. The screenshot file at \`/tmp/gstack-sketch.png\` can be referenced by downstream skills -(\`/plan-design-review\`, \`/design-review\`) to see what was originally envisioned.`; +(\`/plan-design-review\`, \`/design-review\`) to see what was originally envisioned. + +**Step 6: Outside design voices** (optional) + +After the wireframe is approved, offer outside design perspectives: + +\`\`\`bash +which codex 2>/dev/null && echo "CODEX_AVAILABLE" || echo "CODEX_NOT_AVAILABLE" +\`\`\` + +If Codex is available, use AskUserQuestion: +> "Want outside design perspectives on the chosen approach? Codex proposes a visual thesis, content plan, and interaction ideas. A Claude subagent proposes an alternative aesthetic direction." +> +> A) Yes — get outside design voices +> B) No — proceed without + +If user chooses A, launch both voices simultaneously: + +1. **Codex** (via Bash, \`model_reasoning_effort="medium"\`): +\`\`\`bash +TMPERR_SKETCH=$(mktemp /tmp/codex-sketch-XXXXXXXX) +codex exec "For this product approach, provide: a visual thesis (one sentence — mood, material, energy), a content plan (hero → support → detail → CTA), and 2 interaction ideas that change page feel. Apply beautiful defaults: composition-first, brand-first, cardless, poster not document. Be opinionated." -s read-only -c 'model_reasoning_effort="medium"' --enable web_search_cached 2>"$TMPERR_SKETCH" +\`\`\` +Use a 5-minute timeout (\`timeout: 300000\`). After completion: \`cat "$TMPERR_SKETCH" && rm -f "$TMPERR_SKETCH"\` + +2. **Claude subagent** (via Agent tool): +"For this product approach, what design direction would you recommend? What aesthetic, typography, and interaction patterns fit? What would make this approach feel inevitable to the user? Be specific — font names, hex colors, spacing values." + +Present Codex output under \`CODEX SAYS (design sketch):\` and subagent output under \`CLAUDE SUBAGENT (design direction):\`. +Error handling: all non-blocking. On failure, skip and continue.`; } function generateAdversarialStep(ctx: TemplateContext): string { @@ -2212,6 +2297,251 @@ in the decision tree below. If you want to persist deploy settings for future runs, suggest the user run \`/setup-deploy\`.`; } +// ─── Design Outside Voices (parallel Codex + Claude subagent) ─────── + +function generateDesignOutsideVoices(ctx: TemplateContext): string { + // Codex host: strip entirely — Codex should never invoke itself + if (ctx.host === 'codex') return ''; + + const rejectionList = OPENAI_HARD_REJECTIONS.map((item, i) => `${i + 1}. ${item}`).join('\n'); + const litmusList = OPENAI_LITMUS_CHECKS.map((item, i) => `${i + 1}. ${item}`).join('\n'); + + // Skill-specific configuration + const isPlanDesignReview = ctx.skillName === 'plan-design-review'; + const isDesignReview = ctx.skillName === 'design-review'; + const isDesignConsultation = ctx.skillName === 'design-consultation'; + + // Determine opt-in behavior and reasoning effort + const isAutomatic = isDesignReview; // design-review runs automatically + const reasoningEffort = isDesignConsultation ? 'medium' : 'high'; // creative vs analytical + + // Build skill-specific Codex prompt + let codexPrompt: string; + let subagentPrompt: string; + + if (isPlanDesignReview) { + codexPrompt = `Read the plan file at [plan-file-path]. Evaluate this plan's UI/UX design against these criteria. + +HARD REJECTION — flag if ANY apply: +${rejectionList} + +LITMUS CHECKS — answer YES or NO for each: +${litmusList} + +HARD RULES — first classify as MARKETING/LANDING PAGE vs APP UI vs HYBRID, then flag violations of the matching rule set: +- MARKETING: First viewport as one composition, brand-first hierarchy, full-bleed hero, 2-3 intentional motions, composition-first layout +- APP UI: Calm surface hierarchy, dense but readable, utility language, minimal chrome +- UNIVERSAL: CSS variables for colors, no default font stacks, one job per section, cards earn existence + +For each finding: what's wrong, what will happen if it ships unresolved, and the specific fix. Be opinionated. No hedging.`; + + subagentPrompt = `Read the plan file at [plan-file-path]. You are an independent senior product designer reviewing this plan. You have NOT seen any prior review. Evaluate: + +1. Information hierarchy: what does the user see first, second, third? Is it right? +2. Missing states: loading, empty, error, success, partial — which are unspecified? +3. User journey: what's the emotional arc? Where does it break? +4. Specificity: does the plan describe SPECIFIC UI ("48px Söhne Bold header, #1a1a1a on white") or generic patterns ("clean modern card-based layout")? +5. What design decisions will haunt the implementer if left ambiguous? + +For each finding: what's wrong, severity (critical/high/medium), and the fix.`; + } else if (isDesignReview) { + codexPrompt = `Review the frontend source code in this repo. Evaluate against these design hard rules: +- Spacing: systematic (design tokens / CSS variables) or magic numbers? +- Typography: expressive purposeful fonts or default stacks? +- Color: CSS variables with defined system, or hardcoded hex scattered? +- Responsive: breakpoints defined? calc(100svh - header) for heroes? Mobile tested? +- A11y: ARIA landmarks, alt text, contrast ratios, 44px touch targets? +- Motion: 2-3 intentional animations, or zero / ornamental only? +- Cards: used only when card IS the interaction? No decorative card grids? + +First classify as MARKETING/LANDING PAGE vs APP UI vs HYBRID, then apply matching rules. + +LITMUS CHECKS — answer YES/NO: +${litmusList} + +HARD REJECTION — flag if ANY apply: +${rejectionList} + +Be specific. Reference file:line for every finding.`; + + subagentPrompt = `Review the frontend source code in this repo. You are an independent senior product designer doing a source-code design audit. Focus on CONSISTENCY PATTERNS across files rather than individual violations: +- Are spacing values systematic across the codebase? +- Is there ONE color system or scattered approaches? +- Do responsive breakpoints follow a consistent set? +- Is the accessibility approach consistent or spotty? + +For each finding: what's wrong, severity (critical/high/medium), and the file:line.`; + } else if (isDesignConsultation) { + codexPrompt = `Given this product context, propose a complete design direction: +- Visual thesis: one sentence describing mood, material, and energy +- Typography: specific font names (not defaults — no Inter/Roboto/Arial/system) + hex colors +- Color system: CSS variables for background, surface, primary text, muted text, accent +- Layout: composition-first, not component-first. First viewport as poster, not document +- Differentiation: 2 deliberate departures from category norms +- Anti-slop: no purple gradients, no 3-column icon grids, no centered everything, no decorative blobs + +Be opinionated. Be specific. Do not hedge. This is YOUR design direction — own it.`; + + subagentPrompt = `Given this product context, propose a design direction that would SURPRISE. What would the cool indie studio do that the enterprise UI team wouldn't? +- Propose an aesthetic direction, typography stack (specific font names), color palette (hex values) +- 2 deliberate departures from category norms +- What emotional reaction should the user have in the first 3 seconds? + +Be bold. Be specific. No hedging.`; + } else { + // Unknown skill — return empty + return ''; + } + + // Build the opt-in section + const optInSection = isAutomatic ? ` +**Automatic:** Outside voices run automatically when Codex is available. No opt-in needed.` : ` +Use AskUserQuestion: +> "Want outside design voices${isPlanDesignReview ? ' before the detailed review' : ''}? Codex evaluates against OpenAI's design hard rules + litmus checks; Claude subagent does an independent ${isDesignConsultation ? 'design direction proposal' : 'completeness review'}." +> +> A) Yes — run outside design voices +> B) No — proceed without + +If user chooses B, skip this step and continue.`; + + // Build the synthesis section + const synthesisSection = isPlanDesignReview ? ` +**Synthesis — Litmus scorecard:** + +\`\`\` +DESIGN OUTSIDE VOICES — LITMUS SCORECARD: +═══════════════════════════════════════════════════════════════ + Check Claude Codex Consensus + ─────────────────────────────────────── ─────── ─────── ───────── + 1. Brand unmistakable in first screen? — — — + 2. One strong visual anchor? — — — + 3. Scannable by headlines only? — — — + 4. Each section has one job? — — — + 5. Cards actually necessary? — — — + 6. Motion improves hierarchy? — — — + 7. Premium without decorative shadows? — — — + ─────────────────────────────────────── ─────── ─────── ───────── + Hard rejections triggered: — — — +═══════════════════════════════════════════════════════════════ +\`\`\` + +Fill in each cell from the Codex and subagent outputs. CONFIRMED = both agree. DISAGREE = models differ. NOT SPEC'D = not enough info to evaluate. + +**Pass integration (respects existing 7-pass contract):** +- Hard rejections → raised as the FIRST items in Pass 1, tagged \`[HARD REJECTION]\` +- Litmus DISAGREE items → raised in the relevant pass with both perspectives +- Litmus CONFIRMED failures → pre-loaded as known issues in the relevant pass +- Passes can skip discovery and go straight to fixing for pre-identified issues` : + isDesignConsultation ? ` +**Synthesis:** Claude main references both Codex and subagent proposals in the Phase 3 proposal. Present: +- Areas of agreement between all three voices (Claude main + Codex + subagent) +- Genuine divergences as creative alternatives for the user to choose from +- "Codex and I agree on X. Codex suggested Y where I'm proposing Z — here's why..."` : ` +**Synthesis — Litmus scorecard:** + +Use the same scorecard format as /plan-design-review (shown above). Fill in from both outputs. +Merge findings into the triage with \`[codex]\` / \`[subagent]\` / \`[cross-model]\` tags.`; + + const escapedCodexPrompt = codexPrompt.replace(/`/g, '\\`').replace(/\$/g, '\\$'); + + return `## Design Outside Voices (parallel) +${optInSection} + +**Check Codex availability:** +\`\`\`bash +which codex 2>/dev/null && echo "CODEX_AVAILABLE" || echo "CODEX_NOT_AVAILABLE" +\`\`\` + +**If Codex is available**, launch both voices simultaneously: + +1. **Codex design voice** (via Bash): +\`\`\`bash +TMPERR_DESIGN=$(mktemp /tmp/codex-design-XXXXXXXX) +codex exec "${escapedCodexPrompt}" -s read-only -c 'model_reasoning_effort="${reasoningEffort}"' --enable web_search_cached 2>"$TMPERR_DESIGN" +\`\`\` +Use a 5-minute timeout (\`timeout: 300000\`). After the command completes, read stderr: +\`\`\`bash +cat "$TMPERR_DESIGN" && rm -f "$TMPERR_DESIGN" +\`\`\` + +2. **Claude design subagent** (via Agent tool): +Dispatch a subagent with this prompt: +"${subagentPrompt}" + +**Error handling (all non-blocking):** +- **Auth failure:** If stderr contains "auth", "login", "unauthorized", or "API key": "Codex authentication failed. Run \`codex login\` to authenticate." +- **Timeout:** "Codex timed out after 5 minutes." +- **Empty response:** "Codex returned no response." +- On any Codex error: proceed with Claude subagent output only, tagged \`[single-model]\`. +- If Claude subagent also fails: "Outside voices unavailable — continuing with primary review." + +Present Codex output under a \`CODEX SAYS (design ${isPlanDesignReview ? 'critique' : isDesignReview ? 'source audit' : 'direction'}):\` header. +Present subagent output under a \`CLAUDE SUBAGENT (design ${isPlanDesignReview ? 'completeness' : isDesignReview ? 'consistency' : 'direction'}):\` header. +${synthesisSection} + +**Log the result:** +\`\`\`bash +${ctx.paths.binDir}/gstack-review-log '{"skill":"design-outside-voices","timestamp":"'"$(date -u +%Y-%m-%dT%H:%M:%SZ)"'","status":"STATUS","source":"SOURCE","commit":"'"$(git rev-parse --short HEAD)"'"}' +\`\`\` +Replace STATUS with "clean" or "issues_found", SOURCE with "codex+subagent", "codex-only", "subagent-only", or "unavailable".`; +} + +// ─── Design Hard Rules (OpenAI framework + gstack slop blacklist) ─── + +function generateDesignHardRules(_ctx: TemplateContext): string { + const slopItems = AI_SLOP_BLACKLIST.map((item, i) => `${i + 1}. ${item}`).join('\n'); + const rejectionItems = OPENAI_HARD_REJECTIONS.map((item, i) => `${i + 1}. ${item}`).join('\n'); + const litmusItems = OPENAI_LITMUS_CHECKS.map((item, i) => `${i + 1}. ${item}`).join('\n'); + + return `### Design Hard Rules + +**Classifier — determine rule set before evaluating:** +- **MARKETING/LANDING PAGE** (hero-driven, brand-forward, conversion-focused) → apply Landing Page Rules +- **APP UI** (workspace-driven, data-dense, task-focused: dashboards, admin, settings) → apply App UI Rules +- **HYBRID** (marketing shell with app-like sections) → apply Landing Page Rules to hero/marketing sections, App UI Rules to functional sections + +**Hard rejection criteria** (instant-fail patterns — flag if ANY apply): +${rejectionItems} + +**Litmus checks** (answer YES/NO for each — used for cross-model consensus scoring): +${litmusItems} + +**Landing page rules** (apply when classifier = MARKETING/LANDING): +- First viewport reads as one composition, not a dashboard +- Brand-first hierarchy: brand > headline > body > CTA +- Typography: expressive, purposeful — no default stacks (Inter, Roboto, Arial, system) +- No flat single-color backgrounds — use gradients, images, subtle patterns +- Hero: full-bleed, edge-to-edge, no inset/tiled/rounded variants +- Hero budget: brand, one headline, one supporting sentence, one CTA group, one image +- No cards in hero. Cards only when card IS the interaction +- One job per section: one purpose, one headline, one short supporting sentence +- Motion: 2-3 intentional motions minimum (entrance, scroll-linked, hover/reveal) +- Color: define CSS variables, avoid purple-on-white defaults, one accent color default +- Copy: product language not design commentary. "If deleting 30% improves it, keep deleting" +- Beautiful defaults: composition-first, brand as loudest text, two typefaces max, cardless by default, first viewport as poster not document + +**App UI rules** (apply when classifier = APP UI): +- Calm surface hierarchy, strong typography, few colors +- Dense but readable, minimal chrome +- Organize: primary workspace, navigation, secondary context, one accent +- Avoid: dashboard-card mosaics, thick borders, decorative gradients, ornamental icons +- Copy: utility language — orientation, status, action. Not mood/brand/aspiration +- Cards only when card IS the interaction +- Section headings state what area is or what user can do ("Selected KPIs", "Plan status") + +**Universal rules** (apply to ALL types): +- Define CSS variables for color system +- No default font stacks (Inter, Roboto, Arial, system) +- One job per section +- "If deleting 30% of the copy improves it, keep deleting" +- Cards earn their existence — no decorative card grids + +**AI Slop blacklist** (the 10 patterns that scream "AI-generated"): +${slopItems} + +Source: [OpenAI "Designing Delightful Frontends with GPT-5.4"](https://developers.openai.com/blog/designing-delightful-frontends-with-gpt-5-4) (Mar 2026) + gstack design methodology.`; +} + const RESOLVERS: Record string> = { COMMAND_REFERENCE: generateCommandReference, SNAPSHOT_FLAGS: generateSnapshotFlags, @@ -2220,6 +2550,8 @@ const RESOLVERS: Record string> = { BASE_BRANCH_DETECT: generateBaseBranchDetect, QA_METHODOLOGY: generateQAMethodology, DESIGN_METHODOLOGY: generateDesignMethodology, + DESIGN_HARD_RULES: generateDesignHardRules, + DESIGN_OUTSIDE_VOICES: generateDesignOutsideVoices, DESIGN_REVIEW_LITE: generateDesignReviewLite, REVIEW_DASHBOARD: generateReviewDashboard, PLAN_FILE_REVIEW_REPORT: generatePlanFileReviewReport, diff --git a/ship/SKILL.md b/ship/SKILL.md index 1cd1a6e6c30fc94a4685a8c7ae24536fb96362d8..af138e1ddece03c4ec0f2445e8ceb86801653ed4 100644 --- a/ship/SKILL.md +++ b/ship/SKILL.md @@ -1042,6 +1042,28 @@ source <(~/.claude/skills/gstack/bin/gstack-diff-scope 2>/dev/null) Substitute: TIMESTAMP = ISO 8601 datetime, STATUS = "clean" if 0 findings or "issues_found", N = total findings, M = auto-fixed count, COMMIT = output of `git rev-parse --short HEAD`. +7. **Codex design voice** (optional, automatic if available): + +```bash +which codex 2>/dev/null && echo "CODEX_AVAILABLE" || echo "CODEX_NOT_AVAILABLE" +``` + +If Codex is available, run a lightweight design check on the diff: + +```bash +TMPERR_DRL=$(mktemp /tmp/codex-drl-XXXXXXXX) +codex exec "Review the git diff on this branch. Run 7 litmus checks (YES/NO each): 1. Brand/product unmistakable in first screen? 2. One strong visual anchor present? 3. Page understandable by scanning headlines only? 4. Each section has one job? 5. Are cards actually necessary? 6. Does motion improve hierarchy or atmosphere? 7. Would design feel premium with all decorative shadows removed? Flag any hard rejections: 1. Generic SaaS card grid as first impression 2. Beautiful image with weak brand 3. Strong headline with no clear action 4. Busy imagery behind text 5. Sections repeating same mood statement 6. Carousel with no narrative purpose 7. App UI made of stacked cards instead of layout 5 most important design findings only. Reference file:line." -s read-only -c 'model_reasoning_effort="high"' --enable web_search_cached 2>"$TMPERR_DRL" +``` + +Use a 5-minute timeout (`timeout: 300000`). After the command completes, read stderr: +```bash +cat "$TMPERR_DRL" && rm -f "$TMPERR_DRL" +``` + +**Error handling:** All errors are non-blocking. On auth failure, timeout, or empty response — skip with a brief note and continue. + +Present Codex output under a `CODEX (design):` header, merged with the checklist findings above. + Include any design findings alongside the code review findings. They follow the same Fix-First flow below. 4. **Classify each finding as AUTO-FIX or ASK** per the Fix-First Heuristic in diff --git a/test/gen-skill-docs.test.ts b/test/gen-skill-docs.test.ts index 661e7b99b7b0127e864aac694276c0b3b7a006fb..8d8f7be818995cce87b347d35fb96b10ab875c7b 100644 --- a/test/gen-skill-docs.test.ts +++ b/test/gen-skill-docs.test.ts @@ -731,11 +731,126 @@ describe('BENEFITS_FROM resolver', () => { }); }); +// --- {{DESIGN_OUTSIDE_VOICES}} resolver tests --- + +describe('DESIGN_OUTSIDE_VOICES resolver', () => { + test('plan-design-review contains outside voices section', () => { + const content = fs.readFileSync(path.join(ROOT, 'plan-design-review', 'SKILL.md'), 'utf-8'); + expect(content).toContain('Design Outside Voices'); + expect(content).toContain('CODEX_AVAILABLE'); + expect(content).toContain('LITMUS SCORECARD'); + }); + + test('design-review contains outside voices section', () => { + const content = fs.readFileSync(path.join(ROOT, 'design-review', 'SKILL.md'), 'utf-8'); + expect(content).toContain('Design Outside Voices'); + expect(content).toContain('source audit'); + }); + + test('design-consultation contains outside voices section', () => { + const content = fs.readFileSync(path.join(ROOT, 'design-consultation', 'SKILL.md'), 'utf-8'); + expect(content).toContain('Design Outside Voices'); + expect(content).toContain('design direction'); + }); + + test('branches correctly per skillName — different prompts', () => { + const planContent = fs.readFileSync(path.join(ROOT, 'plan-design-review', 'SKILL.md'), 'utf-8'); + const consultContent = fs.readFileSync(path.join(ROOT, 'design-consultation', 'SKILL.md'), 'utf-8'); + // plan-design-review uses analytical prompt (high reasoning) + expect(planContent).toContain('model_reasoning_effort="high"'); + // design-consultation uses creative prompt (medium reasoning) + expect(consultContent).toContain('model_reasoning_effort="medium"'); + }); +}); + +// --- {{DESIGN_HARD_RULES}} resolver tests --- + +describe('DESIGN_HARD_RULES resolver', () => { + test('plan-design-review Pass 4 contains hard rules', () => { + const content = fs.readFileSync(path.join(ROOT, 'plan-design-review', 'SKILL.md'), 'utf-8'); + expect(content).toContain('Design Hard Rules'); + expect(content).toContain('Classifier'); + expect(content).toContain('MARKETING/LANDING PAGE'); + expect(content).toContain('APP UI'); + }); + + test('design-review contains hard rules', () => { + const content = fs.readFileSync(path.join(ROOT, 'design-review', 'SKILL.md'), 'utf-8'); + expect(content).toContain('Design Hard Rules'); + }); + + test('includes all 3 rule sets', () => { + const content = fs.readFileSync(path.join(ROOT, 'plan-design-review', 'SKILL.md'), 'utf-8'); + expect(content).toContain('Landing page rules'); + expect(content).toContain('App UI rules'); + expect(content).toContain('Universal rules'); + }); + + test('references shared AI slop blacklist items', () => { + const content = fs.readFileSync(path.join(ROOT, 'plan-design-review', 'SKILL.md'), 'utf-8'); + expect(content).toContain('3-column feature grid'); + expect(content).toContain('Purple/violet/indigo'); + }); + + test('includes OpenAI hard rejection criteria', () => { + const content = fs.readFileSync(path.join(ROOT, 'plan-design-review', 'SKILL.md'), 'utf-8'); + expect(content).toContain('Generic SaaS card grid'); + expect(content).toContain('Carousel with no narrative purpose'); + }); + + test('includes OpenAI litmus checks', () => { + const content = fs.readFileSync(path.join(ROOT, 'plan-design-review', 'SKILL.md'), 'utf-8'); + expect(content).toContain('Brand/product unmistakable'); + expect(content).toContain('premium with all decorative shadows removed'); + }); +}); + +// --- Extended DESIGN_SKETCH resolver tests --- + +describe('DESIGN_SKETCH extended with outside voices', () => { + const content = fs.readFileSync(path.join(ROOT, 'office-hours', 'SKILL.md'), 'utf-8'); + + test('contains outside design voices step', () => { + expect(content).toContain('Outside design voices'); + }); + + test('offers opt-in via AskUserQuestion', () => { + expect(content).toContain('outside design perspectives'); + }); + + test('still contains original wireframe steps', () => { + expect(content).toContain('wireframe'); + expect(content).toContain('$B goto'); + }); +}); + +// --- Extended DESIGN_REVIEW_LITE resolver tests --- + +describe('DESIGN_REVIEW_LITE extended with Codex', () => { + const content = fs.readFileSync(path.join(ROOT, 'ship', 'SKILL.md'), 'utf-8'); + + test('contains Codex design voice block', () => { + expect(content).toContain('Codex design voice'); + expect(content).toContain('CODEX (design)'); + }); + + test('still contains original checklist steps', () => { + expect(content).toContain('design-checklist.md'); + expect(content).toContain('SCOPE_FRONTEND'); + }); + +}); + // ─── Codex Generation Tests ───────────────────────────────── describe('Codex generation (--host codex)', () => { const AGENTS_DIR = path.join(ROOT, '.agents', 'skills'); + // .agents/ is gitignored (v0.11.2.0) — generate on demand for tests + Bun.spawnSync(['bun', 'run', 'scripts/gen-skill-docs.ts', '--host', 'codex'], { + cwd: ROOT, stdout: 'pipe', stderr: 'pipe', + }); + // Dynamic discovery of expected Codex skills: all templates except /codex const CODEX_SKILLS = (() => { const skills: Array<{ dir: string; codexName: string }> = []; @@ -987,6 +1102,18 @@ describe('Codex generation (--host codex)', () => { } } }); + + // ─── Design outside voices: Codex host guard ───────────────── + + test('codex host produces empty outside voices in design-review', () => { + const codexContent = fs.readFileSync(path.join(AGENTS_DIR, 'gstack-design-review', 'SKILL.md'), 'utf-8'); + expect(codexContent).not.toContain('Design Outside Voices'); + }); + + test('codex host does not include Codex design block in ship', () => { + const codexContent = fs.readFileSync(path.join(AGENTS_DIR, 'gstack-ship', 'SKILL.md'), 'utf-8'); + expect(codexContent).not.toContain('Codex design voice'); + }); }); // ─── Setup script validation ───────────────────────────────── diff --git a/test/helpers/touchfiles.ts b/test/helpers/touchfiles.ts index 5af21021f118e0b8a9f25a01154fd7b0dc4f28ee..07fb19ea2d404bf8e6013d65c8c7cba95ba5b508 100644 --- a/test/helpers/touchfiles.ts +++ b/test/helpers/touchfiles.ts @@ -105,13 +105,13 @@ export const E2E_TOUCHFILES: Record = { 'ship-triage': ['ship/**', 'bin/gstack-repo-mode'], // Design - 'design-consultation-core': ['design-consultation/**'], - 'design-consultation-existing': ['design-consultation/**'], - 'design-consultation-research': ['design-consultation/**'], - 'design-consultation-preview': ['design-consultation/**'], - 'plan-design-review-plan-mode': ['plan-design-review/**'], - 'plan-design-review-no-ui-scope': ['plan-design-review/**'], - 'design-review-fix': ['design-review/**', 'browse/src/**'], + 'design-consultation-core': ['design-consultation/**', 'scripts/gen-skill-docs.ts'], + 'design-consultation-existing': ['design-consultation/**', 'scripts/gen-skill-docs.ts'], + 'design-consultation-research': ['design-consultation/**', 'scripts/gen-skill-docs.ts'], + 'design-consultation-preview': ['design-consultation/**', 'scripts/gen-skill-docs.ts'], + 'plan-design-review-plan-mode': ['plan-design-review/**', 'scripts/gen-skill-docs.ts'], + 'plan-design-review-no-ui-scope': ['plan-design-review/**', 'scripts/gen-skill-docs.ts'], + 'design-review-fix': ['design-review/**', 'browse/src/**', 'scripts/gen-skill-docs.ts'], // gstack-upgrade 'gstack-upgrade-happy-path': ['gstack-upgrade/**'], diff --git a/test/skill-validation.test.ts b/test/skill-validation.test.ts index dd5a5c3d85186fa82acb9f416a09bfaa3070c1b6..2085eaec45d9554971359363a701fd37c0661e1f 100644 --- a/test/skill-validation.test.ts +++ b/test/skill-validation.test.ts @@ -1328,6 +1328,10 @@ describe('Codex skill', () => { }); test('codex-host ship/review do NOT contain adversarial review step', () => { + // .agents/ is gitignored — generate on demand + Bun.spawnSync(['bun', 'run', 'scripts/gen-skill-docs.ts', '--host', 'codex'], { + cwd: ROOT, stdout: 'pipe', stderr: 'pipe', + }); const shipContent = fs.readFileSync(path.join(ROOT, '.agents', 'skills', 'gstack-ship', 'SKILL.md'), 'utf-8'); expect(shipContent).not.toContain('codex review --base'); expect(shipContent).not.toContain('CODEX_REVIEWS'); @@ -1402,6 +1406,11 @@ describe('Skill trigger phrases', () => { describe('Codex skill validation', () => { const AGENTS_DIR = path.join(ROOT, '.agents', 'skills'); + // .agents/ is gitignored (v0.11.2.0) — generate on demand for tests + Bun.spawnSync(['bun', 'run', 'scripts/gen-skill-docs.ts', '--host', 'codex'], { + cwd: ROOT, stdout: 'pipe', stderr: 'pipe', + }); + // Discover all Claude skills with templates (except /codex which is Claude-only) const CLAUDE_SKILLS_WITH_TEMPLATES = (() => { const skills: string[] = [];