# Pipeline & Artifact Architecture — Upstream Formalization

**Run: section-research (R&D) · v1 · 2026-06-10 · for review**

**Purpose.** The architecture explainer compressed everything before client approval into one
"Author" box. This document opens that box: it formalizes the full upstream pipeline — research →
asset curation → product/copy/design definition → toolkit & mock prompt generation → approval —
as a chain of **stages, artifacts, and gates**, maps how each artifact feeds downstream consumers,
and identifies the missing artifacts and processes that would most improve reliability as the
pipeline evolves into the section-taxonomy / build-manifest world.

**Grounding.** (a) `playbook/graphic-craft-playbook.md` (the prescribed flow, Phases 0–5).
(b) Artifact survey of runs 002–016: which documents each run actually produced, their internal
structure, and where rework happened (this session). (c) The section taxonomy + architecture
explainer (this run), whose build-side artifacts (manifest, asset store, deviation ledger) this
document connects to.

---

## 0. TL;DR

- **Eight upstream stages** (U1 research → U8 mock & approval) formalized with an artifact
  registry: producer, consumers, format, gate, lock status.
- **Diagnosis 1 — the prose valley.** The pipeline is machine-readable at both ends
  (`findings.yaml`/`menu.yaml` with provenance; `spec.json`/`gt.json` at the build end) but pure
  prose in the middle (PRODUCT, COPY-BRIEF, DESIGN, SHAPE-BRIEF). Every literal the build needs —
  hex values, copy strings, font names, section lists — crosses the valley by hand. run-015 had to
  hand-write its `spec.json`; every run hand-translates DESIGN.md tables into CSS.
- **Diagnosis 2 — gates are judgment-only.** Today's gates show an artifact to the user. None run
  a mechanical check first. The explainer's "buildability lint" should be the *last* of a family
  of lints, one per artifact boundary.
- **Diagnosis 3 — direction risk lands late.** run-009 reset its entire direction at the
  page-mock stage and re-did toolkit + DESIGN + copy voice. run-016 quietly fixed this with cheap
  concept-probe batches *before* toolkit investment — formalize that as a stage.
- **The unifying rule: single source of truth.** Every literal that appears in a prompt, a mock,
  or CSS must trace to exactly one artifact. *If a hex code or a headline was typed into a prompt
  by hand, that is a pipeline bug.*
- **Top missing artifacts** (tier 1): `tokens.json`, `copy.yaml`, `page-plan.yaml` (the manifest
  skeleton), the lint family, and a run-wide lock/state protocol (generalizing run-016's
  BUILD-STATUS.md). The build manifest then becomes an **assembly**, not an authored document.
- **The element thread (§6):** build data never rides the image channel; stable element ids join
  copy → prompt → mock → manifest → DOM → report, with a closed-set mock-conformance gate +
  rejection sampling closing the prompt→image hop. Pilot metric: element round-trip integrity.
- **SETTLED (2026-06-10): concept-grade approval is the initial goal** — the client approves the
  cheap U8a page mock (passing a contract lint floor, with structured disclosures); the contract
  is design *intent*; U8b and all recovery/rebuild run post-freeze, machine-gated (contract chain,
  §6). Escalate grades only when evidence demands.
- **Two-pass authoring + autonomy (§6, twice refined in review):** the mock is the creative
  designer — elements stay IN generation; the manifest splits into declared (pre-mock) and
  measured (post-mock) fields; approval artifacts come in **grades** (concept → standard →
  hardened → pre-built; *harden the winner, not the candidates*); the accepted mock→build gap is
  governed by a **deviation rulebook**. Stated goal: fully autonomous post-approval — mechanical
  gates with rung-ladder fallbacks (degrade, never stall). Prompts are **versioned instruments**
  (M7): frozen templates + typed slots + per-run style lexicon + eval harness.

---

## 1. The pipeline as practiced today

Stages U1–U8 (upstream) + B (build). "U" avoids collision with archetype S-codes and lane L-codes.
Playbook mapping in parentheses.

