Sub of #18304 (Workspace decomposition). Blocked by #18303 — needs that lane's Group provider to exist. (2026-09-05: the Group provider is PR #18360 / #18310 — Neo.manager.Transaction#getProvider(groupId); this leaf is gated on its merge.) (2026-09-05: dissolved — header state binds to the workspace's own provider, see the ledger; #18310 / PR #18360 landed first regardless.)
The Problem
Census amended 2026-09-05 by @neo-fable (assignee), read at dev 27e23b1084 / c5f02345e8: #18306 (PR #18347) extracted the syncDock* methods this section counted into src/dashboard/dock/projection/HeaderActionPolicy.mjs. Workspace.mjs now has 0 syncDock* definitions; it drives the policy from 11 call sites, and every topology commit runs the policy's syncAll twice on the settled tree (:3028, :3035) plus once after the refresh promise settles (:1949) — each pass = 5 syncs × every projected tab + syncLockRails, every write change-guarded. The re-derivation the paragraph below describes is intact; only its home moved. Original text kept for provenance:
syncDock* has 33 call sites in Workspace.mjs. @neo-fable's cluster census puts the re-derivation at ~330 lines swept per topology commit, setActiveItem included. The reason is structural rather than careless: header state has no home except the commit path, so every commit recomputes presentation that mostly did not change.
@neo-opus-vega's receipt (discussioncomment-18289767) narrowed what is actually destroyed today — no action button, tab button, toolbar or tab container is destroyed by any commit class, and retained containers never receive a headerActions assignment. So this is not a destruction defect; it is a re-derivation defect, and it disappears only when header state has an owner that outlives the commit.
The popup-birth arm showed the target already resolves correctly: header state read from the Group provider resolves identically from any window.
The Fix
Bind header state to the Group's StateProvider (owned by #18303) and delete the per-commit syncDock* sweeps that exist only to recompute it. Presentation reads state; it stops being recomputed by the mutation path.
This is the third of the three deletion budgets D#18150's step 1 named — (a) stand-ins for items with live panes, landed via #18274/PR #18279; (b) the staged shell on stable topology, retainTopology as default admission, gated on the rail reconcile #18320 owns; (c) these sweeps.
Contract Ledger Matrix
| Target Surface |
Source of Authority |
Proposed Behavior |
Fallback |
Docs |
Evidence |
| header state |
the Workspace's own stateProvider under dock.* (engine default {module: StateProvider}; a consumer's own provider on the workspace hosts the same keys beside its own) — amended 2026-09-05: the Group provider (PR #18360) remains the home of Group-wide truth (#18316) |
bound, not re-derived per commit |
— |
Workspace#stateProvider + HeaderActionPolicy docblocks |
permanent Neural Link arm test/playwright/e2e/workstation/WorkstationHeaderStatePopOutNL.spec.mjs — the same provider and the same answers from the opener and from the popup |
HeaderActionPolicy per-commit sweeps (syncAll at Workspace :1949, :3028, :3035; the event dispatches at :1988, :1993, :2152–2153, :2441, :2467, :2726, :2826 that the bindings make redundant) — amended 2026-09-05 from "syncDock* sweeps, 33 call sites" |
D#18150 step 1(c) |
deleted with the binding, same PR |
none — zero-backcompat ruling |
— |
11 call sites; 2 settled syncAll passes per commit = 2 × (5 × projected tabs) evaluations + rails |
toolbar.Base#syncActions |
shipped source |
unchanged — right for direct actions assignments |
— |
— |
@neo-fable: not the churn fix |
Acceptance Criteria
Out of Scope
- Moving the policy itself — that is #18306, and it lands first.
retainTopology as default admission — gated on the rail reconcile #18320 owns; separate leaf, separate lane.
- Any change to the Group provider's own shape (#18303).
Avoided Traps
- Starting before #18303's Group exists. There is no provider to bind to; a stand-in provider here would be a second authority, which is the failure #18303's own ledger was written to prevent. (2026-09-05: the workspace's own provider is not a stand-in for the Group's — it is the tree node header truth belongs to; Group-wide truth keeps its one authority.)
- Deleting the sweeps without the binding. That removes the recomputation and the correctness together.
Related
#18304 (epic) · #18303 (blocker — owns the Group provider) · #18306 (policy vehicle, lands first) · #18320 (rail reconciliation) · #18274 → PR #18279 (step 1a, landed) · D#18150 DC_kwDODSospM4BFxvy
Runtime correctness control: NEO_INLINE_CODE_46. Reload flight state changes header enablement without changing the dock document, so document-only invalidation cannot replace this behavior.
Live latest-open sweep: latest 20 open issues at 2026-09-04T18:12Z; no equivalent. A2A claim sweep: no claim. Own-assignment sweep: none on this surface. Memory Core: stale-2025 recall degradation today — null, not clean.
Decision Record impact: depends-on — consumes #18303's Group contract; amends nothing.
Origin Session ID: b983b2a8-18bc-4dd0-a63f-23380dc3a49d
Sub of #18304 (Workspace decomposition).
Blocked by #18303 — needs that lane's Group provider to exist.(2026-09-05: the Group provider is PR #18360 / #18310 —Neo.manager.Transaction#getProvider(groupId); this leaf is gated on its merge.) (2026-09-05: dissolved — header state binds to the workspace's own provider, see the ledger; #18310 / PR #18360 landed first regardless.)The Problem
syncDock*has 33 call sites inWorkspace.mjs. @neo-fable's cluster census puts the re-derivation at ~330 lines swept per topology commit,setActiveItemincluded. The reason is structural rather than careless: header state has no home except the commit path, so every commit recomputes presentation that mostly did not change.@neo-opus-vega's receipt (discussioncomment-18289767) narrowed what is actually destroyed today — no action button, tab button, toolbar or tab container is destroyed by any commit class, and retained containers never receive a
headerActionsassignment. So this is not a destruction defect; it is a re-derivation defect, and it disappears only when header state has an owner that outlives the commit.The popup-birth arm showed the target already resolves correctly: header state read from the Group provider resolves identically from any window.
The Fix
Bind header state to the Group's
StateProvider(owned by #18303) and delete the per-commitsyncDock*sweeps that exist only to recompute it. Presentation reads state; it stops being recomputed by the mutation path.This is the third of the three deletion budgets D#18150's step 1 named — (a) stand-ins for items with live panes, landed via #18274/PR #18279; (b) the staged shell on stable topology,
retainTopologyas default admission, gated on the rail reconcile #18320 owns; (c) these sweeps.Contract Ledger Matrix
stateProviderunderdock.*(engine default{module: StateProvider}; a consumer's own provider on the workspace hosts the same keys beside its own) — amended 2026-09-05: the Group provider (PR #18360) remains the home of Group-wide truth (#18316)Workspace#stateProvider+HeaderActionPolicydocblockstest/playwright/e2e/workstation/WorkstationHeaderStatePopOutNL.spec.mjs— the same provider and the same answers from the opener and from the popupHeaderActionPolicyper-commit sweeps (syncAllat Workspace:1949,:3028,:3035; the event dispatches at:1988,:1993,:2152–2153,:2441,:2467,:2726,:2826that the bindings make redundant) — amended 2026-09-05 from "syncDock*sweeps, 33 call sites"syncAllpasses per commit = 2 × (5 × projected tabs) evaluations + railstoolbar.Base#syncActionsactionsassignmentsAcceptance Criteria
dock.*onWorkspace#stateProvider, chained upward by the ordinary hierarchy); theHeaderActionPolicyper-commit sweeps that exist solely to re-derive it are deleted in the same PR. (Amended 2026-09-05 — originally "read from the Group provider"; see the ledger row and the Avoided Traps note.)toolbar.Base#syncActionsis untouched.Out of Scope
retainTopologyas default admission — gated on the rail reconcile #18320 owns; separate leaf, separate lane.Avoided Traps
Related
#18304 (epic) · #18303 (blocker — owns the Group provider) · #18306 (policy vehicle, lands first) · #18320 (rail reconciliation) · #18274 → PR #18279 (step 1a, landed) · D#18150 DC_kwDODSospM4BFxvy
Runtime correctness control: NEO_INLINE_CODE_46. Reload flight state changes header enablement without changing the dock document, so document-only invalidation cannot replace this behavior.
Live latest-open sweep: latest 20 open issues at 2026-09-04T18:12Z; no equivalent. A2A claim sweep: no claim. Own-assignment sweep: none on this surface. Memory Core: stale-2025 recall degradation today — null, not clean.
Decision Record impact:
depends-on— consumes #18303's Group contract; amends nothing.Origin Session ID: b983b2a8-18bc-4dd0-a63f-23380dc3a49d