Skip to content

refactor(runtime): own provider-device admission behind a typed capability #2541

Description

@thymikee

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 refactor(daemon): normalize lifecycle participation of platform resource owners #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

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.

Activity

  1. thymikee commented on Sep 13, 2026

    @thymikee
    MemberAuthor

    Landed in #2556 (stacked on #2548's branch).

    The daemon now owns the fact: src/daemon/provider-device-admission.ts declares ProviderDeviceAdmission with the one method the daemon decides on, defaults to the no-provider state an un-composed process already sees, and is installed by root composition at the site that already composes the provider request providers. The ten leaf call sites changed only their import specifier; the predicate keeps its name and its per-call read, so the request-scoped ALS behaviour underneath is untouched.

    Measured: daemon production sites importing src/provider-device-runtime.ts 11 → 1; src/** value/type pairs into that hub 12 → 2. The second remaining pair is src/core/interactors.ts, which this issue's text assumed would take the same seam — it cannot. interactors.ts also needs getProviderDeviceInteractor and sits below the daemon, so importing a daemon module would be a back-edge. Its route out is #2555, which removes the dynamic daemon→core/interactors edge by making interactor resolution a composed capability; after that, interactors.ts keeps a root edge only from the module that owns it.

    Composition touched nine daemon test files and the provider-scenario integration harness: they installed nothing before, because the daemon read the ambient root scope directly. They now compose the admission exactly as daemon-runtime.ts does.

  2. thymikee commented on Sep 16, 2026

    @thymikee
    MemberAuthor

    Closing: #2556 landed the typed provider-device capability, and #2593 (closing #2555) replaced the daemon's dynamic reach into src/core/interactors.ts with a composed capability. On main at ad9b906140 no daemon file imports src/provider-device-runtime.ts except root composition in server/daemon-runtime.ts, which is classified in the R76 inventory; src/core/interactors.ts keeps its edge with an inventory rationale, which is this issue's second completion branch. Truth table characterized in #2556.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions