Skip to content

refactor(controller): extract RelayCoordinator for permission/question relay lifetimes #5

@lespaceman

Description

@lespaceman

Problem

Permission and question relaysRuntimeEvents that need user action and produce a RuntimeDecision once answered — currently have their request-lifetime state machine (pending → dispatched → resolved/timeout/cancelled) implemented inside src/app/channels/sessionBridge.ts (~578 LOC). Two callers wire their controller callbacks independently:

  • src/app/providers/useFeed.ts (interactive shell)
  • src/app/exec/runner.ts (batch exec mode)

Each maintains its own TTL/cancel/reconnect logic. Result: ~1.9k LOC across four modules with relay implicit in the design but absent as a named seam.

Why this matters (leverage)

  • "Relay" is not in CONTEXT.md, despite ~1.9k LOC of code dedicated to it. The missing word is the strongest signal that the seam is missing.
  • Future features — relay rate limits, cross-session dedup, guaranteed-once delivery, retry-on-permission-deny — are scattered concerns today; would become localized after extraction.
  • Both callers (interactive + exec) would consume the same coordinator, killing duplicated relay callback wiring.

Proposal sketch

Create `src/core/relay/relayCoordinator.ts` that:

  • Owns the request-lifetime state machine for relay-shaped `RuntimeEvent`s.
  • Sits between `controller.handleEvent()` and the transport (`SessionBridge`, harness `runtime.sendDecision`).
  • Emits `RuntimeDecision`s back into the controller's intent pipeline when relays resolve / timeout / cancel.
  • `SessionBridge` reduces to a thin transport wrapper.
  • `useFeed` and `runner` both wire through the coordinator.

Add Relay to `CONTEXT.md` as a first-class term once the seam crystallizes.

Files involved

  • `src/app/channels/sessionBridge.ts` (~578 LOC → ~300)
  • `src/app/providers/useFeed.ts` (~593 LOC → ~500)
  • `src/app/exec/runner.ts` (~600 LOC → ~450)
  • `src/core/controller/runtimeController.ts` (~150 LOC, light changes)
  • New: `src/core/relay/relayCoordinator.ts` (~250 LOC)

Open questions

  • Does the coordinator live in `core/controller/` (relays are decision-shaped) or as a sibling `core/relay/`?
  • One coordinator instance per session, or process-global with per-session keying?
  • Does it need SQLite-backed state (relay survives restart) or is in-memory enough?

Blast radius

Medium. Gateway, harnesses, and `infra/sessions` are unaffected.

Provenance

Surfaced by `/improve-codebase-architecture` + `/zoom-out` analysis after landing the IndexedTimeline deepening. Ranked #1 of three candidates.

Metadata

Metadata

Assignees

No one assigned

    Labels

    needs-triageMaintainer needs to evaluate

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions