Capability backlog — the identify → build → available process (ADR-0009)¶
The living output of a process, not a to-do list: the reference sites are forcing functions that expose where the pipeline is blind to, or wrong about, a real Wix feature — so we build each capability once (generic, portfolio-wide) and it is there for all ~1800 sites. This doc records the process, the gaps it has surfaced, and their classification.
Reframed 2026-07-11: the goal is not to hand-tune a reference site's scorecard number up (per-site polish teaches the pipeline nothing). The goal is a process that identifies missing capabilities and makes them available in the pipeline. This operationalises ADR-0009's "GenericSection as a matcher-gap detector" idea and generalises it to every failure mode.
Sibling docs: ADR-0009 (the architecture), the near-match scorecard contract (how we measure fidelity — the identify machinery lives here), the section-style contract and aesthetic token contract (what we emit).
The process (read this first)¶
Four steps, run per reference site and — once the detectors below exist — automatically on every site and the Slice-6 batch:
- Identify — surface the gap. Today's machinery (see below) catches some classes automatically; the rest are caught by a manual audit (the first ran on WCP 2026-07-11).
- Classify each gap by:
- Process coverage — does the machinery flag it today, or does it fail silently?
- Owning stage — capture / matcher / render (adapter or canonical block) / asset-acquisition.
- Class — build-now (structure/render, asset-source-agnostic) vs asset→API (needs the real asset; defer to the Wix API asset plane, ADR-0009 Slice 5).
- Build the generic capability (or defer it to the asset plane).
- Available — the capability serves the whole portfolio, verified via the built artifact, not the wiring.
Reconciliation principle (from ADR-0009): fidelity comes from measured parameters; scale comes from bounded contracts. A capability is only "built" when it maps back to an editable block/prop — never a per-site bespoke hack.
The reference-site ladder¶
Three sites, increasing difficulty, each a forcing function for a tranche of capabilities. Shippable here means "the pipeline reproduces the site's real features" — not a hand-tuned seed.
| Site | Difficulty | Role |
|---|---|---|
| garvanbay | easy | baseline; proves the pipeline reaches SHIP (composite 92.2) |
| waterfordcountypainters (WCP) | medium | first capability audit (2026-07-11); the backlog below |
| trimtech | very hard | the stress test — its known-issues are already a rough pre-existing capability backlog (two-row nav, slideshow active-slide, Pro-Gallery completeness, pinned-layer children, topbar icons, …) |
Sequence: 3 sites → Wix API for assets (Slice 5) → 50+ batch (Slice 6). The 3-site phase is discovery — it produces the asset manifest that scopes the API step. See API timing.
The two blind spots (audit finding, 2026-07-11 — CONFIRMED in code)¶
The existing identify machinery is built to catch absence:
- section drops →
matcherGap.droppedLive→criticalSectionDroppedveto - unmatched sections →
GenericSectionfallback →genericFallbackCriticalveto - missing images / unknown blocks → loud-fail placeholders /
MissingBlock
It is blind by construction to the two failure modes WCP's real gaps actually are:
- Mis-render — a section that matched but rendered the wrong variant.
A matched section banks a large presence share regardless of dim agreement;
propsWithheldcounts only ours-missing dims, never a disagree; the critical veto reads dropped rows only. So a wrong-tone/wrong-variant section scored ~0 cost and could never be surfaced. (The pt4 hero fix was found by operator report, not the machinery.) - Asset-drop — a non-image asset (video, carousel/slideshow frames) captured- but-not-migrated or never captured. Missing images loud-fail; a missing video degrades to a poster/single frame a screenshot can't distinguish from success.
Two detectors close them (both complementary — one catches wrong values, one catches missing assets; together they cover the flagship WCP hero from both sides):
- Detector 1 — variant-mismatch (
lib/structural-diff.js, reporting mode). AvariantMismatchbucket in the matcher-gap roll-up counts decisive-dim disagrees on critical-role sections, surfaced throughlib/scorecard.js. Scope today:{tone, imageSide}— the scoredDIMS; promotingscrim/textTone/hero-variant to first-class scored dims moves S, so it is bundled with the veto into Slice 6. Strictly additive: S byte-identical,structural-diff.validate.mjs96/96. - Detector 2 — asset-manifest (
lib/capture-section-backgrounds.js+lib/cms/seed-from-build.jssidecar +apps/cms/scripts/transform-seed-images.mjs, reporting mode). A per-block expected-vs-supplied asset count + migration status, emitted asbuilds/<domain>/asset-manifest.json(modelled onsection-matchers.json). This sidecar is the asset manifest that scopes the API step.
Both ship as loud reports now; their gate vetoes and thresholds wait for
Slice 6 calibration (lib/calibration.json is still fitted:false).
WCP capability backlog (first audit, 2026-07-11)¶
Ranked most portfolio-impactful first. Evidence: live-vs-ours screenshots
(index / gallery / commercial-painting), section-backgrounds.json, the WCP seed,
and the render code.
| # | Gap (live vs ours) | Process coverage | Owning stage | Class | Capability to build |
|---|---|---|---|---|---|
| 1 | Hero: live = carousel + bg video; ours = one static image. Variant and assets both wrong. | SILENT (mis-render + asset-drop) | capture · render · asset | render now + assets→API | capture+thread slideshowUrls/videoUrl; hero variant from a first-class captured variant enum (retire the brittle imageSide && !media inference); video migration (#7) |
| 2 | Header/logo: live = logo centered in a split nav + full topbar; ours = logo stacked, nav cramped, phone-only topbar | FULLY BLIND — globals junk-filtered out of every axis | capture · matcher · render | now | ✅ layout shipped 2026-07-12 — captured header-layout descriptor (classifyHeaderLayout) → header-wcp layout variant prop (stacked/split) + header CTA (see Status). ⏳ remaining: extend TopBar.js extraction (email/location/social hrefs); bring globals into the scorecard (known-issues) |
| 3 | Floating CTA / social overlays — missing | COVERED (loud-warns) — translators existed, just unwired | render/transformer | now (small) | ✅ done — register fromFloatingCTA/fromFloatingSocial as globals (see Status) |
| 4 | Video gallery (IMAGES/VIDEOS tabs) — ours is flat image grids, 3× too tall, dup'd | partial + silent asset-drop | capture · render · asset | render now + assets→API | expand/enumerate the VIDEOS tab in capture; fromVideoGallery PT translator + <video>-capable block; fix gallery image over-population (partition/de-dup/paginate); VideoGallery→fromGallery currently reads {items} but the matcher emits {videos} → discarded even when matched |
| 5 | Tone/variant disagree: FAQ on navy vs white; brand CTA band matched as plain About | SILENT (not counted as debt) | matcher + the machinery itself | now | Detector 1 (done, reporting); tighten matcher precision |
| 6 | Mis-classification: About intro → empty "Anonymous" review card; checklist collapsed | partial | matcher | now | tighten Testimonials/CTAStrip/Checklist precision (only against built FCR sites — matcher-tuning scope) |
| 7 | Video assets never migrate to R2 (/assets/videos/ dead on worker; real host video.wixstatic.com) |
warn only; gate never sees it | asset-acquisition | assets→API | transform-seed-images.mjs video branch; enumerated by Detector 2's manifest |
API timing¶
After the 3-site capability phase, before 50+ — scoped by the asset manifest. Not pulled forward. Reasoning:
- Discovery-first — the API integration is only as good as the manifest of what it must deliver; the 3 sites (WCP, trimtech) produce exactly that manifest.
- Nothing in the 3-site phase is blocked by not having the API — render/detector work is asset-source-agnostic; a missing asset loud-fails (the process working), and any renderer can be built/verified against one hand-fetched sample.
- A scrape-and-migrate asset path built now is throwaway once the API lands. The API is a scale concern, not a build concern.
Reframe that resolves the apparent contradiction (some gaps genuinely are asset gaps): don't gate the API behind "3 sites ship" — gate it behind "3 sites have produced the renderers + the asset manifest." The sites won't be fully shippable until the API fills assets, and that's fine — the deliverable is the process + the manifest.
Trigger to start it: the audit backlog exists, the asset classes are picked
(video / carousel frames / logos), and ≥1 renderer per class loud-demands its asset
via the r2: ref contract → the API scope collapses to "populate these N asset
classes with provenance." The audit confirms the build-now / asset→API split is real
(items 2/3/5/6 build-now; 1/4/7 have asset halves).
Status (2026-07-11)¶
- ✅ #3 Floating CTA/social globals — built + deployed live 2026-07-11 (pt2). Closes
the CMS-lane silent-drop (~48 warns/run → 0). The two globals were derived from the
live WCP extraction (WhatsApp CTA pill
bottom-right; facebook+youtube railright), graft-checked against live D1 (clean/never-edited), and written surgically into WCP'sec_globalssiterow (no rebuild —Base.astrorenders globals at request time; the worker already carried the render path). Verified live onwcp.dcathal.org(homepage +/gallerysubpage, icons resolve, no loud-fail). Committed seed reconciled to match. - ✅ Detector 1 (variant-mismatch) — shipped reporting mode; additive (validate
96/96, S byte-identical). Flags the WCP commercial-painting hero (
tone default→dark) and FAQ-on-navy. - ✅ Detector 2 (asset-manifest) — shipped reporting mode; verified with a live v3
re-capture. New pure
lib/cms/asset-manifest.js+ captureassets:{videos,slides}(schemaVersion 2→3) +builds/<domain>/asset-manifest.jsonsidecar + atransform-seed-imagesgap-enumeration (asset-migration-gaps.json). Flags WCP's home hero video (raw, unmigrated) and collapsed hero carousel (expected 8 slides vs 0 supplied). Limitation: gallery-video drops are not caught by the live<video>count — Wix renders them as click-to-play posters (<video>count 0), so the authoritative signal there is the supplied-ref migration status + thetransform-seed-imagesenumeration; sourcing expected-videos frombody.htmlcloses it (deferred to Slice 6). -
⏸ Deferred to Slice 6 calibration: both detectors' gate vetoes + thresholds; promoting hero-variant/scrim/textTone to scored dims (moves S); the header/globals scorecard coverage (#2).
-
✅ #2 Header/logo layout — the build half shipped + deployed live 2026-07-12. A bounded
layoutenum onheader-wcp(stacked-centerdefault = garvanbay ·split-center= WCP: nav flanks a centered logo + optional CTA pill), measured on the design plane (chrome.headerRaw→ pureclassifyHeaderLayout, since the matcher is geometry-blind in the seed path), threaded transformer→schema→adapter→block and stamped byseed-from-build. WCP redeployed (workerb4e7a6a4) + surgicalec_globalsheader patch (layout:split-center+SERVICE AREASCTA); garvanbay stacked byte-identical. ADR-0009 Slice 3 + known-patterns "The capture plane is the authority for header layout too". Deferred (known-issues "Header dimension — three fast-follows"): topbar field extraction, header-CTA auto-detection (pinned outside<header>),inline-leftrouting, and the identify half (globals into the scorecard) — build-first was the chosen order. -
✅ #1 Hero variant (build-now half) — shipped 2026-07-12 pt2. A first-class measured
variantenum onfcr.hero(carousel|video|full-bleed|side-image|text) retires the brittleimageSide && !mediainference, derived at the seed join from already-persisted bounded capture values (no re-crawl); render byte-identical (zero-regression diff = onlyvariantadded), carousel/video routing proven locally. The asset half (video migration #7 + carousel frames) stays Slice 5 / API — Detector 2's manifest already flags WCP's home hero video + collapsed carousel. See ADR-0009 Slice 3 + known-patterns "A dimension whose inputs are already-BOUNDED capture values derives at the JOIN". - ✅ #2 TopBar socials — shipped 2026-07-12 pt2. The "phone-only topbar" symptom split
three ways on diagnosis: socials = a portfolio-wide threading bug (
url→hrefintranslateTopBar), fixed one-line both lanes; email/location = capture-plane misses, fix layer pinned, deferred to a re-crawl (known-issues). -
✅ #2 Globals into the scorecard (identify half) — reporting projection shipped 2026-07-12 pt2 (
lib/globals-diff.js, additive, S/T/G_det byte-identical, 36 validate checks). Header/topbar/footer live-vs-ours chrome agreement now surfaces infidelity-scorecard.json;headerLayout/headerCtanull-safe until a v3 re-capture. Gate/thresholds → Slice 6. -
✅ v3 re-capture — three-half landing (2026-07-12 pt3). A single v3 re-capture of WCP + garvanbay (section-background capture only — reads
theme.json, so the brand-nondeterminism landmine never fires) closed three deferred halves at once: (#1) hero variant real measurement — WCP home =carousel(8 slides; v2 readfull-bleed), services =side-image, locations =full-bleed; committed into the WCP seed (assets still Slice 5). (#2) topbar email/location — newchrome.topbarContactextractor (the zero-height#pinnedTopCenterhid it from the bg probe) + fill-if-empty overlay; deployed to WCP live D1 via surgicalec_globalsread-mutate-write, rendered-verified; garvanbay a faithful no-op. (#2) globals real numbers —headerLayoutagree (split=split),headerCtamismatch (fast-follow #2). See known-patterns "A zero-height pinned container hides its measurable children from the bg probe".
Capabilities #1 done — build-now half + real variant measurement (assets → Slice 5), #4 / #6 remain; #3 deployed; #2 done — layout + socials + email/location + globals-identify with real numbers (only header-CTA auto-detection, fast-follow #2, remains — needs the pinned-layer walker).
Cross-refs¶
- ADR-0009 — target architecture
- near-match scorecard contract — the identify machinery (S/T/G_det, matcherGap, GenericSection, and now the two detectors)
- known-issues — the FloatingCTA/Social CMS-drop (now closed by #3), the video-migration gap (#7), trimtech's per-site gaps