Skip to content

Session handover — 2026-07-06 pt4

Continues from session-2026-07-06-pt3-handover.md (ADR-0009 Slice 2a — the emit/consume half). This half: executed ADR-0009 Slice 2b — the design-token capture half. One work commit on master: 196e180.

What changed — where to look

  • ADR-0009 Slice 2b (196e180) — capture() in lib/theme-extractor.js now records borderRadius/borderTopLeftRadius/boxShadow. Re-crawling garvanbay + WCP populates _raw, so lib/design-derive.js yields real bounded tokens and both lanes emit a real per-site override: garvanbay → --btn-radius:0px (square — faithful to the primary "Book an appointment" CTA; contact pills are 100px), --radius:0rem, flat shadows; WCP → --btn-radius:9999px (pill, measured 50px), --radius:0rem, flat. Slice tracker updated in decisions/0009-target-output-architecture.md (2b done; per-section classifiers split out as 2b-band).

  • Deploy gap found + fixed. design.json is gitignored (builds/*/*) and the CMS prebuild never generated it — so the override would have vanished on a fresh deploy. Since theme.json (with _raw) is committed, both emit lanes (apps/cms/scripts/emit-theme-css.mjs, lib/assembler-fulldev/emit.js) now derive tokens from theme._raw when design.json is absent — same "regenerated at prebuild" contract as global.css. design.json is now just an optional local-inspection cache.

  • Section-band rhythm deferred (pre-authorized scope escape). The rhythm measure read section:nth-of-type(2) — a typography selector — which is a 67px trust ribbon on WCP, not a content band. bandPaddingTop now gates on a content-band height floor (<200px → null) so a ribbon can't emit a bogus --section-py; this correctly distinguishes garvanbay's real 536px band (→ normal, no-op) from WCP's ribbon (→ omitted). New known-pattern: "section:nth-of-type(2) is often a trust ribbon". Robust representative-band selection is the same classification work Slice 3 needs → 2b-band.

Verified

  • End-to-end both lanes: CMS emit-theme-css.mjs run → override :root appends after the 4px/shadowed defaults (source-order wins). Per-site emit.js CSS logic reproduced → override after default. _raw fallback proven: override still emits with design.json deleted.
  • Zero-regression: emitSiteCss(null)===''; garvanbay derived theme byte-identical ex-_raw; garvanbay --section-py:3rem == default (no-op). lib/design-derive.validate.mjs 28/28.
  • WCP brand regression caught, not shipped — see below.

Real stop resolved (didn't ship a regression)

The WCP re-crawl flipped its derived brand logo(high, #183a6f) → surface(medium, #092b57) with no brand-code change — a re-crawl nondeterminism (a ctaAccent surface candidate outranked the higher-scored logo cluster). Not shipped: WCP's committed theme was preserved and only the new design _raw fields grafted on (derived theme back to identical). New known-issue "Brand-detection is nondeterministic across re-crawls" with the reopen contract: any capture re-crawl must diff the derived theme (not just _raw) before commit.

Next priority

ADR-0009 Slice 3 — thread section-style props into blocks, OR Slice 2b-band (per-section style records + classifiers + robust content-band selection) if we want the capture foundation before Slice 3. 2b-band and Slice 3 are coupled (the classifier's output shape is defined by what Slice 3 threads), so sequencing them together is reasonable. Start with the loudest gemini-diff dimensions: header/topbar treatment, alignment, image side, columns, density.

Carried/open (see known-issues): section-band selection deferred (2b-band); $card-sample reliability (--radius least-trustworthy token); brand-detection nondeterminism; 3 WCP logo-strip placeholders; cross-account sharding unproven; non-fatal Map/Prose/Features build errors; per-site static @source gap; per-site static noindex,nofollow launch blocker.