LearnNewsExamplesServices
Frontmatter
id18497
titleThe destroy-cancellation arm counts every applyDeltas in the process, so it cannot name what it caught
stateClosed
labels
bugai
assigneesneo-opus-ada
createdAtSep 8, 2026, 11:16 PM
updatedAtSep 9, 2026, 9:44 AM
githubUrlhttps://github.com/neomjs/neo/issues/18497
authorneo-opus-ada
commentsCount0
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtSep 9, 2026, 9:44 AM

The destroy-cancellation arm counts every applyDeltas in the process, so it cannot name what it caught

neo-opus-ada
neo-opus-ada commented on Sep 8, 2026, 11:16 PM

Context

test/playwright/unit/core/VdomDestroyCancellation.spec.mjs:32 reds intermittently under full unit-suite load. Surfaced while measuring something else (#18275's fix), so it was first checked for attribution: 2/2 red on unchanged dev, 1/2 with the #18275 patch, and 10/10 green in isolation both ways. Pre-existing, not introduced.

Operator asked for a closer look. What that found is not (yet) an engine defect — it is that the arm cannot tell an engine defect from ambient traffic, which is why the intermittent has stayed unownable.

Live latest-open sweep: checked the latest 20 open issues at 2026-09-08T21:10Z; no equivalent found. A2A in-flight claim sweep over the last 30 messages (all read-states): no overlapping [lane-claim]. Corpus grep for VdomDestroyCancellation hits three archived PR bodies only. Distinct from #18487 (Maximize, a different spec) and from #18439 (e2e tier, different suite).

The Problem

The failure signature is stable and specific — captured at dev:

Error: a stale success payload from a destroyed flight must apply ZERO deltas
Expected: 0
Received: 1
> 104 | expect(deltaCalls, '…must apply ZERO deltas').toBe(0)

The arm builds two global monkey-patches, and both are wider than the contract it states.

1. The counter is process-wide. Neo.applyDeltas is replaced globally and deltaCalls increments on every call from anywhere:

Neo.applyDeltas = function(...args) {
    deltaCalls++;
    return realApplyDeltas?.apply(this, args)
};

The assertion reads as "this stale payload applied no deltas." What it measures is "no component anywhere in this process applied any delta during these 20ms." Neo is module-global and a Playwright worker runs spec files sequentially in one process, so a prior file's un-awaited async update landing in the window is indistinguishable from the leak under test. Received: 1 therefore does not identify a culprit — which is exactly why nobody has been able to action this.

This is the same instrument shape that produced a wrong magnitude on #18275, where a file-shared static counter carried constructions from earlier arms.

2. One mocked promise is handed to every caller. While armed, updateBatch returns the same promise regardless of who asks:

VdomHelper.updateBatch = function(data) {
    return armed ? armed.promise : realUpdateBatch.call(this, data)
};

Any component reaching updateBatch during the armed window receives the destroyed child's stale payload and, being alive, applies it through a correctly-unguarded continuation — VdomLifecycle.mjs:342's if (me.id && !me.isDestroyed) gates on the initiator, so a live joiner passes it legitimately. In production each caller gets its own payload; here they share one. Measured on a green run by instrumenting the mock: armedCallers: ["vdc-child"] — only the child joins today, so this is currently latent rather than firing. It is one ancestor-timing change away from being the cause, and no arm would say so.

The Architectural Reality

  • src/mixin/VdomLifecycle.mjs:342 — the guard under test, and it is correct: if (me.id && !me.isDestroyed) blocks both the delta application and the vnode distribution for a destroyed initiator.
  • src/component/Base.mjs:1326 and :1866 — the only other applyDeltas emitters on a destroy/unmount path. Both are synchronous and both fall outside the measured window (child.destroy() precedes the deltaCalls = 0 reset; parent.destroy() runs in the finally, after the assertion). So neither explains Received: 1.
  • The window is two fixed setTimeout(20) waits. They are not the defect — the arm fails on a count, not on an assertion that ran early.

What is established, and what is not

Established: the arm's two instruments are broader than the contract; the guard at :342 is correct; the two destroy-path emitters are outside the window; the failure is a count of 1 with no identity attached.

Not established: whether a real engine leak exists. One Received: 1 was captured today; every attempt to re-catch it with a diagnostic attached has been green — 5 default-worker runs, 3 at --workers=1, 3 over the spec's own directory, 8 more full runs. An earlier single --workers=1 red was not identified to a test and is discarded rather than argued from.

The rate drifted from ~50% this morning to 0/19 this evening on unchanged source, so sampling harder is the wrong instrument — the between-sample variance exceeds the effect, which is the trap this repo has already paid for on #18275.

The Fix

Make the arm name what it caught, without weakening it.

  1. Record every applied payload, not just a count, and assert on deltas that reference the destroyed component — the contract is about this flight's payload, not about process-wide quiet.
  2. Carry the full recorded log in the assertion message, so the next occurrence is self-diagnosing on a CI runner where nobody can attach a session.
  3. Scope the updateBatch mock to the child's own batch, so a joiner cannot receive another component's stale payload. Assert the joiner set, so a future ancestor-timing change trips an arm instead of silently becoming the cause.
  4. Then re-measure. If the arm still reds, it now names the culprit and a real defect can be filed against evidence. If it stops, the intermittent was ambient traffic and the guard was never in question.

Acceptance Criteria

  • AC-1 — the zero-delta assertion is scoped to deltas naming the destroyed component, and the arm's failure message carries every payload applied during the window.
  • AC-2 — the updateBatch mock serves the armed promise only to the child's own batch; a second caller during the armed window is asserted rather than silently joined.
  • AC-3 — red-first for both: a deliberately foreign delta applied during the window must NOT red the scoped assertion (proving the over-breadth is gone) and a delta naming the destroyed child MUST red it (proving the guard is not weakened). Green alone certifies nothing here.
  • AC-4 — the full unit suite is run at least 20 times against the repaired arm and the red count recorded on this ticket. A zero is only meaningful beside the pre-repair rate, which is itself unstable, so record both counts and state the instability rather than claiming a fix from absence.
  • AC-5 — if the repaired arm still reds, it names the applied payload, and either a follow-up engine ticket is filed against that evidence or this ticket absorbs it. A surviving red is a success condition for this ticket, not a failure.

Out of Scope

  • Fixing any engine leak this uncovers. No leak is established. If AC-5 surfaces one it gets its own ticket with the payload as evidence.
  • The two fixed 20ms waits. They are load-bearing for the flight-departure sequencing and are not what fails; churning them would be an unmeasured change to a spec already under suspicion.
  • #18487 (Maximize) and #18439 (e2e BigData). Different specs, different suites; named only so a reader does not merge three intermittents into one.
  • Quarantining the arm. It guards a real cancellation boundary and has an owner. Repair beats skip.

Avoided Traps

  • "It is a flaky sleep, widen the timeout." The arm fails on Received: 1, not on an assertion racing a pending update. Widening the window would make a wrong count more likely, not less.
  • "It went green 19 times, it is fixed." Nothing was changed in those 19 runs. Absence at an unstable rate is not evidence, and this ticket exists partly because that inference is available and wrong.
  • "Received: 1 proves the engine leaks." It proves one delta applied somewhere in the process. The instrument cannot say whose, which is the actual finding.

Decision Record impact

none.

Related

  • #18275 (same over-broad-counter instrument shape, and where this surfaced)
  • #18487 (a different pre-existing intermittent — Maximize)
  • #18439 (e2e tier intermittents under load)
  • #12986 (the vdom delta-stream contract epic)

Origin Session ID: 69ff8d6f-0e9e-4c44-aff9-e8007f4d7090

Retrieval Hint: query_raw_memories("VdomDestroyCancellation zero deltas applyDeltas process-wide counter armed promise shared joiner")

Authored by ⚖️ Ada · @neo-opus-ada · Claude Opus 5 · Claude Code