Context
Surfaced during #12807 / #12893 whitebox-e2e verification (locked-column grid DnD). The neuralLink fixture's worker assertions read a different App Worker than the one Playwright's page.mouse drives, producing results decoupled from the test's own gesture. Root-caused via live Neural-Link exploration + a local spec run.
The Problem
test/playwright/fixtures.mjs connectToApp(appName) resolves the bridge session by app name and binds to the oldest matching session. When a persistent dev app of the same name is already connected to the bridge (e.g. a developer's open DevIndex tab), the test's worker queries (getComponent, findInstances, …) bind to that instance, while page.mouse drives the test's own freshly-navigated page. Two layers reading two different App Worker instances — silently.
Empirical proof (#12893 GridColumnCrossBodyDnD.spec.mjs): with a persistent DevIndex connected (~8h uptime, a column stale-dragged to locked-end), the worker assertions read that stale app's state regardless of the test's gesture. After rewriting the worker oracle to the grid's region-column arrays, a local run fails only at the pristine-state guard — centerColumns[0] is the stale app's column, not the test page's totalContributions — confirming the assertion mechanism is correct and the binding is the sole defect. Clean CI (only the test's page connects) is unaffected.
The fixture's own JSDoc states the intent it does not meet: "Waits for the Test's specific App Worker… Mostly we wait for this specific page's workerId." The implementation resolves by appName, not the page's worker id.
The Architectural Reality
test/playwright/fixtures.mjs connectToApp(appName) → NeuralLink_ConnectionService.waitForSession(targetId).
ai/services/neural-link/ConnectionService.mjs waitForSession (~:520-529) iterates sessionData in Map-insertion order and returns the first session matching id === target || appName-match → oldest appName match wins.
- The exact-id branch (
id === target, ~:524) already supports binding to a specific App Worker — so the canonical session key (the app-worker-id, surfaced in bridge session data / get_worker_topology) is the correct target to pass.
- Sibling NL-bridge-correctness bug: #12884 (non-SharedWorker apps never register their window →
simulate_event unroutable) — a different mechanism (window registration vs session selection), same whitebox-e2e cluster.
The Fix
Identity-bind via the App Worker id. connectToApp reads its own page's app-worker-id and passes it to waitForSession (which already exact-id-matches), instead of passing appName. The one addition is a clean page-readable getter for the app-worker-id (Neo.worker.App.id, sent on connect — per @neo-opus-vega's reading of src/ai/Client.mjs; the implementer confirms the exact page.evaluate path).
Why identity over the alternatives:
- newest-connectedAt: works only under
--workers=1 and breaks if the persistent app hot-reloads after the test page connects.
- capture-pre-existing-then-return-new: racy; needs fixture-setup timing coordination.
- identity: parallel-safe + deterministic, no failure mode (frees the whitebox suite from
--workers=1).
Fallback if exposing the app-worker-id proves awkward: a unique per-test Neo.config.appName (already config-set + bridge-sent) — but the app-worker-id is the canonical key, prefer it.
Contract Ledger Matrix
| Target Surface |
Source of Authority |
Proposed Behavior |
Fallback |
Docs |
Evidence |
neuralLink.connectToApp(appName) (test/playwright/fixtures.mjs) |
the test page's own App Worker id (Neo.worker.App.id, surfaced in bridge session data / get_worker_topology) |
bind to the test page's own worker via exact-id match |
unique per-test Neo.config.appName; or a page-scoped RMA oracle |
learn/guides/testing/WhiteboxE2E.md |
#12893 local run binds to a stale persistent app; ConnectionService.mjs id === target branch already supported |
Decision Record impact
none.
Acceptance Criteria
Out of Scope
- The window-registration /
simulate_event routing gap (#12884 — sibling, distinct mechanism).
- The grid DnD defects themselves (#12883 and adjacent).
- A
simulateDrag convenience primitive.
Related
- #12807 (whitebox-e2e simulatability — the feature this surfaced under); #12893 (the spec that root-caused it).
- #12884 (sibling NL-bridge-correctness bug — window registration /
simulate_event).
Implementer
@neo-opus-vega (per A2A agreement 2026-06-11 — NL domain; root-cause cross-verified with @neo-opus-vega on the column model and @neo-fable's worker-tree map).
Release classification: boardless — clean CI is unaffected (only the test's page connects); this fixes local whitebox-e2e dev and unblocks #12893's local verification. Not release-blocking.
Origin Session ID: 702ed4a8-54ae-4b09-911a-15b10e58c9d3
Retrieval Hint: query_raw_memories("neuralLink connectToApp binds oldest appName session not test page")
Live latest-open sweep: checked latest 35 open issues at 2026-06-11T~10:55Z; closest adjacent is #12884 (distinct mechanism — window registration, not session selection); no equivalent session-binding ticket. A2A in-flight sweep: @neo-opus-vega confirmed "you file, I implement" (no competing claim); no other lane-claim on this scope.
Context
Surfaced during #12807 / #12893 whitebox-e2e verification (locked-column grid DnD). The
neuralLinkfixture's worker assertions read a different App Worker than the one Playwright'spage.mousedrives, producing results decoupled from the test's own gesture. Root-caused via live Neural-Link exploration + a local spec run.The Problem
test/playwright/fixtures.mjsconnectToApp(appName)resolves the bridge session by app name and binds to the oldest matching session. When a persistent dev app of the same name is already connected to the bridge (e.g. a developer's open DevIndex tab), the test's worker queries (getComponent,findInstances, …) bind to that instance, whilepage.mousedrives the test's own freshly-navigated page. Two layers reading two different App Worker instances — silently.Empirical proof (#12893
GridColumnCrossBodyDnD.spec.mjs): with a persistent DevIndex connected (~8h uptime, a column stale-dragged to locked-end), the worker assertions read that stale app's state regardless of the test's gesture. After rewriting the worker oracle to the grid's region-column arrays, a local run fails only at the pristine-state guard —centerColumns[0]is the stale app's column, not the test page'stotalContributions— confirming the assertion mechanism is correct and the binding is the sole defect. Clean CI (only the test's page connects) is unaffected.The fixture's own JSDoc states the intent it does not meet: "Waits for the Test's specific App Worker… Mostly we wait for this specific page's workerId." The implementation resolves by appName, not the page's worker id.
The Architectural Reality
test/playwright/fixtures.mjsconnectToApp(appName)→NeuralLink_ConnectionService.waitForSession(targetId).ai/services/neural-link/ConnectionService.mjswaitForSession(~:520-529) iteratessessionDatain Map-insertion order and returns the first session matchingid === target || appName-match→ oldest appName match wins.id === target, ~:524) already supports binding to a specific App Worker — so the canonical session key (the app-worker-id, surfaced in bridge session data /get_worker_topology) is the correcttargetto pass.simulate_eventunroutable) — a different mechanism (window registration vs session selection), same whitebox-e2e cluster.The Fix
Identity-bind via the App Worker id.
connectToAppreads its own page's app-worker-id and passes it towaitForSession(which already exact-id-matches), instead of passingappName. The one addition is a clean page-readable getter for the app-worker-id (Neo.worker.App.id, sent on connect — per @neo-opus-vega's reading ofsrc/ai/Client.mjs; the implementer confirms the exactpage.evaluatepath).Why identity over the alternatives:
--workers=1and breaks if the persistent app hot-reloads after the test page connects.--workers=1).Fallback if exposing the app-worker-id proves awkward: a unique per-test
Neo.config.appName(already config-set + bridge-sent) — but the app-worker-id is the canonical key, prefer it.Contract Ledger Matrix
neuralLink.connectToApp(appName)(test/playwright/fixtures.mjs)Neo.worker.App.id, surfaced in bridge session data /get_worker_topology)Neo.config.appName; or a page-scoped RMA oraclelearn/guides/testing/WhiteboxE2E.mdConnectionService.mjsid === targetbranch already supportedDecision Record impact
none.
Acceptance Criteria
connectToAppbinds to the test page's own App Worker even when another same-named app is connected to the bridge (verify: with a second DevIndex connected, the worker query reads the test page's pristine state).GridColumnCrossBodyDnD.spec.mjspasses its pristine-state guard + worker assertions locally with a second DevIndex open (currently fails only at the guard).--workers=1for correct binding (identity is parallel-safe).WhiteboxE2E.mddocuments howconnectToAppresolves the session (one line on identity-bind).Out of Scope
simulate_eventrouting gap (#12884 — sibling, distinct mechanism).simulateDragconvenience primitive.Related
simulate_event).Implementer
@neo-opus-vega (per A2A agreement 2026-06-11 — NL domain; root-cause cross-verified with @neo-opus-vega on the column model and @neo-fable's worker-tree map).
Release classification: boardless — clean CI is unaffected (only the test's page connects); this fixes local whitebox-e2e dev and unblocks #12893's local verification. Not release-blocking.
Origin Session ID: 702ed4a8-54ae-4b09-911a-15b10e58c9d3 Retrieval Hint:
query_raw_memories("neuralLink connectToApp binds oldest appName session not test page")Live latest-open sweep: checked latest 35 open issues at 2026-06-11T~10:55Z; closest adjacent is #12884 (distinct mechanism — window registration, not session selection); no equivalent session-binding ticket. A2A in-flight sweep: @neo-opus-vega confirmed "you file, I implement" (no competing claim); no other lane-claim on this scope.