You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
chore(gates): classify root hubs, dynamic edges, and the root-to-daemon entry #2542
Three blind spots let daemon/root coupling reappear without failing a gate. Each is a measured hole, not a hypothetical.
Root hubs are not classifiable. R76 (scripts/layering/daemon-platform-runtime-inventory.ts) matches src/daemon/** importing src/platform-runtime(?:\.ts|-[a-z0-9-]+\.ts). A daemon file importing a differently-named root hub that itself reaches platform mechanics is invisible: src/provider-device-runtime.ts accounts for 12 daemon file pairs and imports src/platform-runtime.ts and src/platform-runtime-screen-recording-apple-runner-transport.ts on its own behalf, neither of which is under any inventory. ADR 0022's "one leaked edge remains" is true only for direct edges to gated names.
Dynamic imports are unwatched. R4/R5/R6 see static edges. Today's one instance: src/daemon/snapshot-interactor-capture.ts → src/core/interactors.ts (dynamic; src/core/interactors.ts imports src/provider-device-runtime.ts). docs/dependency-graph-findings.md already records dynamic direction as "outside every rule".
Nothing owns root → daemon. 37 file pairs reach into daemon internals from outside. src/daemon-client/** — which wave 3's refactor(daemon): relocate the daemon client out of src/daemon #2360 moved out of src/daemon/ — still holds 13 pairs / 8 value edges into daemon-request.ts, config.ts, daemon-process.ts, session-repair-tombstone.ts, request-progress-protocol.ts. The move changed the client's location, not its imports.
Extend the R76 inventory so a daemon edge to a root module that reaches platform mechanics is classified even when the module name is not platform-runtime-*. Keep the existing three categories and the same no-drift/no-stale rules; widen the target predicate rather than adding a second table.
Make dynamic import direction observable in the same pass: a dynamic daemon edge into a root or lower zone must be classifiable or reported. Do not add a separate gate job.
Add one declared entry surface through which non-daemon code may import the daemon, and ratchet the rest. src/daemon-client/** and src/cli/** are the named importers today; a file outside the declared entry gaining a daemon import fails. Repair the residue rather than allowlisting it: the daemon-client edges into session-repair-tombstone.ts, daemon-process.ts, and request-progress-protocol.ts are the real work.
Ratchet, don't reset: the R75 handler-owned session-authority ratchet and the R6 type-inversion ratchet keep their current references. Current inversions are commands -> client (2), commands -> daemon-server (1), mcp -> client (1).
Completion conditions
A planted daemon import of an unclassified root hub turns the layering check red. Verify the same for a planted dynamic edge and a planted out-of-entry daemon import, before acceptance.
Stale-entry and symbol-drift failures still fire (existing R76 behavior must not weaken).
src/daemon-client/** → src/daemon/** value edges reach 0, and the shrink-only rule holds the set there.
None for the gate work. Wave A. The provider-device port child and the extraction child both want to touch the inventory; sequence the inventory edit into this branch where the branches meet.
Half of this landed in #2557; the entry half is now #2559.
Landed (#2557). R76's target set is computed from the tree instead of patterned: the src/platform-runtime* family plus every module outside the daemon zone that reaches it, over static and dynamic edges. Intra-daemon hops are excluded because they are not the finding — the unfiltered measurement is 298 pairs, the honest predicate yields 3. All three are classified with rationales: the two provider-runtime hubs the daemon runtime composes (composition-essential), and the dynamic interactor lookup in the snapshot capture, which is a leak and got its own deepening issue (#2555). Dynamic edges are now classified rather than skipped, which is the only place a dynamic edge's direction is enforced at all — R4/R5/R6 still cannot see it. Planted-violation cases cover each new path (24 tests in the inventory test file).
Split out (#2559). Declaring the root→daemon entry surface and repairing the daemon-client -> src/daemon/** value edges is a different shape of change — relocation and narrowing rather than classification — so it should not ride along in a gate PR. #2559 carries the measured cut sets: daemon-process.ts (122 LOC, 4 inbound), session-repair-tombstone.ts (91, 2), request-progress-protocol.ts (48, 3) are shared contract, while config.ts (113, 42 inbound) needs the client's read narrowed to the subset it uses. The 4 type-only daemon-request.ts edges resolve against the daemon-request-wire.ts split from #2318/#2322.
Completion condition for this issue as written is therefore: #2557 plus #2559, and #2555 if the dynamic-edge rule is to reach zero leaks rather than one classified leak.
Closing: #2557 landed the hub-aware R76 target set and dynamic-edge classification; #2594 (closing #2559) declared the root → daemon entry surface and cut the daemon-client → src/daemon/** value edges to 0. On main at ad9b906140 the daemon has 0 dynamic edges leaving the zone, so the dynamic rule reached zero leaks rather than one classified leak. R75 and R6 ratchets kept their references; R6 is 3, down from 4.
Purpose
Three blind spots let daemon/root coupling reappear without failing a gate. Each is a measured hole, not a hypothetical.
scripts/layering/daemon-platform-runtime-inventory.ts) matchessrc/daemon/**importingsrc/platform-runtime(?:\.ts|-[a-z0-9-]+\.ts). A daemon file importing a differently-named root hub that itself reaches platform mechanics is invisible:src/provider-device-runtime.tsaccounts for 12 daemon file pairs and importssrc/platform-runtime.tsandsrc/platform-runtime-screen-recording-apple-runner-transport.tson its own behalf, neither of which is under any inventory. ADR 0022's "one leaked edge remains" is true only for direct edges to gated names.src/daemon/snapshot-interactor-capture.ts→src/core/interactors.ts(dynamic;src/core/interactors.tsimportssrc/provider-device-runtime.ts).docs/dependency-graph-findings.mdalready records dynamic direction as "outside every rule".root → daemon. 37 file pairs reach into daemon internals from outside.src/daemon-client/**— which wave 3's refactor(daemon): relocate the daemon client out of src/daemon #2360 moved out ofsrc/daemon/— still holds 13 pairs / 8 value edges intodaemon-request.ts,config.ts,daemon-process.ts,session-repair-tombstone.ts,request-progress-protocol.ts. The move changed the client's location, not its imports.Umbrella: #2545.
Required behavior
platform-runtime-*. Keep the existing three categories and the same no-drift/no-stale rules; widen the target predicate rather than adding a second table.src/daemon-client/**andsrc/cli/**are the named importers today; a file outside the declared entry gaining a daemon import fails. Repair the residue rather than allowlisting it: thedaemon-clientedges intosession-repair-tombstone.ts,daemon-process.ts, andrequest-progress-protocol.tsare the real work.commands -> client(2),commands -> daemon-server(1),mcp -> client(1).Completion conditions
src/daemon-client/**→src/daemon/**value edges reach 0, and the shrink-only rule holds the set there.Out of scope
Dependencies