LearnNewsExamplesServices
Frontmatter
id17630
titleThe showcase dock examples still teach the retired host pattern
stateClosed
labels
enhancementairefactoringarchitecture
assigneesneo-opus-grace
createdAtAug 23, 2026, 7:14 PM
updatedAtAug 23, 2026, 7:46 PM
githubUrlhttps://github.com/neomjs/neo/issues/17630
authorneo-opus-grace
commentsCount0
parentIssue17539
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtAug 23, 2026, 7:46 PM

The showcase dock examples still teach the retired host pattern

Closed Backlog/active-chunk-19 enhancementairefactoringarchitecture
neo-opus-grace
neo-opus-grace commented on Aug 23, 2026, 7:14 PM

Context

Epic #17539 lifted the dock workspace host into the engine class Neo.dashboard.DockWorkspace (O-1, #17541 / PR #17545), and ADR 0029 §2.1 was amended with it — the record's status line now reads "Amended 2026-08-22 (#17541; §2.1): the normative workspace host becomes the engine class Neo.dashboard.DockWorkspace."

Two hosts have migrated (examples/dashboard/dock/MainContainer.mjs:96 and apps/workstation/view/Workspace.mjs:70). The two relocated demos have not, and #17539's O-4 row has been held as BLOCKER pending #16322.

That hold is released (sequencing ruling): the relocation event is complete — apps/agentos/childapps/ contains missioncontrol and widget only — and none of #16322's four open ACs can move these files again. This ticket is the first of O-4's two leaves.

The Problem

#17539's Out of Scope states that the interval between relocation and migration is protected:

the relocated demos carry an in-file marker pointing at this epic as the successor shape, so the interval mints no new copies

No such marker exists. Verified three ways: 17539 across every file type under examples/dashboard/ (0 hits); successor/migration prose across examples, apps, learn for .mjs/.md/.html (0 hits); both hosts' top-of-file docblocks read directly.

Demo A is worse than unmarked — it asserts the status the ADR retired. examples/dashboard/choreography/DemoAWorkspace.mjs:23:

This class is the normative workspace-ownership pattern (the reducer-container the docking design record fixes as canonical)

True when §2.1 said pattern; false since the amendment. A reader of the showcase example is currently told that a hand-rolled extends Container host is the canonical shape to copy — the exact copy-minting the marker existed to prevent, asserted rather than merely unmarked.

The Architectural Reality

The epic frames this migration as "a parent swap … with spec stubs by method name surviving through inheritance." The names survive; the signatures do not. Measured against dev:

Member DemoAWorkspace DockWorkspace Engine call site
resolvePane (componentRef) :429 (itemId, item) :632 this.resolvePane(itemId, item)DockWorkspace.mjs:649, :660
refreshDockWorkspace (document) :359 (tabInsertDescriptor, document, refreshOptions) :533 me.refreshDockWorkspace(tabInsertDescriptor, document, refreshOptions)DockWorkspace.mjs:464
projectDockModel (resolveComponentRef, document) :335 (tabInsertDescriptor, itemResolver, document) :483 me.projectDockModel(tabInsertDescriptor, …)DockWorkspace.mjs:556
onDockZoneDocumentChange (document) :260 (document, descriptor, source) :452

A bare parent swap leaves Demo A's overrides in place with first-positional-argument collisions in three of the four: the engine hands tabInsertDescriptor where Demo A reads document, and itemId where it reads componentRef. Nothing throws — the panes resolve to the wrong thing and the projection refreshes against a descriptor. This is the failure direction that costs most, because it is not observed.

examples/dashboard/dock/MainContainer.mjs is the precedent for the correct shape: it inherits applyDockZoneOperation, getDockZoneDocument, onDockZoneDocumentChange, projectDockModel and refreshDockWorkspace outright, overriding only resolvePane(itemId, item) (:212) and the beforeRefreshDockWorkspace(document, refreshOptions) hook (:187).

Demo A's tour surface (createTourBar, onTourBeat/onTourScene/onTourComplete/onTourError, startTour, setTourCaption, setPipProgress) is app-owned screenplay code and is untouched by this — it is what the example exists to show.

The Fix

Demo A migratesexamples/dashboard/choreography/DemoAWorkspace.mjs:

  • class DemoAWorkspace extends DockWorkspace (:48), construct/destroy calling super.
  • Delete the five members the engine now owns; do not leave same-name overrides behind.
  • Re-key resolvePane to the engine's (itemId, item) contract.
  • Move the pre-refresh sync into beforeRefreshDockWorkspace(document, refreshOptions).
  • Rewrite the :23 docblock claim: the ADR fixes Neo.dashboard.DockWorkspace as normative; this class is a consumer of it.

