Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
445ff08
docs(adr): write ADR 0001 in ASD-STE100
thymikee Oct 16, 2026
b8164df
docs(adr): remove completed ADR 0002 process history
thymikee Oct 16, 2026
423fda5
docs(adr): write ADR 0002 in ASD-STE100
thymikee Oct 16, 2026
6ee58c3
docs(adr): write ADR 0003 in ASD-STE100
thymikee Oct 16, 2026
4ea1d36
docs(adr): remove completed ADR 0004 process history
thymikee Oct 16, 2026
2d6a749
docs(adr): write ADR 0004 in ASD-STE100
thymikee Oct 16, 2026
9cc762c
docs(adr): remove completed ADR 0005 process history
thymikee Oct 16, 2026
685b340
docs(adr): write ADR 0005 in ASD-STE100
thymikee Oct 16, 2026
c10a428
docs(adr): write ADR 0006 in ASD-STE100
thymikee Oct 16, 2026
5501a02
docs(adr): write ADR 0007 in ASD-STE100
thymikee Oct 16, 2026
03c8bbe
docs(adr): remove completed ADR 0008 process history
thymikee Oct 16, 2026
28bb6b0
docs(adr): write ADR 0008 in ASD-STE100
thymikee Oct 16, 2026
b43a7cd
docs(adr): remove completed ADR 0009 process history
thymikee Oct 16, 2026
a024cad
docs(adr): write ADR 0009 in ASD-STE100
thymikee Oct 16, 2026
d5e33d3
docs(adr): write ADR 0010 in ASD-STE100
thymikee Oct 16, 2026
308742e
docs(adr): remove completed ADR 0011 process history
thymikee Oct 16, 2026
5a88e5f
docs(adr): write ADR 0011 in ASD-STE100
thymikee Oct 16, 2026
8de1193
docs(adr): remove completed ADR 0013 process history
thymikee Oct 16, 2026
2edd926
docs(adr): write ADR 0013 in ASD-STE100
thymikee Oct 16, 2026
f4cbaa9
docs(adr): remove completed ADR 0014 process history
thymikee Oct 16, 2026
2c81e55
docs(adr): write ADR 0014 in ASD-STE100
thymikee Oct 16, 2026
62e3783
docs(adr): write ADR 0015 in ASD-STE100
thymikee Oct 16, 2026
887ad68
docs(adr): remove completed ADR 0016 process history
thymikee Oct 16, 2026
4553733
docs(adr): write ADR 0016 in ASD-STE100
thymikee Oct 16, 2026
7b7b3c0
docs(adr): write ADR 0017 in ASD-STE100
thymikee Oct 16, 2026
0e11c67
docs(adr): repoint stale owner paths
thymikee Oct 16, 2026
ca19d96
docs(adr): write ADR index in ASD-STE100
thymikee Oct 16, 2026
418b4b0
docs(adr): remove completed ADR 0001 process history
thymikee Oct 16, 2026
8799443
docs(adr): remove completed ADR 0003 process history
thymikee Oct 16, 2026
35ffc5c
docs(adr): remove completed ADR 0014 facet migration note
thymikee Oct 16, 2026
2d42ce6
docs(adr): restore the frozen meaning in ADR 0001
thymikee Oct 16, 2026
b257cae
docs(adr): restore the frozen meaning in ADR 0002
thymikee Oct 16, 2026
ebe30c8
docs(adr): restore the frozen meaning in ADR 0003
thymikee Oct 16, 2026
bf24c68
docs(adr): restore the frozen meaning in ADR 0004
thymikee Oct 16, 2026
ad6e336
docs(adr): restore the frozen meaning in ADR 0005
thymikee Oct 16, 2026
ffa2660
docs(adr): restore the frozen meaning in ADR 0006
thymikee Oct 16, 2026
d9f7634
docs(adr): restore the frozen meaning in ADR 0007
thymikee Oct 16, 2026
62470f5
docs(adr): restore the frozen meaning in ADR 0008
thymikee Oct 16, 2026
fe76624
docs(adr): restore the frozen meaning in ADR 0009
thymikee Oct 16, 2026
4901940
docs(adr): restore the frozen meaning in ADR 0010
thymikee Oct 16, 2026
0c3bf0a
docs(adr): restore the frozen meaning in ADR 0011
thymikee Oct 16, 2026
65d01bb
docs(adr): restore the frozen meaning in ADR 0013
thymikee Oct 16, 2026
4d5ac5f
docs(adr): restore the frozen meaning in ADR 0014
thymikee Oct 16, 2026
e18eb32
docs(adr): restore the frozen meaning in ADR 0015
thymikee Oct 16, 2026
fa58ed3
docs(adr): restore the frozen meaning in ADR 0016
thymikee Oct 16, 2026
8167e2f
docs(adr): restore the frozen meaning in ADR 0017
thymikee Oct 16, 2026
c947b75
docs(adr): restore the frozen meaning in the ADR index
thymikee Oct 16, 2026
7246192
docs(adr): scope landmark mode to wait in ADR 0017
thymikee Oct 16, 2026
965723b
docs(adr): scope the ADR 0002 fail-closed rule to Android snapshots
thymikee Oct 17, 2026
622e936
docs(adr): state the ADR 0007 lease allow list as fail closed
thymikee Oct 17, 2026
632b210
docs(adr): name the retired table parity test in ADR 0008
thymikee Oct 17, 2026
aefc915
docs(adr): keep the ADR 0016 ABORTED arming rule in the present tense
thymikee Oct 17, 2026
55c3eb4
docs(adr): restore the ADR 0007 glance limits for cancellation and he…
thymikee Oct 17, 2026
4e88d45
docs(adr): restore the ADR 0005 precondition referent
thymikee Oct 17, 2026
4150630
docs(adr): keep the ADR 0008 AgentDeviceClient import in the present …
thymikee Oct 17, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
233 changes: 195 additions & 38 deletions docs/adr/0001-provider-first-integration-scenarios.md

