Context
Measured live on apps/workstation at dev@2ee23d7801 (blob 115a3f9b98) through the Neural Link while running the D#18224 popup-birth arm (receipt). Neo.create('Neo.dashboard.dock.Workspace', {dockModel}) with no parent and no window succeeds, does two wrong things, and one of them is silent. Every shipping host hides both by seeding a first shell in its own constructor — knowledge only the engine has, which is this epic's theme.
Filed at the operator's request to capture the arm's findings as engine work; the D#18224 Step-Back (DC_kwDODSospM4BFxQB, addendum) lists the same two requirements as acceptance criteria of the owner design, so this leaf is prerequisite work for it, not part of its vehicle decision.
The Problem
- Construct arms geometry on a window it does not have.
construct calls observeWindowGeometry(this.windowId) unconditionally (src/dashboard/dock/Workspace.mjs:548). With windowId: null the remote call carries no routing key; src/worker/Base.mjs:478–484 delivers it to ports[0] and the worker console prints sendMessage destination "main" is deprecated … remoteMethod: setConfigs, data: {observeMovement: true, observeResize: true, windowId: null}. WindowPosition#setConfigs (src/main/addon/WindowPosition.mjs:330) deletes data.windowId, so nothing is poisoned — but an arbitrary window was armed by a workspace that has no render target.
- The class owns no first projection, and the failure is silent. After binding (
Neo.worker.App#moveComponent into a viewport → container.Base#createItem NeoInstance branch, :413–437, adopts windowId/appName, mounted: true), onDockZoneDocumentChange(document) renders nothing: Reconciler.reconcileProjection throws Dock projection could not find a current shell at index 0 (src/dashboard/dock/projection/Reconciler.mjs:353–357), the error is untyped, onDockProjectionFailed re-throws it (Workspace.mjs:3923), and the rejected refreshPromise reaches no console. (A rejected refresh tail was left outside #18039's fixed scope on purpose; this leaf adds no generic logging either — it makes the no-shell state not an error.) examples/dashboard/dock/MainContainer.mjs:147, :310 and every host seed items = [projectDockModel()] in construct, which is why nobody sees it.
Seeded through a hot-patched beforeRefreshDockWorkspace (this.add(this.projectDockModel(null, null, document)) when no shell exists), the same instance projected its document, and with a registry-answered resolvePane adopted two LIVE panes of the main workspace by projection alone (same ids, verify_component_consistency 1/1/1).
The Architectural Reality
construct :516–549: preview producer, optional tear-out handlers + worker connect/disconnect subscription (:535), then the unconditional geometry arm (:548).
refreshDockWorkspace :3741 waits for the host to mount, then projects and reconciles; the reconciler requires host.items[shellIndex] to exist (:353–357).
onDockProjectionFailed :3918–3949 recovers only errors marked isDockProjectionFailure; everything else re-throws into the refresh tail.
- ADR 0029 §2.1: the workspace container owns the committed document and re-projects it through
LayoutAdapter.project() — the first projection is part of that contract, not the consumer's.
The Fix
Narrowed 2026-09-04 14:55Z per intake (IC_kwDODSospM8AAAABSlARkA), verified against Reconciler.mjs:353–399 and Workspace.mjs:2074: once this leaf lands, "no current shell" is the first projection's precondition, not a projection failure. Typing the no-shell throw as recoverable would retry against the same missing shell; with a seed present the typed error would be unreachable.
- Guard the geometry arm on a window: one idempotent window-binding hook arms only a real, newly bound
windowId (afterSetWindowId from null to an id); construct calls the same guarded helper, so a headless instance arms nothing — beside component/Abstract#afterSetWindowId :289–304 (theme files) and component/Base#afterSetWindowId :959–962 (drag zones).
- Own the first projection as the normal path:
Workspace#afterSetMounted (:2074) detects an absent shell at dockShellIndex and schedules the ordinary document refresh; Reconciler.reconcileProjection treats oldShell === null (:353) as an explicit first projection — mount exactly one shell at shellIndex, resolve its panes, make it visible, return a normal result with landedInPlace: false (:394–399 vocabulary) — never staging beside a shell that does not exist. Consumers that already supply a shell keep their static boot path.
- Only a failing initial mount is a failure: if the first projection's mount or reconciliation rejects, retire the failed initial shell back to zero shells and mark the real error as a typed projection failure with the named recovery
retired-initial, so the existing exactly-one retry rule of onDockProjectionFailed operates on it. Generic refresh-tail logging stays out of this leaf.
Contract Ledger Matrix
| Target Surface |
Source of Authority |
Proposed Behavior |
Fallback |
Docs |
Evidence |
Workspace#construct geometry arm (:548) |
ADR 0029 §2.8.5 (the host arms its own render target) |
arms only with a windowId; a headless instance arms nothing |
none needed |
class docblock |
worker console shows no deprecated-destination warning on Neo.create(Workspace, {dockModel}) |
Workspace#afterSetWindowId (inherited) + afterSetMounted (:2074) |
component/Abstract:289 cascade |
first bind arms geometry once (idempotent, real id only); the mount schedules the first projection |
— |
class docblock |
headless → bind renders the document with no consumer seeding |
Reconciler.reconcileProjection with oldShell === null (:353–357) |
ADR 0029 §2.1 (the workspace owns projection) |
explicit first projection: one shell mounted at shellIndex, landedInPlace: false; a rejected initial mount retires to zero shells and surfaces as a typed failure with recovery retired-initial |
current untyped throw |
Reconciler docblock |
headless → bind renders; a forced initial-mount rejection surfaces once and retries once |
Decision Record impact
aligned-with ADR 0029 §2.1 / §2.8.5 — no authority changes; the leaf makes the class honour a contract it already states.
Acceptance Criteria
Out of Scope
- The owner-level item→instance registry (the D#18224 owner design; the arm faked it with
Neo.getComponent(componentRef)).
- Moving the tear-out lifecycle out of
Workspace (D#18150 cluster 1).
- Popup-birth latency (needs the Playwright rig; separate arm).
Avoided Traps
- Projecting in
construct: a headless instance must project nothing (Step-Back addendum: headless arms neither geometry nor visible projection); the first projection belongs to the first bind.
- Typing the no-shell throw as a recoverable failure: the retry would run against the same missing shell, and with a seed present the typed error is unreachable (intake correction). Only a failing initial mount is a failure; other throws stay loud.
- Reviving generic refresh-tail logging here: #18039 left it out explicitly; it needs its own evidence and owner.
Related
- #18151 (parent: the dock asks consumers to re-derive knowledge only the engine has) · #18039 (the swallowed refresh rejection) · #18143 (a rejected update mid-swap) · D#18224 DC_kwDODSospM4BFxN1 (the arm) and DC_kwDODSospM4BFxQB (Step-Back addendum) · ADR 0029 §2.1, §2.8.5.
Live latest-open sweep: checked the latest 40 open issues (created-desc) at 2026-09-04 13:37Z; no equivalent found (#18272, #18274, #18275 are neighbours, not this). A2A claim sweep (last 60 min, all read-states): no claim on this scope. Memory Core sweep: only this session's own arm memories. Own-assignment sweep: none on this surface.
unowned-rationale: found while measuring for D#18224 in peer-role; claimable by any seat; prerequisite for the owner design's headless → bound lifecycle, independent of its vehicle decision.
Origin Session ID: ee061205-8271-4458-b73b-b132c1bec310
Retrieval Hint: query_raw_memories("headless dock Workspace geometry ports[0] first projection no shell silent reject moveComponent bind")
Context
Measured live on
apps/workstationatdev@2ee23d7801(blob115a3f9b98) through the Neural Link while running the D#18224 popup-birth arm (receipt).Neo.create('Neo.dashboard.dock.Workspace', {dockModel})with no parent and no window succeeds, does two wrong things, and one of them is silent. Every shipping host hides both by seeding a first shell in its own constructor — knowledge only the engine has, which is this epic's theme.Filed at the operator's request to capture the arm's findings as engine work; the D#18224 Step-Back (DC_kwDODSospM4BFxQB, addendum) lists the same two requirements as acceptance criteria of the owner design, so this leaf is prerequisite work for it, not part of its vehicle decision.
The Problem
constructcallsobserveWindowGeometry(this.windowId)unconditionally (src/dashboard/dock/Workspace.mjs:548). WithwindowId: nullthe remote call carries no routing key;src/worker/Base.mjs:478–484delivers it toports[0]and the worker console printssendMessage destination "main" is deprecated … remoteMethod: setConfigs, data: {observeMovement: true, observeResize: true, windowId: null}.WindowPosition#setConfigs(src/main/addon/WindowPosition.mjs:330) deletesdata.windowId, so nothing is poisoned — but an arbitrary window was armed by a workspace that has no render target.Neo.worker.App#moveComponentinto a viewport →container.Base#createItemNeoInstance branch,:413–437, adoptswindowId/appName,mounted: true),onDockZoneDocumentChange(document)renders nothing:Reconciler.reconcileProjectionthrowsDock projection could not find a current shell at index 0(src/dashboard/dock/projection/Reconciler.mjs:353–357), the error is untyped,onDockProjectionFailedre-throws it (Workspace.mjs:3923), and the rejectedrefreshPromisereaches no console. (A rejected refresh tail was left outside #18039's fixed scope on purpose; this leaf adds no generic logging either — it makes the no-shell state not an error.)examples/dashboard/dock/MainContainer.mjs:147, :310and every host seeditems = [projectDockModel()]inconstruct, which is why nobody sees it.Seeded through a hot-patched
beforeRefreshDockWorkspace(this.add(this.projectDockModel(null, null, document))when no shell exists), the same instance projected its document, and with a registry-answeredresolvePaneadopted two LIVE panes of the main workspace by projection alone (same ids,verify_component_consistency1/1/1).The Architectural Reality
construct:516–549: preview producer, optional tear-out handlers + workerconnect/disconnectsubscription (:535), then the unconditional geometry arm (:548).refreshDockWorkspace:3741waits for the host to mount, then projects and reconciles; the reconciler requireshost.items[shellIndex]to exist (:353–357).onDockProjectionFailed:3918–3949recovers only errors markedisDockProjectionFailure; everything else re-throws into the refresh tail.LayoutAdapter.project()— the first projection is part of that contract, not the consumer's.The Fix
windowId(afterSetWindowIdfromnullto an id);constructcalls the same guarded helper, so a headless instance arms nothing — besidecomponent/Abstract#afterSetWindowId:289–304(theme files) andcomponent/Base#afterSetWindowId:959–962(drag zones).Workspace#afterSetMounted(:2074) detects an absent shell atdockShellIndexand schedules the ordinary document refresh;Reconciler.reconcileProjectiontreatsoldShell === null(:353) as an explicit first projection — mount exactly one shell atshellIndex, resolve its panes, make it visible, return a normal result withlandedInPlace: false(:394–399vocabulary) — never staging beside a shell that does not exist. Consumers that already supply a shell keep their static boot path.retired-initial, so the existing exactly-one retry rule ofonDockProjectionFailedoperates on it. Generic refresh-tail logging stays out of this leaf.Contract Ledger Matrix
Workspace#constructgeometry arm (:548)windowId; a headless instance arms nothingNeo.create(Workspace, {dockModel})Workspace#afterSetWindowId(inherited) +afterSetMounted(:2074)component/Abstract:289cascadeReconciler.reconcileProjectionwitholdShell === null(:353–357)shellIndex,landedInPlace: false; a rejected initial mount retires to zero shells and surfaces as a typed failure with recoveryretired-initialDecision Record impact
aligned-with ADR 0029§2.1 / §2.8.5 — no authority changes; the leaf makes the class honour a contract it already states.Acceptance Criteria
Neo.create(Workspace, {dockModel})with no window emits no remote geometry call (spy onNeo.main.addon.WindowPosition.setConfigs), and binding it into a container with awindowIdarms exactly one.viewport.add(headlessWorkspace)andonDockZoneDocumentChange(document), the host holds one shell projecting the document's tabs nodes — no consumer seeding, no hot patch.dev@2ee23d7801: the same sequence today leavesitemsempty andrefreshPromiserejected with the untyped no-shell error.dockProjectionFailedwith recoveryretired-initial, and retries exactly once — never a silent rejection.examples/dashboard/dock/MainContainer.mjskeeps working with its constructor seeding removed (deletion, per the accretion rule), and the Workstation is unchanged.Out of Scope
Neo.getComponent(componentRef)).Workspace(D#18150 cluster 1).Avoided Traps
construct: a headless instance must project nothing (Step-Back addendum: headless arms neither geometry nor visible projection); the first projection belongs to the first bind.Related
Live latest-open sweep: checked the latest 40 open issues (created-desc) at 2026-09-04 13:37Z; no equivalent found (#18272, #18274, #18275 are neighbours, not this). A2A claim sweep (last 60 min, all read-states): no claim on this scope. Memory Core sweep: only this session's own arm memories. Own-assignment sweep: none on this surface.
unowned-rationale: found while measuring for D#18224 in peer-role; claimable by any seat; prerequisite for the owner design's headless → bound lifecycle, independent of its vehicle decision.
Origin Session ID: ee061205-8271-4458-b73b-b132c1bec310
Retrieval Hint:
query_raw_memories("headless dock Workspace geometry ports[0] first projection no shell silent reject moveComponent bind")