Files
whynot-design/designbook/preview/page-prototype-detail.html
tegwick 0d688ca94a feat(designbook): technology-neutral IR + stack-adapter pipeline (WHYNOT-WP-0002 T01-T06)
Author the design language once in the canonical React designbook and project it
one-way onto each stack: React -> designbook/ -> ir/ -> adapters/<stack>/.

Phase 0 — contracts & governance (T01-T03):
- ir/SCHEMA.md + ir/schema/{component,tokens}.schema.json — neutral IR contract
  (W3C DTCG tokens; React prop -> HTML attribute mapping; non-portable props flagged).
- adapters/ADAPTER_CONTRACT.md — inputs, drift-report + parity-result shapes,
  idempotency rules, CI exit codes (0 ok / 2 usage / 3 drift / 4 parity / 5 internal).
- .claude/rules/designbook-propagation.md + DesignSystemIntroduction.md §5.1 —
  one-way directionality + drift-resolution workflow.

T04 — canonical React designbook + the missing pull tool:
- The bundled /design-sync skill only PUSHES repo->cloud; it cannot populate
  designbook/. Added scripts/designbook_pull.py + `make designbook-pull`, which drives
  the local claude binary headless (acceptEdits) so DesignSync fetch+write runs in a
  subprocess (contents never hit the orchestrator's context). Pulled 44 files;
  excludes the _whynot-design-seed/ self-copy. Corrected the docs that wrongly called
  /design-sync the pull.

T05 — IR extractor (scripts/ir-extract.mjs + `make ir`):
- ir/tokens.json (80 tokens, DTCG, var() -> {ref} alias resolution); ir/components/*.json
  (10 contracts parsed from .jsx signatures: enum/boolean/number inference, prop->attr
  map, style/callback marked non-portable); ir/exemplars/*.

T06 — Lit token adapter (adapters/lit/ + `make adapt-lit`):
- Full-gen tokens into src/styles/colors_and_type.css :root (marker-bounded, idempotent
  no-op on re-run; hand-authored type CSS preserved).

NOTE: token regen synced Lit to canonical React — fonts IBM Plex -> system stacks and 8
status tokens added. This is a VISUAL change: review and run `pnpm test:visual:update`
before merge. Remaining: T07 scaffold+drift, T08 parity, T09 runbook, T10 2nd-adapter.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 12:36:24 +02:00

159 lines
12 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!doctype html>
<!-- @dsCard group="Pages" name="Pages · Prototype detail" subtitle="Single prototype · pipeline, learning question, signal sidebar" viewport="1280x860" -->
<html lang="en">
<head>
<meta charset="utf-8">
<title>WNO-017 · Prototype</title>
<link rel="icon" href="../assets/whynot-logo.png">
<link rel="stylesheet" href="../colors_and_type.css">
<style>
html, body { margin: 0; background: var(--paper); color: var(--fg-1); }
a { color: inherit; }
.nav { height: 60px; display: flex; align-items: center; gap: 24px; padding: 0 40px; border-bottom: 1px solid var(--line); }
.nav .brand { display: flex; align-items: center; gap: 10px; }
.nav .brand img { width: 22px; height: 22px; }
.nav .brand .nm { font: 500 15px var(--ff-sans); letter-spacing: -0.01em; }
.nav .brand .slug { font: 400 12px var(--ff-mono); color: var(--fg-3); }
.nav .right { margin-left: auto; display: flex; align-items: center; gap: 16px; }
.nav .search { font: 400 12px var(--ff-mono); color: var(--fg-3); border: 1px solid var(--line); border-radius: var(--r-1); padding: 6px 10px; min-width: 220px; display: flex; gap: 8px; }
.nav .search .kbd { margin-left: auto; border: 1px solid var(--line); border-radius: 2px; padding: 0 5px; font-size: 10px; }
.app { display: grid; grid-template-columns: 220px 1fr; min-height: calc(100vh - 60px); }
/* left nav */
.leftnav { display: flex; flex-direction: column; gap: 28px; padding: 28px 8px 24px 16px; border-right: 1px solid var(--line-soft); }
.leftnav .section { display: flex; flex-direction: column; gap: 8px; }
.leftnav .lbl { font: 500 11px/1 var(--ff-mono); letter-spacing: 0.08em; text-transform: uppercase; color: var(--fg-3); padding-left: 12px; opacity: 0.7; }
.leftnav .items { display: flex; flex-direction: column; gap: 1px; }
.leftnav .item { display: flex; align-items: center; gap: 10px; padding: 6px 10px; border-left: 2px solid transparent; color: var(--fg-3); font: 400 13px var(--ff-sans); text-decoration: none; }
.leftnav .item .ic { width: 16px; height: 16px; stroke: currentColor; stroke-width: 1.5; fill: none; flex: none; }
.leftnav .item .n { margin-left: auto; font: 400 11px var(--ff-mono); color: var(--ink-5); }
.leftnav .item.active { color: var(--fg-1); font-weight: 500; border-left-color: var(--ink); }
.leftnav .item.doc { font: 400 12px var(--ff-mono); }
.leftnav .footer { margin-top: auto; display: flex; align-items: center; gap: 8px; padding: 0 12px; font: 400 11px var(--ff-mono); letter-spacing: 0.06em; text-transform: uppercase; color: var(--fg-3); }
.leftnav .footer .dot { width: 5px; height: 5px; border-radius: 999px; background: var(--ink-4); }
/* main */
.main { padding: 40px 56px 80px; max-width: 980px; }
.crumb { font: 400 12px/1.5 var(--ff-mono); color: var(--fg-3); margin-bottom: 22px; display: flex; gap: 7px; }
.crumb a { text-decoration: none; color: var(--fg-2); }
.crumb .sep { color: var(--ink-5); }
.crumb .cur { color: var(--fg-1); }
.head { display: flex; flex-direction: column; gap: 10px; margin-bottom: 36px; }
.head .eyebrow { font: 500 11px/1 var(--ff-mono); letter-spacing: 0.08em; text-transform: uppercase; color: var(--fg-3); }
.head .row { display: flex; align-items: flex-start; gap: 24px; }
.head h1 { font: 400 32px/1.2 var(--ff-sans); letter-spacing: -0.02em; margin: 0; flex: 1; max-width: 22ch; }
.head .actions { display: flex; gap: 8px; flex: none; padding-top: 4px; }
.btn { font: 500 13px var(--ff-sans); padding: 9px 15px; border-radius: var(--r-2); border: 1px solid var(--line); background: var(--paper); color: var(--ink); cursor: pointer; display: inline-flex; align-items: center; gap: 8px; white-space: nowrap; }
.btn:hover { border-color: var(--ink); }
.btn .ic { width: 14px; height: 14px; stroke: currentColor; stroke-width: 1.5; fill: none; }
.btn.primary { background: var(--ink); color: var(--paper); border-color: var(--ink); }
.btn.primary:hover { background: var(--ink-2); }
/* pipeline */
.pipeline { display: grid; grid-template-columns: repeat(5, 1fr); margin: 0 0 40px; }
.pstage { padding: 10px 12px 14px; border-top: 2px solid var(--line); display: flex; flex-direction: column; gap: 4px; position: relative; }
.pstage.done { border-top-color: var(--ink); }
.pstage.active { border-top-color: var(--hi-2); }
.pstage .num { font: 500 10px/1 var(--ff-mono); letter-spacing: 0.1em; text-transform: uppercase; color: var(--fg-3); }
.pstage.done .num, .pstage.active .num { color: var(--fg-1); }
.pstage .nm { font: 500 13px/1.25 var(--ff-sans); color: var(--fg-1); }
.pstage.pending .nm { color: var(--fg-3); }
.pstage .meta { font: 400 11px/1.3 var(--ff-mono); color: var(--fg-3); }
.pstage .arrow { position: absolute; top: -8px; right: -7px; font: 400 14px var(--ff-mono); color: var(--ink-5); }
.pstage.done .arrow, .pstage.active .arrow { color: var(--ink); }
.body { display: grid; grid-template-columns: 1.4fr 1fr; gap: 48px; }
.col { display: flex; flex-direction: column; gap: 24px; }
.field { display: flex; flex-direction: column; gap: 7px; }
.field .k { font: 500 11px/1 var(--ff-mono); letter-spacing: 0.08em; text-transform: uppercase; color: var(--fg-3); }
.field .v { font: 400 15px/1.6 var(--ff-sans); color: var(--fg-1); max-width: 56ch; }
.aside { display: flex; flex-direction: column; gap: 20px; }
.arow { display: flex; flex-direction: column; gap: 7px; }
.arow .k { font: 500 11px/1 var(--ff-mono); letter-spacing: 0.08em; text-transform: uppercase; color: var(--fg-3); }
.arow .v { font: 400 14px/1.5 var(--ff-sans); color: var(--fg-1); }
.tag { align-self: flex-start; font: 500 10px/1 var(--ff-mono); letter-spacing: 0.1em; text-transform: uppercase; padding: 5px 10px; border-radius: 999px; background: var(--ink); color: var(--paper); }
.dot { display: inline-flex; align-items: center; gap: 6px; font: 500 11px/1 var(--ff-mono); letter-spacing: 0.06em; color: var(--fg-2); }
.dot .b { width: 8px; height: 8px; border-radius: 999px; background: var(--ink); }
.mono { font-family: var(--ff-mono); }
.caveat { border: 1px dashed var(--line-strong); border-radius: var(--r-2); padding: 16px; margin-top: 4px; }
.caveat .k { font: 500 11px/1 var(--ff-mono); letter-spacing: 0.08em; text-transform: uppercase; color: var(--fg-3); display: block; margin-bottom: 8px; }
.caveat .v { font: 400 13px/1.55 var(--ff-sans); color: var(--fg-2); margin: 0; }
</style>
</head>
<body>
<nav class="nav">
<div class="brand"><img src="../assets/whynot-logo.png" alt=""><span class="nm">whynot</span><span class="slug">/ control</span></div>
<div class="right">
<div class="search"><span>Search…</span><span class="kbd">⌘ K</span></div>
</div>
</nav>
<div class="app">
<nav class="leftnav">
<div class="section">
<span class="lbl">Work</span>
<div class="items">
<a class="item"><svg class="ic" viewBox="0 0 24 24"><path d="M22 12h-6l-2 3h-4l-2-3H2"/><path d="M5.45 5.11 2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z"/></svg><span>Inbox</span><span class="n">7</span></a>
<a class="item active"><svg class="ic" viewBox="0 0 24 24"><path d="M14 2v6a2 2 0 0 0 .245.96l5.51 10.08A2 2 0 0 1 18 22H6a2 2 0 0 1-1.755-2.96l5.51-10.08A2 2 0 0 0 10 8V2"/><path d="M6.453 15h11.094"/><path d="M8.5 2h7"/></svg><span>Prototypes</span><span class="n">4</span></a>
<a class="item"><svg class="ic" viewBox="0 0 24 24"><path d="M22 12h-2.48a2 2 0 0 0-1.93 1.46l-2.35 8.36a.5.5 0 0 1-.96 0L9.24 2.18a.5.5 0 0 0-.96 0l-2.35 8.36A2 2 0 0 1 4 12H2"/></svg><span>Signals</span><span class="n">12</span></a>
<a class="item"><svg class="ic" viewBox="0 0 24 24"><path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/><path d="M22 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/><path d="M9 11a4 4 0 1 0 0-8 4 4 0 0 0 0 8z"/></svg><span>Betas</span><span class="n">1</span></a>
<a class="item"><svg class="ic" viewBox="0 0 24 24"><path d="M9 11l3 3L22 4"/><path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"/></svg><span>Decisions</span><span class="n">3</span></a>
</div>
</div>
<div class="section">
<span class="lbl">Control docs</span>
<div class="items">
<a class="item doc"><span>INTENT.md</span></a>
<a class="item doc"><span>OPERATING_MODEL.md</span></a>
<a class="item doc"><span>PROTOTYPE_PIPELINE.md</span></a>
</div>
</div>
<div class="footer"><span class="dot"></span><span>A1 · Incubating</span></div>
</nav>
<main class="main">
<div class="crumb"><a href="#">whynot</a><span class="sep">/</span><a href="#">Prototypes</a><span class="sep">/</span><span class="cur">WNO-017</span></div>
<div class="head">
<span class="eyebrow">WNO-017 · Prototype</span>
<div class="row">
<h1>A LEGO-brick mood board for engineers who dont think in mood boards.</h1>
<div class="actions">
<button class="btn"><svg class="ic" viewBox="0 0 24 24"><path d="M21 8v13H3V8"/><path d="M1 3h22v5H1z"/><path d="M10 12h4"/></svg>Park</button>
<button class="btn primary"><svg class="ic" viewBox="0 0 24 24"><path d="M5 12h14"/><path d="M12 5l7 7-7 7"/></svg>Promote → Helix</button>
</div>
</div>
</div>
<div class="pipeline">
<div class="pstage done"><span class="num">Stage 0</span><span class="nm">Raw idea</span><span class="meta">inbox/</span></div>
<div class="pstage done"><span class="num">Stage 1</span><span class="nm">Triage</span><span class="meta">2026-02-15</span><span class="arrow"></span></div>
<div class="pstage done"><span class="num">Stage 2</span><span class="nm">Prototype card</span><span class="meta">prototypes/</span><span class="arrow"></span></div>
<div class="pstage done"><span class="num">Stage 3</span><span class="nm">Experiment</span><span class="meta">closed 2026-03-04</span><span class="arrow"></span></div>
<div class="pstage active"><span class="num">Stage 4</span><span class="nm">Signal review</span><span class="meta">in progress</span><span class="arrow"></span></div>
</div>
<div class="body">
<div class="col">
<div class="field"><span class="k">Learning question</span><span class="v">Will engineers attach metaphors to their tickets, and do those metaphors help anyone else read the work later?</span></div>
<div class="field"><span class="k">Smallest useful test</span><span class="v">A Slack bot in three teams for two weeks. One command attaches a “brick” — a one-line metaphor — to any ticket.</span></div>
<div class="field"><span class="k">Expected signal</span><span class="v">At least one team voluntarily keeps using the bricks after the two weeks, or references a brick in a review without being prompted.</span></div>
<div class="field"><span class="k">Risks</span><span class="v">Cute but unused after a week. Or: engineers treat it as a chore rather than a shortcut.</span></div>
</div>
<aside class="aside">
<div class="arow"><span class="k">Stage</span><span class="tag">Signal review</span></div>
<div class="arow"><span class="k">Signal</span><span class="dot"><span class="b"></span>S3 · Strong</span></div>
<div class="arow"><span class="k">Promotion target</span><span class="v mono">→ Helix</span></div>
<div class="arow"><span class="k">Audience</span><span class="v">Engineering teams already writing terse tickets.</span></div>
<div class="caveat"><span class="k">Caveat</span><p class="v">Strong signal is not a decision. Promotion to Helix still requires an explicit record in <span class="mono">DECISIONS.md</span>.</p></div>
</aside>
</div>
</main>
</div>
</body>
</html>