Skip to content

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:

  1. 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).
  2. Classify each gap by:
  3. Process coverage — does the machinery flag it today, or does it fail silently?
  4. Owning stage — capture / matcher / render (adapter or canonical block) / asset-acquisition.
  5. Classbuild-now (structure/render, asset-source-agnostic) vs asset→API (needs the real asset; defer to the Wix API asset plane, ADR-0009 Slice 5).
  6. Build the generic capability (or defer it to the asset plane).
  7. 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 dropsmatcherGap.droppedLivecriticalSectionDropped veto
  • unmatched sectionsGenericSection fallback → genericFallbackCritical veto
  • missing images / unknown blocks → loud-fail placeholders / MissingBlock

It is blind by construction to the two failure modes WCP's real gaps actually are:

  1. Mis-render — a section that matched but rendered the wrong variant. A matched section banks a large presence share regardless of dim agreement; propsWithheld counts 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.)
  2. 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). A variantMismatch bucket in the matcher-gap roll-up counts decisive-dim disagrees on critical-role sections, surfaced through lib/scorecard.js. Scope today: {tone, imageSide} — the scored DIMS; promoting scrim/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.mjs 96/96.
  • Detector 2 — asset-manifest (lib/capture-section-backgrounds.js + lib/cms/seed-from-build.js sidecar + apps/cms/scripts/transform-seed-images.mjs, reporting mode). A per-block expected-vs-supplied asset count + migration status, emitted as builds/<domain>/asset-manifest.json (modelled on section-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 rail right), graft-checked against live D1 (clean/never-edited), and written surgically into WCP's ec_globals site row (no rebuild — Base.astro renders globals at request time; the worker already carried the render path). Verified live on wcp.dcathal.org (homepage + /gallery subpage, 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 + capture assets:{videos,slides} (schemaVersion 2→3) + builds/<domain>/asset-manifest.json sidecar + a transform-seed-images gap-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 + the transform-seed-images enumeration; sourcing expected-videos from body.html closes 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 layout enum on header-wcp (stacked-center default = garvanbay · split-center = WCP: nav flanks a centered logo + optional CTA pill), measured on the design plane (chrome.headerRaw → pure classifyHeaderLayout, since the matcher is geometry-blind in the seed path), threaded transformer→schema→adapter→block and stamped by seed-from-build. WCP redeployed (worker b4e7a6a4) + surgical ec_globals header patch (layout:split-center + SERVICE AREAS CTA); 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-left routing, 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 variant enum on fcr.hero (carousel|video|full-bleed|side-image|text) retires the brittle imageSide && !media inference, derived at the seed join from already-persisted bounded capture values (no re-crawl); render byte-identical (zero-regression diff = only variant added), 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 (urlhref in translateTopBar), 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 in fidelity-scorecard.json; headerLayout/headerCta null-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 read full-bleed), services = side-image, locations = full-bleed; committed into the WCP seed (assets still Slice 5). (#2) topbar email/location — new chrome.topbarContact extractor (the zero-height #pinnedTopCenter hid it from the bg probe) + fill-if-empty overlay; deployed to WCP live D1 via surgical ec_globals read-mutate-write, rendered-verified; garvanbay a faithful no-op. (#2) globals real numbersheaderLayout agree (split=split), headerCta mismatch (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