Context
Operator directive, mid-#15591 lane (2026-07-20): apps/agentos/childapps/dockdemo/view/DemoBWorkspace.mjs has grown to 4803 lines — beyond sanity against the repo's size discipline (~1k lines target; 1500–2000 only in rare edge cases). The friction is live, not theoretical: implementing the third-claimant feature required reading the file in three tool-capped chunks (>50KB) just to locate the staging seams. Every future Demo-B / matrix change pays the same context-window, review-quality, and merge-surface tax — and this class is the reference implementation of workspace-set composition, so its shape teaches.
Live latest-open sweep: checked latest 20 open issues at 2026-07-20T17:31Z; no equivalent. A2A in-flight sweep: 30 recent messages; no competing claim. Local content grep: only a historical PR-review mention (#15024, different scope).
The Problem
One class currently owns at least nine separable responsibilities (line anchors at dev b2a6a97a46):
- Composition / projection / reducer ownership —
construct (373), projectDockModel, refresh machinery
- Perspective capture / load / topology restore — ~950–1240, ~3618–3680
- Cross-window staging + participation + geometry + positioning — ~1240–1620, ~2100–2245
- Cross-window gesture execution + witness + probes — ~2259–3150
- Whole-stack return — ~1625–1750
- Tear-out host (vessel grants, admissions, embodiment glue, park, retire, reintegrate, placements) — scattered
- Keyboard command host (routing, cycle, focus, announce) — ~530–900
- Click-popout + window connect/disconnect routing — ~3183–3450
- Cross-window commit core (adoption + reconcile) — ~1530–1580
The costs compound: agent context windows burn on multi-chunk reads; 4800-line diffs let behavior changes hide inside mechanical moves; every Demo-B lane (matrix receipts, tear-out, keyboard, perspectives) contends on one file.
The Architectural Reality
- The extraction precedent exists and is proven in this very class:
createDockTearOutHandlers, createVesselParkHandlers, createDockKeyboardCommands, createDockVesselEmbodiment — factory functions composed over host seams (src/dashboard/Dock*.mjs). The refactor generalizes a pattern the file already uses four times.
- The unit specs (
test/playwright/unit/apps/agentos/childapps/dockdemo/DemoBWorkspace.spec.mjs, 1700+ lines) stub instance methods directly (e.g. workspace.openCrossWindowStage = async () => …, spec line 1315) — method facades on the workspace MUST be preserved, delegating to the extracted modules.
- The matrix E2E specs (
DemoBCrossWindowDragNL, DemoBKeyboardDetachNL, DemoBVesselConversionNL, DemoBPerspectivesNL) call the same facades via Neural Link (app.callMethod(wsId, 'openCrossWindowStage', …)) — same constraint.
- The G3 claim protocol,
DockWorkspaceSet, and DockCrossWindowParticipation are the measured authority for matrix row 6 — zero changes there.
The Fix
Phased, behavior-preserving extraction — one PR per phase, facades retained, full suite green per phase:
- Phase 1 — cross-window staging seam (responsibilities 3+5+9): extract
openCrossWindowStage, mountCrossWindowTarget, createCrossWindowParticipation, isCrossWindowTargetCurrent, positionCrossWindowStage, geometry wait/measure, retireReturnedPopupWorkspace, whole-stack return, and the adopt/reconcile commit core into a composed module (factory-over-seams pattern). Sequenced FIRST: #15591 then parameterizes the extracted seam (window/stage/document keyed by workspaceId) and adds demo-b-popup-2 — refactor and feature touch the staging machinery once, in order, instead of twice in conflict.
- Phase 2 — gesture execution (4):
executeCrossWindowStep + witness + probes + cancel choreography.
- Phase 3 — tear-out / pop-out host (6+8+9's routing half): vessel grants, admissions, placements, embodiment glue, connect/disconnect routing.
- Phase 4 — keyboard host (+ perspectives if size still demands) (7, then 2): keydown routing, cycle glue, focus/announce seams.
Target end state: DemoBWorkspace.mjs ≤ ~1500 lines (composition + projection + facades); each extracted module ≤ ~1000. Modules land app-side under apps/agentos/childapps/dockdemo/view/ (sibling precedent: CounterPane.mjs); promotion to src/dashboard/ only if a seam proves framework-general — separate ticket. Structural pre-flight Stage 1 (sibling pattern) fires at implementation.
Contract Ledger Matrix
| Target Surface |
Source of Authority |
Proposed Behavior |
Fallback |
Docs |
Evidence |
openCrossWindowStage() facade |
DemoBWorkspace.mjs:1453 + unit spec line 381/1315 + DemoBKeyboardDetachNL.spec.mjs:140 |
Signature + return shape unchanged; delegates |
N/A (facade preserved) |
module JSDoc |
unit + E2E green unmodified |
executeCrossWindowStep(step, opts) facade |
DemoBWorkspace.mjs:2259 + DemoBCrossWindowDragNL.spec.mjs:316 + TourRunner.spec.mjs |
Unchanged; delegates |
N/A |
module JSDoc |
unit + E2E green |
mountCrossWindowTarget(app, windowId) |
DemoBWorkspace.mjs:1360 (connect routing line 3218) |
Unchanged; delegates |
N/A |
module JSDoc |
E2E green |
| Matrix rows 4–5 receipts |
TearOutPortabilityMatrix.md living ledger |
Re-run green on final state |
revert phase |
ledger |
headed runner receipts |
Decision Record impact
none.
Acceptance Criteria
Out of Scope
- Any behavioral change (this is pure decomposition).
- N-popup generalization of the staging seam (that parameterization is #15591's implementation, landing AFTER Phase 1 on the extracted seam).
- Promoting any seam into
src/dashboard/ (separate ticket if a seam proves framework-general).
- Demo A, the tour script content, FM cockpit consumers.
- A mechanical file-size lint / standing convention — worth its own follow-up if the swarm wants the discipline enforced; NOT bundled here.
Avoided Traps
- Big-bang rewrite — rejected: phased extraction with facade preservation keeps every phase independently green and revertable.
- Extracting into
src/dashboard/ prematurely — framework-surface inflation; app-side first, promote on evidence.
- Getter/setter indirection to keep singletons (e.g. accessor-delegating
crossWindowTargetWindowId into a Map) — hides semantics from grep and review; explicit parameterized seams instead.
- Touching the G3 claim protocol or workspace-set registry — they are the measured authority row 6 measures.
Related
#15591 (sequenced after Phase 1 — its implementation parameterizes the extracted seam) · #15243 (parent tear-out matrix) · #15504 (Vega's keyboard-fallback witness — same file, coordinate during phases) · #15396 (vessel composition, touches tear-out seams) · PR #15465 (G3 protocol, untouched) · PR #15589 (row-4 receipt — regression bar).
Origin Session ID: 8d4ce1c3-0bf2-4bb0-bad9-e49836248afe (design session; ticket authored in its solo-refresh successor)
Structure-map gate (§1c): N/A — apps-side view decomposition; no ai/ placement involved.
Retrieval Hint: DemoBWorkspace decomposition monolith cross-window staging extraction refactor phases
Context
Operator directive, mid-#15591 lane (2026-07-20):
apps/agentos/childapps/dockdemo/view/DemoBWorkspace.mjshas grown to 4803 lines — beyond sanity against the repo's size discipline (~1k lines target; 1500–2000 only in rare edge cases). The friction is live, not theoretical: implementing the third-claimant feature required reading the file in three tool-capped chunks (>50KB) just to locate the staging seams. Every future Demo-B / matrix change pays the same context-window, review-quality, and merge-surface tax — and this class is the reference implementation of workspace-set composition, so its shape teaches.Live latest-open sweep: checked latest 20 open issues at 2026-07-20T17:31Z; no equivalent. A2A in-flight sweep: 30 recent messages; no competing claim. Local content grep: only a historical PR-review mention (#15024, different scope).
The Problem
One class currently owns at least nine separable responsibilities (line anchors at dev
b2a6a97a46):construct(373),projectDockModel, refresh machineryThe costs compound: agent context windows burn on multi-chunk reads; 4800-line diffs let behavior changes hide inside mechanical moves; every Demo-B lane (matrix receipts, tear-out, keyboard, perspectives) contends on one file.
The Architectural Reality
createDockTearOutHandlers,createVesselParkHandlers,createDockKeyboardCommands,createDockVesselEmbodiment— factory functions composed over host seams (src/dashboard/Dock*.mjs). The refactor generalizes a pattern the file already uses four times.test/playwright/unit/apps/agentos/childapps/dockdemo/DemoBWorkspace.spec.mjs, 1700+ lines) stub instance methods directly (e.g.workspace.openCrossWindowStage = async () => …, spec line 1315) — method facades on the workspace MUST be preserved, delegating to the extracted modules.DemoBCrossWindowDragNL,DemoBKeyboardDetachNL,DemoBVesselConversionNL,DemoBPerspectivesNL) call the same facades via Neural Link (app.callMethod(wsId, 'openCrossWindowStage', …)) — same constraint.DockWorkspaceSet, andDockCrossWindowParticipationare the measured authority for matrix row 6 — zero changes there.The Fix
Phased, behavior-preserving extraction — one PR per phase, facades retained, full suite green per phase:
openCrossWindowStage,mountCrossWindowTarget,createCrossWindowParticipation,isCrossWindowTargetCurrent,positionCrossWindowStage, geometry wait/measure,retireReturnedPopupWorkspace, whole-stack return, and the adopt/reconcile commit core into a composed module (factory-over-seams pattern). Sequenced FIRST: #15591 then parameterizes the extracted seam (window/stage/document keyed by workspaceId) and addsdemo-b-popup-2— refactor and feature touch the staging machinery once, in order, instead of twice in conflict.executeCrossWindowStep+ witness + probes + cancel choreography.Target end state:
DemoBWorkspace.mjs≤ ~1500 lines (composition + projection + facades); each extracted module ≤ ~1000. Modules land app-side underapps/agentos/childapps/dockdemo/view/(sibling precedent:CounterPane.mjs); promotion tosrc/dashboard/only if a seam proves framework-general — separate ticket. Structural pre-flight Stage 1 (sibling pattern) fires at implementation.Contract Ledger Matrix
openCrossWindowStage()facadeDemoBWorkspace.mjs:1453+ unit spec line 381/1315 +DemoBKeyboardDetachNL.spec.mjs:140executeCrossWindowStep(step, opts)facadeDemoBWorkspace.mjs:2259+DemoBCrossWindowDragNL.spec.mjs:316+TourRunner.spec.mjsmountCrossWindowTarget(app, windowId)DemoBWorkspace.mjs:1360(connect routing line 3218)Decision Record impact
none.
Acceptance Criteria
DemoBWorkspace.spec.mjs+TourRunner.spec.mjsgreen with zero or facade-only updates; the three cross-window E2E specs green.DemoBWorkspace.mjs≤ 1500 lines.src/dashboard/protocol classes (DockWorkspaceSet,DockCrossWindowParticipation,GestureClaimArbiter).@summary(Gate 2).Out of Scope
src/dashboard/(separate ticket if a seam proves framework-general).Avoided Traps
src/dashboard/prematurely — framework-surface inflation; app-side first, promote on evidence.crossWindowTargetWindowIdinto a Map) — hides semantics from grep and review; explicit parameterized seams instead.Related
#15591 (sequenced after Phase 1 — its implementation parameterizes the extracted seam) · #15243 (parent tear-out matrix) · #15504 (Vega's keyboard-fallback witness — same file, coordinate during phases) · #15396 (vessel composition, touches tear-out seams) · PR #15465 (G3 protocol, untouched) · PR #15589 (row-4 receipt — regression bar).
Origin Session ID: 8d4ce1c3-0bf2-4bb0-bad9-e49836248afe (design session; ticket authored in its solo-refresh successor)
Structure-map gate (§1c): N/A — apps-side view decomposition; no
ai/placement involved.Retrieval Hint:
DemoBWorkspace decomposition monolith cross-window staging extraction refactor phases