| Stage | What happens | Artifacts produced | Gate today | Format |
|---|---|---|---|---|
| **U1 Research** | Scrape/compile business reality: existing site, Google Places, Instagram, Facebook, menu PDF | `findings.yaml` (incl. `discrepancies`/`gaps` keys), `menu.yaml` (with extraction provenance: pdf sha1, method, timestamp) | none (done once, pre-project) | ✅ structured |
| **U2 Asset curation** | Fetch + describe real photos | `assets/*/manifest.json` (file, attribution, bytes, visual description) | none | ✅ structured |
| **U3 Product definition** (playbook Phase 0) | Positioning, personality, anti-references, a11y bar — via `/impeccable teach` | `PRODUCT.md` | implicit approval | prose |
| **U4 Copy** (Phase 1 step 1) | Voice + per-section copy: headlines, body, CTAs, glossary, CTA inventory, a11y copy rules | `COPY-BRIEF.md` | ✅ two gates (direction, then draft) | prose |
| **U5 Visual identity** (Phase 1 step 2) | Concept probes → toolkit image(s) → lock. run-016 added probe *batches* + type-treatment specimens before locking | `brand-toolkit/*.png` (v1…vN, lock by filename convention) | ✅ approval gate | images |
| **U6 Design system** (Phase 1 step 3 + Phase 2 step 6) | DESIGN.md v1 authored from PRODUCT + locked toolkit; **v2 pinned after the page mock** reveals exact scale/spacing | `DESIGN.md` (OKLCH/hex tables, AA contrast rules, type scale as clamp(), spacing, components) | none (authored, not gated) | prose + embedded CSS |
| **U7 Page program** (Phase 1 step 4) | Confirmed brief via `/impeccable shape` + section order + per-section lane plan | `SHAPE-BRIEF.md` (lane table, photo shortlist, constraints) | brief confirmation | prose + table |
| **U8 Mock & approval** (Phase 2 step 5 + Phase 4 step 8b) | Two levels: **U8a** ONE continuous page mock (the consistency anchor; also the concept-grade artifact) → **U8b** per-section high-res mocks conditioned on U8a crops, *where needed* (these are the recovery sources) | `mockups/page-mock.png` → per-section probes → composite | ✅ approval gate + image rubric (a proto-lint!) | image |
| **B Build** (Phases 3–5) | Shell → per-section loop → assembly/critique/polish — and, in the target world, the explainer's recover→rebuild→verify→derive | `website/*`, `BUILD-STATUS.md` (run-016), `generation-log.md`, `page-elements.json` (post-build extraction) | section gates + critique ≥32 | mixed |

Cross-cutting today: `generation-log.md` (manual markdown table of every image generation),
toolkit/version filename conventions, `RUN_LOG.md` (cross-run).

