The architecture challenge on #15136 exposed a shared ownership boundary: repeated dock projection is not a Workstation-private concern. PR #15170 therefore introduces Neo.dashboard.DockProjectionReconciler in src/dashboard/ and proves the same renderer-safe transaction in two consumers with different pane policies: Workstation and Dock Demo B.
A live all-state GitHub duplicate sweep, latest-open queue check, and source-consumer sweep were revalidated on 2026-07-14. No equivalent ticket or landed successor exists. Three direct DockLayoutAdapter consumers still rebuild their projection shell coarsely:
DemoAWorkspace.refreshDockWorkspace() removes/inserts child 0 while preserving overlay siblings;
FleetCockpit.refreshDockWorkspace() uses removeAll() around interactive drag/splitter scheduling;
examples/dashboard/dock/MainContainer.refreshProjection() uses removeAll() as the public example pattern.
The Problem
After #15170, the shared identity-preserving transaction exists, but these three consumers still destroy and recreate surviving tab chrome on every committed dock-document projection. That leaves repository examples and demos teaching two contradictory ownership models:
surviving logical tab nodes lose their tab.Container, toolbar, body, strip, Overflow, and button identities;
app-local pane parking or recreation substitutes for shared native reparenting;
coarse teardown remains entangled with overlay ordering and mid-interaction scheduling;
new consumers can copy the stale pattern even though the dashboard layer now owns the correct one.
The migration cannot be a blind mechanical replacement. Each consumer has a distinct boundary that must remain intact.
Architectural Reality
DockLayoutAdapter remains the pure, stateless document-to-config projector.
DockProjectionReconciler.reconcileProjection(options) owns the staged target shell, separate descendant/ancestor ownership commits, retired-shell cleanup, and final Overflow reprojection.
Existing live items are discovered from model-owned tab order before the app resolver is consulted; an app resolver creates or retrieves only genuinely absent items.
Fleet Cockpit deliberately defers refresh out of splitter/drag handlers because destroying an active interaction owner mid-handler is unsafe.
The dashboard example is public architectural teaching substrate; its implementation must reflect the normative ownership path.
Intended Solution
Migrate Demo A to DockProjectionReconciler using projection child index 0 while preserving its preview and indicator siblings, drag-affordance invalidation, and FLIP bracket.
Migrate Fleet Cockpit without weakening its deferred-refresh interaction guard; retain controller/state ownership and prove active splitter/drag handlers are never destroyed mid-callback.
Migrate examples/dashboard/dock/MainContainer so the public example demonstrates the shared staged transaction rather than removeAll().
Give each consumer a narrow item-placeholder factory and resolver policy; do not duplicate the reconciler's four ownership commits in app code.
Remove or rewrite comments that call coarse rebuilding normative after the migration.
Keep DockLayoutAdapter stateless and keep app-specific animation, overlay, and controller policies in their consumers.
missing shell/item fails named; never duplicates ownership
surviving tab chrome
dock node id + live tab.Container
component identities survive committed projection
removed logical node destroys once
existing live item
current tab order
reuse before invoking app resolver
resolver owns only absent-item policy
Demo A overlays
dock-host child ordering
preview/indicator siblings survive every refresh
projection remains child 0
Fleet interaction
existing deferred-refresh guard
no active drag/splitter owner is destroyed mid-handler
refresh remains scheduled outside callback
public example
examples/dashboard/dock/MainContainer.mjs
teaches shared identity reconciliation
no removeAll() projection pattern
Acceptance Criteria
Demo A, Fleet Cockpit, and the dashboard dock example call the shared projection reconciler for committed document refresh.
None of the three uses removeAll() or projection-child remove/insert as its normal refresh path.
Every surviving logical tabs node keeps its tab.Container, header toolbar, body, strip, Overflow plugin/control, and surviving tab-button component identities.
Existing live pane identity is preserved; an app resolver is consulted only for an item absent from the current live projection.
A genuinely removed logical tab node and its retired chrome are destroyed exactly once.
Demo A's preview and indicator overlay component identities and ordering survive refresh.
Fleet drag, resize, and deferred-refresh behavior remains green with no use-after-destroy interaction owner.
The dashboard example's comments describe the reconciler as the normative repeated-projection path.
Focused unit coverage pins each consumer's special boundary, and whitebox E2E covers Demo A plus Fleet interaction continuity.
DockLayoutAdapter remains stateless; no second app-local copy of the ownership transaction appears.
Decision Record Impact
None expected. ADR 0029 and learn/agentos/HarnessDockZoneModel.md already place reusable dock projection in src/dashboard/. Escalate only if a consumer falsifies the current reconciler contract rather than extending it through app-local branching.
Out of Scope
Changing the dock-zone document schema · rewriting FLIP geometry · changing tab.Container public APIs · merging app-specific overlay/controller policy into the reconciler · generic keyed-VDOM diffing.
Avoided Traps
Do not migrate by copying Workstation's animation/menu witnesses into every app.
Do not remove Fleet's mid-handler scheduling guard merely because shell teardown disappears.
Do not destroy Demo A's persistent overlay siblings while replacing projection child 0.
Do not make the adapter stateful.
Do not claim pane permanence from a cache while leaving tab chrome disposable.
Context
The architecture challenge on #15136 exposed a shared ownership boundary: repeated dock projection is not a Workstation-private concern. PR #15170 therefore introduces
Neo.dashboard.DockProjectionReconcilerinsrc/dashboard/and proves the same renderer-safe transaction in two consumers with different pane policies: Workstation and Dock Demo B.A live all-state GitHub duplicate sweep, latest-open queue check, and source-consumer sweep were revalidated on 2026-07-14. No equivalent ticket or landed successor exists. Three direct
DockLayoutAdapterconsumers still rebuild their projection shell coarsely:DemoAWorkspace.refreshDockWorkspace()removes/inserts child 0 while preserving overlay siblings;FleetCockpit.refreshDockWorkspace()usesremoveAll()around interactive drag/splitter scheduling;examples/dashboard/dock/MainContainer.refreshProjection()usesremoveAll()as the public example pattern.The Problem
After #15170, the shared identity-preserving transaction exists, but these three consumers still destroy and recreate surviving tab chrome on every committed dock-document projection. That leaves repository examples and demos teaching two contradictory ownership models:
tab.Container, toolbar, body, strip, Overflow, and button identities;The migration cannot be a blind mechanical replacement. Each consumer has a distinct boundary that must remain intact.
Architectural Reality
DockLayoutAdapterremains the pure, stateless document-to-config projector.DockProjectionReconciler.reconcileProjection(options)owns the staged target shell, separate descendant/ancestor ownership commits, retired-shell cleanup, and final Overflow reprojection.Intended Solution
DockProjectionReconcilerusing projection child index 0 while preserving its preview and indicator siblings, drag-affordance invalidation, and FLIP bracket.examples/dashboard/dock/MainContainerso the public example demonstrates the shared staged transaction rather thanremoveAll().DockLayoutAdapterstateless and keep app-specific animation, overlay, and controller policies in their consumers.Contract Ledger
DockProjectionReconciler.reconcileProjection(options)tab.Containerexamples/dashboard/dock/MainContainer.mjsremoveAll()projection patternAcceptance Criteria
removeAll()or projection-child remove/insert as its normal refresh path.tab.Container, header toolbar, body, strip, Overflow plugin/control, and surviving tab-button component identities.DockLayoutAdapterremains stateless; no second app-local copy of the ownership transaction appears.Decision Record Impact
None expected. ADR 0029 and
learn/agentos/HarnessDockZoneModel.mdalready place reusable dock projection insrc/dashboard/. Escalate only if a consumer falsifies the current reconciler contract rather than extending it through app-local branching.Out of Scope
Changing the dock-zone document schema · rewriting FLIP geometry · changing
tab.Containerpublic APIs · merging app-specific overlay/controller policy into the reconciler · generic keyed-VDOM diffing.Avoided Traps
Related
Blocked by #15136 · Related: #15170 · #15099 · #14780
Origin Session ID: adddb25d-fc36-4b08-b9a3-3a62a108cda1
Retrieval Hint:
DockProjectionReconciler remaining coarse consumers DemoA FleetCockpit dashboard example removeAll identity handoffAuthored by Emmy (GPT-5.6 Sol Ultra, Codex). Session adddb25d-fc36-4b08-b9a3-3a62a108cda1.