Demo B carries the interval pointerexamples/dashboard/crossWindow/DemoBWorkspace.mjs gets the docblock marker #17539 already claims it has, naming DockWorkspace as the successor shape and its migration as pending. Demo B's own migration is not in this ticket.

Contract Ledger Matrix

# Target surface Source of authority Before After Fallback Evidence
1 DemoAWorkspace base class ADR 0029 §2.1 (amended #17541) extends Container extends DockWorkspace none — the pattern it embodied is retired DemoAWorkspace.mjs:48; witness MainContainer.mjs:96
2 resolvePane DockWorkspace.mjs:632, called :649/:660 (componentRef) (itemId, item) none — engine owns the call MainContainer.mjs:212 adopted the same re-key
3 refreshDockWorkspace / projectDockModel / getDockZoneDocument / onDockZoneDocumentChange / applyDockZoneOperation DockWorkspace.mjs:533/:483/:289/:452/:168 locally implemented inherited, override deleted pre-refresh work moves to beforeRefreshDockWorkspace MainContainer.mjs overrides none of the five
4 neo-dock-workspace baseCls DockWorkspace.mjs:94 absent present once per workspace none override anchor per #17539's theme ruling; --dock-* defaults stay on .neo-dashboard
5 Demo B docblock #17539 Out of Scope claims a marker that does not exist marker present none 3-layer search recorded above

Acceptance Criteria

  • DemoAWorkspace extends DockWorkspace; the five engine-owned members carry no local override.
  • resolvePane matches the engine's (itemId, item) contract, and pre-refresh work runs through beforeRefreshDockWorkspace. A control proves the re-key: an arm that fails if a pane is resolved from the wrong argument — a name-only stub passing here would make this AC vacuous, which is the whole hazard.
  • The Demo A choreography e2e set is green at the new base class, and the tour still plays end to end.
  • DemoAWorkspace.mjs:23 no longer claims normative status; it names Neo.dashboard.DockWorkspace as the normative host.
  • DemoBWorkspace.mjs carries the successor marker #17539 asserts is already there.
  • .neo-dashboard keeps the engine --dock-* defaults; no default moves onto neo-dock-workspace.

Out of Scope

  • Demo B's migration — 4484 LOC coupled to cross-window tear-out, which sequences with O-3's tear-out half, not with Demo A. Bundling produces the ~5k-line diff #17539 already rejected under "Migrating all hosts in the engine PR". Files as O-4's second leaf.
  • The FleetCockpit migration (O-3) and the tear-out/vessel engine half.
  • #16322's residue — the widget disposition and the #16315/#16316 spec reconciliation.
  • Any change to DockWorkspace itself. If Demo A needs a hook that does not exist, that is a #17539 class delta under the hook-admission rule, not a local patch.
  • DemoBWorkspace decomposition (#15614).

Avoided Traps

  • Parent swap alone, trusting "names survive through inheritance." Three of four shared members collide on the first positional argument, and every collision is silent. The signature table above is the falsifier that has to be applied member by member.
  • Deleting the false docblock sentence instead of migrating. That removes the wrong claim and leaves the wrong code — the example would still teach the retired pattern by being it.
  • Moving --dock-* defaults onto the new root while the root is right there. Re-opens the invisible-splitter class (#17211) for every unmigrated consumer; .neo-dashboard is the default carrier, the root is an override anchor only (#17539 theme ruling).
  • Marking Demo B and calling O-4 progressed. A marker is interval protection, not migration; O-4 closes only when both hosts extend the engine class.

Decision Record impact

aligned-with ADR 0029 — executes the §2.1 amendment on a consumer; changes no ADR authority.

Structure-map gate: N/A — Body-side examples/dashboard/**, no file introduced or relocated, no ai/ or placement surface touched.

Live latest-open sweep: checked latest 20 open issues at 2026-08-23T17:10Z; no equivalent found. A2A in-flight claim sweep: latest 30 all-state messages at the same timestamp; nearest is @neo-gpt-emmy's [lane-claim][#17419] (tab close through model policy, PR #17626) — DockWorkspace-adjacent, disjoint scope, no collision.

Related

#17539 (parent epic, O-4) · #17541 / PR #17545 (the engine class + ADR amendment) · #16322 (the relocation, mine — sequencing released) · #17546 / PR #17565 (O-2 workstation migration precedent) · #15614 (Demo B decomposition) · #17211 (the invisible-splitter defect the theme ruling protects)

Origin Session ID: eb671e6e-ca17-4a53-8069-64fd5885ce84

Retrieval Hint: query_raw_memories("Demo A migrate DockWorkspace signature collision resolvePane itemId componentRef showcase teaches retired pattern")

tobiu referenced in commit 6f0b661 - "refactor(examples): Demo A consumes the engine dock host (#17630) (#17632) on Aug 23, 2026, 7:46 PM
tobiu closed this issue on Aug 23, 2026, 7:46 PM