Skip to content

Session handover — 2026-07-08 pt2

Continues from session-2026-07-08-handover.md (G_det alignment fix). This session ran ADR-0009 Slice 4a — the matcherGap roll-up into S — after splitting the bundled Slice 4 into 4a (metric) + 4b (render). One slice, closed; metric-only, no deploy.

The split (scope decision, user-confirmed)

Slice 4 as written bundled two independently-shippable pieces with an asymmetric dependency, so it was split (see ADR-0009 tracker rows 4a/4b):

  • 4a — matcherGap into S (this session): derivable from data structural-diff.js already tallies; small, pure, deploy-free, always verifiable.
  • 4b — measured GenericSection renderer (next): a new render surface across both lanes; also closes the CMS-lane silent-drop. genericFallbackRate lives here (structurally 0 until the renderer ships; needs a build-time {sectionId→matcher} sidecar — the capture can't see block identity).

Sequencing rationale: build the meter (4a) before the remediation (4b) — the matcherGap numbers tell us how much GenericSection work 4b actually warrants.

Slice 4a — what changed (where to look)

  • Code: lib/structural-diff.js — new summariseMatcherGap(rows) (per-page, in diffPage) + rollupMatcherGap(pages) (per-site, in diffSite); emits {droppedLive, insertedOurs, propsWithheld, byRole, genericFallbackRate:null}. lib/scorecard.js carries it into fidelity-scorecard.json's structural block + a console line. The design invariant: matcherGap is a reported projection of debt already in S, not a second penalty — S is byte-identical before/after. The criticalSectionDropped veto reads the same drop rows, unchanged.
  • Validate: lib/structural-diff.validate.mjs42 → 53 checks (drop/insert weights, propsWithheld counts only ours-missing, site totals, byRole, genericFallbackRate===null).
  • known-patterns: new entry — a diagnostic that projects debt already inside a score must NOT re-penalise (ask "is X already inside S?" before folding X into S).
  • contract / ADR / known-issues: near-match-scorecard-contract.md §3/§6/§7 + a Step-4a Status block; ADR-0009 tracker split 4a done / 4b planned; known-issues scorecard entry updated (4a done, stale structural-diff.js validate mention corrected).

Verified

  • node lib/structural-diff.validate.mjs53/53; scorecard-gate.validate.mjs19/19 (regression: gate untouched).
  • Re-scored both anchors offline (no re-crawl): S byte-identical (garvanbay 96, WCP 60.9), verdicts stable (garvanbay SHIP, WCP MANUAL-POLISH). matcherGap populated: garvanbay 0/0/0; WCP 3 dropped (w=2) / 5 inserted / 4 props-withheld, byRole content 1d·5i, ribbon 2d. No drop at role-weight ≥ 1.3 → no criticalSectionDropped veto, coherent with MANUAL-POLISH.
  • Standalone structural-diff.js CLI + perceptual-diff.js (alignPage, untouched) both exercised via the full scorecard — G_det still computes.
  • Regression (Phase 3): N/A for shared primitives — no canonical block touched; the shared file is the metric lib, exercised through all three consumers.

Next priority — Slice 4b (measured GenericSection renderer)

The render surface that reduces the debt 4a now measures: a lowest-priority always-matches GenericSection matcher (lib/matchers/, auto-registers) + a new canonical block (composes Section/SectionProse/SectionGrid, resembles content-1/content-2) + translators in both lanes + FcrBlocks + check-renderers + loud-report + deploy + verify. Closes the CMS-lane silent-drop (lib/cms/transformer.js drops matcher:null with no warning) and makes genericFallbackRate nonzero. Then Slice 6 (calibrate calibration.json).

Carried / open (see known-issues)

New residual this session: isJunk misses WCP's heading-bearing "Watch Now" video-chrome strip (surfaced by matcherGap — a low-impact ribbon drop S already counted; widen the heading-gated regex) — contract Follow-ups. Plus all prior carried items: seed thresholds uncalibrated (Slice 6); theme-fidelity.js still lacks a .validate.mjs; T re-measures emission not the deployed DOM; imageSide over-fires on media heroes; deployed-site fidelity gaps (garvanbay muted bands, WCP columns/align/FAQ-tone) unfixed; per-site static lane consumes no capture; cross-account sharding; brand-detection nondeterminism; noindex launch blocker.