Parent: #2132
Blocked by: #2166 (merged). First child in the #2132 chain.
Outcome
Remove the neutral session selection/admission helpers and inventory implementation from the handler grab-bag, then establish the lifecycle concern's single public facade. This is the foundation for the open/close children; it must not move open or close behavior yet.
Exact production scope
- Move
src/daemon/handlers/session-device-utils.ts to src/daemon/session-device-resolution.ts. Keep it neutral: it is already used by inventory, state, deployment, reverse-port, selector dispatch, prepare, clipboard, open, and close.
- Move
src/daemon/handlers/session-runtime-admission.ts to src/daemon/session-runtime-admission.ts. It remains a shared admission helper over InspectDeviceRuntimeFacts and BindDeviceRuntime, not lifecycle-owned internals.
- Move
src/daemon/handlers/session-inventory.ts to src/daemon/session-lifecycle/internal/inventory.ts.
- Add
src/daemon/session-lifecycle/index.ts as the only external lifecycle facade. Initially export only the inventory route operation and its narrow input type.
- Keep
src/daemon/handlers/session.ts as the route adapter. It may import the facade, but no caller may import session-lifecycle/internal/**.
- Update all exact production importers of the two neutral moves. Do not add a barrel or compatibility re-export at the old paths.
- Update
scripts/layering/architecture-ownership.ts and its tests with the lifecycle facade and both forbidden deep-import directions.
Commands covered by the facade in this child: session_list, devices, capabilities, and apps.
Authority contract
Inventory may read only the current fields it uses: name, createdAt, device, surface, appBundleId, and appName. It keeps SessionStore as read authority for now; it must not acquire a write operation or copy SessionState.
Injected capabilities are exactly InspectDeviceRuntimeFacts, BindDeviceRuntime, and optional ProviderAppCatalog. Device claims continue through inspectDeviceClaims; lease scoping continues through resolveLeaseScope. No new capability aggregate unless the diff deletes more plumbing than it adds.
The neutral selection module preserves explicit-selector checks, session-device conflict behavior, readiness refresh, and typed errors. The neutral admission module preserves fact-first admission and the opaque admitted-plan token.
Behavior proof
Move the inventory/device-selection/admission tests with their owners, including:
session-inventory-*, session-capabilities*, and session-devices-batch-runtime.test.ts
session-device-utils.test.ts
session-runtime-admission.test.ts
session-inventory-apps-runtime.test.ts
Before moving implementation, add a route-level characterization proving the four commands still pass through the lifecycle facade with identical response data and typed failures.
Plant and observe red for: (1) a handler deep-importing session-lifecycle/internal/inventory.ts; (2) lifecycle internal code importing src/daemon/handlers/**; and (3) restoring either old helper path. The structural gate must name the invariant and path.
Shrink and validation
Delete the old modules and any superseded test harness/fixtures. Report gross production additions/deletions and move-adjusted LOC; move-adjusted production LOC must not grow. Run focused tests, pnpm check:layering, the fallow audit for moved keys, and pnpm check:affected --run after staging structural files.
Non-goals
No open/close move, no replay/test move, no state-field write relocation, no wire/help behavior change, and no generic SessionContext/Pick<SessionStore> wrapper.
Parent: #2132
Blocked by: #2166 (merged). First child in the #2132 chain.
Outcome
Remove the neutral session selection/admission helpers and inventory implementation from the handler grab-bag, then establish the lifecycle concern's single public facade. This is the foundation for the open/close children; it must not move open or close behavior yet.
Exact production scope
src/daemon/handlers/session-device-utils.tstosrc/daemon/session-device-resolution.ts. Keep it neutral: it is already used by inventory, state, deployment, reverse-port, selector dispatch, prepare, clipboard, open, and close.src/daemon/handlers/session-runtime-admission.tstosrc/daemon/session-runtime-admission.ts. It remains a shared admission helper overInspectDeviceRuntimeFactsandBindDeviceRuntime, not lifecycle-owned internals.src/daemon/handlers/session-inventory.tstosrc/daemon/session-lifecycle/internal/inventory.ts.src/daemon/session-lifecycle/index.tsas the only external lifecycle facade. Initially export only the inventory route operation and its narrow input type.src/daemon/handlers/session.tsas the route adapter. It may import the facade, but no caller may importsession-lifecycle/internal/**.scripts/layering/architecture-ownership.tsand its tests with the lifecycle facade and both forbidden deep-import directions.Commands covered by the facade in this child:
session_list,devices,capabilities, andapps.Authority contract
Inventory may read only the current fields it uses:
name,createdAt,device,surface,appBundleId, andappName. It keepsSessionStoreas read authority for now; it must not acquire a write operation or copySessionState.Injected capabilities are exactly
InspectDeviceRuntimeFacts,BindDeviceRuntime, and optionalProviderAppCatalog. Device claims continue throughinspectDeviceClaims; lease scoping continues throughresolveLeaseScope. No new capability aggregate unless the diff deletes more plumbing than it adds.The neutral selection module preserves explicit-selector checks, session-device conflict behavior, readiness refresh, and typed errors. The neutral admission module preserves fact-first admission and the opaque admitted-plan token.
Behavior proof
Move the inventory/device-selection/admission tests with their owners, including:
session-inventory-*,session-capabilities*, andsession-devices-batch-runtime.test.tssession-device-utils.test.tssession-runtime-admission.test.tssession-inventory-apps-runtime.test.tsBefore moving implementation, add a route-level characterization proving the four commands still pass through the lifecycle facade with identical response data and typed failures.
Plant and observe red for: (1) a handler deep-importing
session-lifecycle/internal/inventory.ts; (2) lifecycle internal code importingsrc/daemon/handlers/**; and (3) restoring either old helper path. The structural gate must name the invariant and path.Shrink and validation
Delete the old modules and any superseded test harness/fixtures. Report gross production additions/deletions and move-adjusted LOC; move-adjusted production LOC must not grow. Run focused tests,
pnpm check:layering, the fallow audit for moved keys, andpnpm check:affected --runafter staging structural files.Non-goals
No open/close move, no replay/test move, no state-field write relocation, no wire/help behavior change, and no generic
SessionContext/Pick<SessionStore>wrapper.