callstack / callstack/agent-device
refactor(runtime): own provider-device admission behind a typed capability
- Dominant language
- TypeScript
- Stars
- 4.6k
- Forks
- 299
- Avg merge
- 10h 17m
- Merged PRs (30d)
- 515
Description
## Purpose
`src/provider-device-runtime.ts` (303 LOC) is the largest remaining daemon → root hub: 12 file pairs (11 daemon files plus `src/core/interactors.ts`).
The symbol sets say the port is already narrow. Ten of the twelve importers take **only** `isActiveProviderDevice`:
- `android-foreground-surface.ts`, `android-system-dialog.ts`, `device-ready.ts`, `direct-ios-selector.ts`, `handlers/session-doctor.ts`, `interaction/internal/interaction-gesture.ts`, `interaction/internal/interaction-touch-reference-frame.ts`, `request-generic-dispatch.ts`, `session-device-resolution.ts`, `snapshot-session.ts`
- `server/daemon-runtime.ts` takes `createProviderDeviceRuntimeRequestProviders`
- `src/core/interactors.ts` takes `getProviderDeviceInteractor` and `isActiveProviderDevice`
This is therefore a small typed capability, not an interface with a method per importer. Do not design an eleven-method surface; the data refuses it.
Umbrella: #2545.
## Required behavior
1. The daemon consumes provider-device admission through a typed contract it can name, composed at the process root by one module that is the sole importer of provider/platform mechanics — the shape already accepted for lifecycle participation in #2333 (`src/daemon/platform-owner-lifecycle.ts` + `src/platform-runtime-daemon-lifecycle.ts`).
2. `isActiveProviderDevice` keeps its exact truth table. Characterize it against unmodified code first: it is read on 10 call sites spanning Android foreground detection, alert handling, iOS selector fast paths, doctor, device refresh, and gesture reference frames, and each reads a boolean to decide a fast-path or a refusal.
3. `src/core/interactors.ts` consumes the same contract rather than the root module, so `src/core` loses its root dependency on `provider-device-runtime.ts`.
4. `createProviderDeviceRuntimeRequestProviders` stays root-composed. If the request-provider composition genuinely belongs to the daemon runtime, classify that edge in the R76 inventory rather than deepening it.
## Completion conditions
- No daemon file imports `src/provider-device-runtime.ts`.
- `src/core/interactors.ts` does not import it either, or the remaining edge is recorded in `DAEMON_PLATFORM_RUNTIME_EDGES` with a rationale — after the sibling gates child makes root hubs classifiable.
- Key behavior is on typed reasons, not on error text.
- Characterization tests for the admission predicate observed on unmodified code, then passing unchanged.
## Out of scope
- Provider allocation, leases, or `packages/managed-allocation` semantics.
- The direct-iOS selector seam tracked by #2274.
## Dependencies
- None. Wave A, independent branch. Coordinate with the gates child if both touch the R76 inventory; keep the inventory edit in the gates child if the branch order allows it.
Contributor guide
Assessment
This issue has not been assessed yet.