Context
Second leaf of Epic #12986 (the vdom delta-stream contract). The #12942 census's spec-precedent sweep found the suite capturing deltas through three hand-rolled patterns, and the #12946 forensics hunt added a fourth (per @neo-fable's post-census scope note, DC_kwDODSospM4BB6cG). Every new delta-asserting spec re-invents capture plumbing; every forensics session re-pays the windowing cost by hand (~2h for the #12929/#12939 harvest vs ~5 lines under a capture kernel, per the cost inventory recorded in the census thread).
Release classification: post-release (contract-layer substrate; v13.0.0 shipped).
The Problem
The four patterns in the wild, each with different reach and different blind spots:
- Producer direct return —
VdomHelper.update({vdom, vnode}) return value (Node-mode specs: Advanced, Calendar, Helper, table/Container, …). Precise, but only sees what the caller invokes — no component lifecycle, no batching context.
Neo.applyDeltas interceptor — mock-replace the app-worker boundary fn and accumulate (RaceCondition, AsymmetricMerging, HiddenChildren, …). Sees real lifecycle traffic, but coarse: all components' deltas interleave with no windowing.
- Component method return —
const {deltas} = await component.set({...}) (button/Base, grid/Teleportation hybrid, grid/Pooling, …). Single-component focused; only works where the method returns deltas.
- Wrapped console at the apply boundary — monkey-patching
console.log around Neo.config.logDeltaUpdates output (the #12946 hunt; also the original GridDeltaCapture harvest). Fragile, lossy, format-coupled — and the only pattern that sees the FINAL post-mutation batch on the Main side.
Cross-cutting gaps: no epoch windowing (per-gesture-phase delta attribution was hand-joined against trace timestamps), no layer naming (a captured batch doesn't say whether it is pre-send intent or post-mutation final truth — the #12942 OQ3 resolution requires capture instruments to name their layer), and no shared classification (each spec hand-rolls its op filtering; the kernel's vocabulary now exists for exactly this).
The Architectural Reality
- The kernel (#12987,
src/vdom/util/DeltaGrammar.mjs) provides the vocabulary + predicates this API classifies with — this leaf consumes, never duplicates it.
- Sibling placement precedent:
test/playwright/util/RmaHelpers.mjs is the existing shared test-utility home; the capture module lands beside it (structural pre-flight Stage-1 fast-path).
- The two capture layers per the OQ3 two-layer split: VDom pre-send (attribution/intent — patterns 1/3 approximate it) and Main pre-apply / app-worker boundary (final-batch truth — patterns 2/4 approximate it). In the single-threaded unit env both layers collapse onto observable seams (
VdomHelper.update return + Neo.applyDeltas), which is precisely why ONE facade can subsume all four patterns there.
- The unit env's seams are stable, framework-owned functions — interception is deterministic (no console-format coupling, no timing races).
The Fix
New shared test utility test/playwright/util/DeltaCapture.mjs + spec test/playwright/unit/vdom/DeltaCapture.spec.mjs:
- One facade, explicit taps:
createDeltaCapture({tap}) where tap ∈ {'applyDeltas', 'helperReturn'} — each captured batch is recorded with its tap name (the OQ3 layer-naming requirement), batch boundaries preserved (no flattening-by-default; the batch is the grammar's unit).
- Epoch windows:
capture.epoch('drag-hold') → subsequent batches attribute to that label; capture.window('drop', async () => {...}) scopes an async action; reads like capture.deltasIn('drop'). Kills the hand-joined-timestamp workflow.
- Kernel-vocabulary classification:
capture.opsIn('drop') returns effective-action counts via resolveAction (the implicit-updateNode spelling handled once, centrally); capture.findingsIn('drop', opts) runs validateBatch per captured batch — making every captured window grammar-checkable for free.
- Lifecycle hygiene:
capture.restore() reinstates the tapped seam; double-install guards; teardown-safe under test failure (try/finally discipline documented in the JSDoc).
- The four legacy patterns get a migration note in the module JSDoc (behavior-level: which tap replaces which pattern) — migrating existing specs is explicitly out of scope (organic adoption; forced migration would churn 26 spec files in one PR).
Contract Ledger Matrix
| Target Surface |
Source of Authority |
Proposed Behavior |
Fallback |
Docs |
Evidence |
test/playwright/util/DeltaCapture.mjs (NEW) |
Epic #12986; census capture-pattern inventory + DC_kwDODSospM4BB6cG fourth pattern |
Facade: tap selection, layer-named batch records, epoch windows, kernel classification, restore() |
n/a (new surface; nothing depends on it until specs adopt it) |
Module JSDoc incl. pattern-migration map |
Sibling RmaHelpers.mjs placement verified; 4-pattern inventory peer-verified in the census thread |
Neo.applyDeltas (EXISTING — tapped, not modified) |
src/worker/App.mjs:119-125 (normalizes to array → promiseMessage) |
Tap wraps and forwards unchanged; capture is observational |
restore() reinstates the original fn |
n/a |
Signature verified during the census |
VdomHelper.update() return (EXISTING — tapped, not modified) |
src/vdom/Helper.mjs:815-837 (returns {deltas, updateVdom, vnode}) |
Tap records the returned deltas array per call |
restore() |
n/a |
Return shape verified during the census |
src/vdom/util/DeltaGrammar.mjs (EXISTING #12987 — consumed) |
Kernel exports (resolveAction, validateBatch) |
Imported for classification; never re-implemented |
n/a |
n/a |
Kernel PR #12988 (this leaf is blocked-by #12987) |
Decision Record impact: none (test-substrate utility consuming the kernel; no runtime behavior change).
Acceptance Criteria
Out of Scope
- Migrating the 26 existing delta-asserting specs (organic adoption; forced churn rejected).
- Browser-context / whitebox-e2e capture (the e2e variant rides the NL streaming leaf, OQ4) and the Main-thread
DeltaUpdates pre-apply event tap (real-browser-only; meaningless in the single-threaded unit env where applyDeltas IS the boundary).
- Replay/fixture persistence (OQ5 leaf — versioning story first).
- Signature shape-class assertion helpers (OQ2 leaf — they will CONSUME this capture API).
- Any change to the tapped seams' behavior.
Avoided Traps
- Flattening batches by default: the batch is the grammar's unit (ordering rules are per-batch); a flattened stream destroys exactly what
validateBatch checks. Flatten only as an explicit accessor.
- Console-format coupling (pattern 4's fragility): tap framework seams, never log output.
- Auto-migrating existing specs in the same PR: 26-file churn bundled with new substrate = unreviewable; the
#12941 precedent shows delta-spec churn rots fast.
- Re-implementing classification: the kernel owns the vocabulary; this module imports it (the epic's one-vocabulary constraint).
Related
Epic #12986 (parent). Blocked-by #12987 (imports the kernel). Source: #12942 census + DC_kwDODSospM4BB6cG (fourth pattern + epoch-marker inventory). Adjacent: #12940 (logDeltaUpdates docs — the manual lever this eventually supersedes for unit work).
Live latest-open sweep: checked latest 15 open issues at 2026-06-12T11:41Z; no equivalent found. A2A in-flight sweep: clean (@neo-gpt on #12989 + reviews; capture decomposition announced as mine in the graduation + leaf-1 broadcasts).
Origin Session ID: 3e1566f6-6336-4db4-8726-189004578d8b
Retrieval Hint: "delta capture API epoch windows tap applyDeltas helperReturn four patterns DeltaCapture"
Context
Second leaf of Epic #12986 (the vdom delta-stream contract). The
#12942census's spec-precedent sweep found the suite capturing deltas through three hand-rolled patterns, and the#12946forensics hunt added a fourth (per @neo-fable's post-census scope note,DC_kwDODSospM4BB6cG). Every new delta-asserting spec re-invents capture plumbing; every forensics session re-pays the windowing cost by hand (~2h for the#12929/#12939harvest vs ~5 lines under a capture kernel, per the cost inventory recorded in the census thread).Release classification:post-release (contract-layer substrate; v13.0.0 shipped).The Problem
The four patterns in the wild, each with different reach and different blind spots:
VdomHelper.update({vdom, vnode})return value (Node-mode specs:Advanced,Calendar,Helper,table/Container, …). Precise, but only sees what the caller invokes — no component lifecycle, no batching context.Neo.applyDeltasinterceptor — mock-replace the app-worker boundary fn and accumulate (RaceCondition,AsymmetricMerging,HiddenChildren, …). Sees real lifecycle traffic, but coarse: all components' deltas interleave with no windowing.const {deltas} = await component.set({...})(button/Base,grid/Teleportationhybrid,grid/Pooling, …). Single-component focused; only works where the method returns deltas.console.logaroundNeo.config.logDeltaUpdatesoutput (the#12946hunt; also the originalGridDeltaCaptureharvest). Fragile, lossy, format-coupled — and the only pattern that sees the FINAL post-mutation batch on the Main side.Cross-cutting gaps: no epoch windowing (per-gesture-phase delta attribution was hand-joined against trace timestamps), no layer naming (a captured batch doesn't say whether it is pre-send intent or post-mutation final truth — the
#12942OQ3 resolution requires capture instruments to name their layer), and no shared classification (each spec hand-rolls its op filtering; the kernel's vocabulary now exists for exactly this).The Architectural Reality
src/vdom/util/DeltaGrammar.mjs) provides the vocabulary + predicates this API classifies with — this leaf consumes, never duplicates it.test/playwright/util/RmaHelpers.mjsis the existing shared test-utility home; the capture module lands beside it (structural pre-flight Stage-1 fast-path).VdomHelper.updatereturn +Neo.applyDeltas), which is precisely why ONE facade can subsume all four patterns there.The Fix
New shared test utility
test/playwright/util/DeltaCapture.mjs+ spectest/playwright/unit/vdom/DeltaCapture.spec.mjs:createDeltaCapture({tap})wheretap ∈ {'applyDeltas', 'helperReturn'}— each captured batch is recorded with its tap name (the OQ3 layer-naming requirement), batch boundaries preserved (no flattening-by-default; the batch is the grammar's unit).capture.epoch('drag-hold')→ subsequent batches attribute to that label;capture.window('drop', async () => {...})scopes an async action; reads likecapture.deltasIn('drop'). Kills the hand-joined-timestamp workflow.capture.opsIn('drop')returns effective-action counts viaresolveAction(the implicit-updateNode spelling handled once, centrally);capture.findingsIn('drop', opts)runsvalidateBatchper captured batch — making every captured window grammar-checkable for free.capture.restore()reinstates the tapped seam; double-install guards; teardown-safe under test failure (try/finallydiscipline documented in the JSDoc).Contract Ledger Matrix
test/playwright/util/DeltaCapture.mjs(NEW)DC_kwDODSospM4BB6cGfourth patternRmaHelpers.mjsplacement verified; 4-pattern inventory peer-verified in the census threadNeo.applyDeltas(EXISTING — tapped, not modified)src/worker/App.mjs:119-125(normalizes to array →promiseMessage)VdomHelper.update()return (EXISTING — tapped, not modified)src/vdom/Helper.mjs:815-837(returns{deltas, updateVdom, vnode})deltasarray per callsrc/vdom/util/DeltaGrammar.mjs(EXISTING #12987 — consumed)resolveAction,validateBatch)Decision Record impact:none (test-substrate utility consuming the kernel; no runtime behavior change).Acceptance Criteria
createDeltaCapture({tap: 'applyDeltas'})captures real lifecycle batches with batch boundaries + tap name preserved;restore()reinstates the seam (verified by post-restore traffic NOT being captured).tap: 'helperReturn'recordsVdomHelper.update()return batches equivalently.epoch()re-labeling and scoped asyncwindow();deltasIn(label)returns only that window's batches.opsIn(label)counts by effective action (action-less updateNode resolved correctly);findingsIn(label)surfacesvalidateBatchfindings per batch.Out of Scope
DeltaUpdatespre-apply event tap (real-browser-only; meaningless in the single-threaded unit env where applyDeltas IS the boundary).Avoided Traps
validateBatchchecks. Flatten only as an explicit accessor.#12941precedent shows delta-spec churn rots fast.Related
Epic #12986 (parent). Blocked-by #12987 (imports the kernel). Source:
#12942census +DC_kwDODSospM4BB6cG(fourth pattern + epoch-marker inventory). Adjacent:#12940(logDeltaUpdates docs — the manual lever this eventually supersedes for unit work).Live latest-open sweep: checked latest 15 open issues at 2026-06-12T11:41Z; no equivalent found. A2A in-flight sweep: clean (@neo-gpt on #12989 + reviews; capture decomposition announced as mine in the graduation + leaf-1 broadcasts).
Origin Session ID: 3e1566f6-6336-4db4-8726-189004578d8b
Retrieval Hint: "delta capture API epoch windows tap applyDeltas helperReturn four patterns DeltaCapture"