Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion .device-evidence/CHECKLIST.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ appended warning:
### Proof the review asked for on this lane

The false-attribution class is covered by unit tests rather than a device run, because the device
cannot show a cache hit on command: `packages/daemon/src/__tests__/capture-disclosure-target-activation.test.ts`
cannot show a cache hit on command: `packages/daemon/src/core/__tests__/capture-disclosure-target-activation.test.ts`
pins that a cache-reused tree discloses nothing, and
`src/__tests__/daemon-composition/interaction/interaction-capture-disclosure.test.ts` pins that
a press which consumed no capture is not disclosed against an older tree.
Expand Down
14 changes: 9 additions & 5 deletions .fallowrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@
"scripts/help-conformance-sample-outputs.d.mts",
"scripts/help-conformance-runner-output.d.mts"
],
"dynamicallyLoaded": ["packages/daemon/src/interaction/index.ts"],
"dynamicallyLoaded": ["packages/daemon/src/execution/interaction/index.ts"],
"ignoreDependencies": [
"@theme",
// @agent-device/maestro owns the source import, while the published root
Expand Down Expand Up @@ -155,19 +155,23 @@
},
{
// Daemon route handlers are reached only through the dynamic `import()` table in request-handler-chain.ts, which --production analysis cannot follow to a consumer.
"file": "packages/daemon/src/handlers/{human-control,lease,session,snapshot,react-native,record-trace}.ts",
"file": "packages/daemon/src/sessions/handlers/{human-control,lease,session,snapshot,record-trace}.ts",
"exports": [
"handleHumanControlCommand",
"handleLeaseCommands",
"handleSessionCommands",
"handleSnapshotCommands",
"handleReactNativeCommands",
"handleRecordTraceCommands"
]
},
{
// The react-native route handler lives in the execution layer with its mechanics; same dynamic-loader exemption as the sessions-side route handlers above.
"file": "packages/daemon/src/execution/handlers/react-native.ts",
"exports": ["handleReactNativeCommands"]
},
{
// The interaction facade exposes #2178's deferred route handlers plus the existing ref mutation response consumed by help conformance. Fallow's production graph cannot follow the request-handler-chain dynamic imports, so this exact list is the facade contract.
"file": "packages/daemon/src/interaction/index.ts",
"file": "packages/daemon/src/execution/interaction/index.ts",
"exports": ["refMutationAdmissionResponse", "handleFindCommands", "handleInteractionCommands"]
},
{
Expand Down Expand Up @@ -251,7 +255,7 @@
]
},
{
// packages/daemon/src/__tests__/request-runtime-binding.test.ts drives the daemon's exact bind through the recovery authority, whose cancellation race is what rejects; production reaches it only through finishRecoveredDurableCapture and recoverDurableCaptureResource in the same package.
// packages/daemon/src/core/__tests__/request-runtime-binding.test.ts drives the daemon's exact bind through the recovery authority, whose cancellation race is what rejects; production reaches it only through finishRecoveredDurableCapture and recoverDurableCaptureResource in the same package.
"file": "packages/capture-kit/src/durable-capture/index.ts",
"exports": ["acquireDurableCaptureRecoveryAuthorityBeforeDeadline"]
},
Expand Down
6 changes: 3 additions & 3 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,11 @@ Read the declaration rather than maintaining a prose copy:

- commands and their surface, runtime-use, batch, and timeout traits:
`packages/command-registry/src/registry.ts`
- daemon route ownership and request-policy traits: `packages/daemon/src/daemon-command-registry.ts`
- daemon route ownership and request-policy traits: `packages/daemon/src/core/daemon-command-registry.ts`
- interaction paths and guarantees: `packages/contracts/src/interaction-guarantees.ts`
- canonical command names: `packages/command-registry/src/catalog.ts`
- device runtime-use declarations and fact admission:
`packages/command-registry/src/registry.ts`, `packages/daemon/src/runtime-admission.ts`, and
`packages/command-registry/src/registry.ts`, `packages/daemon/src/core/runtime-admission.ts`, and
`src/platform-runtime-gateway.ts`
- common command input fields, and which surface may write an input key (model, operator, retired):
`src/commands/common-input-fields.ts` and `src/commands/input-audience.ts`
Expand Down Expand Up @@ -110,7 +110,7 @@ under `contracts/fixtures/`.
## Runtime and diagnostics seams

Diagnostics use `@agent-device/host-kit/diagnostics`. Request diagnostics belong in the session request log;
session artifact paths come from `packages/daemon/src/session-artifact-paths.ts`. App/device logs remain in `app.log`;
session artifact paths come from `packages/daemon/src/core/session-artifact-paths.ts`. App/device logs remain in `app.log`;
Apple runner and xcodebuild output remains in `runner.log`.

