Problem
The dockZone.v1 semantic-operation vocabulary exists in three hand-maintained copies (verified 2026-07-04 against dev + the open capture-line heads 0e590e1a7/987771143):
- The executor switch —
DockZoneModel.applyOperation() (src/dashboard/DockZoneModel.mjs), the behavioral source of truth: addTab, moveItem, splitNode, resizeSplit, detachItem, closeItem, setItemPinned, setItemAutoHidden.
- The worker-side mirror —
DockService.operations (src/ai/client/DockService.mjs); its own JSDoc states it is "mirrored verbatim from its switch". The NL tool gate fail-closes against exactly this set.
- Prose enumeration — the
execute_dock_operation description in ai/mcp/server/neural-link/openapi.yaml.
The unit spec (test/playwright/unit/ai/client/DockService.spec.mjs) pins copy 2's exact array — it freezes the mirror but ties nothing to the switch.
Failure mode: add an operation to the executor and forget the mirror → no test fails, and the Neural Link tier silently rejects the new operation as unknown. Fail-closed keeps it safe, but the tool surface goes stale with no signal — an agent reading getDockTopology().operations gets an incomplete vocabulary and cannot reach a capability the executor already has.
Shape
- Hoist the vocabulary onto the executor as the ONE authority: either a
static operations member on DockZoneModel, or (preferred) an operation→handler map from which applyOperation() dispatches and the vocabulary is derived — the switch then cannot fork from the list by construction.
DockService.operations becomes an import/re-export of the executor's vocabulary (same worker, zero cost).
- Add one parity regression: the service's advertised vocabulary === the executor's executable vocabulary (pins the seam forever; the existing exact-array spec pin can then assert against the imported source).
- The
openapi.yaml description stays prose (static Node-side artifact); optionally extend the #14587 NL parity-contract checks with a vocabulary-count line.
Idiom note (#14714): Body-side file, but the change is pure data-plane (a static member + import, no instance mutation, no view state) — inside the childapp-precedented allowed half.
Acceptance Criteria
Sizing / claim
Small, single-PR, fully specified — non-fable claimable.
Refs #13158 (docking epic) · Refs #14587 (NL dock tools — the fail-closed gate consuming the mirror) · Refs #14652 (capture line where the executor grew) · Refs #14714 (idiom-boundary kinship)
Filed by Clio (Claude Fable 5, Claude Code) from a Base.mjs/Neo.mjs best-practices sweep. Session fa2a6fd5-7488-4af6-a0d2-3855c86003e4.
Problem
The dockZone.v1 semantic-operation vocabulary exists in three hand-maintained copies (verified 2026-07-04 against dev + the open capture-line heads
0e590e1a7/987771143):DockZoneModel.applyOperation()(src/dashboard/DockZoneModel.mjs), the behavioral source of truth:addTab, moveItem, splitNode, resizeSplit, detachItem, closeItem, setItemPinned, setItemAutoHidden.DockService.operations(src/ai/client/DockService.mjs); its own JSDoc states it is "mirrored verbatim from its switch". The NL tool gate fail-closes against exactly this set.execute_dock_operationdescription inai/mcp/server/neural-link/openapi.yaml.The unit spec (
test/playwright/unit/ai/client/DockService.spec.mjs) pins copy 2's exact array — it freezes the mirror but ties nothing to the switch.Failure mode: add an operation to the executor and forget the mirror → no test fails, and the Neural Link tier silently rejects the new operation as unknown. Fail-closed keeps it safe, but the tool surface goes stale with no signal — an agent reading
getDockTopology().operationsgets an incomplete vocabulary and cannot reach a capability the executor already has.Shape
static operationsmember onDockZoneModel, or (preferred) an operation→handler map from whichapplyOperation()dispatches and the vocabulary is derived — the switch then cannot fork from the list by construction.DockService.operationsbecomes an import/re-export of the executor's vocabulary (same worker, zero cost).openapi.yamldescription stays prose (static Node-side artifact); optionally extend the #14587 NL parity-contract checks with a vocabulary-count line.Idiom note (#14714): Body-side file, but the change is pure data-plane (a static member + import, no instance mutation, no view state) — inside the childapp-precedented allowed half.
Acceptance Criteria
DockZoneModel; dispatch cannot diverge from it.src/ai/client/DockService.mjscarries no hand-written copy.DockZoneModelandDockServiceunit suites stay green with no behavior change.Sizing / claim
Small, single-PR, fully specified — non-fable claimable.
Refs #13158 (docking epic) · Refs #14587 (NL dock tools — the fail-closed gate consuming the mirror) · Refs #14652 (capture line where the executor grew) · Refs #14714 (idiom-boundary kinship)
Filed by Clio (Claude Fable 5, Claude Code) from a Base.mjs/Neo.mjs best-practices sweep. Session fa2a6fd5-7488-4af6-a0d2-3855c86003e4.