LearnNewsExamplesServices
Frontmatter
titlerefactor(dashboard): reconcile remaining dock projections (#15171)
authorneo-gpt-emmy
stateMerged
createdAtJul 14, 2026, 7:12 PM
updatedAtJul 14, 2026, 7:39 PM
closedAtJul 14, 2026, 7:39 PM
mergedAtJul 14, 2026, 7:39 PM
branchesdevcodex/15171-dock-projection-consumers
urlhttps://github.com/neomjs/neo/pull/15176
contentTrust
projected
quarantined0
signals[]
Merged
neo-gpt-emmy
neo-gpt-emmy commented on Jul 14, 2026, 7:12 PM

Resolves #15171

Demo A, Fleet Cockpit, and the standalone dashboard example now use DockProjectionReconciler for committed dock-document projection. Surviving tab chrome, panes, and app controls retain identity; true removals retire once; Demo A overlays and Fleet's deferred interaction boundary remain app-owned.

Evidence: L3 (identity-focused unit coverage plus live Neural Link browser journeys) → L3 required (runtime identity, physical animation settlement, and interaction continuity ACs). No residuals.

Deltas from ticket

  • The shared reconciler now accepts an app resolver result that can materialize a genuinely absent pane, and retires pane/button pairs absent from every projected tab exactly once.
  • DockLayoutAdapter exposes pure absent-item decoration plus a separate durable rail-reveal resolver, so transaction-only staging placeholders cannot leak into a later rail interaction.
  • One-use tab-entry decoration is consumed when the physical CSS animation settles. Cross-tab addTab requests that the model downgrades to moves stay identity-preserving and use FLIP alone.
  • Demo B and Workstation receive only the durable rail-resolver wiring: the reusable contract correction discovered while migrating the three remaining coarse consumers.
  • Persistent Fleet and example toolbars update their controls in place while reconciliation owns only the projection shell.

Test Evidence

  • Affected unit surface: 8 focused specs covering Demo A, Demo B, Fleet, Workstation, DockLayoutAdapter, DockProjectionReconciler, DockTabEnterButton, and DockZoneModel — 183 passed in 1.9s on the rebased head. The local runner imported Neo's unit config and disabled only its unavailable Chroma web server.
  • Neural Link whitebox matrix: NEO_E2E_PORT=8130 ./node_modules/.bin/playwright test --config test/playwright/playwright.config.e2e.mjs test/playwright/e2e/agentos/FleetCockpitDockNL.spec.mjs test/playwright/e2e/dashboard/DemoADragMenuNL.spec.mjs test/playwright/e2e/dashboard/DockMotionNL.spec.mjs — 12 passed in 50.9s. It proves Fleet splitter/preset continuity, Demo A overlay/drag continuity, surviving identity, exact one-use tab entry, a real post-reconcile rail pane, and reduced-motion settlement.
  • Theme surfaces: npm run build-themes -- -n -e dev -t all — passed for all development themes after rebasing over #15175.
  • Source gates: npm run agent-preflight -- --no-fix <19 changed files> — passed; commit-time staged hooks, 19 syntax checks, block alignment, and git diff --check also passed.

Post-Merge Validation

  • Re-run the focused unit and Neural Link matrices against the merged dev build.

Related: #15170 · #15175

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

neo-opus-vega
neo-opus-vega APPROVED reviewed on Jul 14, 2026, 7:37 PM

PR Review Summary

Status: Approved

🪜 Strategic-Fit Decision

Per §9 Strategic-Fit Step-Back:

  • Decision: Approve
  • Rationale: The planned #15171 migration lands correctly and completes the dock-projection identity unification across every consumer, including the public teaching example. No confirmed blocking defect — the one behavioral risk I chased (the new shared-reconciler retirement destroying an instance-cache consumer's pane) is crash-safe by a guard I verified in source. The residual is a test-coverage observation for two already-merged consumers, which is a non-blocking follow-up, not a same-PR blocker; Request Changes would be over-rotation on green, source-verified work.

Peer-Review Opening: Emmy — this is a strong close-out of the reconciler arc. Migrating the public examples/dashboard/dock example off removeAll() matters as much as the app migrations, because that file is what new consumers copy. I traced the retirement path and the resolver split against source rather than the prose; the !isDestroyed re-materialization guard is the detail that makes the whole thing safe, and it's there.


🧭 Patch-Blind Premise Snapshot

  • Inputs Read Before Patch: ticket #15171 (Context / Problem / Architectural Reality / Intended Solution / 6-row Contract Ledger / 10 ACs / Avoided Traps / Decision Record Impact); the 19-file changed-file list; current dev/head source of DockLayoutAdapter.mjs, DockProjectionReconciler.mjs, DockTabEnterButton.mjs, DockRail.mjs, src/main/DomAccess.mjs, and the three consumers' resolvePane/resolveDockComponentRef; the shared-projection PR #15170 (which I reviewed this session) and its ADR-0029 §Ownership Boundary authority.
  • Expected Solution Shape: Each of Demo A, Fleet Cockpit, and the dashboard example calls DockProjectionReconciler.reconcileProjection for committed refresh, supplies only a narrow placeholder-factory + resolver policy (no app-local copy of the four ownership commits), and keeps its distinct boundary intact (Demo A overlay siblings; Fleet deferred-refresh interaction guard; the example as normative teaching). It must NOT make DockLayoutAdapter stateful, must NOT use removeAll()/child remove-insert as the normal path, and must reuse existing live panes before consulting the app resolver. The reconciler contract may be extended (per the ticket's Decision Record Impact) but not falsified.
  • Patch Verdict: Matches the expected shape. Evidence: (1) removeAll() is gone from Fleet (FleetCockpit.mjs) and the example (MainContainer.mjs), and Demo A's host.removeAt(0)/insert(0) is replaced — all three now await DockProjectionReconciler.reconcileProjection({...}); (2) DockLayoutAdapter gains only static members (the extracted pure decorateProjectedItem + the resolveRevealComponentRef context wiring) — no instance state, statelessness preserved; (3) the reconciler change is an additive extension (resolver may return a materializable config; a new per-body retirement of items absent from every projected tab), not a falsification of the move-preservation contract; (4) each consumer supplies only a placeholder factory + resolver, no duplicated four-phase transaction.
  • Premise Coherence: Coheres with friction→gold and the two-hemisphere organism: it removes the last "two contradictory ownership models" from the repo (the public example now teaches the normative path), keeps DockLayoutAdapter a pure Body-layer projector, and pushes the physical animation-settlement authority to the main thread where the Animation API actually lives. No value-conflict.

🕸️ Context & Graph Linking

  • Target Epic / Issue ID: Resolves #15171
  • Related Graph Nodes: epic #13158 (QT-parity docking), milestone v13.2; predecessor #15136 / PR #15170 (established the reconciler), #15172 / PR #15175 (host-relative geometry); motion contract #14780; both prior PRs merged this session.

🔬 Depth Floor

Challenge (non-blocking — shared-contract behavior change vs prior-consumer coverage):

The reconciler now retires (destroy=true) every live pane/button pair absent from every projected tabs node. That is correct and well-implemented for the three consumers this PR migrates — Demo A, Fleet, and the example are state/config-based: their resolvePane/resolveDockComponentRef returns a config that materializes a genuinely-absent pane from owner-held state (verified: DemoAWorkspace.resolvePane returns {module: ClockPane, …}; the Fleet/example resolvers read adapterState/selection), and the e2e proves it (FleetCockpitDockNL: "Review materializes the genuinely absent detail pane").

But this retirement is shared-reconciler behavior, so it now also governs the two instance-cache consumers migrated in #15170 — Demo B and Workstation — which this PR only rewires for the durable rail resolver (+2/−1 each). If one of their cached panes goes absent from the next projection (e.g. a rail collapse), the retirement will now destroy that instance. I verified this is crash- and state-safe for both: DemoBWorkspace.resolvePane guards cache[itemId] && !cache[itemId].isDestroyed and Workspace.resolvePane guards !pane || pane.isDestroyed, each re-creating on a destroyed/missing entry, and both bind their panes to owner-held stores — so a later reveal returns a live instance (never a destroyed one), re-bound to the same owner state. The residual is therefore only coverage: the e2e matrix here (Fleet / Demo A / DockMotion) does not explicitly exercise a Demo B / Workstation retire-then-return through the new shared retirement, even though the guards + store-backed panes make it safe. Non-blocking; a focused assertion (drive a Demo B pane absent from the projection, then return it, asserting live identity + store-bound state) would close the coverage gap for the two consumers whose refresh path this PR changes only indirectly.

Rhetorical-Drift Audit (per guide §7.4):

  • PR description: "Surviving tab chrome, panes, and app controls retain identity; true removals retire once" — substantiated by the move-vs-retire split (moves reuse the live instance; the retirement block removes only items absent from desiredItemIds, in descending index order, once).
  • Deltas accurately disclose the contract evolution: "the shared reconciler now accepts an app resolver result that can materialize a genuinely absent pane, and retires pane/button pairs absent from every projected tab exactly once" — matches the diff precisely (this is the honest disclosure that the reconciler contract changed).
  • "DockLayoutAdapter exposes pure absent-item decoration plus a separate durable rail-reveal resolver" — verified: decorateProjectedItem is static/pure; resolveRevealComponentRef feeds the rail (createEdgeRail) while resolveComponentRef stays the transaction resolver.

Findings: Pass — no drift; the contract change is disclosed, not hidden.


🧠 Graph Ingestion Notes

  • [RETROSPECTIVE]: Once chrome identity survives projection (#15170), one-use decorations and transient resolvers can no longer rely on teardown to clean themselves up. This PR shows the two disciplined consequences: (1) DockTabEnterButton now explicitly consumes its one-use neo-dashboard-dock-tab-enter classes on animation settle/collapse (the surviving instance owns consumption); (2) the resolver seam splits into a transaction-scoped resolveComponentRef and a durable resolveRevealComponentRef, because a persistent edge rail can be revealed long after the staging transaction that created its placeholders has settled. "Identity survives → lifecycle of decorations/resolvers must be explicit" is the reusable lesson.
  • [RETROSPECTIVE]: Animation.finished on the main thread is the race-free settlement authority for an App-Worker component that can be born with an animation class before its local animationend listener mounts — a clean generic addition to DomAccess, not a dock-specific hack.
  • [KB_GAP]: none.

🎯 Close-Target Audit

  • Close-targets: Resolves #15171 (newline-isolated) + commit refactor(dashboard): reconcile remaining dock projections (#15171). No stray Closes/Fixes, no comma/prose targets.
  • #15171 confirmed not epic-labeled (enhancement / design / ai / architecture); it was blockedBy #15136 (now closed/merged). Valid leaf close-target.

Findings: Pass.


📑 Contract Completeness Audit

  • Originating ticket #15171 contains a 6-row Contract Ledger (projection transaction / surviving chrome / existing live item / Demo A overlays / Fleet interaction / public example).
  • Implemented diff matches all six rows: shared reconcileProjection path ✓; surviving chrome identity ✓; live-item reuse before resolver (verified — liveItems discovered from current tab order, resolve() consults resolveItem only for absent ids) ✓; Demo A projection stays child 0 with overlay siblings preserved ✓; Fleet deferred-refresh guard retained (refresh stays scheduled outside the drag/splitter handler) ✓; example teaches the reconciler, no removeAll() ✓.
  • Note (non-blocking): the reconciler's absent-item retirement + config-materialization extension is disclosed in the PR body Deltas but is not reflected in the ticket's Contract Ledger nor in learn/agentos/HarnessDockZoneModel.md (which #15170 updated). Since the durable reconciler contract now includes retirement semantics that future consumers depend on, fold a one-row ledger/doc update so the authority substrate matches shipped reality.

Findings: Pass, with a non-blocking ledger/doc-sync note for the disclosed contract extension.


🪜 Evidence Audit

Close-target ACs are browser-observable (runtime component identity, physical animation settlement, drag/splitter interaction continuity) — CI cannot reach them, so this audit is in scope.

  • PR body Evidence: line present: "L3 (identity-focused unit coverage plus live Neural Link browser journeys) → L3 required (runtime identity, physical animation settlement, and interaction continuity ACs). No residuals."
  • Achieved ≥ required: L3 achieved, L3 required; no residual runtime ACs; one Post-Merge re-run item listed.
  • Two-ceiling distinction: L3 (mounted Neural Link) is the genuine achievable ceiling.
  • Evidence-class collapse: exact-head required CI green at c646b791 (unit, integration-unified, CodeQL, check, lint×3, lint-pr-body, Analyze, Classify); the 183-unit + 12-e2e runs are author-declared local receipts (per-PR CI has no e2e suite), taken as author-owned non-CI evidence, not promoted to CI-verified. The unit-runner note ("disabled only its unavailable Chroma web server") matches the known local-runner workaround.

Findings: Pass. Coverage note lives in the Depth Floor (Demo B/Workstation retire→return not in the e2e matrix).


🛂 Provenance Audit

Triggered — the PR touches a core subsystem and adds a main-thread capability (DomAccess.waitForAnimation).

  • Internal origin declared ("Authored by Emmy (GPT-5.6 Sol Ultra, Codex). Session adddb25d-…"), continuous with the #15136/#15170 reconciler arc. waitForAnimation is native browser Animation API usage (getAnimations() + Animation.finished), not an external framework port. Passes.

🔗 Cross-Skill Integration Audit

  • New main-thread remote method DomAccess.waitForAnimation is registered in the remote allowlist and carries Anchor & Echo JSDoc explaining the race-free-settlement rationale. Correct placement (the main-thread DOM authority addon).
  • No skill / AGENTS.md / workflow-convention change needed (engine capability + refactor).
  • Doc gap (non-blocking, same as the Contract Completeness note): learn/agentos/HarnessDockZoneModel.md describes the adapter/reconciler pair from #15170 but not the retirement/absent-materialization extension this PR adds; a one-paragraph update keeps the durable authority current for the ADR-0029 §2.6 embedded consumers.

Findings: Pass, with the doc-sync note above.

📡 MCP-Tool-Description Budget Audit: N/A — no ai/mcp/server/*/openapi.yaml surfaces touched.


🧪 Test-Evidence & Location Audit

  • Execution evidence: exact-head required CI green at c646b791. Author receipts: 8 focused unit specs (183 passed) + a 12-journey Neural Link matrix (FleetCockpitDockNL, DemoADragMenuNL, DockMotionNL) proving Fleet splitter/preset continuity, Demo A overlay/drag continuity, surviving identity, exact one-use tab entry, a real post-reconcile rail pane, and reduced-motion settlement; theme build; scoped agent-preflight over all 19 files.
  • Reviewer falsifier: N/A — no defect confirmed; the one residual (Demo B/Workstation retire→return) is a suggested author-run isolation test in the Depth Floor rather than a synthetic falsifier I run here.
  • Test location: unit specs mirror src/dashboard/ and apps/** canonically; e2e under test/playwright/e2e/{agentos,dashboard}/. Correct.
  • Substance: unit specs pin each consumer's special boundary (Demo A overlays, Fleet projection, adapter, reconciler, tab-enter button, zone model) and the reconciler spec extends the identity matrix; not smoke tests.

Findings: Pass.


📋 Required Actions

No required actions — eligible for human merge.

Non-blocking, author's-discretion: (1) optionally add a focused Demo B / Workstation retire→return assertion — I verified both resolvePane guards (!isDestroyed) and their store-backed panes already make retire→recreate crash- and state-safe, so this is coverage-completeness for the two indirectly-affected consumers, not a defect; (2) fold a one-row Contract-Ledger / HarnessDockZoneModel.md update for the disclosed retirement + config-materialization reconciler extension so the durable authority matches shipped reality.


📊 Evaluation Metrics

Verdict weights: 30% premise, 30% architecture + placement, 30% diff correctness, 10% AC/audit sanity.

  • [ARCH_ALIGNMENT]: 93 — all three consumers migrated onto the shared reconciler with removeAll() fully removed; DockLayoutAdapter verified stateless (decoration extracted as a pure static for reuse, not duplicated into apps); clean transaction-vs-durable resolver split fixing the rail-reveal leak; DomAccess.waitForAnimation correctly placed as a generic main-thread capability; no app-local copy of the four ownership commits. −7: the shared reconciler's contract evolved (retirement + config-materialization) and now governs two already-merged consumers, without a corresponding durable-doc/ledger update.
  • [CONTENT_COMPLETENESS]: 92 — thorough Fat Ticket (Evidence line, five deltas that honestly disclose the contract evolution, detailed 183-unit + 12-e2e receipts, Post-Merge); Anchor & Echo JSDoc on every new member (decorateProjectedItem, waitForAnimation, consumeTabEnterDecoration, retirement-block rationale). −8: the retirement/materialization extension isn't captured in the ticket ledger or HarnessDockZoneModel.md.
  • [EXECUTION_QUALITY]: 93 — retirement removes body/button pairs in descending index order, grouped per body, with a missing-button invariant throw; both instance-cache consumers' !isDestroyed resolver guards + store-backed panes make retire→recreate crash- and state-safe (verified in source for Demo B and Workstation); DomAccess.waitForAnimation is race-free via Animation.finished with safe missing/cancelled fallbacks; one-use tab-enter decoration consumption is idempotent across settle/cancel/unmount. −7: the new shared retirement changes the absent-pane contract for the two prior instance-cache consumers with no dedicated retire→return assertion in the e2e matrix (verified safe, so coverage-completeness rather than risk).
  • [PRODUCTIVITY]: 95 — all 10 ACs delivered for the three migrated consumers, plus the reusable durable-rail-resolver correction folded back into Demo B / Workstation.
  • [IMPACT]: 82 — completes the dock-projection identity unification across every consumer (including the normative public example) and adds a reusable main-thread animation-settlement capability; core dashboard + a main-thread addon.
  • [COMPLEXITY]: 90 — 19 files, +1356/−334, five src/ files including a main-thread addon, three consumer migrations each with a distinct preserved boundary, plus the retirement algorithm; high cognitive load.
  • [EFFORT_PROFILE]: Architectural Pillar — completes and extends the shared projection-reconciliation contract across the codebase and adds a reusable engine capability.

Cross-family review (Claude ⇄ GPT): this review is the cross-family merge-gate signal. No confirmed blocking defect; recommending Approve. Eligible for @tobiu's merge once checks-of-record and reviewer slots are satisfied — merge stays human-owned (§critical_gates). This closes the reconciler arc cleanly, Emmy — nicely disclosed contract evolution in the deltas, and the !isDestroyed guard is the right safety net.