Normalize failures with `normalizeError` and preserve `hint`, `diagnosticId`, `logPath`, and typed
Expand Down
4 changes: 2 additions & 2 deletions docs/adr/0003-daemon-command-registry.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ to public command identity, and it required duplicate command sets to stay align
Keep public command identity in `src/command-catalog.ts` and public input/output contracts in
`src/commands/**`.

Add `packages/daemon/src/daemon-command-registry.ts` as the daemon-side source of truth for command route
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.
Expand Down Expand Up @@ -85,5 +85,5 @@ derived daemon registry is therefore permitted **only if** it preserves all of t
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/daemon-command-registry.ts` remains the source of
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.
4 changes: 2 additions & 2 deletions docs/adr/0004-ios-snapshot-backend-strategy.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ facet in `@agent-device/capture-kit` owns snapshot policy generally rather than
stated as a duration budget; the loop derives the deadline from the window's `markedAt` itself, so
the budget is always spent from the action and a caller has no absolute instant it could get
wrong.
`packages/daemon/src/session-snapshot-freshness.ts` keeps only what needs a session: reading and retiring
`packages/daemon/src/core/session-snapshot-freshness.ts` keeps only what needs a session: reading and retiring
the window on store-owned `SessionState`, and choosing the comparison baseline from snapshot
lineage. It remains the declared R7 owner of `androidSnapshotFreshness`.
- **Timeout evidence.** Whether a capture failed because the hierarchy never arrived is decided
Expand Down Expand Up @@ -150,7 +150,7 @@ those roots and are outside that gate. It carries a positive control, because a
matching would look identical to a boundary being obeyed.

The residual call sites #1983 also named are audited and deliberately left in place.
`packages/daemon/src/direct-ios-selector.ts` carries no presentation policy: `isLocalIosRunnerSession` and
`packages/daemon/src/core/direct-ios-selector.ts` carries no presentation policy: `isLocalIosRunnerSession` and
`readSimpleIosSelectorTarget` are session routing (device family, provider ownership, the
stabilization window), while `deriveDirectIosNodeSelector` and `isDirectIosSelectorFallbackError`
are selector derivation and ADR 0011 delegation-on-error. The latter two are pure and
Expand Down
2 changes: 1 addition & 1 deletion docs/adr/0008-command-descriptor-registry.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ Accepted
A command's identity is restated, by hand, across roughly ten tables that must stay aligned by
convention: `PUBLIC_COMMANDS` (`src/command-catalog.ts`), the per-command metadata and family facets
(`src/commands/**`), the capability matrix (`src/core/capabilities.ts`), the daemon command registry
(`packages/daemon/src/daemon-command-registry.ts`, ADR 0003), the structured-batch allowlist
(`packages/daemon/src/core/daemon-command-registry.ts`, ADR 0003), the structured-batch allowlist
(`src/batch-policy.ts`), the MCP exposure sets, the Node client interface and impl (`src/client-types.ts`,
`src/client.ts`), and the generic-dispatch `switch` (`src/core/dispatch.ts`, whose `default: throw` makes a
missing or renamed command a runtime error, not a compile error). Adding one command touches ~24 files, the
Expand Down
30 changes: 15 additions & 15 deletions docs/adr/0012-interactive-replay.md
Original file line number Diff line number Diff line change
Expand Up @@ -730,7 +730,7 @@ path — no text editing, no silent similarity-heal.

**Core mechanism — the healed script IS `session.actions`.** No new splice engine. `session.actions`
already accumulates every executed action for the session's lifetime
(`packages/daemon/src/session-action-recorder.ts:37`, unconditional push), and the divergence refusal is
(`packages/daemon/src/core/session-action-recorder.ts:37`, unconditional push), and the divergence refusal is
pre-dispatch (target verification refuses before the device action, decision 3), so a divergent step is
never pushed. Across a repair session: original steps that verified and dispatched land in
`session.actions`; the divergent step is absent (refused pre-dispatch, never recorded); the agent's
Expand Down Expand Up @@ -766,7 +766,7 @@ works" and "healed scripts are always valid":
steps are re-executed during the repair replay; only if recording is armed from the start do they land
in `session.actions` with fresh `target-v1` evidence. Arming late yields a hybrid healed script (an
annotated corrective step glued to a bare, unannotated prefix) that re-diverges on its own next replay
(`packages/daemon/src/interaction/internal/interaction-common.ts:64-65` attaches evidence only when `recordSession` was
(`packages/daemon/src/execution/interaction/internal/interaction-common.ts:64-65` attaches evidence only when `recordSession` was
true when the step ran).
- **R2 — `--from` continuation only; never re-run the full replay on the same session.** After a
divergence at step N and the corrective action, the agent must continue with `replay --from k
Expand Down Expand Up @@ -808,8 +808,8 @@ works" and "healed scripts are always valid":
instead of leaving it to agent judgment.
- **R4 — corrective actions must materialize to selector form; the writer fails loudly on a bare `@ref`
cross-session export.** A `press @e12` normally resolves a `selectorChain` at runtime
(`packages/daemon/src/interaction/internal/interaction-touch-targets.ts`), which `buildOptimizedActions`
(`packages/daemon/src/session-script-writer.ts:69-83`) rewrites to a selector line. If no `selectorChain` was
(`packages/daemon/src/execution/interaction/internal/interaction-touch-targets.ts`), which `buildOptimizedActions`
(`packages/daemon/src/core/session-script-writer.ts:69-83`) rewrites to a selector line. If no `selectorChain` was
captured, the writer must refuse to emit a bare `@ref` line into a persisted `.ad` — a session-bound
ref will not resolve in a fresh run. It **fails loudly**: an error surfaced to the user with a non-zero
exit, never a swallowed or silently-dropped line (existing session-write paths swallow such failures;
Expand Down Expand Up @@ -893,7 +893,7 @@ by construction. The healed `.ad` is written only when the repair ends (below) a
- The healed `.ad` is published when a teardown commits the transaction at state `COMPLETE` — normally the
agent's own `close`/`close --save-script <out>`, but any teardown of a completed transaction commits
(see the commit state machine below) — reusing the existing `session.actions` serializer
(`SessionScriptWriter.write`, `packages/daemon/src/session-script-writer.ts:30-52`) over the post-watermark slice
(`SessionScriptWriter.write`, `packages/daemon/src/core/session-script-writer.ts:30-52`) over the post-watermark slice
only (R6). When no
`<out>` path is given, the default is the **`<original-stem>.healed.ad` sibling**: the original script's
path with its `.ad` extension replaced by `.healed.ad` (e.g. `flows/login.ad` → `flows/login.healed.ad`),
Expand Down Expand Up @@ -943,7 +943,7 @@ a separate naming change, not part of this decision.

**Scope: this refusal is uniform across repair AND ordinary recording, not repair-only.** Everything
above is written in the context of decision 6's repair-armed heal, but `publishHealedScriptAtomically`
(`packages/daemon/src/session-script-writer.ts`) is the single publish primitive `SessionScriptWriter.write` calls
(`packages/daemon/src/core/session-script-writer.ts`) is the single publish primitive `SessionScriptWriter.write` calls
for every `--save-script` target, with no repair-armed-vs-ordinary branch — a repair-armed heal
(`saveScriptBoundary` set) and an ordinary, non-repair `open --save-script`/`close --save-script`
recording (`saveScriptBoundary` absent) publish through the exact same exclusive `linkSync`. Before this
Expand Down Expand Up @@ -1002,10 +1002,10 @@ The tombstone itself expires after its bounded window, after which the key is fu
> Provenance travels as `internal.replayPlanStep`, stamped by `invokeResolvedReplayAction`
> (`src/daemon/handlers/session-replay-action-runtime.ts`) — the single point every plan step is
> dispatched, so the marker covers annotated and unannotated steps alike. `internal` is a daemon-only
> channel (`toDaemonRequest`, `packages/daemon/src/server/http-rpc-methods.ts`, never copies it off the wire), so
> channel (`toDaemonRequest`, `packages/daemon/src/server/server/http-rpc-methods.ts`, never copies it off the wire), so
> authored provenance cannot be spoofed by a client; this is the same channel `replayTargetGuard` and
> `findResolvedTarget` already use. The rule is written once, in `isInteractiveObservation`
> (`packages/daemon/src/session-action-recorder.ts`), and both recording call sites consume it.
> (`packages/daemon/src/core/session-action-recorder.ts`), and both recording call sites consume it.
>
> **2. The corrective-read trap: why even out-of-band reads need an opt-in.** Excluding every out-of-band
> read is still not safe on its own: a divergence's **correction** can itself be a read. In the wave-3 E3 drift, the diverged step **was** a `get`, so its corrective replacement is a
Expand Down Expand Up @@ -1044,11 +1044,11 @@ The tombstone itself expires after its bounded window, after which the key is fu
>
> **4. How the rule projects across CLI/Node/MCP surfaces.** The exclusion is enforced at a single
> daemon-side choke point — `isExcludedRepairSegmentObservation` in `recordActionEntry`
> (`packages/daemon/src/session-action-recorder.ts`), gated on `session.saveScriptBoundary !== undefined` (set ONLY
> (`packages/daemon/src/core/session-action-recorder.ts`), gated on `session.saveScriptBoundary !== undefined` (set ONLY
> by a repair-armed `replay --save-script`; an ordinary, non-repair `open --save-script`/`close --save-script`
> authoring recording never sets it — see decision 6's "Scope" note above) and on the entry's
> `interactiveObservation` marker, which both recording call sites
> (`packages/daemon/src/selector-recording.ts` for `get`/`is`/read-only `find`, `packages/daemon/src/snapshot-runtime.ts` for
> (`packages/daemon/src/core/selector-recording.ts` for `get`/`is`/read-only `find`, `packages/daemon/src/sessions/snapshot-runtime.ts` for
> `snapshot`) compute from the shared `isInteractiveObservation` predicate rather than each re-deriving it.
> Every surface reaches this same function because every surface's request reaches the same daemon.
>
Expand Down Expand Up @@ -1159,8 +1159,8 @@ above and the gates they name are the authoritative record of current behavior.

At acceptance, replay was deterministic. `.ad` scripts were plain text — one action per line, `#`
comments, and a `context platform=... device=... theme=...` header (`src/replay/script.ts`) — recorded
via `open --save-script` (`packages/daemon/src/session-action-recorder.ts`,
`packages/daemon/src/session-script-writer.ts`) or hand-written, and executed step-by-step by
via `open --save-script` (`packages/daemon/src/core/session-action-recorder.ts`,
`packages/daemon/src/core/session-script-writer.ts`) or hand-written, and executed step-by-step by
`runReplayScriptFile`
(`src/daemon/handlers/session-replay-runtime.ts`) under the daemon's `replay`/`test` commands
(`src/daemon/handlers/session-replay.ts`). Recorded touch/fill/get targets are selector chains with
Expand Down Expand Up @@ -1289,15 +1289,15 @@ both `.ad` and Maestro paths) grounds the same conclusions from the caller's sea
"Retry with --debug and inspect diagnostics log for details." (`defaultHintForCode`,
`src/kernel/errors.ts:253-254`).
- **Recordings contain zero verification steps.** The script writer strips every recorded `snapshot`
action (`buildOptimizedActions`, `packages/daemon/src/session-script-writer.ts:69`: `if (action.command ===
action (`buildOptimizedActions`, `packages/daemon/src/core/session-script-writer.ts:69`: `if (action.command ===
'snapshot') continue;` — only synthetic ref-scoped snapshots are re-inserted, as resolution aids, not
observations), and the record-time flag allowlist (`SANITIZED_FLAG_KEYS`,
`packages/daemon/src/session-action-recorder.ts:46-77`) carries neither `settle`/`settleQuietMs` nor `verify`,
`packages/daemon/src/core/session-action-recorder.ts:46-77`) carries neither `settle`/`settleQuietMs` nor `verify`,
so `--settle`/`--verify` are dropped from recorded steps. A recording therefore replays actions with
no outcome observation at all — exactly the gap decision 3's record-time identity evidence fills.

A related, currently under-used precedent: recorded `@ref` steps already carry an optional identity
hint in the `.ad` file. `appendRefLabel` (`packages/daemon/src/session-script-writer.ts:235-240`) writes the
hint in the `.ad` file. `appendRefLabel` (`packages/daemon/src/core/session-script-writer.ts:235-240`) writes the
node's label as a trailing token, parsed back into `action.result.refLabel`
(`src/replay/script.ts:269,295,315`). Today that label is used only as a fallback LOOKUP key
(`tryResolveRefNode`'s `fallbackLabel`, `resolution.ts:393,413-430`) when the ref itself fails to
Expand Down
2 changes: 1 addition & 1 deletion docs/adr/0014-session-ref-frame-lifetime.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ ref-frame vocabulary is promoted into `CONTEXT.md`.
issuance scope) — kept separate from the latest operational observation (`session.snapshot`).
The frame is one value on `SessionState`, replaced whole by a transition and never edited in
place; its type is nominal (`#`-private fields), so no module outside
`packages/daemon/src/ref-frame.ts` can construct a frame, edit one, or derive one from an existing
`packages/daemon/src/core/ref-frame.ts` can construct a frame, edit one, or derive one from an existing
frame. What the type cannot judge is a whole frame moved unchanged — clearing the field, or
assigning another session's frame — which the field-owner gate still covers.
- A complete snapshot activates an `all` frame; `find`, settled diffs, and replay divergence
Expand Down
4 changes: 2 additions & 2 deletions docs/adr/0016-active-session-script-publication.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

## Status

Accepted; implemented on `main` (`packages/daemon/src/handlers/session-script-publication.ts`,
`packages/daemon/src/session-script-active-publication.ts`), including the #1349 identity-level
Accepted; implemented on `main` (`packages/daemon/src/sessions/handlers/session-script-publication.ts`,
`packages/daemon/src/core/session-script-active-publication.ts`), including the #1349 identity-level
destination-guard amendment.

## Rules at a glance
Expand Down
Loading
Loading