callstack / callstack/agent-device
chore(gates): classify root hubs, dynamic edges, and the root-to-daemon entry
- Dominant language
- TypeScript
- Stars
- 4.6k
- Forks
- 299
- Avg merge
- 10h 14m
- Merged PRs (30d)
- 537
Description
## Purpose
Three blind spots let daemon/root coupling reappear without failing a gate. Each is a measured hole, not a hypothetical.
1. **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.
2. **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".
3. **Nothing owns `root → daemon`.** 37 file pairs reach into daemon internals from outside. `src/daemon-client/**` — which wave 3's #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.
Umbrella: #2545.
## Required behavior
1. 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.
2. 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.
3. 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.
4. 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.
- One gate run, no new CI job.
## Out of scope
- New detectors for problems not measured above. Per #2531's rules: fix-shaped, not detector-shaped. If a hole turns out to have no reachable edge, delete the candidate rule instead of shipping it.
## Dependencies
- 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.
Contributor guide
Research direction
Start with scripts/layering/daemon-platform-runtime-inventory.ts and the existing R75/R6 gate references, then review docs/dependency-graph-findings.md and the named imports under src/daemon-client, src/cli, and src/daemon. Verify planted root-hub, dynamic, and out-of-entry imports fail while stale-entry and symbol-drift checks remain intact; completion requires zero daemon-client value edges and one gate run.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- build-system, tooling
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100