Large diffs are not rendered by default.

239 changes: 140 additions & 99 deletions docs/adr/0002-persistent-platform-helper-sessions.md

Large diffs are not rendered by default.

159 changes: 95 additions & 64 deletions docs/adr/0003-daemon-command-registry.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,86 +4,117 @@

Accepted

## Rules at a glance

- Keep public command identity in `packages/command-registry/src/catalog.ts` and public input/output contracts in `src/commands/**`.
- Keep daemon route ownership and request-policy traits in the daemon command registry.
- Request modules consume named predicates from the registry. They do not recreate command string sets.
- Handler modules own execution logic only. They do not export duplicate coverage tables to prove route membership.
- The daemon registry must not define CLI grammar, Node.js client options, MCP schemas, user-facing help, or platform capability support.
- A derived daemon registry is permitted only if it preserves all four invariants in the 2026-06 update.

## Context

Daemon request handling depends on command traits that are not part of the public command surface:
which handler route owns a command, whether tenant lease admission applies, whether session
execution should lock, whether selector validation applies, whether replay can run an action in the
current session scope, whether invalid recordings block the request, whether Android blocking-dialog
recovery applies, and how request-scoped providers resolve a device.
Daemon request behavior depends on traits outside the public command surface:

- Which handler route owns a command.
- Whether tenant lease admission applies.
- Whether session execution should lock.
- Whether selector validation applies.
- Whether replay can run an action in the current session scope.
- Whether invalid recordings block the request.
- Whether Android blocking-dialog recovery applies.
- How request-scoped providers resolve a device.

Those traits used to be spread across `src/command-catalog.ts`, request-policy modules, and
handler-local coverage tables. That made `src/command-catalog.ts` carry daemon-only behavior next
to public command identity, and it required duplicate command sets to stay aligned by convention.
Previously, `src/command-catalog.ts`, request-policy modules, and handler-local coverage tables each held some of these traits.
Thus, `src/command-catalog.ts` contained daemon-only behavior beside public command identity.
Duplicate command sets required alignment by convention.

## Decision

