Skip to content

refactor(builder): slim the design layer — guidelines over machinery#177

Draft
yyyyaaa wants to merge 6 commits into
mainfrom
feat/builder-ui-design
Draft

refactor(builder): slim the design layer — guidelines over machinery#177
yyyyaaa wants to merge 6 commits into
mainfrom
feat/builder-ui-design

Conversation

@yyyyaaa

@yyyyaaa yyyyaaa commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

What

Slim the constructive-builder skill's design layer from machinery → guidelines.
The design compiler was already dead code on the live build path (the agent hand-authors the CSS),
so this deletes it and the contradictory "compiler writes your tokens" story, collapses three docs
into one lean opinionated guide, ships condensed anonymized exemplars, and keeps exactly one
functional gate
(the built globals.css must keep the shadcn token names + Tailwind-v4 wiring so
Blocks render).

Net: +291 / −5,484 lines.

Changes

  • Deleted (vestigial engine): wire-design.mjs; compile/oklch/invariants/fonts.mjs + their tests;
    the engine fixtures; references/design-system.md (587) + references/art-direction.md (400);
    the 6 heavy fixtures/design/*.md presets.
  • Added: scripts/lib/design/tokens.mjs (single-source 39-name shadcn contract);
    references/design-guide.md (232 lines — the one opinionated guide);
    references/examples/*.md (7 condensed exemplars: Graphite / Prism / Eclipse / Folio / Solaris /
    Concrete + the constructive opt-out).
  • check-design.mjs → functional Rail-2 validator only (366 → 188 lines).
  • Gate rewired onto the built globals.css; SKILL.md S6.5 removed; speedrun S6.5 → S7.5
    (after the skeleton); brief-grammar.md design block slimmed (lines 1–563 byte-unchanged).

Validation

  • Static GREEN: blocks-contract test; the functional gate proven bidirectional (valid → exit 0,
    dropped token → 1, broken @theme inline → 1); static canaries; single-model coherence across
    SKILL.md / speedrun / design-guide / brief-grammar.
  • Live stress test — 3 distinct apps built end-to-end via this skill (Eclipse dark console /
    Folio editorial / Prism colorful gallery), each design-gate pass + RLS round-trip pass. The
    thesis holds: agents authored genuine restructures from the guidelines, not recolors.

Known gaps — why this is a DRAFT (fix before merge)

  • GAP-1 (headline): scripts/lib/brief-policy.mjs KNOWN_DESIGN_PRESETS still lists the old
    preset names, so design: { preset: eclipse|folio|prism } — the exemplars design-guide.md
    advertises — hard-errors at provision. Fix: replace the set with the 7 shipped exemplar aliases.
  • GAP-2: check-design.mjs --app <root> resolves <root>/src/app/globals.css literally and does
    not probe packages/app (the verify-phase gate wraps it correctly; only the bare S7.5 command is off).
  • GAP-3 (pre-existing, not this layer): scaffold-frontend quick-add fills only the first required
    field → multi-required-field entities ship type-broken until hand-fixed.
  • Minor: live-qa.mjs doesn't auto-locate the brief under the cleome outer-dir layout (needs
    LIVE_QA_SPEC); the nested relation select:{} shape is undocumented (tsc catches it).

Stress-test scorecard: built 3/3 · design-gate 3/3 · live-QA 3/3 · hands-free 0/3 (the 0/3 is GAP-1 + GAP-2).

🤖 Generated with Claude Code

yyyyaaa and others added 6 commits June 19, 2026 23:28
Add a design.md→globals.css pipeline so any build can re-skin without breaking
shadcn/Blocks, with a "keep default constructive" opt-out.

Engine (scripts/lib/design/, zero-dep): oklch (convert + WCAG contrast + lightness
math), design-md (parse/serialize, reuses the existing YAML reader), invariants
(taste lint: ≤1 accent, sat<80%, no pure black, AI-purple ban, contrast pairs),
compile (role→shadcn remap + missing-var synthesis + .dark derivation + AA contrast
repair, never throws), fonts (next/font allowlist) + 50 unit tests.

Applier: scripts/wire-design.mjs — idempotent --dry-run codemod overriding :root/.dark
token VALUES after the Blocks @import (restyles template + Blocks at once); optional
layout.tsx font/defaultTheme + branding.ts; preset:constructive = no-op.
scripts/check-design.mjs — lint gate that fails an un-themeable design (no green-wash).

Presets: fixtures/design/{constructive,minimalist,editorial,soft,brutalist,playful}.md
+ compiler fixtures.

Docs/grammar: references/design-system.md (dials + invariants + compile contract),
brief-grammar `design:` block + auto-propose default, brief-policy validateDesign,
SKILL.md S-step + phase docs.

Layout taste (scope B): scaffold-frontend mandatory loading/empty/error states +
dial-driven density (generic, no entity literals).

Verify: design rot-canary wired into verify-gates.sh / verify-phase.sh /
genericity-check.sh.

Static gates green (50/50 tests, 6 presets lint clean, structural-safe, idempotent).
Adversarially reviewed + fixed. Live Chrome-QA acceptance pending.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RLz6M63G5vFJDZhW7dwBtb
…gnment, opt-out canary

Follow-ups from live acceptance (two themed builds, both PASS end-to-end):

- scaffold-frontend.mjs: resolve layout density from brief.design.dials.density →
  emitted design.md dials fallback → cozy default, so an auto-proposed theme's density
  "just works" regardless of where the agent recorded the dial (generic, zero-dep, idempotent).
- references/design-system.md §8: document density as emit-time baked Tailwind spacing
  literals (comfortable/cozy/compact), NOT a runtime data-density attribute; state
  brief.design.dials as the single source of truth + the emit-once/re-emit note.
- references/brief-grammar.md: note the zero-dep YAML reader has no folded/literal block
  scalars — design.brief must be a single-line quoted string.
- fixtures/design/constructive.md: add `preset: constructive` so the opt-out design.md
  self-identifies → `wire-design --design …/constructive.md` is a byte NO-OP. Fixes the
  design rot-canary's opt-out assertion (was a pre-existing canary defect, not a regression).

Verified: 50 design unit tests pass; all 6 presets lint clean; density resolves across all
4 paths; rot-canary opt-out=no-op AND editorial-applies both pass. Live acceptance:
owner+auto-propose (focus-green) and public-read+editorial (terracotta) each built <10min
warm, Chrome-QA light+dark, contrast AA/AAA, flows through RLS, independent evaluator PASS.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RLz6M63G5vFJDZhW7dwBtb
…per the design.md

Fixes the gap that themed variants were re-skinned but STRUCTURALLY identical (same icon-sidebar +
centered card-over-list). Adds GUIDANCE so agent builders vary an app's art direction (shell + page
composition) per the agreed design.md, while preserving the functional contract. Guidance-only (no
worked examples / no new scaffold templates); auto-propose now proposes STRUCTURE by default.

- references/art-direction.md (NEW): the two-layer model — the PRESERVE checklist (the <entity>-*
  testid family + -empty/-select/social-btn- conventions + row-scoping/interactability +
  hooks/selection/refetch/Stack-pushes + RLS scoping per policy + flow route/block/manifest/sentinels
  + provider order/auth-bridge + static gates) vs the FREE presentational layer; the dial->structure
  mapping (VARIANCE->shell boldness, DENSITY->table-vs-card/rhythm); an archetype palette (shells x
  compositions) as guidance; the edit seams (app-shell.tsx frame + hideSidebar, the entity-page
  return block, the width clamp); the consistency-to-design.md rule (recorded in an art_direction
  block for turn-to-turn consistency); safety rails + a self-verify step.
- SKILL.md: a rule granting the latitude + cross-ref. design-system.md: VARIANCE drives structure;
  auto-propose proposes structure too; the optional design.art_direction block; the CSS-compiler
  (token-only) vs frontend-structure distinction. brief-grammar.md: the art_direction block.
- entity-page.tsx + entity-page.mjs: PRESENTATION/PRESERVE seam-marker comments (comments only).

Validated (ultracode): independent verify — the preserve checklist is ACCURATE vs the real code,
gates green. LIVE — an agent restructured the owner canary into a top-nav + data-table + compact
"console-teal" dense dashboard (maximally distinct from the default mold); pnpm build green; live-qa
OVERALL PASS (full CRUD-through-RLS on the data-table); theme light+dark; theme toggle works.
Independent eval PASS: brokeTheMold + contractHeld + coherentWithDesignMd + guidanceSufficient
(rules alone sufficed, no worked examples). Bold restructure ~12min (vanilla path unaffected).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RLz6M63G5vFJDZhW7dwBtb
…ontend

The token-compiler + enforced invariants produced correct-but-generic output (variants were
re-skinned, not art-directed). Pivot: the design.md is the FULL design spec; the agent AUTHORS the
frontend from it; Blocks compose; only two hard rails remain. Taste comes from authoring, not
token-swapping a generic template.

- compile.mjs: faithful design.md -> shadcn token block — drop the override-surface creative-cap +
  the contrast-clamp; custom tokens/fonts pass through; keep synthesis (Rail 2) + .dark derivation.
- check-design.mjs: default = hard-validate ONLY the Blocks/shadcn-token contract (40 names +
  @theme/@source/@custom-variant wiring) + ADVISORY warnings for contrast/AI-purple/>1-accent (never
  clamp/fail); add a Blocks-contract validator over an app globals.css; --strict opt-in.
- invariants.mjs: taste rules -> advisory (warn/info); only missing-primary stays an error.
- references/art-direction.md + SKILL.md: the frontend phase is now "scaffold the functional
  skeleton, then AUTHOR the presentation tastefully from the design.md" (taste-skill playbook; Blocks
  as ingredients; Rail 1 preserved). scaffold-frontend/templates: comment/seam-marker only (SHA-proved).
- design-system.md + brief-grammar.md + fixtures/design/*: the design.md is now a rich, opinionated
  spec (atmosphere/dials/type-scale/spacing/components/ornament/prose/banned-patterns); presets upgraded
  from thin token sets to distinctive art directions; auto-propose generates it richly by default.

Two hard rails only: (1) functional contract (testids/hooks/RLS/Blocks-mounts — unchanged),
(2) shadcn-token contract (names + Tailwind wiring, so Blocks render). Everything else = design.md-
authored, free.

Static-verified: 65 design tests pass; faithful emit + custom-token pass-through; Blocks-contract
validator catches a dropped name / broken @theme and passes a good globals.css; contrast/anti-slop now
advisory; functional gates (Rail 1) intact. Live taste validation pending (next wave).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RLz6M63G5vFJDZhW7dwBtb
…umeric test refs, token canary

Follow-ups from the pivot's static verify + the live TASTE proof (build PASS):
- design-md.mjs: tolerate unquoted CSS-function color values (oklch/oklab/lch/lab/rgb/hsl/hwb/
  color-mix/color/var/calc/gradient) — a hand-authored `primary: oklch(0.55 0.34 280)` no longer
  splits on internal commas into a false missing-primary. Balanced-paren aware; idempotent on already-
  quoted values. +4 unit tests (65 -> 68).
- references/art-direction.md: the stale "50 design unit tests" literal -> non-numeric phrasing.
- fixtures/design/editorial.md: a tokens: block (custom props) so the design.md -> parse -> compile
  pass-through is rot-canaried end-to-end.

Live taste proof (scratch build, independent eval PASS): an agent authored a rich design.md
("Margins — a reading room": Spectral serif + iron-gall ink + ~190 lines hand-written component CSS +
an authored editorial entity page + an auth title-page) and the evaluator returned genuinelyTasteful +
distinctiveVsOldGeneric + blocksCompose + contractHeld + pivotValidated — a clear step-change from the
old generic output, with live-qa CRUD-through-RLS PASS and Blocks still composing. (Authoring took
~62min — the real cost of genuine taste vs the ~10min token-swap path.)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RLz6M63G5vFJDZhW7dwBtb
The design compiler was already dead code on the live build path (the agent
hand-authors the CSS), so delete it and the contradictory "compiler writes your
tokens" story. Collapse design-system.md + art-direction.md into one lean
design-guide.md, ship 6 anonymized real-world exemplars + the constructive
opt-out, and keep ONE functional gate (check-design.mjs --app validates the
built globals.css still defines the shadcn token names + Tailwind-v4 wiring so
Blocks render — functional, not stylistic).

- DELETE: wire-design.mjs; compile/oklch/invariants/fonts.mjs + their tests;
  the engine fixtures; design-system.md; art-direction.md; 6 heavy presets
- ADD: scripts/lib/design/tokens.mjs (single-source 39-name contract);
  references/design-guide.md (232 lines); references/examples/*.md (7 exemplars)
- check-design.mjs -> functional Rail-2 validator only (366 -> 188 lines)
- gate rewired onto the BUILT globals.css (verify-gates.sh / genericity-check.sh)
- SKILL.md S6.5 removed; speedrun S6.5 -> S7.5 (after the skeleton); brief-grammar
  design block slimmed (lines 1-563 byte-unchanged); density read + all rails kept

Net +291 / -5484. Verified GREEN: blocks-contract test, gate bidirectional proof
(valid->0, dropped-token->1, broken-wiring->1), static canaries, single-model coherence.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant