refactor(gates): own the client-only replay rule as daemon-process reachability (R82)

Merged
TH
3d ago
+182-118

Follow-up to the #3517 review (comment). Based on main.

The design question

You asked whether the src/daemon.ts hub closure should own the client-only rule instead of a hand-listed path set. The answer is yes: what the daemon process can load owns it. But the eager closure can't carry it. Measured on main:

  • src/daemon.ts eagerly evaluates 393 modules. Neither src/daemon-composition.ts nor any src/platform-runtime-* module is among them, because they load behind a function-scoped await import. A planted import '@agent-device/replay-port/source-discovery' in src/platform-runtime-daemon-services.ts passes an eager-closure deny set.
  • Over static plus dynamic edges, src/daemon.ts reaches 1,366 modules, including the composition root, the platform-runtime family and every package they reach. Neither collector module is among them.

So the rule is a layering check over the resolved import graph, like R76, instead of a deny set in eager-closure-budgets.

What

  • New R82 daemon-process-reach (scripts/layering/daemon-process-reach.ts): a breadth-first walk from src/daemon.ts over value and dynamic edges. It fails if packages/replay-port/src/script-source-bundle.ts or source-discovery.ts is reachable, naming the importing edge and the shortest chain.
    • The planted import above now fails: src/daemon.ts -> … -> src/platform-runtime-daemon-services.ts -> packages/replay-port/src/source-discovery.ts.
    • Type-only edges load nothing, and client code may import the collector.
  • Deleted: the R11 path gate (CLIENT_ONLY_SUBPATHS, DAEMON_PROCESS_ROOT_FILES, the daemon-path predicate) and client-only-subpaths.test.ts.
  • Non-blocking note taken: the root wiring test imports testRequestScope, now exported next to testInventoryHost, instead of redeclaring it.
  • The seven inline threads you listed were resolved on #3517.

Validation

  • check:layering: 326 tests, guard OK, R82 in the success line.
  • check:quick, check:fallow (no issues in 10 changed files), check:gate-manifest, test-file-size ratchet, check:affected --run: all runnable checks passed.

🤖 Generated with Claude Code