Keep public command identity in `src/command-catalog.ts` and public input/output contracts in
`src/commands/**`.
Keep public command identity in `packages/command-registry/src/catalog.ts`.
Keep public input/output contracts in `src/commands/**`.

Add `packages/daemon/src/core/daemon-command-registry.ts` as the daemon-side source of truth for command route ownership and request-policy traits.
Request modules consume predicate functions from the registry.
They do not recreate command string sets.
Handler modules own execution logic only.
They do not export duplicate coverage tables to prove route membership.

The daemon registry is internal-only.
It must not define:

Add `packages/daemon/src/core/daemon-command-registry.ts` as the daemon-side source of truth for command route
ownership and daemon request-policy traits. Request modules consume predicate functions from the
registry instead of recreating command string sets. Handler modules own execution logic only; they do
not export duplicate coverage tables to prove route membership.
- CLI grammar.
- Node.js client options.
- MCP schemas.
- User-facing help.
- Platform capability support.

The daemon registry is internal-only. It must not define CLI grammar, Node.js client options, MCP
schemas, user-facing help, or platform capability support. Those remain owned by the command
contract, projection, help, and capability modules.
The command contract, projection, help, and capability modules retain these responsibilities.

## Alternatives Considered

- Keep daemon groups in `src/command-catalog.ts`: this keeps one command-name file, but it mixes
public command identity with daemon runtime policy and makes the catalog grow for internal-only
routing decisions.
- Keep handler-local coverage tables: this makes each handler self-describing, but creates a second
route membership source that can drift from the router and request-policy modules.
- Put route checks directly in request modules: this is locally simple, but scatters command
classification across admission, locking, provider scoping, replay, recording, and generic
dispatch.
- **Keep daemon groups in `src/command-catalog.ts`.** This retains one file for command names.
However, it mixes public command identity with daemon runtime policy.
It also increases catalog size for internal-only route decisions.
- **Keep handler-local coverage tables.** Each handler describes its routes locally.
However, this creates a second source of route membership that can disagree with the router and request-policy modules.
- **Put route checks directly in request modules.** This is locally simple.
However, command classification becomes distributed across admission, locking, provider scope, replay, recording, and generic dispatch.

## Consequences

Adding or moving a daemon-handled command requires updating the daemon command registry with its
route and request-policy traits. The registry tests pin the trait decisions, while provider-backed
integration scenarios verify important request-policy behavior through the real daemon request path.
When a daemon-handled command is added or moved, its route and request-policy traits require updates in the daemon command registry.
Comment thread
thymikee marked this conversation as resolved.
The registry tests pin the trait decisions.
Provider-backed integration scenarios verify important request-policy behavior through the real daemon request path.

The registry file is intentionally a dense internal contract. Its interface should stay small:
callers ask daemon-policy questions through named predicates rather than reading or mutating command
sets.
The registry file is intentionally a dense internal contract.
Its interface should remain small.
Callers ask daemon-policy questions through named predicates.
They do not read or mutate command sets.

`AGENTS.md` should contain only the operating rule and relevant file pointers for agents. This ADR
owns the rationale so future changes do not need to infer it from agent instructions.
`AGENTS.md` should contain only the operating rule and relevant file pointers for agents.
This ADR owns the rationale.
Thus, future changes do not require readers to infer the rationale from agent instructions.

## Update (2026-06): single-declaration / derivation model

A later proposal (the `CommandDescriptor` direction, now [ADR 0008](0008-command-descriptor-registry.md)) unifies a command's
declarations so the public catalog, capability matrix, CLI/MCP projections, batch allowlist, and this
daemon registry are *derived* from one registration site, to remove the cross-table drift that several
of these surfaces are kept aligned against by convention.

**This ADR's decision stands.** Its boundary is about *ownership* and the *predicate interface*, not
about the physical file a trait is typed in. "Separate source of truth" means separately owned and
exposed through named predicates — a property that survives a projected/derived backing table. A
derived daemon registry is therefore permitted **only if** it preserves all of the following invariants:

1. **Daemon-owned declaration.** Route and request-policy traits are declared in a daemon-owned facet
(under `packages/daemon/src/`) and *composed* into the registration — never inlined as fields on the public
command contract in `src/commands/**` or `src/command-catalog.ts`. Co-locating a registration *call*
is fine; co-locating *ownership* of daemon policy in the public surface is the contamination this ADR
rejected (see Alternatives, "Keep daemon groups in `src/command-catalog.ts`").
2. **Predicate interface unchanged.** Consumers keep asking daemon-policy questions through the named
predicates (`getDaemonCommandRoute`, `isLeaseAdmissionExempt`, `shouldLockSessionExecution`, …). The
daemon registry remains their sole exposer; derivation changes how the backing table is *built*, not
how it is *read*.
3. **No leakage into public projections.** The catalog/CLI/MCP/help/capability projections must be
type-prevented from reading daemon-only traits, and the daemon registry must still not define CLI
grammar, Node.js options, MCP schemas, user-facing help, or capability support.
4. **One declaration per concern, enforced by types.** The single registration site must make a missing
or duplicated daemon trait a *compile error* — replacing today's "aligned by convention". This is the
structural improvement that justifies derivation over a separately hand-authored table.

A single flat public descriptor whose daemon fields leak into public views is **not** permitted — that is
the "collapse daemon policy into a public command registry" failure this ADR exists to prevent. Compose
facets owned by their domains; derive the registry from them. Until that derivation lands and is pinned
by the registry tests, the hand-authored `packages/daemon/src/core/daemon-command-registry.ts` remains the source of
truth and the operating rule in `AGENTS.md` is unchanged.
A later proposal, the `CommandDescriptor` direction in [ADR 0008](0008-command-descriptor-registry.md), unifies a command's declarations at one registration site.
It derives these surfaces from that site:

- Public catalog.
- Capability matrix.
- CLI/MCP projections.
- Batch allowlist.
- Daemon registry.

Its purpose is to remove the drift between these tables.
Convention keeps several of these surfaces aligned against that drift.

**This ADR's decision remains accepted.**
Its boundary concerns ownership and the predicate interface, not the physical file that contains a trait.
“Separate source of truth” means separate ownership and exposure through named predicates.
That property remains true for a projected or derived backing table.
A derived daemon registry is therefore permitted **only if** it preserves all these invariants:

1. **Daemon-owned declaration.** A daemon-owned facet under `packages/daemon/src/` declares route and request-policy traits.
The registration composes that facet.
The public command contract in `src/commands/**` or `packages/command-registry/src/catalog.ts` must never contain these traits as inline fields.
Comment thread
thymikee marked this conversation as resolved.
A registration call may be co-located with public declarations.
Ownership of daemon policy must remain separate from the public surface.
See Alternatives, “Keep daemon groups in `src/command-catalog.ts`”.
2. **Predicate interface unchanged.** Consumers continue to use named predicates such as `getDaemonCommandRoute`, `isLeaseAdmissionExempt`, and `shouldLockSessionExecution`.
Only the daemon registry exposes these predicates.
Derivation changes table construction, not table access.
3. **No leakage into public projections.** Types must prevent the catalog/CLI/MCP/help/capability projections from reading daemon-only traits.
The daemon registry must still not define CLI grammar, Node.js options, MCP schemas, user-facing help, or capability support.
4. **One declaration per concern, enforced by types.** At the single registration site, a missing or duplicate daemon trait must cause a compile error.
This replaces alignment by convention.
This structural improvement justifies derivation instead of a separate hand-authored table.

A single flat public descriptor that exposes daemon fields in public views is **not** permitted.
This would combine daemon policy with a public command registry, which this ADR rejects.
Compose facets owned by their domains.
Derive the registry from those facets.
Until that derivation is implemented and the registry tests pin it, the hand-authored `packages/daemon/src/core/daemon-command-registry.ts` remains the source of truth.
Until then, the operating rule in `AGENTS.md` does not change.
Comment thread
thymikee marked this conversation as resolved.
Loading
Loading