Skip to content

refactor(daemon): stop importing the commands barrel through src/runtime.ts #2540

Closed

Description

@thymikee

Purpose

Five daemon files import src/runtime.ts, which imports the command barrel with a value import:

// src/runtime.ts
import { bindCommands, type BoundAgentDeviceCommands } from './commands/index.ts';

The value closure of src/runtime.ts is 122 files, 48 of them under src/commands/. The daemon's total value closure is 717 files and carries 121 src/commands/** files — roughly the entire commands zone (140 files, 21k LOC) — plus 17 root flat files. The daemon loads a command surface it does not dispatch through, and every change in commands is potentially a daemon-restart-cost event.

Umbrella: #2545. Chosen first because it is the only child that changes what the daemon actually loads, and because #2469 (eager-closure budget tolerance) is unblocked by cutting closure, not by adding budget.

Required behavior

  1. src/runtime.ts keeps its public shape: AgentDevice, createAgentDevice, createMemorySessionStore, localCommandPolicy, restrictedCommandPolicy, and the re-exported command types. No consumer-side API change is part of this issue.
  2. Daemon consumers of src/runtime.ts take the specific declarations they use through their owning module or contract, not through the runtime facade. Determine the used symbol set per importer before choosing the seam; do not invent an interface wider than the symbols in use.
  3. The barrel src/commands/index.ts either stops being the value entry for runtime binding, or the binding moves to the composition root that needs it. One construction path, no second registry.

Completion conditions

  • Value-only transitive closure from src/daemon/** contains zero files under src/commands/. Measured with a probe over scripts/layering/model.ts as described in the parent, and reported in the PR.
  • src/runtime.ts has no value edge into src/commands/**, or is no longer reachable from src/daemon/** at all.
  • No new eager work: the eager-import-closure budgets for the daemon entry surfaces do not rise.
  • R4 stays 0, R5 stays 0, R6 does not grow past the ratchet.

Out of scope

  • Repackaging src/commands as a workspace package. That decision needs the cli-schema orientation settled first (sibling child).
  • Any change to the command descriptor registry or the command-registry package.

Dependencies

  • None. Wave A, independent branch.

Activity

  1. thymikee commented on Sep 13, 2026

    @thymikee
    MemberAuthor

    Closure measurement: the runtime.ts cut alone does not reach the completion condition

    Probe over scripts/layering/model.ts (value edges only, static, from src/daemon/**) at e6f288012b:

    cut daemon closure src/commands/** files
    none (today) 719 121
    src/runtime.ts → commands/index.ts only 662 75
    that + the daemon's two cli-schema/command-schema.ts imports 584 0

    The second path is src/daemon → src/cli-schema/command-schema.ts → src/commands/command-metadata.ts → src/commands/family/registry.ts → every command family. Two daemon files hold it, one symbol each:

    • src/daemon/request-execution-scope.ts takes applyCommandDefaults. It resolves the full per-command schema, which is authored inside the command facets. Exactly one command declares defaults today (app, appsFilter), and src/cli/parser/args.ts already applies the same defaults on the CLI side.
    • src/daemon/session-action-recorder.ts takes recordedFlagKeys. That function lives in src/commands/cli-grammar/flag-registry.ts and its closure is only the four flag-definitions-*.ts files plus flag-types.ts.

    So "zero src/commands/** files" needs these two edges cut as well. Either this issue owns them (import recordedFlagKeys from the flag registry directly and take the daemon-side defaults application through something that does not resolve the whole facet table), or the condition is relaxed here and #2543 owns it. I have posted the same data on #2545 and #2543 so the sequencing is decided in one place.

  2. thymikee commented on Sep 13, 2026

    @thymikee
    MemberAuthor

    Completion contract: the achievable floor is 34, not 0 — needs a decision before #2548 can close this

    PR #2548 ships the src/runtime.ts cut. Measured value-only closure from src/daemon/**, with the change in place:

    state daemon closure src/commands/** commands LOC
    before (9aa6465768) 717 121 19,659
    after #2548 705 108 17,434
    #2548 + #2543's two command-schema.ts edges 628 34 6,364

    The issue's probe of 75 assumed nothing replaces the barrel. It cannot: the daemon calls capture.snapshot/diffSnapshot/screenshot, selectors.find/get/is/wait and interactions.gesture/click/press/fill/hover/settleObservation, so the 33-34 files implementing those three families return the moment a scoped binder replaces createAgentDevice. That residue is the daemon's own dispatch surface, not accidental coupling.

    Two consequences:

    1. "Zero src/commands/** in the daemon closure" is only reachable if the daemon stops executing commands in-process. Proposed replacement condition: the daemon reaches only the command families it dispatches through (capture, selectors, interactions), imports no all-family barrel, and holds 0 src/commands files outside those families plus whatever refactor(cli-schema): orient the schema layer below commands #2543 resolves.
    2. recordedFlagKeys cannot be imported from commands/cli-grammar/flag-registry.ts by the daemon. That is a direct daemon → commands value edge and R2 commands-floor rejects it (scripts/layering/zone-policy.ts:47). The symbol has to move down to packages/command-registry, which is refactor(cli-schema): orient the schema layer below commands #2543's "flag grammar goes down" step — so this issue should not own that edge.

    Suggest: #2548 merges as the runtime split, the condition above replaces the zero, and the remaining 74-file path is #2543's.

  3. thymikee commented on Sep 14, 2026

    @thymikee
    MemberAuthor

    Closing on the decision above: the completion condition is the dispatched-families invariant, not zero. #2548 delivered the runtime split, #2562 cut the second path through cli-schema/command-schema.ts. At 775163bb35 the daemon reaches src/commands only through src/runtime-command-surface.ts, and the closure holds 34 files (30 under */runtime/**, 4 shared runtime helpers), no facet, reader, formatter, or barrel. src/runtime.ts keeps its public shape. Umbrella body updated with the same wording.

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