Context
The extensibility audit on #18304 identified RevealStateMachine as a candidate requiring its own runtime assessment. That assessment now reproduces the missing customization path on merged 8d6c4216ca: install Neo.overwrites.Neo.dashboard.dock.interaction.RevealStateMachine.tabClick before importing Rail, asking it to transition to revealed; a real Rail click still returns revealed-focused, and the owner namespace is absent.
The Problem
interaction/Rail.mjs constructs an unregistered plain RevealStateMachine with new. The owner has useful methods and timer injection, but the ordinary Neo application-wide overwrite path cannot reach them. Its JSDoc incorrectly treats runtime-only state as a reason to bypass Neo classes.
The Architectural Reality
A Rail already owns exactly one machine and destroys it before container teardown. The machine owns pending item, revealed item, transition state and dwell/grace timer. Its output is the existing onChange callback; it has no dock-document write path. This is a per-instance Base owner, not a singleton. The newly landed VesselConversion cut supplies the adjacent construction/lifecycle precedent.
The Fix
Convert the existing RevealStateMachine module to a registered Base-derived class, with policy defaults and callbacks as configs, transient state per instance, and internal decisions through methods. Replace Rail's raw construction with Neo.create through one overridable creation method; Rail retains teardown ownership. Remove the raw constructor path rather than preserving an alias. Keep the current state transitions, timing defaults, hover opt-in, focus hold and callback shape.
Contract Ledger
| Surface |
Authority |
Behavior |
Default/refusal |
Docs |
Evidence |
| RevealStateMachine |
Base registration/config/overwrite path |
Real Neo.dashboard.dock.interaction.RevealStateMachine identity; independent instances and overridable methods |
Existing click/hover policy |
Class JSDoc |
Pre-import Neo.overwrites through a real Rail |
| Rail creation and teardown |
Existing revealMachine lifecycle |
One independently replaceable owner per Rail, destroyed with that Rail |
Default owner retains current behavior |
Rail factory JSDoc |
Specialization and two-Rail isolation/cleanup |
| Reveal transitions |
Existing state table |
Existing snapshots and onChange notifications; no document operations |
150ms dwell, 300ms grace; hover off |
Existing state table |
Current fake-timer regressions and teardown controls |
Acceptance Criteria
Out of Scope
Other unregistered dock modules, native windows, Group/history binding, Placement composition, Workspace decomposition, new reveal behavior, broad lint/skills or guides. No changes to merged #18383 or #18392.
Avoided Traps
Registering a wrapper around the old machine; converting transient state to a singleton; copying transition logic into Rail; treating registration as persistence; treating every unregistered module in the audit as the same defect.
Decision Record impact: aligned-with ADR 0029; reveal semantics and document boundary unchanged.
Decision Record: existing ADR 0029 remains authoritative.
Related
Parent: #18304
Related: #18388
Related: #18389
Live latest20open and latest30all-statusA2A sweeps at2026-09-06T19:39Z found no equivalent/competingclaim. All-state RevealStateMachine and reveal-overridable searches found prior behavior tickets and the parent audit, not this cut. MC problem queries returned unrelated records; current public audit plus realRail probe supply authority. Four open own assignments checked; #17540 is guide work and #16498 is a separate deferred film/composition witness. Labels verified live. Structure-map command unavailable (exit1/nooutput); existing interaction home is retained, no new source path.
Origin Session ID: d14f2413-4744-43e9-a6c1-191166d58fe9
Retrieval Hint: RevealStateMachine Rail ignored overwrite transient per-instance lifecycle
Context
The extensibility audit on #18304 identified
RevealStateMachineas a candidate requiring its own runtime assessment. That assessment now reproduces the missing customization path on merged8d6c4216ca: installNeo.overwrites.Neo.dashboard.dock.interaction.RevealStateMachine.tabClickbefore importing Rail, asking it to transition torevealed; a real Rail click still returnsrevealed-focused, and the owner namespace is absent.The Problem
interaction/Rail.mjsconstructs an unregistered plainRevealStateMachinewithnew. The owner has useful methods and timer injection, but the ordinary Neo application-wide overwrite path cannot reach them. Its JSDoc incorrectly treats runtime-only state as a reason to bypass Neo classes.The Architectural Reality
A Rail already owns exactly one machine and destroys it before container teardown. The machine owns pending item, revealed item, transition state and dwell/grace timer. Its output is the existing onChange callback; it has no dock-document write path. This is a per-instance Base owner, not a singleton. The newly landed VesselConversion cut supplies the adjacent construction/lifecycle precedent.
The Fix
Convert the existing RevealStateMachine module to a registered Base-derived class, with policy defaults and callbacks as configs, transient state per instance, and internal decisions through methods. Replace Rail's raw construction with Neo.create through one overridable creation method; Rail retains teardown ownership. Remove the raw constructor path rather than preserving an alias. Keep the current state transitions, timing defaults, hover opt-in, focus hold and callback shape.
Contract Ledger
Acceptance Criteria
Out of Scope
Other unregistered dock modules, native windows, Group/history binding, Placement composition, Workspace decomposition, new reveal behavior, broad lint/skills or guides. No changes to merged #18383 or #18392.
Avoided Traps
Registering a wrapper around the old machine; converting transient state to a singleton; copying transition logic into Rail; treating registration as persistence; treating every unregistered module in the audit as the same defect.
Decision Record impact: aligned-with ADR 0029; reveal semantics and document boundary unchanged. Decision Record: existing ADR 0029 remains authoritative.
Related
Parent: #18304 Related: #18388 Related: #18389
Live latest20open and latest30all-statusA2A sweeps at2026-09-06T19:39Z found no equivalent/competingclaim. All-state RevealStateMachine and reveal-overridable searches found prior behavior tickets and the parent audit, not this cut. MC problem queries returned unrelated records; current public audit plus realRail probe supply authority. Four open own assignments checked; #17540 is guide work and #16498 is a separate deferred film/composition witness. Labels verified live. Structure-map command unavailable (exit1/nooutput); existing interaction home is retained, no new source path.
Origin Session ID: d14f2413-4744-43e9-a6c1-191166d58fe9 Retrieval Hint: RevealStateMachine Rail ignored overwrite transient per-instance lifecycle