Context
Born from the #14985 investigation (operator-directed continuation). The Demo-A tour's reveal beat has never painted — the merged whitebox spec test/playwright/e2e/dashboard/DemoATourNL.spec.mjs stands red on revealSeen, and that red was attributed to the vdom update wedge (#12946 class) as one of its two witnesses. Tonight's forensics falsify that attribution for THIS surface: the defect is an app-level race in the demo workspace, upstream of vdom entirely.
Live latest-open sweep: checked latest 20 open issues at 2026-07-10T23:47Z; no equivalent found. A2A in-flight claim sweep: last-hour window clean (peers on #14616/#14618/#14938/#14992).
The Problem
Three instrumented runs on dev cf6147455 (full logs in the origin session):
- Run 1 — no split-brain exists here. Worker-side samples of
Neo.dashboard.DockRevealOverlay during the tour show isVdomUpdating: false, needsVdomUpdate: false, and cls STILL containing neo-dashboard-dock-reveal-overlay-hidden — while the DOM sampler agrees (4672 hidden frames, 0 visible). Worker truth and DOM truth AGREE: the unhide never happened anywhere. Not a lost reply; nothing was ever sent.
- Run 3 — the silent link convicted. An instance-field trail inside
onTourBeat at the real cue moment:
{
"beat": 14, "cueItemId": "preview",
"hostFound": true,
"railFound": false,
"railId": null, "railTabIds": [], "ret": null, "err": null,
"machineState": null, "overlayId": null
}getReference('dock-host') resolves, but down({ntype: 'dashboard-dock-rail'}) returns null at cue time — the triple-optional-chain swallows it and the reveal dies without a trace. The rail incarnations DO exist moments later (run-1 sampled neo-dashboard-dock-rail-3 alive on a 400ms grid); the cue's instant falls in the rebuild gap.
The Architectural Reality
The race is a three-party timing seam, all by-design behaviors composing into a silent no-op:
apps/agentos/childapps/dockdemo/view/DemoAWorkspace.mjs:255 — onDockZoneDocumentChange() stores the committed document immediately (document truth is always fresh) but defers refreshDockWorkspace() one tick (me.timeout(0)), the normative guard so a committing interaction surface is never destroyed mid-handler.
src/ai/client/TourRunner.mjs:276 — the runner fires the beat event (carrying the cue) BEFORE executing the step, and executeDockOperation resolves at document-commit; the runner is document-tier by design and never awaits view-sync.
apps/agentos/tour/demoADockChoreography.mjs s3 — three setItemAutoHidden commits back-to-back, then the pause step carrying cue: {type: 'reveal', itemId: 'preview'}. The cue handler (DemoAWorkspace.mjs:488) runs within ~0ms of the third commit; the deferred projection that builds the 3-tab rail has not landed.
- The 1600ms pause then elapses uselessly — the cue already returned null.
The composing defect is the cue handler's assumption that the projection is synchronous with the commit. It is not, and the workspace ITSELF owns that deferral — so the workspace owns the settle.
The Fix
Settle-aware cue path in DemoAWorkspace (no wall-clock sleeps, no runner changes):
- Track the deferred projection as an awaitable:
onDockZoneDocumentChange() assigns me.refreshPromise = me.timeout(0).then(() => me.isDestroyed ? null : me.refreshDockWorkspace()).
onTourBeat cue branch becomes async: await me.refreshPromise (tolerates null/settled), guard me.isDestroyed, THEN resolve the rail and feed onTabClick. Awaiting the actual promise is deterministic — the rail exists in the worker tree immediately after refreshDockWorkspace()'s host.insert(0, ...) returns.
- JSDoc on both sites naming the race and the settle contract (Anchor & Echo).
The scratch forensic spec (Wedge14985Diag.spec.mjs, untracked) is deleted; the EXISTING merged spec is the regression witness — DemoATourNL.spec.mjs's revealSeen assertion flips green with this fix and pins the contract forever.
Acceptance Criteria
Out of Scope
- The genuine split-brain on the FM cockpit surface (worker-committed sizes vs stale DOM) — that IS
#14985 and stays there.
- The Neural Link fixture service gaps discovered en route (
getConsoleLogs and patchCode declared in test/playwright/fixtures.mjs but missing on the underlying services) — separate tooling ticket.
- Demo-B cue handlers — they call workspace methods on document/store state and do not resolve projected components; unaffected.
Decision Record impact
none (aligned-with ADR 0029 — the holder contract's deferred view-sync stays; the fix consumes it honestly).
Related
#14985 (evidence reattribution), #14984/#14986 (the detector lineage that kept this red honest), #14589 (Demo-A origin), #13158 (QT-parity epic).
Origin Session ID: 748f9b8f-20cd-4360-9a29-c3084d059052
Retrieval Hint: "reveal cue race deferred re-projection railFound false instance-field trail"
Context
Born from the
#14985investigation (operator-directed continuation). The Demo-A tour's reveal beat has never painted — the merged whitebox spectest/playwright/e2e/dashboard/DemoATourNL.spec.mjsstands red onrevealSeen, and that red was attributed to the vdom update wedge (#12946class) as one of its two witnesses. Tonight's forensics falsify that attribution for THIS surface: the defect is an app-level race in the demo workspace, upstream of vdom entirely.Live latest-open sweep: checked latest 20 open issues at 2026-07-10T23:47Z; no equivalent found. A2A in-flight claim sweep: last-hour window clean (peers on
#14616/#14618/#14938/#14992).The Problem
Three instrumented runs on dev
cf6147455(full logs in the origin session):Neo.dashboard.DockRevealOverlayduring the tour showisVdomUpdating: false,needsVdomUpdate: false, and cls STILL containingneo-dashboard-dock-reveal-overlay-hidden— while the DOM sampler agrees (4672 hidden frames, 0 visible). Worker truth and DOM truth AGREE: the unhide never happened anywhere. Not a lost reply; nothing was ever sent.onTourBeatat the real cue moment:{ "beat": 14, "cueItemId": "preview", "hostFound": true, "railFound": false, "railId": null, "railTabIds": [], "ret": null, "err": null, "machineState": null, "overlayId": null }getReference('dock-host')resolves, butdown({ntype: 'dashboard-dock-rail'})returnsnullat cue time — the triple-optional-chain swallows it and the reveal dies without a trace. The rail incarnations DO exist moments later (run-1 sampledneo-dashboard-dock-rail-3alive on a 400ms grid); the cue's instant falls in the rebuild gap.The Architectural Reality
The race is a three-party timing seam, all by-design behaviors composing into a silent no-op:
apps/agentos/childapps/dockdemo/view/DemoAWorkspace.mjs:255—onDockZoneDocumentChange()stores the committed document immediately (document truth is always fresh) but defersrefreshDockWorkspace()one tick (me.timeout(0)), the normative guard so a committing interaction surface is never destroyed mid-handler.src/ai/client/TourRunner.mjs:276— the runner fires thebeatevent (carrying the cue) BEFORE executing the step, andexecuteDockOperationresolves at document-commit; the runner is document-tier by design and never awaits view-sync.apps/agentos/tour/demoADockChoreography.mjss3 — threesetItemAutoHiddencommits back-to-back, then the pause step carryingcue: {type: 'reveal', itemId: 'preview'}. The cue handler (DemoAWorkspace.mjs:488) runs within ~0ms of the third commit; the deferred projection that builds the 3-tab rail has not landed.The composing defect is the cue handler's assumption that the projection is synchronous with the commit. It is not, and the workspace ITSELF owns that deferral — so the workspace owns the settle.
The Fix
Settle-aware cue path in
DemoAWorkspace(no wall-clock sleeps, no runner changes):onDockZoneDocumentChange()assignsme.refreshPromise = me.timeout(0).then(() => me.isDestroyed ? null : me.refreshDockWorkspace()).onTourBeatcue branch becomes async:await me.refreshPromise(toleratesnull/settled), guardme.isDestroyed, THEN resolve the rail and feedonTabClick. Awaiting the actual promise is deterministic — the rail exists in the worker tree immediately afterrefreshDockWorkspace()'shost.insert(0, ...)returns.The scratch forensic spec (
Wedge14985Diag.spec.mjs, untracked) is deleted; the EXISTING merged spec is the regression witness —DemoATourNL.spec.mjs'srevealSeenassertion flips green with this fix and pins the contract forever.Acceptance Criteria
onDockZoneDocumentChangetracks the deferred re-projection asrefreshPromise; the reveal-cue path awaits it before rail resolution, with anisDestroyedguard.DemoATourNL.spec.mjspasses (revealSeen true) — deterministic across 3 consecutive runs.DemoADragMenuNL.spec.mjs+ the dockdemo unit suites stay green.#14985receives the reattribution comment: the tour-red witness is withdrawn as wedge evidence; the cockpit repro (from#14658/PR#14996instrumentation) remains its sole standing witness.Out of Scope
#14985and stays there.getConsoleLogsandpatchCodedeclared intest/playwright/fixtures.mjsbut missing on the underlying services) — separate tooling ticket.Decision Record impact
none (aligned-with ADR 0029 — the holder contract's deferred view-sync stays; the fix consumes it honestly).
Related
#14985(evidence reattribution),#14984/#14986(the detector lineage that kept this red honest),#14589(Demo-A origin),#13158(QT-parity epic).Origin Session ID: 748f9b8f-20cd-4360-9a29-c3084d059052 Retrieval Hint: "reveal cue race deferred re-projection railFound false instance-field trail"