refactor(design-catalog): craft tells are checker-only entries; trim design-review and plan-design-review growth to the planned size (GSTA-92)

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
This commit is contained in:
garrytanandcapy-ai[bot] committed 2026-10-25 23:28:13 +00:00
1 parent f9d825bee6
commit a65d492cf8
11 files changed
+74 -125

No files matched your search

+2 -2
View File
@@ -12,7 +12,7 @@
- **Accepted craft exceptions persist.** /design-html's finalized.json records each accepted `CRAFT_CHECK` finding in `craft_exceptions` (`id`, `count`, `reason`) and carries forward earlier ones. A later run passes that file with `--accepted`, so a finding the user already accepted is reported as accepted instead of flagged again. A higher count is still flagged as a new issue.
- **/design-review audits a Persuade page or a redesign for craft** (`sections/craft-audit.md`, read in Phases 1-6; design-review is now carved on every host, so the audit is one lazy section, not inlined text). It runs `bin/gstack-design-craft` on each page's served HTML (or the repo file when no app runs) and files each `CRAFT_CHECK` line as a category 9 finding; checks what the tool cannot see (claims only from the user, realistic sample content, the ship floor, a designed 404, cookie consent where required, the motion rules); files a failed Persuade check (one primary action, named objections, proof beside each claim, a real risk reversal, an index/noindex decision) as High impact; and in Phase 8 breaks impact ties with Elaya's fix order and reads the installed Tailwind major version before touching its config. The audit's rules render from /design-html's craft section, so the build and the audit cannot drift.
- **/plan-design-review**: a Persuade plan gets the same five checks in Pass 1, Pass 4 rejects unsourced proof, and the runner checklist names them.
- **The craft tells are catalog entries.** Placeholder people and brands, lorem, round sample numbers, `[Needs proof:` markers, "Oops" errors, scroll-listener reveals, dead `href="#"`, `alert()`, images without alt, `100vh`, and a missing title, description, favicon, skip link or `aria-current` live in `lib/design-catalog.ts`, so they render into Methodology category 9, `review/design-checklist.md` and the consultation bullets like every other tell. `lib/design-craft.ts` prints only catalog ids, with each entry's `fix` text; `transition: all` now reports under the existing `layout-transition` id instead of `transition-all`.
- **The craft tells are catalog entries.** Placeholder people and brands, lorem, round sample numbers, `[Needs proof:` markers, "Oops" errors, scroll-listener reveals, dead `href="#"`, `alert()`, images without alt, `100vh`, and a missing title, description, favicon, skip link or `aria-current` live in `lib/design-catalog.ts` as `checkerOnly` entries: one id, one short line and the fix, reported by the craft check and kept out of Methodology category 9, `review/design-checklist.md` and the consultation bullets, so no skill carries their prose. `lib/design-craft.ts` prints only catalog ids, with each entry's `fix` text; `transition: all` now reports under the existing `layout-transition` id instead of `transition-all`.
### Added
- **`bin/gstack-design-craft`** (`lib/design-craft.ts`): prints `CRAFT_CHECK: <id> <count> <fix>` per tell and `CRAFT_RESULT: clean` (exit 0) or `<n> issue(s)` (exit 1); exit 2 on a non-HTML, empty or unreadable file. Copy rules read decoded visible text; `alert()` counts only in scripts; head and navigation rules run only on a full document. Buzzwords report under the catalog's `marketing-buzzword` id.
@@ -26,7 +26,7 @@
- `test/design-craft.test.ts`: every id the check prints is a grep-detectable catalog entry, and the fix it prints is the catalog's. design-review gets its first carve guard (skeleton, union and parity ratio at their measured sizes).
### Gate integrity
- Size budgets raised to measured sizes only (Garry 2026-10-25 "Do it" (GSTA-92)): the SKILL.md pins of design-review on 13 hosts (+2,011 Claude, +2,041 external), plan-design-review on 12 external hosts (+1,387) and design-consultation on 9 external hosts (+1,259), and design-consultation's parity ratio from 1.16 to its measured 1.1694. No other threshold, budget, timeout, retry or trial count changed.
- Size budgets raised to measured sizes only (Garry 2026-10-25 "Do it" (GSTA-92)): the SKILL.md pins of design-review on 13 hosts (+584 Claude, +614 external: the Section index the carve test requires and the STOP pointer) and plan-design-review on 12 external hosts (+1,082: the five Persuade plan checks). The craft tells are catalog entries the craft check reports with their fix, kept out of every rendered skill list, so design-consultation and the review checklist do not grow. No other threshold, budget, timeout, retry or trial count changed.
## [1.91.218.0] - 2026-10-25
@@ -90,18 +90,6 @@ User asks for a listed face by name: comply, state the tradeoff once.
- Sparklines, progress rings, and fake avatars filling space where content should be. Real data or an honest empty state.
- Dark because it is a dev tool, light because it is health. Light or dark comes from the use scene: who, where, under what light.
- Only the happy path is designed. Empty, loading, error, and long-content states are part of the component.
- John Doe and Jane Doe in quotes, team rows and demo data. A supplied name, or one that fits the product.
- Acme, Nexus and SmartFlow as the customers in logos, quotes and sample data. A supplied customer, or none.
- Lorem ipsum where a visitor can read it. Write the draft copy.
- 99.9% uptime, 50% faster, 10x, 10,000+ users: round figures nobody measured. The supplied number, or no claim.
- A `[Needs proof: …]` marker still on the page. Supply the proof or cut the claim before it ships.
- Reveals driven by an unthrottled `scroll` listener. IntersectionObserver for reveals; scroll-linked effects read scroll inside requestAnimationFrame.
- `href="#"` on links and buttons that go nowhere. Link it, or render the control disabled.
- `alert()` for errors and confirmations. Show the message inline, next to what it is about.
- An `<img>` with no alt attribute. Describe a meaningful image; `alt=""` on a decorative one.
- A full HTML document with no `<title>`. The tab, the bookmark and the search result all read it.
- A full HTML document with no meta description. Search results and link previews make one up.
- No "Skip to content" link as the first focusable element, targeting `<main>`. Keyboard users tab through the whole header on every page.
### Coherence Validation
+6 -18
View File
@@ -1250,7 +1250,7 @@ Apply these at each page. Each finding gets an impact rating (high/medium/polish
- Instructions detection: any visible instructions longer than one sentence. If users need to read instructions, the design has failed. Flag the instructions AND the interaction they're compensating for.
- Happy talk word count: count total visible words on the page. Classify each text block as "useful content" vs "happy talk" (welcome paragraphs, self-congratulatory text, instructions nobody reads). Report: "This page has X words. Y (Z%) are happy talk."
**9. AI Slop Detection** (11 blacklist patterns, 30 detector rules, 32 judgment tells; polish-level ones on the last line)
**9. AI Slop Detection** (11 blacklist patterns, 30 detector rules, 16 judgment tells; polish-level ones on the last line)
The test: would a human designer at a respected studio ever ship this? A `[rule-id]` is the detector's name for the same pattern; a scan hit and a judgment hit on one element are one finding.
@@ -1283,20 +1283,8 @@ Judgment tells (no detector rule; you are the detector):
- Sparklines, progress rings, and fake avatars filling space where content should be. Real data or an honest empty state.
- Dark because it is a dev tool, light because it is health. Light or dark comes from the use scene: who, where, under what light.
- Only the happy path is designed. Empty, loading, error, and long-content states are part of the component.
- John Doe and Jane Doe in quotes, team rows and demo data. A supplied name, or one that fits the product.
- Acme, Nexus and SmartFlow as the customers in logos, quotes and sample data. A supplied customer, or none.
- Lorem ipsum where a visitor can read it. Write the draft copy.
- 99.9% uptime, 50% faster, 10x, 10,000+ users: round figures nobody measured. The supplied number, or no claim.
- A `[Needs proof: …]` marker still on the page. Supply the proof or cut the claim before it ships.
- Reveals driven by an unthrottled `scroll` listener. IntersectionObserver for reveals; scroll-linked effects read scroll inside requestAnimationFrame.
- `href="#"` on links and buttons that go nowhere. Link it, or render the control disabled.
- `alert()` for errors and confirmations. Show the message inline, next to what it is about.
- An `<img>` with no alt attribute. Describe a meaningful image; `alt=""` on a decorative one.
- A full HTML document with no `<title>`. The tab, the bookmark and the search result all read it.
- A full HTML document with no meta description. Search results and link previews make one up.
- No "Skip to content" link as the first focusable element, targeting `<main>`. Keyboard users tab through the whole header on every page.
Polish-level tells, note but do not grade: [monotonous-spacing], [bounce-easing], [pulsing-dot], [blinking-cursor], [numbered-section-labels], [em-dash-overuse], [extreme-negative-tracking], [gpt-thin-border-wide-shadow], [repeating-stripes-gradient], [codex-grid-background], [image-hover-transform], monospace as costume, unthemed browser surfaces, "oops" errors, 100vh sections, missing favicon, unmarked current page.
Polish-level tells, note but do not grade: [monotonous-spacing], [bounce-easing], [pulsing-dot], [blinking-cursor], [numbered-section-labels], [em-dash-overuse], [extreme-negative-tracking], [gpt-thin-border-wide-shadow], [repeating-stripes-gradient], [codex-grid-background], [image-hover-transform], monospace as costume, unthemed browser surfaces.
**10. Performance as Design** (6 items)
- LCP < 2.0s (web apps), < 1.5s (informational sites)
@@ -1574,7 +1562,7 @@ Tie everything to user goals and product objectives. Always suggest specific imp
**Calibration: the three looks.** AI-built interfaces land in one of three looks no matter what the product is: (1) cream ground, high-contrast serif display, terracotta or signal-red accent; (2) near-black, one neon accent, glowing edges; (3) broadsheet hairlines, italic display serif, tiny tracked mono labels. Each is fine when the brief asks for it. If the brief left the look open and you landed in one anyway, you stopped looking. The test: could someone guess your look from the category alone? From "the category, but avoiding the obvious"? Either way, start over. "It's about books, so cream and a serif" fails this test. Book cloth and jackets come in every saturated color there is.
**AI Slop blacklist:** the 11 legacy patterns, the 30 detector rules, and the 32 judgment tells are Methodology category 9. Grade against that list; do not re-derive it here.
**AI Slop blacklist:** the 11 legacy patterns, the 30 detector rules, and the 16 judgment tells are Methodology category 9. Grade against that list; do not re-derive it here.
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.
@@ -1585,9 +1573,9 @@ sections. Read a section in full before doing its step; do not work from memory.
| When | Read this section |
|------|-------------------|
| auditing a Persuade page, or running a redesign or craft audit | `sections/craft-audit.md` |
| auditing a Persuade page or a redesign | `sections/craft-audit.md` |
> **STOP.** Before auditing a Persuade page, or running a redesign or craft audit, Read `~/.claude/skills/gstack/design-review/sections/craft-audit.md` and execute it
> **STOP.** Before auditing a Persuade page or a redesign, Read `~/.claude/skills/gstack/design-review/sections/craft-audit.md` and execute it
> in full. Do not work from memory — that section is the source of truth for this step.
Record baseline design score and AI slop score at end of Phase 6.
@@ -1771,7 +1759,7 @@ Detector findings carry their `[rule-id]`; a deferred one ends with its `handoff
## Phase 8: Fix Loop
For each fixable finding, in impact order (ties: the craft audit's fix order, when it ran):
For each fixable finding, in impact order:
### 8a. Locate source
+1 -1
View File
@@ -165,7 +165,7 @@ Detector findings carry their `[rule-id]`; a deferred one ends with its `handoff
## Phase 8: Fix Loop
For each fixable finding, in impact order (ties: the craft audit's fix order, when it ran):
For each fixable finding, in impact order:
### 8a. Locate source
+1 -1
View File
@@ -8,7 +8,7 @@
"id": "craft-audit",
"file": "craft-audit.md",
"title": "Craft audit: Persuade pages and redesigns",
"trigger": "auditing a Persuade page, or running a redesign or craft audit"
"trigger": "auditing a Persuade page or a redesign"
}
]
}
+25 -20
View File
@@ -62,6 +62,8 @@ export interface DesignSlopEntry {
handoff?: Handoff;
/** one-line fix the free craft check prints beside this id (lib/design-craft.ts) */
fix?: string;
/** reported only by gstack-design-craft, with its fix; never rendered into skill prose (token weight) */
checkerOnly?: true;
source: 'gstack' | 'impeccable' | 'both';
/** the 11 originals; AI_SLOP_BLACKLIST derives from these verbatim */
legacyBlacklist?: true;
@@ -463,119 +465,119 @@ export const DESIGN_SLOP_CATALOG: DesignSlopEntry[] = [
},
// ── Craft tells (GSTA-92): grep-checkable; the free craft check (lib/design-craft.ts)
// prints each id with its `fix`. Adapted from Elaya's landing-page-design and
// prints each id with its `fix`. checkerOnly keeps them out of the rendered skill lists. Adapted from Elaya's landing-page-design and
// redesign-skill (MIT); see NOTICE.md.
{
id: 'placeholder-name', name: 'Placeholder people',
prose: 'John Doe and Jane Doe in quotes, team rows and demo data. A supplied name, or one that fits the product.',
category: 'copy', kind: 'slop', detect: ['grep'], confidence: 'HIGH', tier: 'ask', impact: 'medium',
heuristic: 'Grep visible copy for "John Doe" and "Jane Doe".',
handoff: 'clarify', source: 'gstack', fix: 'use a supplied name, or one that fits the product in sample data',
handoff: 'clarify', source: 'gstack', checkerOnly: true, fix: 'use a supplied name, or one that fits the product in sample data',
},
{
id: 'placeholder-brand', name: 'Placeholder brands',
prose: 'Acme, Nexus and SmartFlow as the customers in logos, quotes and sample data. A supplied customer, or none.',
category: 'copy', kind: 'slop', detect: ['grep'], confidence: 'MEDIUM', tier: 'ask', impact: 'medium',
heuristic: 'Grep visible copy for Acme, Nexus, SmartFlow.',
handoff: 'clarify', source: 'gstack', fix: 'use a supplied customer, or a fitting name in sample data',
handoff: 'clarify', source: 'gstack', checkerOnly: true, fix: 'use a supplied customer, or a fitting name in sample data',
},
{
id: 'lorem', name: 'Lorem ipsum',
prose: 'Lorem ipsum where a visitor can read it. Write the draft copy.',
category: 'copy', kind: 'slop', detect: ['grep'], confidence: 'HIGH', tier: 'ask', impact: 'high',
heuristic: 'Grep visible copy for "lorem ipsum".',
handoff: 'clarify', source: 'gstack', fix: 'write real copy',
handoff: 'clarify', source: 'gstack', checkerOnly: true, fix: 'write real copy',
},
{
id: 'round-number', name: 'Round sample numbers',
prose: '99.9% uptime, 50% faster, 10x, 10,000+ users: round figures nobody measured. The supplied number, or no claim.',
category: 'copy', kind: 'slop', detect: ['grep'], confidence: 'MEDIUM', tier: 'ask', impact: 'medium',
heuristic: 'Grep copy for 99.9%, a round percentage before faster, more or uptime, 10x, and "<n>0+ users".',
handoff: 'clarify', source: 'gstack', fix: 'use the supplied figure, or drop the claim',
handoff: 'clarify', source: 'gstack', checkerOnly: true, fix: 'use the supplied figure, or drop the claim',
},
{
id: 'needs-proof', name: 'Unresolved proof marker',
prose: 'A `[Needs proof: …]` marker still on the page. Supply the proof or cut the claim before it ships.',
category: 'copy', kind: 'slop', detect: ['grep'], confidence: 'HIGH', tier: 'ask', impact: 'high',
heuristic: 'Grep for `[Needs proof:`.',
handoff: 'clarify', source: 'gstack', fix: 'supply the proof or remove the claim before publishing',
handoff: 'clarify', source: 'gstack', checkerOnly: true, fix: 'supply the proof or remove the claim before publishing',
},
{
id: 'oops', name: '"Oops" errors',
prose: '"Oops!" in place of what happened and what to do next.',
category: 'copy', kind: 'slop', detect: ['grep'], confidence: 'HIGH', tier: 'ask', impact: 'polish',
heuristic: 'Grep visible copy for "oops".',
handoff: 'clarify', source: 'gstack', fix: 'say what happened and what to do',
handoff: 'clarify', source: 'gstack', checkerOnly: true, fix: 'say what happened and what to do',
},
{
id: 'scroll-listener', name: 'Scroll-listener reveals',
prose: 'Reveals driven by an unthrottled `scroll` listener. IntersectionObserver for reveals; scroll-linked effects read scroll inside requestAnimationFrame.',
category: 'motion', kind: 'slop', detect: ['grep'], confidence: 'HIGH', tier: 'ask', impact: 'medium',
heuristic: 'Grep scripts for `addEventListener(\'scroll\'`.',
handoff: 'animate', source: 'gstack', fix: 'use IntersectionObserver; read scroll inside requestAnimationFrame for a scroll-linked effect',
handoff: 'animate', source: 'gstack', checkerOnly: true, fix: 'use IntersectionObserver; read scroll inside requestAnimationFrame for a scroll-linked effect',
},
{
id: 'dead-link', name: 'Dead links',
prose: '`href="#"` on links and buttons that go nowhere. Link it, or render the control disabled.',
category: 'states', kind: 'slop', detect: ['grep'], confidence: 'HIGH', tier: 'ask', impact: 'medium',
heuristic: 'Grep markup for `href="#"`.',
handoff: 'harden', source: 'gstack', fix: 'link it, or render the control disabled',
handoff: 'harden', source: 'gstack', checkerOnly: true, fix: 'link it, or render the control disabled',
},
{
id: 'alert', name: 'alert() messages',
prose: '`alert()` for errors and confirmations. Show the message inline, next to what it is about.',
category: 'states', kind: 'slop', detect: ['grep'], confidence: 'HIGH', tier: 'ask', impact: 'medium',
heuristic: 'Grep scripts and inline handlers for `alert(`.',
handoff: 'harden', source: 'gstack', fix: 'show the message inline',
handoff: 'harden', source: 'gstack', checkerOnly: true, fix: 'show the message inline',
},
{
id: 'img-no-alt', name: 'Image without alt',
prose: 'An `<img>` with no alt attribute. Describe a meaningful image; `alt=""` on a decorative one.',
category: 'imagery', kind: 'slop', detect: ['grep'], confidence: 'HIGH', tier: 'ask', impact: 'medium',
heuristic: 'Grep markup for `<img` tags with no `alt=`.',
handoff: 'harden', source: 'gstack', fix: 'add alt text (alt="" when decorative)',
handoff: 'harden', source: 'gstack', checkerOnly: true, fix: 'add alt text (alt="" when decorative)',
},
{
id: 'vh-height', name: '100vh sections',
prose: 'Full-height sections on `100vh`, which jump under mobile browser chrome. `min-height: 100dvh`.',
category: 'layout', kind: 'slop', detect: ['grep'], confidence: 'HIGH', tier: 'ask', impact: 'polish',
heuristic: 'Grep CSS for `height: 100vh` with no later `100dvh`, and for `h-screen`.',
handoff: 'layout', source: 'gstack', fix: 'use min-height: 100dvh (a 100vh line followed by a 100dvh fallback is fine)',
handoff: 'layout', source: 'gstack', checkerOnly: true, fix: 'use min-height: 100dvh (a 100vh line followed by a 100dvh fallback is fine)',
},
{
id: 'missing-title', name: 'Missing title',
prose: 'A full HTML document with no `<title>`. The tab, the bookmark and the search result all read it.',
category: 'browser-surface', kind: 'slop', detect: ['grep'], confidence: 'HIGH', tier: 'ask', impact: 'medium',
heuristic: 'Grep each full document for a non-empty `<title>`.',
handoff: 'harden', source: 'gstack', fix: 'add a <title>',
handoff: 'harden', source: 'gstack', checkerOnly: true, fix: 'add a <title>',
},
{
id: 'missing-meta-description', name: 'Missing meta description',
prose: 'A full HTML document with no meta description. Search results and link previews make one up.',
category: 'browser-surface', kind: 'slop', detect: ['grep'], confidence: 'HIGH', tier: 'ask', impact: 'medium',
heuristic: 'Grep each full document for `<meta name="description"`.',
handoff: 'harden', source: 'gstack', fix: 'add <meta name="description">',
handoff: 'harden', source: 'gstack', checkerOnly: true, fix: 'add <meta name="description">',
},
{
id: 'missing-favicon', name: 'Missing favicon',
prose: 'No favicon, so every tab shows the browser default. A mark from the brand; an inline SVG when no file exists.',
category: 'browser-surface', kind: 'slop', detect: ['grep'], confidence: 'HIGH', tier: 'ask', impact: 'polish',
heuristic: 'Grep each full document for `<link rel="icon"`.',
handoff: 'polish', source: 'gstack', fix: 'add a favicon <link rel="icon">',
handoff: 'polish', source: 'gstack', checkerOnly: true, fix: 'add a favicon <link rel="icon">',
},
{
id: 'missing-skip-link', name: 'Missing skip link',
prose: 'No "Skip to content" link as the first focusable element, targeting `<main>`. Keyboard users tab through the whole header on every page.',
category: 'scaffold', kind: 'slop', detect: ['grep'], confidence: 'HIGH', tier: 'ask', impact: 'medium',
heuristic: 'Check that the first focusable element in `<body>` links to the id on `<main>`.',
handoff: 'harden', source: 'gstack', fix: 'add a "Skip to content" link first in <body> whose #id exists on <main>',
handoff: 'harden', source: 'gstack', checkerOnly: true, fix: 'add a "Skip to content" link first in <body> whose #id exists on <main>',
},
{
id: 'missing-aria-current', name: 'Unmarked current page',
prose: 'Route navigation with no `aria-current="page"` on the current link. Mark it and style it; in-page section links stay unmarked.',
category: 'states', kind: 'slop', detect: ['grep'], confidence: 'MEDIUM', tier: 'ask', impact: 'polish',
heuristic: 'Grep `<nav>` blocks with two or more route links for `aria-current="page"`.',
handoff: 'harden', source: 'gstack', fix: 'mark the current nav link aria-current="page"',
handoff: 'harden', source: 'gstack', checkerOnly: true, fix: 'mark the current nav link aria-current="page"',
},
// ── Quality rules the detector knows. ──
@@ -826,8 +828,11 @@ export interface RenderCatalogOptions {
omitImpact?: Impact[];
}
/** Entries skill prose renders: everything except the craft check's own ids. */
export const RENDERED: readonly DesignSlopEntry[] = DESIGN_SLOP_CATALOG.filter(e => !e.checkerOnly);
export function selectCatalog(o: RenderCatalogOptions): DesignSlopEntry[] {
return DESIGN_SLOP_CATALOG.filter(e =>
return RENDERED.filter(e =>
(!o.kind || e.kind === o.kind)
&& !(o.omitImpact && o.omitImpact.includes(e.impact)),
);
@@ -840,12 +845,12 @@ export function renderCatalog(o: RenderCatalogOptions): string {
/** Slop the detector knows, minus the 11 legacy lines: what design doctrine renders as bracketed ids. */
export function detectorSlopEntries(o: { omitPolish?: boolean } = {}): DesignSlopEntry[] {
return DESIGN_SLOP_CATALOG.filter(e => e.kind === 'slop' && e.impeccableId && !e.legacyBlacklist && !(o.omitPolish && e.impact === 'polish'));
return RENDERED.filter(e => e.kind === 'slop' && e.impeccableId && !e.legacyBlacklist && !(o.omitPolish && e.impact === 'polish'));
}
/** gstack-only slop tells (no detector rule), minus the legacy lines: the LLM pass is the detector. */
export function judgmentTellEntries(o: { omitPolish?: boolean } = {}): DesignSlopEntry[] {
return DESIGN_SLOP_CATALOG.filter(e => e.kind === 'slop' && !e.impeccableId && !e.legacyBlacklist && !(o.omitPolish && e.impact === 'polish'));
return RENDERED.filter(e => e.kind === 'slop' && !e.impeccableId && !e.legacyBlacklist && !(o.omitPolish && e.impact === 'polish'));
}
/** Catalog entries by id, throwing with the id when one is missing (a rename must fail loudly at gen time). */
@@ -214,7 +214,7 @@ Review these as UI requirements in the plan, approved mockups, and referenced ex
11. system-ui or `-apple-system` as the PRIMARY display/body font — the "I gave up on typography" signal. Pick a real typeface.
Detector rule ids for the rest of the catalog (a `[rule-id]` in a finding is one of these): border-accent-on-rounded: Border accent on a rounded card; overused-font: Overused display font; flat-type-hierarchy: Flat type hierarchy; gradient-text: Gradient text; cream-palette: Cream default palette; nested-cards: Nested cards; monotonous-spacing: Monotonous spacing; bounce-easing: Bounce easing; pulsing-dot: Pulsing status dot; blinking-cursor: Blinking cursor effect; shape-assembled-illustration: Shape-assembled illustration; dark-glow: Dark-mode glow; radial-halo: Radial halo; radial-spotlight-glow: Radial spotlight glow; marquee: Logo marquee; icon-tile-stack: Icon tile above every heading; italic-serif-display: Italic serif display; hero-eyebrow-chip: Hero eyebrow chip; kicker-above-heading: Kicker above heading; numbered-section-labels: Numbered section labels; em-dash-overuse: Em-dash overuse; marketing-buzzword: Marketing buzzwords; aphoristic-cadence: Aphoristic cadence; oversized-h1: Oversized h1; extreme-negative-tracking: Extreme negative tracking; gpt-thin-border-wide-shadow: Thin border plus wide shadow; repeating-stripes-gradient: Repeating stripes gradient; codex-grid-background: Grid-paper background; theater-slop-phrase: Theater phrases; image-hover-transform: Image hover zoom.
Judgment tells with no detector rule: gradient cta button, stock-photo hero, cards as the default component, generic testimonial section, left-text right-image hero, generic cta labels, hero metric template, identical card grids, glassmorphism, hand-drawn svg illustration, modal by default, monospace as costume, content stand-ins, mode picked by category, unthemed browser surfaces, missing states, placeholder people, placeholder brands, lorem ipsum, round sample numbers, unresolved proof marker, "oops" errors, scroll-listener reveals, dead links, alert() messages, image without alt, 100vh sections, missing title, missing meta description, missing favicon, missing skip link, unmarked current page.
Judgment tells with no detector rule: gradient cta button, stock-photo hero, cards as the default component, generic testimonial section, left-text right-image hero, generic cta labels, hero metric template, identical card grids, glassmorphism, hand-drawn svg illustration, modal by default, monospace as costume, content stand-ins, mode picked by category, unthemed browser surfaces, missing states.
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.
+1 -33
View File
@@ -85,7 +85,7 @@ If no frontend files changed: skip silently, no output.
## Categories
### 1. AI Slop Detection (43 items) — highest priority
### 1. AI Slop Detection (27 items) — highest priority
These are the telltale signs of AI-generated UI that no designer at a respected studio would ship.
@@ -103,32 +103,6 @@ These are the telltale signs of AI-generated UI that no designer at a respected
- **[HIGH]** [extreme-negative-tracking] Letter-spacing below -0.04em on display type. Tight tracking is a taste; crushed tracking is a tell. Grep `letter-spacing` for values below -0.04em.
- **[HIGH]** John Doe and Jane Doe in quotes, team rows and demo data. A supplied name, or one that fits the product. Grep visible copy for "John Doe" and "Jane Doe".
- **[HIGH]** Lorem ipsum where a visitor can read it. Write the draft copy. Grep visible copy for "lorem ipsum".
- **[HIGH]** A `[Needs proof: …]` marker still on the page. Supply the proof or cut the claim before it ships. Grep for `[Needs proof:`.
- **[HIGH]** "Oops!" in place of what happened and what to do next. Grep visible copy for "oops".
- **[HIGH]** Reveals driven by an unthrottled `scroll` listener. IntersectionObserver for reveals; scroll-linked effects read scroll inside requestAnimationFrame. Grep scripts for `addEventListener('scroll'`.
- **[HIGH]** `href="#"` on links and buttons that go nowhere. Link it, or render the control disabled. Grep markup for `href="#"`.
- **[HIGH]** `alert()` for errors and confirmations. Show the message inline, next to what it is about. Grep scripts and inline handlers for `alert(`.
- **[HIGH]** An `<img>` with no alt attribute. Describe a meaningful image; `alt=""` on a decorative one. Grep markup for `<img` tags with no `alt=`.
- **[HIGH]** Full-height sections on `100vh`, which jump under mobile browser chrome. `min-height: 100dvh`. Grep CSS for `height: 100vh` with no later `100dvh`, and for `h-screen`.
- **[HIGH]** A full HTML document with no `<title>`. The tab, the bookmark and the search result all read it. Grep each full document for a non-empty `<title>`.
- **[HIGH]** A full HTML document with no meta description. Search results and link previews make one up. Grep each full document for `<meta name="description"`.
- **[HIGH]** No favicon, so every tab shows the browser default. A mark from the brand; an inline SVG when no file exists. Grep each full document for `<link rel="icon"`.
- **[HIGH]** No "Skip to content" link as the first focusable element, targeting `<main>`. Keyboard users tab through the whole header on every page. Check that the first focusable element in `<body>` links to the id on `<main>`.
- **[MEDIUM]** [ai-color-palette] Purple/violet/indigo gradient backgrounds or blue-to-purple color schemes. Look for `linear-gradient` with values in the `#6366f1` to `#8b5cf6` range, or CSS custom properties resolving to purple/violet.
- **[MEDIUM]** Centered everything (`text-align: center` on all headings, descriptions, cards). Grep for `text-align: center` density: if more than 60% of text containers center, flag it.
@@ -161,12 +135,6 @@ These are the telltale signs of AI-generated UI that no designer at a respected
- **[MEDIUM]** Selection color, caret, scrollbars, focus rings, underline offset, tabular numerals left at browser defaults. Theme them from the palette. Grep for `::selection`, `caret-color`, `accent-color`, `scrollbar-color`, `text-underline-offset`, `font-variant-numeric`: none present means none themed.
- **[MEDIUM]** Acme, Nexus and SmartFlow as the customers in logos, quotes and sample data. A supplied customer, or none. Grep visible copy for Acme, Nexus, SmartFlow.
- **[MEDIUM]** 99.9% uptime, 50% faster, 10x, 10,000+ users: round figures nobody measured. The supplied number, or no claim. Grep copy for 99.9%, a round percentage before faster, more or uptime, 10x, and "<n>0+ users".
- **[MEDIUM]** Route navigation with no `aria-current="page"` on the current link. Mark it and style it; in-page section links stay unmarked. Grep `<nav>` blocks with two or more route links for `aria-current="page"`.
- **[LOW]** **The 3-column feature grid:** icon-in-colored-circle + bold title + 2-line description, repeated 3x symmetrically. THE most recognizable AI layout. Look for a grid/flex container with exactly 3 children that each contain a circular element + heading + paragraph.
- **[LOW]** Icons in colored circles as section decoration (SaaS starter template look). Look for elements with `border-radius: 50%` + a background color used as decorative containers for icons.
+2 -2
View File
@@ -12,7 +12,7 @@
*
* Derived in part from pbakaus/impeccable (Apache-2.0), modified. See NOTICE.md.
*/
import { DESIGN_SLOP_CATALOG, BANNED_FONTS, type DesignSlopEntry } from '../../lib/design-catalog';
import { DESIGN_SLOP_CATALOG, RENDERED, BANNED_FONTS, type DesignSlopEntry } from '../../lib/design-catalog';
import { SENTINEL, DETECT_EXIT_ECHO } from '../../lib/design-detect-contract';
export const DESIGN_CHECKLIST_HEADER =
@@ -27,7 +27,7 @@ const TIER_ORDER: Record<DesignSlopEntry['confidence'], number> = { HIGH: 0, MED
/** Category 1: slop entries a code reader can grep for, plus the legacy blacklist lines. */
export function checklistSlopEntries(): DesignSlopEntry[] {
// Array.prototype.sort is stable, so catalog order survives within a tier.
return DESIGN_SLOP_CATALOG
return RENDERED
.filter(e => e.kind === 'slop' && (e.detect.includes('grep') || e.legacyBlacklist))
.sort((a, b) => TIER_ORDER[a.confidence] - TIER_ORDER[b.confidence]);
}
+31 -31
View File
@@ -3,91 +3,91 @@
"limit": 100000,
"pins": {
"agy/gstack-autoplan": 130504,
"agy/gstack-design-consultation": 109443,
"agy/gstack-design-review": 145569,
"agy/gstack-design-consultation": 108184,
"agy/gstack-design-review": 144142,
"agy/gstack-land-and-deploy": 117722,
"agy/gstack-plan-design-review": 141400,
"agy/gstack-plan-design-review": 141095,
"agy/gstack-plan-devex-review": 135760,
"agy/gstack-plan-eng-review": 152626,
"agy/gstack-review": 124218,
"claude/design-review": 138542,
"claude/design-review": 137115,
"codex/gstack-autoplan": 130628,
"codex/gstack-design-consultation": 110711,
"codex/gstack-design-review": 147069,
"codex/gstack-design-consultation": 109452,
"codex/gstack-design-review": 145642,
"codex/gstack-land-and-deploy": 122272,
"codex/gstack-office-hours": 102339,
"codex/gstack-plan-design-review": 143853,
"codex/gstack-plan-design-review": 143548,
"codex/gstack-plan-devex-review": 138649,
"codex/gstack-plan-eng-review": 155172,
"codex/gstack-review": 125122,
"copilot/gstack-autoplan": 131637,
"copilot/gstack-codex": 100235,
"copilot/gstack-design-consultation": 110795,
"copilot/gstack-design-review": 146874,
"copilot/gstack-design-consultation": 109536,
"copilot/gstack-design-review": 145447,
"copilot/gstack-land-and-deploy": 118977,
"copilot/gstack-office-hours": 100495,
"copilot/gstack-plan-design-review": 142271,
"copilot/gstack-plan-design-review": 141966,
"copilot/gstack-plan-devex-review": 136912,
"copilot/gstack-plan-eng-review": 153351,
"copilot/gstack-review": 125489,
"cursor/gstack-autoplan": 129278,
"cursor/gstack-design-consultation": 108481,
"cursor/gstack-design-review": 144548,
"cursor/gstack-design-consultation": 107222,
"cursor/gstack-design-review": 143121,
"cursor/gstack-land-and-deploy": 116645,
"cursor/gstack-plan-design-review": 140355,
"cursor/gstack-plan-design-review": 140050,
"cursor/gstack-plan-devex-review": 134591,
"cursor/gstack-plan-eng-review": 151480,
"cursor/gstack-review": 140750,
"factory/gstack-autoplan": 131562,
"factory/gstack-codex": 100655,
"factory/gstack-design-consultation": 109929,
"factory/gstack-design-review": 146338,
"factory/gstack-design-consultation": 108670,
"factory/gstack-design-review": 144911,
"factory/gstack-land-and-deploy": 120817,
"factory/gstack-office-hours": 101374,
"factory/gstack-plan-design-review": 143136,
"factory/gstack-plan-design-review": 142831,
"factory/gstack-plan-devex-review": 137960,
"factory/gstack-plan-eng-review": 154594,
"factory/gstack-review": 144529,
"gbrain/gstack-autoplan": 129815,
"gbrain/gstack-design-review": 134883,
"gbrain/gstack-design-review": 133456,
"gbrain/gstack-land-and-deploy": 117167,
"gbrain/gstack-plan-design-review": 129242,
"gbrain/gstack-plan-design-review": 128937,
"gbrain/gstack-plan-devex-review": 115129,
"gbrain/gstack-plan-eng-review": 134212,
"hermes/gstack-autoplan": 129905,
"hermes/gstack-design-review": 134990,
"hermes/gstack-design-review": 133563,
"hermes/gstack-land-and-deploy": 117269,
"hermes/gstack-plan-design-review": 129351,
"hermes/gstack-plan-design-review": 129046,
"hermes/gstack-plan-devex-review": 115215,
"hermes/gstack-plan-eng-review": 134297,
"kiro/gstack-autoplan": 129040,
"kiro/gstack-design-consultation": 108323,
"kiro/gstack-design-review": 144362,
"kiro/gstack-design-consultation": 107064,
"kiro/gstack-design-review": 142935,
"kiro/gstack-land-and-deploy": 116533,
"kiro/gstack-plan-design-review": 140195,
"kiro/gstack-plan-design-review": 139890,
"kiro/gstack-plan-devex-review": 134431,
"kiro/gstack-plan-eng-review": 151330,
"kiro/gstack-review": 140562,
"openclaw/gstack-autoplan": 129520,
"openclaw/gstack-design-review": 133688,
"openclaw/gstack-design-review": 132261,
"openclaw/gstack-land-and-deploy": 116757,
"openclaw/gstack-plan-design-review": 128093,
"openclaw/gstack-plan-design-review": 127788,
"openclaw/gstack-plan-devex-review": 113968,
"openclaw/gstack-plan-eng-review": 132609,
"opencode/gstack-autoplan": 131640,
"opencode/gstack-design-consultation": 110616,
"opencode/gstack-design-review": 146746,
"opencode/gstack-design-consultation": 109357,
"opencode/gstack-design-review": 145319,
"opencode/gstack-land-and-deploy": 118762,
"opencode/gstack-office-hours": 100329,
"opencode/gstack-plan-design-review": 142125,
"opencode/gstack-plan-design-review": 141820,
"opencode/gstack-plan-devex-review": 136812,
"opencode/gstack-plan-eng-review": 153232,
"opencode/gstack-review": 142985,
"slate/gstack-autoplan": 129159,
"slate/gstack-design-consultation": 108402,
"slate/gstack-design-review": 144455,
"slate/gstack-design-consultation": 107143,
"slate/gstack-design-review": 143028,
"slate/gstack-land-and-deploy": 116589,
"slate/gstack-plan-design-review": 140275,
"slate/gstack-plan-design-review": 139970,
"slate/gstack-plan-devex-review": 134511,
"slate/gstack-plan-eng-review": 151405,
"slate/gstack-review": 140656
+4 -4
View File
@@ -413,7 +413,7 @@ do not launch the downstream skill or open a browser.`,
// eureka log, the office-hours lookup and the taste-profile read; measured
// 1.0834 (2026-09-30). + named design-direction defaults to avoid in the native
// subagent prompt (s02 H4a); measured 1.0911 (2026-10-02).
maxSizeRatio: 1.1694, // + GSTA-92 Phase 2: the craft tells join the catalog's slop bullets in the proposal section (Garry 2026-10-25 'Do it' (GSTA-92)); measured 105,683 / 90,375 = 1.1694 (2026-10-25), from 104,422 / 90,375 = 1.1554. + v1.91.19.0 wave: design --version probe (B4), taste-profile load (F3), verdict-form outside voice (INV-1), plus v1.91.17.0's round accounting and board/approval blocks (#3022) merged in; measured 100,446 / 90,375 = 1.1114 (2026-10-03). + Oct 6 fix wave A3/CEO-12: free text reaches commands only through agent-written mktemp files (mktemp block, write rule, path rebuild and refusal at each posting, prompt and brief site); measured 103,384 / 90,375 = 1.1439 (2026-10-06). + Oct 7 wave B6: Aside rules 1, 4, 6 and 7 (offer a target-origin tab before openTab, a second sign-in wall means a tab- or URL-bound session, no early-return hint, fast [ok without the sentinel is an abort); measured 103,576 / 90,375 = 1.1461 (2026-10-07). + Oct 7 wave B3: the Codex probe runs as a command (select-model capture lines, check-sandbox/probe-model/run-with-timeout subcommands) instead of being sourced; measured 103,522 / 90,375 = 1.1455 (2026-10-07). Both merged: measured 103,726 / 90,375 = 1.1477 (2026-10-07). + Oct 7 wave guard (CEO-20): conditional `run_in_background` request plus the background-run recovery at each foreground dispatch site; measured 103,466 / 90,375 = 1.1449 (2026-10-07). Merged with the guard area: measured 103,804 / 90,375 = 1.1486 (2026-10-07). + Honest Work wave merged (W2/W3 gate integrity in review/ship, W4 kernel + DONE line in every tier-2+ skill, W5/W7 in spec/ship): re-measured on the integrated tree (2026-10-10).
maxSizeRatio: 1.1600, // + v1.91.19.0 wave: design --version probe (B4), taste-profile load (F3), verdict-form outside voice (INV-1), plus v1.91.17.0's round accounting and board/approval blocks (#3022) merged in; measured 100,446 / 90,375 = 1.1114 (2026-10-03). + Oct 6 fix wave A3/CEO-12: free text reaches commands only through agent-written mktemp files (mktemp block, write rule, path rebuild and refusal at each posting, prompt and brief site); measured 103,384 / 90,375 = 1.1439 (2026-10-06). + Oct 7 wave B6: Aside rules 1, 4, 6 and 7 (offer a target-origin tab before openTab, a second sign-in wall means a tab- or URL-bound session, no early-return hint, fast [ok without the sentinel is an abort); measured 103,576 / 90,375 = 1.1461 (2026-10-07). + Oct 7 wave B3: the Codex probe runs as a command (select-model capture lines, check-sandbox/probe-model/run-with-timeout subcommands) instead of being sourced; measured 103,522 / 90,375 = 1.1455 (2026-10-07). Both merged: measured 103,726 / 90,375 = 1.1477 (2026-10-07). + Oct 7 wave guard (CEO-20): conditional `run_in_background` request plus the background-run recovery at each foreground dispatch site; measured 103,466 / 90,375 = 1.1449 (2026-10-07). Merged with the guard area: measured 103,804 / 90,375 = 1.1486 (2026-10-07). + Honest Work wave merged (W2/W3 gate integrity in review/ship, W4 kernel + DONE line in every tier-2+ skill, W5/W7 in spec/ship): re-measured on the integrated tree (2026-10-10).
},
cso: {
skill: 'cso',
@@ -751,10 +751,10 @@ do not launch the downstream skill or open a browser.`,
// No section-loading paid case yet: its touchfile row lives in
// test/helpers/touchfiles-data.ts, which #3413 owns.
behavioral: 'none',
maxSkeletonBytes: 139_202, // measured 139202 (2026-10-25): the craft-audit pointer and its index row, the Phase 8 tie-break, and the craft tells in Methodology category 9 (GSTA-92; Garry 2026-10-25 'Do it' (GSTA-92)).
minUnionBytes: 143_300, // measured union 146511
maxSkeletonBytes: 137_773, // measured 137773 (2026-10-25): the Section index the external-host carve test requires, its craft-audit row, and the STOP pointer (GSTA-92; Garry 2026-10-25 'Do it' (GSTA-92)).
minUnionBytes: 143_300, // measured union 145082
mustContain: ['AI Slop Detection', 'Fix Loop', 'gstack-design-craft', 'Proof beside each claim'],
maxSizeRatio: 1.3843, // first parity guard for design-review: measured 146511 / 105,842 = 1.3843 (2026-10-25), the skeleton's growth since v1.64.1.0 plus the craft audit (Garry 2026-10-25 'Do it' (GSTA-92)).
maxSizeRatio: 1.3708, // first parity guard for design-review: measured 145082 / 105,842 = 1.3708 (2026-10-25), the skeleton's growth since v1.64.1.0 plus the craft audit (Garry 2026-10-25 'Do it' (GSTA-92)).
},
// ── Token-reduction Phase 4 wave 4 (v1.69.x branch): design doctrine carve ──