LearnNewsExamplesServices
Frontmatter
id15171
titleDock projection: migrate remaining consumers to the identity reconciler
stateClosed
labels
enhancementdesignaiarchitecture
assigneesneo-gpt-emmy
createdAtJul 14, 2026, 1:15 PM
updatedAtJul 14, 2026, 7:39 PM
githubUrlhttps://github.com/neomjs/neo/issues/15171
authorneo-gpt-emmy
commentsCount0
parentIssue13158
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[x] 15136 Workstation dock projection: preserve tab-chrome identity across document refresh
blocking[]
closedAtJul 14, 2026, 7:39 PM
milestonev13.2

Dock projection: migrate remaining consumers to the identity reconciler

Closed Backlog/active-chunk-6 enhancementdesignaiarchitecture
neo-gpt-emmy
neo-gpt-emmy commented on Jul 14, 2026, 1:15 PM

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.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.
  • Demo A's preview/indicator overlays are persistent siblings outside projection child 0.
  • 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

  1. Migrate Demo A to DockProjectionReconciler using projection child index 0 while preserving its preview and indicator siblings, drag-affordance invalidation, and FLIP bracket.
  2. 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.
  3. Migrate examples/dashboard/dock/MainContainer so the public example demonstrates the shared staged transaction rather than removeAll().
  4. Give each consumer a narrow item-placeholder factory and resolver policy; do not duplicate the reconciler's four ownership commits in app code.
  5. Remove or rewrite comments that call coarse rebuilding normative after the migration.
  6. Keep DockLayoutAdapter stateless and keep app-specific animation, overlay, and controller policies in their consumers.

Contract Ledger

Surface Authority Required behavior Failure boundary
projection transaction DockProjectionReconciler.reconcileProjection(options) one shared staged ownership path 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.

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 handoff

Authored by Emmy (GPT-5.6 Sol Ultra, Codex). Session adddb25d-fc36-4b08-b9a3-3a62a108cda1.

tobiu referenced in commit bdd741f - "refactor(dashboard): reconcile remaining dock projections (#15171) (#15176)" on Jul 14, 2026, 7:39 PM
tobiu closed this issue on Jul 14, 2026, 7:39 PM