**What the survey shows about maturity:** the five core docs converged across runs (PRODUCT,
COPY-BRIEF, DESIGN, SHAPE-BRIEF, + run-016's BUILD-STATUS); DESIGN tokens are *always* embedded
prose, never machine-readable; the only structured mid-pipeline artifact ever produced
(`spec.json`, run-015) was hand-written for the prototype; `page-elements.json` exists but only as
a *post-build* extraction.

---

## 2. Artifact registry — producers, consumers, contracts

The interaction map. **Bold consumers** are where reliability currently leaks (prose → hand
translation).

| Artifact | Produced by | Consumed by | Contract it must satisfy |
|---|---|---|---|
| `findings.yaml` | U1 | U3 (positioning facts), U4 (**verified-facts-only copy rule**), U2 (what to fetch) | facts current + sourced; discrepancies explicit |
| `menu.yaml` | U1 | U4 (menu copy), **S5 data binding at build**, mock greeking structure | true counts/lengths/prices; provenance |
| `assets/manifest.json` | U2 | U7 (photo shortlist), **Lane-2/S6 sourcing at build** | description + attribution per asset; (missing: rights, quality tier, approved-uses) |
| `PRODUCT.md` | U3 | U4 (voice), U5 (**personality → style words in toolkit prompts**), U6 (principles) | personality/anti-refs stable enough to survive to mock stage |
| `COPY-BRIEF.md` | U4 | U8 (**exact baked copy in mock prompts** — powers content-match detection D2), build (live text), **manifest copy fields** | per-element strings, case, length compatible with archetype + font |
| `brand-toolkit/*.png` | U5 | U6 (palette/type extraction), U8 (**style anchor in mock prompts**) | locked = the visual identity contract |
| `DESIGN.md` | U6 | B shell (**hand-translated to CSS custom properties**), U8 (**palette hexes re-typed into prompts**), component theming, **verify gate ΔE reference** | tokens exact, AA-verified, fonts tier-checked |
| `SHAPE-BRIEF.md` | U7 | U8 (mock composition), B (per-section lane confirm 8a) | every section has lane + asset plan |
| `page-mock.png` | U8 | THE client contract; B (conditioning crops, detection, verify reference) | buildable by construction (post-lint) |
| `generation-log.md` | all gen stages | reproducibility, audit | (missing: inputs/conditioning refs, machine format) |
| `BUILD-STATUS.md` | B (run-016) | session handoffs, gate audit | (missing: coverage of U-stages; standard schema) |
| `spec.json` → **build manifest** | hand-written (run-015) → **target: assembled** | detector (known copy/roles), rebuild, verify | should be a *projection* of upstream artifacts, not authored |
| `page-elements.json` | post-build parse | (today: nothing) | **opportunity: diff against the manifest = free structural verify** |

The pattern in one sentence: **U1/U2 produce data, U3–U8 produce prose and pixels, and the build
needs data again** — so every run pays a hand-translation tax at exactly the boundary where the
client contract makes errors expensive.

---

## 3. The formalized target flow

The change is not new stages — the stage order is right and battle-tested. The change is that
**each prose artifact gains a structured twin, each gate gains a mechanical lint, and the build
manifest becomes an assembly**:

```
U1 research ─────────► findings.yaml · menu.yaml · business.yaml*        [data layer]
U2 curation ─────────► asset-register.yaml* (extends manifest.json)
U3 product ──────────► PRODUCT.md (prose is fine — it feeds humans & prompts, not machines)
U4 copy ─────────────► COPY-BRIEF.md  +  copy.yaml*          ──┐
U5 identity ─────────► direction-brief* → toolkit lock         │ lint: copy*
U6 design system ────► DESIGN.md      +  tokens.json*        ──┤ lint: tokens*
U7 page program ─────► SHAPE-BRIEF.md +  page-plan.yaml*     ──┤ lint: plan*
U8a page mock ───────► page-mock.png — the CONSISTENCY ANCHOR ──┤ lint: structural conformance
U8b section mocks ───► per-section hi-res, conditioned on U8a    │ lint: element conformance
                       crops (where needed) → composite+manifest*┘       + buildability (explainer)
                          ▼ CLIENT APPROVAL = freeze(mock + manifest)
B build ─────────────► recover → rebuild → verify → derive   (architecture explainer)
                          verify consumes tokens.json (ΔE) + copy.yaml (exact match) + manifest geometry
```
\* = proposed (does not exist today). The **manifest = assemble(page-plan, copy, tokens,
asset-register refs, generation refs)** — if assembling it requires *reading prose*, an upstream
artifact is missing structure. That is the test.

Three structural rules:

1. **Single source of truth.** Every literal in a prompt, mock, or stylesheet traces to one
   artifact: hex → `tokens.json` · copy string → `copy.yaml` · fact → `findings/business.yaml` ·
   image → `asset-register`/generation id. Hand-typed literals are pipeline bugs.
2. **Gate = lint + judgment.** Every approval gate runs its mechanical lint first; the human
   judges taste, never catches mechanical errors. (The playbook's image rubric is the embryo of
   this — generalize it per artifact.)
3. **Prompts are derived, not authored.** A generation prompt is a template (from the pattern
   library) bound with values from artifacts: `prompt = pattern(archetype, tokens, copy,
   conditioning-asset)`. The generation log records the binding, making any image reproducible.

**Sequencing consequence of the two-source mock** (explainer §2): if conventional sections
(S5 menu, S9 chrome, S2 bands) enter the approval composite as *HTML screenshots*, then
`tokens.json` → shell CSS → conventional-section drafts must exist **before** U8 assembles the
composite. The playbook's Phase-3 shell partially moves ahead of the mock. (DESIGN v2's "pin after
the mock" survives for the *art* sections; tokens v1 must already be machine-firm for the drafts.)

---

## 4. Missing artifacts & processes — ranked gap list

Each gap names the observed failure it prevents.

### Tier 1 — needed for the wave-1 pilot (the contract machinery)

**M1 · `tokens.json`** — structured twin of DESIGN.md. Palette (role, OKLCH/hex, AA-checked
pairs), type scale, **per-font passport** (tier from the safety catalog, corrective facts,
cap-ratio, case rules), spacing scale.
*Consumers:* shell CSS generation, mock prompts (hex injection), component theming, **the
faithfulness gate's ΔE reference** — today the verify gate has no machine-readable ground truth.
*Prevents:* hand-translation drift; un-checkable palette claims. *Evidence:* every run
hand-translates; tokens have never existed as data.

**M2 · `copy.yaml`** — structured twin of COPY-BRIEF.md. Per section, per element: role, exact
string, case, **length budget**, data-binding (authored vs bound to `menu.yaml`/`business.yaml`),
fact-source refs.
*Consumers:* mock prompts (the known-copy assumption that powers content-match detection),
manifest, build text, **copy lint** (budget vs archetype + font at target size — the Entry-6
copy-balance failure caught at *author* time instead of build time).
*Prevents:* run-005's condense-copy-after-the-fact loop; the unknown-copy detection wall.

**M3 · `page-plan.yaml`** — structured twin/evolution of SHAPE-BRIEF.md, and the **manifest
skeleton**. Ordered sections: id, purpose, **archetype (S1–S10)** — this is where lanes evolve
into archetypes (lane remains the per-element asset technique *within* a section) — element
inventory (copy refs), asset refs, **bleed declarations, proxy declarations**, reflow intent,
target rung (A/B/C).
*Consumers:* mock composition, manifest assembly, plan lint (archetypes within the validated
grammar or explicitly flagged rung-B), build order.

**M4 · The lint family** — one mechanical pre-check per gate:
- *copy lint:* budgets vs archetype/font, case declared, facts trace to sources, voice rules
  (no em dashes etc.)
- *token lint:* AA pairs computed (not asserted), fonts on SAFE tier or flagged with fallback,
  cap-ratios present
- *plan lint:* every section has archetype + copy refs + asset plan; bleeds/proxies declared;
  unproven archetypes flagged with rung
- *prompt lint:* prompts are rendered from templates, never hand-written; required slots bound;
  banned tokens absent ("website", letterform-shape adjectives, empty-zone descriptions);
  corrective facts present iff the font passport requires them
- *mock/buildability lint* (already specified in the explainer): toolkit palette conformance,
  font check, dense-text greeked, proxies deliverable
*Prevents:* judgment gates silently absorbing mechanical errors. run-009's DESIGN contrast
contract was checked by hand; run-016's BUILD-STATUS logs gates but nothing verifies the artifact
content mechanically.

**M5 · Lock & state protocol** — generalize run-016's `BUILD-STATUS.md` innovation to the whole
run: one `RUN-STATE.md` (or extend BUILD-STATUS) tracking **every artifact's status
(draft / locked / superseded-by), lock timestamp, gate results**, from U3 onward. Artifact headers
carry the same status. The freeze ladder becomes explicit: copy lock → toolkit lock → tokens lock
→ plan lock → **mock freeze (client)**. Changes after a lock re-gate that artifact and cascade
consumers (the deviation-ledger discipline, moved upstream).
*Prevents:* the silent-supersession pattern (run-009's lodge artifacts lingering beside riso
ones); lost state across session handoffs — this project's recurring pain.

### Tier 2 — reliability multipliers (next)

**M6 · Direction gate (formalize run-016's fix).** A cheap stage between U3 and U5: N concept
probes in batches → user locks a `direction-brief` (chosen probe, named direction, anti-refs)
**before** any toolkit investment. *Prevents:* the run-009 cascade (direction reset at mock stage
= toolkit + DESIGN + copy voice all redone). Cost asymmetry is huge: probes are minutes; the
cascade was a day.

**M7 · Prompt template registry — prompts as versioned instruments.** *(Promoted toward tier 1
after review: prompt structure and language measurably change output — describing letterforms
beyond the font name worsens fidelity (run-007); verbose layout language degrades it (run-011);
"website" injects browser chrome — so hand-built prompts are an uncontrolled experimental
variable.)* Mechanisms: **frozen template phrasing with typed slots** bound from
`tokens.json`/`copy.yaml`/the asset register (custom variation contained to data, never prose);
a per-run **style lexicon** (the direction's style words, settled once at toolkit time and reused
verbatim in every section prompt — no re-paraphrasing across a run); template **version +
changelog + eval results** — phrasing changes go through the run-007-style matrix harness, never
ad hoc; and **telemetry**: `generations.jsonl` logs template-id@version + bindings, so production
outcomes accumulate per template version (continuous calibration, regression detection — "conformance
rate dropped after v3 → revert"). Embeds the hard-won rules (specimen-anchor +
corrective-facts-only, "hero banner" not "website", no-text negatives, per-font correctives).
Lives in `playbook/prompts/` (shared, like the font catalog).

**M8 · `asset-register.yaml`** — extends the curation manifest with: rights/attribution status,
quality tier, subject tags, **approved-uses** (gallery / story / hero), constraints as data
(run-009's "only `*_pro2.png` usable" was declared mid-run in prose), processed-variant lineage
(webp, alpha cutouts → source + recipe). *Consumers:* S6/Lane-2 sourcing, mock conditioning,
build.

**M9 · `business.yaml` + `reviews.yaml`** — binding-ready business data beyond the menu: hours,
address, phone, email, social, certifications — plus **curated review quotes** (attributed,
permission-flagged). Today hours/contact live semi-structured inside `findings.yaml`; testimonial
sections (S4/S1 quote bands) have **no source artifact at all**.

**M10 · `generations.jsonl` + kept-asset store** — automate the long-deferred generation log
(extend `generate-image.sh` to auto-append): id, file, provider/mode, prompt-template + bindings,
conditioning inputs, purpose ref (plan section/element), status (candidate/approved/superseded).
This *is* the explainer's asset store: the manifest references generation ids instead of bare
filenames. *Prevents:* unreproducible assets; orphan files; the "which toolkit version is locked?"
filename archaeology.

### Tier 3 — production-readiness (later)

**M11 · Client intake artifact.** U1 today assumes scraping an existing business. Production adds
a questionnaire (goals, must-haves, photo uploads, taste references) merged into findings. Also:
fact freshness policy (hours change).

**M12 · Manifest ↔ page-elements diff.** `page-elements.json` already extracts the built page's
section/element structure post-build. Once the manifest exists, `diff(manifest,
page-elements.json)` is a free structural verification — the build provably contains every
declared element. Keep emitting it; wire the diff into the verify stage.

---

## 5. What stays prose (deliberately)

Not everything should be structured. **PRODUCT.md stays prose** — personality and anti-references
feed human judgment and prompt *language*, not machine checks. **DESIGN.md and COPY-BRIEF.md keep
their prose bodies** for rationale, with the twin carrying the values (rule: *twin canonical for
values, prose canonical for why*). The structured twins are small files; the thinking stays where
thinking belongs. Recommendation: sibling files (`DESIGN.md` + `tokens.json`) rather than
front-matter — cleaner machine reads, and the .md stays human-first.

---

## 6. The element thread — hardening spec → prompt → mock → build

**The worry (raised in review):** the build depends on per-section element data that must be present
in the image-gen prompt *and* verified to exist in the generated mock; prompts are hand-built today,
and the mapping from "element in the mock" to "build instruction" is implicit. Decomposed, that is
two lossy hops (spec→prompt, prompt→image) and one unclear mapping (image→build).

**Principle: build data never rides the image channel.** The prompt carries only what the *image*
needs (strings to paint, style language, composition). Everything the *build* needs (roles, fonts,
bindings, budgets, archetype, recovery recipe) rides the manifest channel, which is lossless. The
only payload crossing the lossy generative channel is painted strings + geometry — both verifiable
on the far side.

### The element-ID thread

Every element gets a stable id (e.g. `hero.headline`) that is the join key across all artifacts:

| Checkpoint | Artifact | What the id carries there | Mechanical check |
|---|---|---|---|
| born | `copy.yaml` | exact string, case, role, font ref, budget | copy lint |
| declared | `page-plan.yaml` | membership in a section + archetype + zone intent | plan lint (every element has a copy ref) |
| bound | rendered prompt | the string, via template binding | **prompt lint: every declared string appears in the prompt text** (hop 1 = complete by construction) |
| painted | mock | pixels | **mock conformance gate** (below) |
| frozen | manifest | id + measured bbox + recovery recipe | manifest assembly check |
| built | DOM | `data-element-id` attribute on the node | structural verify: diff manifest ids ↔ DOM ids (evolves `page-elements.json`) |
| reported | faithfulness report | per-id pass/fail row | ship gate |

A disappearance at any hop is a diff failure at that boundary — not a delivery-time mystery.

### The mock conformance gate (hop 2)

Closed-set verification — we know exactly what to look for, which is why it is tractable
(run-014/015 evidence: 0 role errors, ~100% CTA grounding, *because copy is known*):

- per declared element: found? · copy fuzzy-match ≥ threshold? · exactly once? · legible? ·
  within its declared zone?
- **inverse sweep:** any text in the image NOT declared in the manifest → reject (catches garbled
  injections, the run-003 failure).
- per-class tolerances: display text = exact string · greeked data = structure only (row count,
  column anatomy) · graphics (badges/mascots) = VLM presence check, no string.
- **rejection sampling:** generate → check → re-roll up to a cap; only conformant candidates reach
  the human gate. Reliability cost becomes API cost. (The playbook's image rubric is the manual
  ancestor of this check — automate it per element.)
- keep per-generation element load small: per-section generation (~3–7 elements) is the proven
  regime; a one-shot full-page mock carrying ~30 elements is the high-risk path.

### Model predictability — there are no seeds on the default provider *(researched 2026-06-10)*

**Seeds:** the default model (`gemini-3-pro-image-preview` / nano banana pro) exposes **no seed and
no determinism control** — official Gemini API docs list only `aspectRatio`, `imageSize`
(512/1K/2K/4K), `responseModalities`, and `thinkingConfig`; third-party analysis confirms no
seed/`random_seed` exists (autoregressive architecture; internal randomness is uncontrollable).
Imagen on Vertex *did* support true seeds (watermark-off required) but is deprecated — **shutdown
2026-06-24** — so not a path. OpenAI's Images API historically exposes no seed either
(seed + `system_fingerprint` are Chat-Completions features); unconfirmed for `gpt-image-2` — one
probe call settles it. Note: even where seeds exist they buy *repeatability of a draw*, not
*control* — same seed + edited prompt does not mean "same design, one change," especially for
autoregressive models. So their absence costs regression-replay, not creative control.

**The levers we do have:**
1. **Pin everything pinnable, then record it.** Model ID (already pinned in `generate-image.sh`) +
   `imageSize` (**currently unset — defaults can drift under us; also the lever for the
   crop-resolution path: generate section mocks at 2K/4K**) + aspect + thinkingLevel (a testable
   adherence knob). Every generation logs its full config (→ M10 `generations.jsonl`).
2. **Golden canary battery — the seed substitute for regression testing.** A fixed probe suite
   (~10 prompts covering fonts, composition, untext, conformance) run before any big run / on
   provider updates; score it and compare distributions to baseline. Detects silent model drift
   mechanically ("same battery → same score distribution" replaces "same seed → same pixels").
3. **Conditioning is the composition anchor** (playbook Principle 1): img2img on a locked mock
   collapses composition variance; multi-image conditioning with the **locked toolkit as a style
   reference** in every section generation is a testable extension (the script already accepts
   repeated `--in`).
4. **N-sample statistics, not single rolls** (the run-011 method, formalized per template):
   conformance *rate* + spread; template changes judged on paired batteries with adequate N.
5. **One job per image — at the right level** (run-011's lesson generalized, refined in review):
   this does NOT mean skipping the full-page mock. The page mock (U8a) is the **consistency
   anchor** — global composition, section order, palette continuity, cross-section relationships —
   and is linted *structurally* (sections present, order right, archetype silhouettes), not
   per-glyph. The **element-fidelity burden** sits on the per-section conditioned regenerations
   (U8b, ~3–7 elements each, where needed) — never on one 30-element page generation. Consistency
   across U8b comes from conditioning on the ONE page mock's crops + the style lexicon + the
   toolkit style reference; generating sections independently was tried and rejected (drift).
6. **Prefer deterministic local ops over generative ops — within their envelope.** Local
   `cv2.inpaint` is *deterministic*, but run-014 (June 8–10 layer) found it **cannot reconstruct
   structured artwork behind text** (sun rays, scene art). Current best practice: color-keyed
   text-ink masks per element; flat backgrounds → local inpaint in the mask; structured art →
   AI-erased plate **composited onto the original only inside the mask** (the AI plate re-colors
   the whole image, so it is never used whole) — background fidelity outside text is bit-identical
   by construction, which retires the 14%-corruption gate for everything outside the mask.
7. **Rejection sampling** absorbs residual stochasticity in production (§ above); the **second
   provider** stays wired as a control arm for isolating provider-side drift.

Script TODO (extend shared `generate-image.sh`, per project rule): add `--res` (imageSize) now;
add a `--seed` passthrough only if the OpenAI probe confirms support.

### Two-pass authoring — approve outputs, not intentions *(corrected after review)*

An earlier draft proposed "taking elements out of generation" (render-composite). **Review
correctly rejected this:** for art-led sections the generation IS the designer — coordinates,
scale, color relationships, and art integration do not exist before the mock. Elements must be in
the design pass, because the model designs *with* and *around* them. The valid kernel lands
differently — as a reordering, not a removal:

1. **Design pass** — generate the section with all elements baked (manifest copy, SAFE fonts),
   conformance-gated with rejection sampling. The model invents the composition.
2. **Measure & adopt** — run the detect/measure machinery on the conforming candidate. The
   manifest's fields split by lifecycle: **declared** (pre-mock, from upstream: inventory, copy,
   fonts, bindings) and **measured/adopted** (post-mock, from the design: geometry, colors, line
   breaks, scale relationships). Measurement is how the model's design decisions become data.
3. **Fidelity pass** — untext → plate → rebuild the text elements as real HTML at the measured
   geometry → render (the proven hero flow). **Grade-conditional:** at concept/standard grades
   this pass IS the build and runs *post-freeze*, machine-gated; only at hardened/pre-built grades
   does it move pre-freeze for chosen sections. (This resolves the sequencing drift vs the
   explainer: the explainer's original order — recover/rebuild after approval — stands for the
   default path.)

**Where the fidelity pass applies is a dial, not a default** *(refined again after review:
fidelity-passing everything means the client approves a fully built site — strongest contract, but
slowest and most expensive, and wrong for cheap exploratory mocks such as three concept variants in
a pre-sales funnel)*. The approval artifact comes in **grades**, chosen per engagement moment and
per section:

| Grade | Artifact | Marginal cost | Use |
|---|---|---|---|
| **concept** *(settled initial goal — the default approval step)* | raw U8a page mock passing the **contract lint floor** + structured disclosures | minutes | funnel variants AND the normal approval; the contract is the **design intent** (see the contract chain below) |
| **standard** | U8a + the composite assembled from U8b section regenerations (full element conformance) | + U8b generation & checks | when intent-level contracting isn't enough; recovery sources are pixel-identical to what was approved |
| **hardened** | + fidelity-pass renders for the riskiest sections (usually the hero) | + recover/rebuild on those sections | demanding clients; sections where font/plate divergence would be most visible |
| **pre-built** | fidelity-pass everywhere | ≈ building the site | rare: final pre-launch sign-off |

The grades form a monotone ladder: **how much of the downstream build is pulled in front of the
freeze.** Concept pulls nothing; pre-built pulls everything.

Rule: **harden the winner, not the candidates.** Variants are concept-grade; only the chosen
direction pays for conformance and, selectively, fidelity. The **deviation budget scales with the
grade** — a concept-grade approval licenses a wider mock→build envelope, and the client-facing
language matches ("design concept", not "proof"). Where the fidelity pass IS applied it keeps its
benefits (font-metrics gap surfaces pre-contract; measurement error self-forgiving; plate quality
judged before approval). As recover/rebuild automates, the hardened grade gets cheaper — expect the
default to migrate upward over time. Elements stay out of generation only where the design system,
not the image model, is the designer (greeked S5/S9/S8 — the existing two-source split).

### The contract chain at concept grade *(settled 2026-06-10)*

**Settled direction:** if end quality proves true to the design intention, the **concept-grade
U8a mock is the approval step** — escalate grade only when evidence demands it. That makes three
things explicit:

**1. Three contract layers.** The freeze no longer freezes pixels for most of the page; it freezes
intent, and machines derive the rest:
- **U8a = the client contract (intent level):** composition, section order, palette, style,
  hierarchy, and correct display-tier copy. "True to the mock" at this grade means true to *these*.
- **U8b = the derived pixel contract:** each section regeneration is machine-accepted against its
  approved U8a crop (composition-fidelity gate — the background-fidelity diff generalized — plus
  element conformance). Once accepted, U8b is the pixel reference recovery runs on.
- **The build** is verified against U8b (pixel/structure, per archetype contract) AND against U8a
  (intent — the perceptual judge asking the client-notice question). The judge is the **binding
  automated reviewer**; no human sits between freeze and ship.

**2. The U8a contract lint floor** (what must be true before a concept mock may be client-shown as
a contract): headline-tier copy correct and legible · body-tier approximately right · dense text
greeked · all planned sections present, in order, with recognizable archetype silhouettes · palette
within token tolerance · proxies painted as deliverable · **structured disclosures attached** — an
`approval-record` (mock hash, manifest@freeze version, grade, disclosure ids) so the autonomous
Class-2 gate has something machine-readable to check against.

**3. Progressive manifest completion.** At freeze the manifest contains **declared** fields plus
only *coarse* U8a geometry (section bands, approximate zones). **Measured** fields are filled in
post-freeze from accepted U8b outputs and the build (`manifest@freeze` vs `manifest@build`; both
retained). Element ids are `sectionId.role` with page-unique section ids.

**Funnel inversion (supported, mock-first ordering).** For pre-sales variants, full U5/U6
investment per variant is too heavy: variants run on a direction-brief + light lexicon, each
carrying its declared manifest-lite (we author every prompt, so copy/fonts are known even for
funnel mocks). After a variant wins: lock the toolkit/tokens **from the winner** (palette values
extracted from the approved mock + linted; fonts already declared) — and the winner mock remains
the visual anchor. Never regenerate the winner "cleaner" post-win without re-approval; that is a
Class-3 change to the thing the client picked.

**What this puts on the critical path** (the wave-1 pilot's validation set): (1) U8a lint floor +
conformance on real page mocks; (2) **U8b composition-fidelity gate calibrated against human
perceived-sameness** — the single most load-bearing new gate, since generative ops now run
post-freeze with no human backstop; (3) detect/measure envelopes incl. the parked fine-position
tolerance; (4) rebuild + per-contract verify; (5) perceptual-judge calibration vs human
client-notice judgments; (6) end-to-end element round-trip integrity; (7) U8a geometry — aspect ×
`imageSize` × section count → crop adequacy (a 4K 2:3 page mock yields ~2.7K-wide section crops;
verify the model really delivers 4K and what it costs).

### Run-014's June 8–10 layer, folded in *(read directly 2026-06-10 — it advanced past the run-015 snapshot and the root handoff)*

1. **Masked AI-plate compositing** (above) — supersedes the blanket "prefer local inpaint" guidance.
2. **The perceptual judge already has a prototype:** `runs/run-014/scripts/grade_vision.py` — a
   cached 6-dimension 0–10 rubric (layout/typography/background/readability/color/overall +
   critique) comparing original vs rebuild screenshot, used to drive iterate-until-pass across 168
   cases (mean 7.10, 163/168 ≥ 7). The pilot adapts it (add the client-notice question + the
   human-agreement calibration) instead of building one. Lesson it already paid for: **stale grade
   caches + browser screenshot caches produce false grades — invalidate on every rebuild change.**
3. **Rebuild is N-element now** (`rebuild_all.py`), with font weight-shipping rules (single-weight
   display faces ship 400 — synthetic bold is too thick; true heavy-display ship 800; serif
   headlines lighter) → these become font-passport fields in `tokens.json`.
4. **Visual font identification is a negative result** (`results/fontid.json`: Inter→Oswald,
   Poppins→Anton, repeatedly) — hard confirmation of declare-don't-detect for fonts; appearance
   verification needs reference-image grounding (matches run-011's appearance-axis caveat).
5. Current detector scores for pilot calibration: certified 90.2% full-suite (oracle 94.4; T4
   touching-text 67%); N-element track 96.7%.

### The autonomy requirement

Stated goal (review, 2026-06-10): **fully autonomous after mock approval.** This reshapes gate
design:

- **Freeze as late as economics allow.** Every lossy or judgment-laden transformation (generation,
  conformance, measurement, plate, text rebuild) moves *before* the freeze via two-pass authoring.
  Post-freeze work reduces to assembly, data binding, responsive derivation, and interactivity —
  all rule-driven and mechanically verifiable. No human gates after approval.
- **Termination guarantee.** Any post-freeze gate that exhausts its retry cap auto-falls down the
  rung ladder (A → B → C). Rung C (the approved pixels as an image + mitigations) passes visual
  fidelity by construction, so the autonomous run can **degrade but never stall**; every fallback
  is flagged in the ship report.
- **Deviation classes get an autonomy policy:** Class 0/1 auto-accept + log · Class 2 auto-accept
  **only against standing disclosures pre-authorized at approval** (the disclosure set is part of
  the approval artifact) · Class 3 auto-fail → next rung; a section that somehow fails at rung C
  is parked and flagged for async review — never silently shipped.
- **Deviation rulebook — justified variation, encoded.** The accepted mock→build gap (review:
  "changes need to be well justified design-wise and executed well, staying close to the mock") is
  governed by a pre-enumerated rulebook of allowed transformations, each with trigger, bound, and
  design justification — e.g. re-break lines to kill a widow/orphan · size-to-zone refit within ±N% ·
  lift contrast to WCAG-AA within a ΔE budget on non-token colors · normalize spacing to the type
  scale · extend data per the approved pattern · reflow per archetype on mobile. The deviation
  ledger records *which rule* justified each change; the perceptual judge checks the execution.
  "Well justified" becomes citable policy instead of improvised judgment — which is exactly what
  lets the post-approval phase stay autonomous.
- **Autonomy critical path (validation prerequisites):** the parked fine-position/alignment
  tolerance work (run-010 §7.6) is now *blocking* — an autonomous rebuild cannot fall back to
  "ask the user to eyeball it"; plus measurement-skill envelopes (the T-tier ladders),
  conformance-checker calibration, and perceptual-judge calibration against human judgment.

### Residual risks (honest)

Checker accuracy degrades on heavily stylized display type (touching/warped lettering) — bounded
because the buildability lint controls how stylized baked text may get; model-invented *non-text*
additions need a VLM "anything unexpected?" sweep + the existing human gate; slow rejection-sampling
convergence on complex art is a visible cost signal, not a silent failure.

**Pilot metric:** *element round-trip integrity* — % of manifest elements reaching the built DOM
having passed every checkpoint — plus per-class generation-conformance rate and retries-to-conformant.

---

## 7. Decision registry — canonical (settled + open)

This section supersedes the explainer's §8 table as the single open-decisions registry.

**Settled 2026-06-10 (architecture review):**
- **S-A. Approval default = concept grade** (U8a + contract lint floor + disclosures); escalate
  grades only on evidence. Contract chain per §6.
- **S-B. Page-mock-first, always** (U8a is the consistency anchor); the HTML-screenshot two-source
  composite and pre-freeze fidelity passes are **hardened-grade options**, not the default —
  resolves explainer open decision #1.
- **S-C. Shell timing** (old Q3): under the concept-grade default the shell returns to post-freeze;
  `tokens.json` must still exist pre-U8a (prompts bind from it).
- **S-D. U8b runs post-freeze at concept grade** (old Q8), machine-gated against the approved U8a
  crop; "detail added by conditioned regeneration" is a deviation-rulebook entry.
- **S-E. Lanes ⊥ archetypes, stated once:** `section.archetype` = one of S1–S10 (section level);
  `element.lane` = asset technique (element level, within the section). The manifest data model
  uses both, at those levels.

**Open:**

1. **Twin drift:** when prose and twin disagree, lint flags it — but who reconciles, and does a
   twin edit require re-approval of the prose gate? (Proposal: twin edits re-run the lint only;
   prose edits after lock re-gate.)
2. **How much for the experiment vs production:** Tier 1 only for the wave-1 pilot; resist
   building Tier 2/3 before one page has flowed through the machinery end-to-end.
3. ~~Where the shell build lands~~ — **settled, see S-C.**
4. **`page-plan.yaml` vs SHAPE-BRIEF:** replace or twin? (Lean replace — SHAPE-BRIEF's prose
   content is thin once the plan is structured; the brief's north-star paragraph can live in
   PRODUCT.md or the plan header.)
5. **Schema ownership:** twins' schemas live in `playbook/schemas/` (shared, versioned) so runs
   stay comparable across the experiment series.
6. **Conformance-checker calibration:** how stylized can baked display type get before the OCR
   ensemble fails and a VLM-only read must carry the check — and what does that imply for the
   buildability lint's style ceiling? (Couples to the run-014 walls; measure in the pilot.)
7. **Explainer v1.2 revision (direction now settled, edit pending):** Fig 2's order (recover/
   rebuild after approval) **stands** for the default path; revise §2's two-source framing to
   "hardened-grade option," show the full A→B→C fallback chain + the rung-C failure path
   (park + flag), align S8 "cannot ship rung C" phrasing, and clarify the manifest as *assembled*.
8. ~~What does standard grade approve~~ — **settled, see S-A/S-D** (concept grade approves U8a;
   U8b is post-freeze and gated; standard grade = the composite, used only when intent-level
   contracting isn't enough).
9. **Disclosures / approval-record schema:** the structured artifact the autonomous Class-2 gate
   checks against (mock hash, manifest@freeze, grade, disclosure ids) — draft alongside the
   tier-1 twins.
10. **U8a geometry study:** aspect × `imageSize` × section count → crop adequacy and 4K
    availability/cost — measure in the pilot before fixing the page-mock spec.
11. **Perceptual-judge calibration protocol:** the judge is the binding automated reviewer at
    concept grade; define the human-agreement benchmark it must pass before it may gate alone.
12. **Funnel token extraction:** how much of `tokens.json` is extracted from the winning mock vs
    re-declared (palette extraction is easy; spacing/type scale less so) — pilot the mock-first
    ordering on one funnel-style variant.

**Doc-maintenance backlog** (from the 2026-06-10 audit; none load-bearing): unify mock naming
(U8a / U8b / composite) across all three docs · taxonomy §8 cross-references to the answers now in
this doc · state copy *provenance* ⊥ *recovery decision* as separate axes in the taxonomy text
policy · add skill dependency ordering to the explainer's inventory · propagate the
`sectionId.role` element-id scheme · note the run-014 wall names (touching text, small baked text)
explicitly in the explainer's S1 row · single-source audit lint folded into M4.

---

*Sources: `playbook/graphic-craft-playbook.md` (Phases 0–5, Appendices A–E, risk table) ·
artifact survey of runs 002–016 (this session) · `findings.yaml` / `menu.yaml` /
`assets/google-places/manifest.json` schemas · run-015 `spec.json` · run-016 `BUILD-STATUS.md` ·
this run's `SECTION-TAXONOMY.md` + `ARCHITECTURE-EXPLAINER.html`.*
