From f73dc05b5f71b86ef1c62a5ccbc5daa10317fc56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Pierzcha=C5=82a?= Date: Mon, 20 Jul 2026 16:29:49 +0200 Subject: [PATCH 1/5] docs: propose active-session replay publication --- CONTEXT.md | 5 + .../0016-active-session-replay-publication.md | 140 ++++++++++++++++++ docs/adr/README.md | 1 + 3 files changed, 146 insertions(+) create mode 100644 docs/adr/0016-active-session-replay-publication.md diff --git a/CONTEXT.md b/CONTEXT.md index 7ed8b0df91..4abb17cc4c 100644 --- a/CONTEXT.md +++ b/CONTEXT.md @@ -28,6 +28,11 @@ - Target: selected automation destination, such as mobile, tv, or desktop. - Modality: broad supported device family, such as mobile, tv, or desktop. - Session: daemon-owned state for a selected target and opened app or surface. +- Script recording: opt-in session mode armed before actions so a persisted `.ad` can carry portable + action inputs and recording-time target identity evidence. It is distinct from screen/video recording. +- Open-to-destination replay: self-contained `.ad` recording that starts with `open`, ends before + `close`, and leaves a replay session active at its recorded destination. Avoid: fragment (reserved for + lifecycle-free composition), partial script. - Recording backend: daemon-internal module interface selected per recording target that owns platform recording validation, output path policy, start/stop execution, and record-only cleanup below the daemon recording lifecycle. - Device lease: logical remote ownership of one selected device for a tenant/run/client and lease provider, separate from platform helper process diff --git a/docs/adr/0016-active-session-replay-publication.md b/docs/adr/0016-active-session-replay-publication.md new file mode 100644 index 0000000000..68620ebcdb --- /dev/null +++ b/docs/adr/0016-active-session-replay-publication.md @@ -0,0 +1,140 @@ +# ADR 0016: Active-Session Replay Publication + +## Status + +Proposed + +## Context + +The replay repair study found re-recording a drifted journey cheaper than repairing it in two of the +three measured drift classes. The immediate reusable unit is an **open-to-destination replay**: a +self-contained `.ad` script that opens the app, performs the complete journey to screen X, and leaves +the replay session active there so an agent can continue with new work. + +Ordinary script recording currently combines two concerns. `open --save-script[=]` arms +recording before the first interaction so selector chains and ADR 0012 `target-v1` identity evidence +are captured at action time. `close` publishes the accumulated script, records a terminal `close`, and +tears down the session. That artifact is suitable as a closed test flow, but not as a live starting +state. + +Publishing only at the end of an ordinary unarmed session is insufficient. Session history retains the +commands, but the resolved target tree needed for `target-v1` evidence is deliberately discarded after +each action. Late publication can serialize selector text; it cannot reconstruct which element was +actually acted on. Making identity capture unconditional for every session would change normal +interaction execution: recording disables direct selector fast paths when a capture-backed route is +required for evidence. That performance and data-retention change has not been measured. + +Issue [#1346](https://github.com/callstack/agent-device/issues/1346) and the throwaway +[`session-save-replay` prototype](https://github.com/callstack/agent-device/tree/agent/prototype-session-save-replay/scripts/prototypes/session-save-replay) +record the motivating workflow and API trial. Composable lifecycle-free fragments remain a separate +decision in [#1336](https://github.com/callstack/agent-device/issues/1336). + +## Decision + +Add an explicit publication action for an already-armed ordinary script recording: + +```sh +agent-device open com.example.app --relaunch --save-script=screen-x.ad +# perform the complete journey to screen X +agent-device session save-replay +``` + +`session save-replay [path] [--force]` publishes the current ordinary script recording without +closing the app or deleting the session. An explicit `path` retargets the recording using the existing +target/force authorization rules. Without it, publication uses the path armed by `open --save-script` +or the existing generated default. + +### Recording lifecycle + +Ordinary script recording gains a two-state publication lifecycle: + +- **ARMED**: established by `open --save-script[=]` before target-bearing actions run. The session + records portable action inputs and fresh target identity evidence. +- **PUBLISHED**: reached only after `session save-replay` atomically commits the complete history from + the recorded `open` through the current action. The session remains active at the destination, but + close-time script publication is disarmed. + +A publication failure leaves the recording ARMED, including its path and same-target `--force` +authorization, so the caller can correct the target or permissions and retry. PUBLISHED is terminal for +that recording: later actions are ordinary session work and a later `close` performs teardown only. V1 +does not re-arm or publish multiple open-to-destination artifacts from one session. + +This lifecycle is distinct from ADR 0012's repair transaction. `session save-replay` rejects a session +with `saveScriptBoundary` set and directs the caller to finish or abort the repair through its existing +`replay --from` and teardown commit protocol. Active-session publication never marks a repair COMPLETE, +commits a healed slice, writes `# agent-device:heal-complete`, or changes repair tombstone semantics. + +### Artifact contract + +The published `.ad`: + +- contains one recorded `open` and every recordable action through the publication request; +- does not append or serialize `session save-replay` or `close`; +- uses the ordinary session context header, selector-chain optimization, and canonical `target-v1` + annotations captured while ARMED; +- fails loudly rather than emitting an unresolved session-local `@ref` or dropping target evidence that + ADR 0012 requires for an element-targeting recorded action; and +- uses the existing same-directory atomic publication primitive, refusing every existing target unless + `--force` authorizes atomic replacement. + +The success response identifies the final path and session and reports the number of serialized actions. +The command must fail before writing when there is no active session, recording was not armed, no +recorded `open` exists, or a repair transaction owns the session. Every failure explains the recovery +action; none degrades to `{ written: false }` success. + +### Surface and naming + +V1 extends the existing `session` command and typed session client surface. It does not introduce +`script start/stop`, marks, or a second replay engine. CLI help makes the two phases explicit: the +existing `--save-script` flag arms evidence capture, while `session save-replay` publishes without +teardown. + +This ADR does not rename or deprecate `--save-script`. Renaming the arming flag is a separate compatibility +decision; `--save-replay` would be misleading if it only armed capture, while `--record-replay` would add +new vocabulary without improving the v1 workflow. + +## Consequences + +- Agents can record login, onboarding, or deep navigation as one self-contained starting state, replay + it from scratch, and continue from the resulting live session. +- The workflow has two explicit moments because evidence must be armed before the first target action and + the destination is known only when the caller publishes. +- Normal unarmed interactions keep their current fast paths and retention behavior. +- A successful active-session publication cannot collide with a later close-time auto-save. +- Intermediate lifecycle-free fragments, entry guards, include semantics, composed digests, and shared + fragment pinning remain entirely under #1336. +- Credential handling and arbitrary history ranges remain out of scope. Agents retain responsibility for + reviewing recorded inputs before sharing an artifact. + +## Alternatives Considered + +- **Save any session history at the end:** rejected because target identity evidence cannot be + reconstructed after the interaction and the resulting artifact would undercut ADR 0012's provenance + model. +- **Capture full target evidence in every session:** rejected until its direct-path latency, capture + count, memory, and event-log costs are measured. It would alter ordinary interaction behavior to make + one authoring command shorter. +- **`close --no-close` or `--save-script --no-close`:** rejected because a command named for teardown + would conditionally preserve the session and because it would not solve late arming. +- **General `script start/stop` or history marks:** rejected because the accepted v1 boundary is exactly + one recorded `open` through one destination. Arbitrary slices require entry-state semantics and belong + with fragment design. +- **`replay save`:** rejected because `replay ` consumes an artifact while publication consumes a + live session; the session owns the source data and lifecycle. + +## Validation Required for Implementation + +- An unarmed session refuses publication before filesystem work and names `open --save-script` as the + recovery. +- An armed session publishes `open` plus target-annotated actions without `close`, returns the final path, + remains active, and can continue accepting commands. +- The artifact replays in a fresh session, reaches the destination, and leaves that replay session active. +- Every supported element-targeting action has canonical identity evidence and no unresolved `@ref` + reaches disk. +- Existing-target refusal preserves the original bytes; `--force` replaces atomically; a failed publish + remains retryable. +- Closing after successful publication performs no second write, while closing an unpublished ARMED + ordinary recording preserves current close-time publication behavior. +- Repair-armed sessions refuse this action without changing repair state. +- Provider-backed integration scenarios cover the public daemon route, and live iOS and Android runs + prove the saved artifact and post-save session behavior on real backends. diff --git a/docs/adr/README.md b/docs/adr/README.md index 1caeab1dcc..3f7fbd7e9b 100644 --- a/docs/adr/README.md +++ b/docs/adr/README.md @@ -17,6 +17,7 @@ | [0013 Unified Gesture Plans](0013-unified-gesture-plans.md) | gesture API/routing, contact topology, multi-touch geometry, native pointer injection, two-finger pan | | [0014 Session Ref-Frame Lifetime](0014-session-ref-frame-lifetime.md) | ref authorization epochs, complete/partial issuance, pre-side-effect expiration, replay/batch compatibility, and cross-platform stale-mutation policy | | [0015 Direct Maestro Compatibility Engine](0015-direct-maestro-engine.md) | Maestro YAML parsing/execution, compatibility observation policy, conformance, performance gates, gesture integration | +| [0016 Active-Session Replay Publication](0016-active-session-replay-publication.md) | publishing an armed open-to-destination `.ad` recording without closing its live session | ADRs record *why*; the registries and gates they describe are the living source of truth — when prose and a registry disagree, the registry wins and the ADR needs a follow-up. From 30fed3b70e6771990ccf09e6cb3583525d005e8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Pierzcha=C5=82a?= Date: Mon, 20 Jul 2026 16:44:52 +0200 Subject: [PATCH 2/5] docs: address active-session replay review --- CONTEXT.md | 8 ++- .../0016-active-session-replay-publication.md | 58 +++++++++++++++---- docs/adr/README.md | 2 +- 3 files changed, 53 insertions(+), 15 deletions(-) diff --git a/CONTEXT.md b/CONTEXT.md index 4abb17cc4c..3dc12f0d2d 100644 --- a/CONTEXT.md +++ b/CONTEXT.md @@ -30,9 +30,11 @@ - Session: daemon-owned state for a selected target and opened app or surface. - Script recording: opt-in session mode armed before actions so a persisted `.ad` can carry portable action inputs and recording-time target identity evidence. It is distinct from screen/video recording. -- Open-to-destination replay: self-contained `.ad` recording that starts with `open`, ends before - `close`, and leaves a replay session active at its recorded destination. Avoid: fragment (reserved for - lifecycle-free composition), partial script. +- Open-to-destination script: self-contained `.ad` script that starts with `open`, proves its destination + with a destination guard, ends before `close`, and leaves the app session active. Avoid: replay + (artifact noun), fragment (reserved for lifecycle-free composition), partial script. +- Destination guard: target-bearing `wait` near the end of an open-to-destination script that proves a + landmark on the ready destination screen before replay hands the live session to its caller. - Recording backend: daemon-internal module interface selected per recording target that owns platform recording validation, output path policy, start/stop execution, and record-only cleanup below the daemon recording lifecycle. - Device lease: logical remote ownership of one selected device for a tenant/run/client and lease provider, separate from platform helper process diff --git a/docs/adr/0016-active-session-replay-publication.md b/docs/adr/0016-active-session-replay-publication.md index 68620ebcdb..63598b701c 100644 --- a/docs/adr/0016-active-session-replay-publication.md +++ b/docs/adr/0016-active-session-replay-publication.md @@ -1,4 +1,4 @@ -# ADR 0016: Active-Session Replay Publication +# ADR 0016: Active-Session Script Publication ## Status @@ -7,9 +7,9 @@ Proposed ## Context The replay repair study found re-recording a drifted journey cheaper than repairing it in two of the -three measured drift classes. The immediate reusable unit is an **open-to-destination replay**: a -self-contained `.ad` script that opens the app, performs the complete journey to screen X, and leaves -the replay session active there so an agent can continue with new work. +three measured drift classes. The immediate reusable unit is an **open-to-destination script**: a +self-contained `.ad` script that opens the app, performs the complete journey to screen X, verifies a +destination landmark, and leaves the app session active there so an agent can continue with new work. Ordinary script recording currently combines two concerns. `open --save-script[=]` arms recording before the first interaction so selector chains and ADR 0012 `target-v1` identity evidence @@ -36,6 +36,7 @@ Add an explicit publication action for an already-armed ordinary script recordin ```sh agent-device open com.example.app --relaunch --save-script=screen-x.ad # perform the complete journey to screen X +agent-device wait 'role="heading" label="Screen X"' agent-device session save-replay ``` @@ -64,11 +65,38 @@ with `saveScriptBoundary` set and directs the caller to finish or abort the repa `replay --from` and teardown commit protocol. Active-session publication never marks a repair COMPLETE, commits a healed slice, writes `# agent-device:heal-complete`, or changes repair tombstone semantics. +### Destination readiness and replay handoff + +The destination is an authored postcondition, not the last navigation action. Before publication, the +recorded suffix after the last mutating action must contain a target-bearing `wait` for a landmark that +identifies the ready destination screen. A duration wait or `wait stable` alone does not qualify, though +`wait stable` may follow the landmark wait. `session save-replay` refuses publication without this +**destination guard** and tells the author to record one. V1 does not infer a screen identity from a +snapshot or synthesize an implicit guard. + +On consumption, a script without `close` preserves the existing replay behavior: the named session stays +active and the successful `ReplayCommandResult` returns its `session` id. The caller binds subsequent +commands to that returned id. Replay reports success only after the destination guard completes; the +absence of `close` changes neither action dispatch nor the success response shape. + +### Sensitive inputs + +Executable `.ad` artifacts serialize action inputs literally. A recorded `fill` therefore writes its +text to the published file; diagnostic and event-log redaction cannot protect an input that the replay +engine must later execute. V1 does not claim secret-bearing login flows are safe to publish. + +Native `.ad` replay already supports late-bound `${VAR}` values, but ordinary recording cannot yet +execute with a real value while publishing only its placeholder. That safe-authoring capability is +tracked in [#1348](https://github.com/callstack/agent-device/issues/1348). Until it ships, authors must not +record a journey that enters a secret: use pre-authenticated test state or deliberately non-secret fixture +credentials that are safe to persist. CLI help must state this warning next to the authoring workflow. + ### Artifact contract The published `.ad`: - contains one recorded `open` and every recordable action through the publication request; +- contains a destination guard after its last mutating action; - does not append or serialize `session save-replay` or `close`; - uses the ordinary session context header, selector-chain optimization, and canonical `target-v1` annotations captured while ARMED; @@ -79,8 +107,8 @@ The published `.ad`: The success response identifies the final path and session and reports the number of serialized actions. The command must fail before writing when there is no active session, recording was not armed, no -recorded `open` exists, or a repair transaction owns the session. Every failure explains the recovery -action; none degrades to `{ written: false }` success. +recorded `open` or destination guard exists, or a repair transaction owns the session. Every failure +explains the recovery action; none degrades to `{ written: false }` success. ### Surface and naming @@ -95,16 +123,16 @@ new vocabulary without improving the v1 workflow. ## Consequences -- Agents can record login, onboarding, or deep navigation as one self-contained starting state, replay - it from scratch, and continue from the resulting live session. +- Agents can record onboarding or deep navigation as one self-contained starting state, replay it from + scratch, and continue from the resulting live session. - The workflow has two explicit moments because evidence must be armed before the first target action and the destination is known only when the caller publishes. - Normal unarmed interactions keep their current fast paths and retention behavior. - A successful active-session publication cannot collide with a later close-time auto-save. - Intermediate lifecycle-free fragments, entry guards, include semantics, composed digests, and shared fragment pinning remain entirely under #1336. -- Credential handling and arbitrary history ranges remain out of scope. Agents retain responsibility for - reviewing recorded inputs before sharing an artifact. +- Secret-bearing authoring remains unsafe until #1348; the initial workflow is limited to journeys that + do not enter secrets. Arbitrary history ranges remain out of scope. ## Alternatives Considered @@ -121,14 +149,20 @@ new vocabulary without improving the v1 workflow. with fragment design. - **`replay save`:** rejected because `replay ` consumes an artifact while publication consumes a live session; the session owns the source data and lifecycle. +- **Infer a destination fingerprint at publication:** rejected for v1 because screen identity and + readiness are app semantics. A caller-authored target wait is explicit, already recordable, and fails + at the correct point during cold replay. ## Validation Required for Implementation - An unarmed session refuses publication before filesystem work and names `open --save-script` as the recovery. +- An armed session without a destination guard refuses publication before filesystem work and names a + target-bearing `wait` as the recovery. - An armed session publishes `open` plus target-annotated actions without `close`, returns the final path, remains active, and can continue accepting commands. -- The artifact replays in a fresh session, reaches the destination, and leaves that replay session active. +- The artifact replays from a cold start, completes its destination guard, returns the live session id, + and accepts a subsequent command on that session. - Every supported element-targeting action has canonical identity evidence and no unresolved `@ref` reaches disk. - Existing-target refusal preserves the original bytes; `--force` replaces atomically; a failed publish @@ -136,5 +170,7 @@ new vocabulary without improving the v1 workflow. - Closing after successful publication performs no second write, while closing an unpublished ARMED ordinary recording preserves current close-time publication behavior. - Repair-armed sessions refuse this action without changing repair state. +- CLI help warns that literal `fill` inputs are persisted and tells authors not to record secret-bearing + journeys until #1348's parameterized-input mechanism is available. - Provider-backed integration scenarios cover the public daemon route, and live iOS and Android runs prove the saved artifact and post-save session behavior on real backends. diff --git a/docs/adr/README.md b/docs/adr/README.md index 3f7fbd7e9b..15c6057836 100644 --- a/docs/adr/README.md +++ b/docs/adr/README.md @@ -17,7 +17,7 @@ | [0013 Unified Gesture Plans](0013-unified-gesture-plans.md) | gesture API/routing, contact topology, multi-touch geometry, native pointer injection, two-finger pan | | [0014 Session Ref-Frame Lifetime](0014-session-ref-frame-lifetime.md) | ref authorization epochs, complete/partial issuance, pre-side-effect expiration, replay/batch compatibility, and cross-platform stale-mutation policy | | [0015 Direct Maestro Compatibility Engine](0015-direct-maestro-engine.md) | Maestro YAML parsing/execution, compatibility observation policy, conformance, performance gates, gesture integration | -| [0016 Active-Session Replay Publication](0016-active-session-replay-publication.md) | publishing an armed open-to-destination `.ad` recording without closing its live session | +| [0016 Active-Session Script Publication](0016-active-session-replay-publication.md) | publishing an armed open-to-destination `.ad` script without closing its live session | ADRs record *why*; the registries and gates they describe are the living source of truth — when prose and a registry disagree, the registry wins and the ADR needs a follow-up. From 73cdff0cba8568f30a68db3ced07ae3921b2d5c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Pierzcha=C5=82a?= Date: Mon, 20 Jul 2026 16:54:14 +0200 Subject: [PATCH 3/5] docs: align active-session publication on save-script --- ...0016-active-session-script-publication.md} | 23 ++++++++++--------- docs/adr/README.md | 2 +- 2 files changed, 13 insertions(+), 12 deletions(-) rename docs/adr/{0016-active-session-replay-publication.md => 0016-active-session-script-publication.md} (92%) diff --git a/docs/adr/0016-active-session-replay-publication.md b/docs/adr/0016-active-session-script-publication.md similarity index 92% rename from docs/adr/0016-active-session-replay-publication.md rename to docs/adr/0016-active-session-script-publication.md index 63598b701c..ea11de6943 100644 --- a/docs/adr/0016-active-session-replay-publication.md +++ b/docs/adr/0016-active-session-script-publication.md @@ -25,7 +25,7 @@ interaction execution: recording disables direct selector fast paths when a capt required for evidence. That performance and data-retention change has not been measured. Issue [#1346](https://github.com/callstack/agent-device/issues/1346) and the throwaway -[`session-save-replay` prototype](https://github.com/callstack/agent-device/tree/agent/prototype-session-save-replay/scripts/prototypes/session-save-replay) +[`session-save-script` prototype](https://github.com/callstack/agent-device/tree/agent/prototype-session-save-replay/scripts/prototypes/session-save-script) record the motivating workflow and API trial. Composable lifecycle-free fragments remain a separate decision in [#1336](https://github.com/callstack/agent-device/issues/1336). @@ -37,10 +37,10 @@ Add an explicit publication action for an already-armed ordinary script recordin agent-device open com.example.app --relaunch --save-script=screen-x.ad # perform the complete journey to screen X agent-device wait 'role="heading" label="Screen X"' -agent-device session save-replay +agent-device session save-script ``` -`session save-replay [path] [--force]` publishes the current ordinary script recording without +`session save-script [path] [--force]` publishes the current ordinary script recording without closing the app or deleting the session. An explicit `path` retargets the recording using the existing target/force authorization rules. Without it, publication uses the path armed by `open --save-script` or the existing generated default. @@ -51,7 +51,7 @@ Ordinary script recording gains a two-state publication lifecycle: - **ARMED**: established by `open --save-script[=]` before target-bearing actions run. The session records portable action inputs and fresh target identity evidence. -- **PUBLISHED**: reached only after `session save-replay` atomically commits the complete history from +- **PUBLISHED**: reached only after `session save-script` atomically commits the complete history from the recorded `open` through the current action. The session remains active at the destination, but close-time script publication is disarmed. @@ -60,7 +60,7 @@ authorization, so the caller can correct the target or permissions and retry. PU that recording: later actions are ordinary session work and a later `close` performs teardown only. V1 does not re-arm or publish multiple open-to-destination artifacts from one session. -This lifecycle is distinct from ADR 0012's repair transaction. `session save-replay` rejects a session +This lifecycle is distinct from ADR 0012's repair transaction. `session save-script` rejects a session with `saveScriptBoundary` set and directs the caller to finish or abort the repair through its existing `replay --from` and teardown commit protocol. Active-session publication never marks a repair COMPLETE, commits a healed slice, writes `# agent-device:heal-complete`, or changes repair tombstone semantics. @@ -70,7 +70,7 @@ commits a healed slice, writes `# agent-device:heal-complete`, or changes repair The destination is an authored postcondition, not the last navigation action. Before publication, the recorded suffix after the last mutating action must contain a target-bearing `wait` for a landmark that identifies the ready destination screen. A duration wait or `wait stable` alone does not qualify, though -`wait stable` may follow the landmark wait. `session save-replay` refuses publication without this +`wait stable` may follow the landmark wait. `session save-script` refuses publication without this **destination guard** and tells the author to record one. V1 does not infer a screen identity from a snapshot or synthesize an implicit guard. @@ -97,7 +97,7 @@ The published `.ad`: - contains one recorded `open` and every recordable action through the publication request; - contains a destination guard after its last mutating action; -- does not append or serialize `session save-replay` or `close`; +- does not append or serialize `session save-script` or `close`; - uses the ordinary session context header, selector-chain optimization, and canonical `target-v1` annotations captured while ARMED; - fails loudly rather than emitting an unresolved session-local `@ref` or dropping target evidence that @@ -114,12 +114,13 @@ explains the recovery action; none degrades to `{ written: false }` success. V1 extends the existing `session` command and typed session client surface. It does not introduce `script start/stop`, marks, or a second replay engine. CLI help makes the two phases explicit: the -existing `--save-script` flag arms evidence capture, while `session save-replay` publishes without +existing `--save-script` flag arms evidence capture, while `session save-script` publishes without teardown. -This ADR does not rename or deprecate `--save-script`. Renaming the arming flag is a separate compatibility -decision; `--save-replay` would be misleading if it only armed capture, while `--record-replay` would add -new vocabulary without improving the v1 workflow. +This ADR does not rename or deprecate `--save-script`. The flag and session action name the same persisted +artifact: `open --save-script` configures the armed recording's eventual target, while +`session save-script` requests publication now. `save-replay` is rejected because replay is the act of +executing that script, not the artifact being saved. ## Consequences diff --git a/docs/adr/README.md b/docs/adr/README.md index 15c6057836..b956a8f957 100644 --- a/docs/adr/README.md +++ b/docs/adr/README.md @@ -17,7 +17,7 @@ | [0013 Unified Gesture Plans](0013-unified-gesture-plans.md) | gesture API/routing, contact topology, multi-touch geometry, native pointer injection, two-finger pan | | [0014 Session Ref-Frame Lifetime](0014-session-ref-frame-lifetime.md) | ref authorization epochs, complete/partial issuance, pre-side-effect expiration, replay/batch compatibility, and cross-platform stale-mutation policy | | [0015 Direct Maestro Compatibility Engine](0015-direct-maestro-engine.md) | Maestro YAML parsing/execution, compatibility observation policy, conformance, performance gates, gesture integration | -| [0016 Active-Session Script Publication](0016-active-session-replay-publication.md) | publishing an armed open-to-destination `.ad` script without closing its live session | +| [0016 Active-Session Script Publication](0016-active-session-script-publication.md) | publishing an armed open-to-destination `.ad` script without closing its live session | ADRs record *why*; the registries and gates they describe are the living source of truth — when prose and a registry disagree, the registry wins and the ADR needs a follow-up. From 78c108c933830a771ff00592b9df1162b1581025 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Pierzcha=C5=82a?= Date: Mon, 20 Jul 2026 17:13:35 +0200 Subject: [PATCH 4/5] docs: qualify destination guard identity --- CONTEXT.md | 6 +++--- docs/adr/0016-active-session-script-publication.md | 12 ++++++++++-- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/CONTEXT.md b/CONTEXT.md index 3dc12f0d2d..886b1c7681 100644 --- a/CONTEXT.md +++ b/CONTEXT.md @@ -30,11 +30,11 @@ - Session: daemon-owned state for a selected target and opened app or surface. - Script recording: opt-in session mode armed before actions so a persisted `.ad` can carry portable action inputs and recording-time target identity evidence. It is distinct from screen/video recording. -- Open-to-destination script: self-contained `.ad` script that starts with `open`, proves its destination +- Open-to-destination script: self-contained `.ad` script that starts with `open`, checks its destination with a destination guard, ends before `close`, and leaves the app session active. Avoid: replay (artifact noun), fragment (reserved for lifecycle-free composition), partial script. -- Destination guard: target-bearing `wait` near the end of an open-to-destination script that proves a - landmark on the ready destination screen before replay hands the live session to its caller. +- Destination guard: target-bearing `wait` near the end of an open-to-destination script that confirms a + landmark selector on the ready destination screen before replay hands the live session to its caller. - Recording backend: daemon-internal module interface selected per recording target that owns platform recording validation, output path policy, start/stop execution, and record-only cleanup below the daemon recording lifecycle. - Device lease: logical remote ownership of one selected device for a tenant/run/client and lease provider, separate from platform helper process diff --git a/docs/adr/0016-active-session-script-publication.md b/docs/adr/0016-active-session-script-publication.md index ea11de6943..38a2271564 100644 --- a/docs/adr/0016-active-session-script-publication.md +++ b/docs/adr/0016-active-session-script-publication.md @@ -74,6 +74,14 @@ identifies the ready destination screen. A duration wait or `wait stable` alone **destination guard** and tells the author to record one. V1 does not infer a screen identity from a snapshot or synthesize an implicit guard. +The initial guard is selector-level. `wait` does not yet carry ADR 0012 `target-v1` evidence through +recording and replay verification, so the guard proves that an element matching its selector exists, not +that it is the same landmark element observed while authoring. Authors must choose a selective +destination-specific landmark; a reshuffled screen containing the same weak label elsewhere can +false-pass. [#1349](https://github.com/callstack/agent-device/issues/1349) extends identity annotations +and replay-time refusal to target-bearing waits, at which point the same guard gains recorded-landmark +identity verification without changing the ADR 0016 artifact shape. + On consumption, a script without `close` preserves the existing replay behavior: the named session stays active and the successful `ReplayCommandResult` returns its `session` id. The caller binds subsequent commands to that returned id. Replay reports success only after the destination guard completes; the @@ -164,8 +172,8 @@ executing that script, not the artifact being saved. remains active, and can continue accepting commands. - The artifact replays from a cold start, completes its destination guard, returns the live session id, and accepts a subsequent command on that session. -- Every supported element-targeting action has canonical identity evidence and no unresolved `@ref` - reaches disk. +- Every action in ADR 0012's existing target-binding command set has canonical identity evidence and no + unresolved `@ref` reaches disk; the destination guard remains selector-level until #1349 lands. - Existing-target refusal preserves the original bytes; `--force` replaces atomically; a failed publish remains retryable. - Closing after successful publication performs no second write, while closing an unpublished ARMED From 450c605da3154269a57083186502a1bc355a9f38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Pierzcha=C5=82a?= Date: Mon, 20 Jul 2026 18:11:38 +0200 Subject: [PATCH 5/5] docs: close active-session publication boundaries --- CONTEXT.md | 10 +- .../0016-active-session-script-publication.md | 100 ++++++++++++------ 2 files changed, 73 insertions(+), 37 deletions(-) diff --git a/CONTEXT.md b/CONTEXT.md index 886b1c7681..6956b48169 100644 --- a/CONTEXT.md +++ b/CONTEXT.md @@ -30,11 +30,11 @@ - Session: daemon-owned state for a selected target and opened app or surface. - Script recording: opt-in session mode armed before actions so a persisted `.ad` can carry portable action inputs and recording-time target identity evidence. It is distinct from screen/video recording. -- Open-to-destination script: self-contained `.ad` script that starts with `open`, checks its destination - with a destination guard, ends before `close`, and leaves the app session active. Avoid: replay - (artifact noun), fragment (reserved for lifecycle-free composition), partial script. -- Destination guard: target-bearing `wait` near the end of an open-to-destination script that confirms a - landmark selector on the ready destination screen before replay hands the live session to its caller. +- Open-to-destination script: self-contained `.ad` script with exactly one initial `open`, a destination + guard after its last app-state mutation, no `close`, and an app session left active for subsequent work. + Avoid: replay (artifact noun), fragment (reserved for lifecycle-free composition), partial script. +- Destination guard: portable selector-targeted `wait` near the end of an open-to-destination script that + confirms a landmark on the ready destination screen before replay hands the live session to its caller. - Recording backend: daemon-internal module interface selected per recording target that owns platform recording validation, output path policy, start/stop execution, and record-only cleanup below the daemon recording lifecycle. - Device lease: logical remote ownership of one selected device for a tenant/run/client and lease provider, separate from platform helper process diff --git a/docs/adr/0016-active-session-script-publication.md b/docs/adr/0016-active-session-script-publication.md index 38a2271564..db7160a28d 100644 --- a/docs/adr/0016-active-session-script-publication.md +++ b/docs/adr/0016-active-session-script-publication.md @@ -47,18 +47,30 @@ or the existing generated default. ### Recording lifecycle -Ordinary script recording gains a two-state publication lifecycle: - -- **ARMED**: established by `open --save-script[=]` before target-bearing actions run. The session - records portable action inputs and fresh target identity evidence. +An ordinary recording eligible for active-session publication has three states: + +- **ARMED**: established only when a new session's initial successful + `open --save-script[=]` is recorded as action zero. The session records portable action inputs + and fresh target identity evidence. +- **ABORTED**: reached when another plain `open` succeeds while ARMED. The app operation may continue, + but the recording is no longer a single-open bootstrap and close-time publication is disarmed. The + successful `open` response warns that a fresh session is required to author another script. - **PUBLISHED**: reached only after `session save-script` atomically commits the complete history from - the recorded `open` through the current action. The session remains active at the destination, but - close-time script publication is disarmed. - -A publication failure leaves the recording ARMED, including its path and same-target `--force` -authorization, so the caller can correct the target or permissions and retry. PUBLISHED is terminal for -that recording: later actions are ordinary session work and a later `close` performs teardown only. V1 -does not re-arm or publish multiple open-to-destination artifacts from one session. + the sole recorded `open` through the current action. The session remains active at the destination, + but close-time script publication is disarmed. + +A filesystem or target-collision failure leaves the recording ARMED, including its path and same-target +`--force` authorization, so the caller can correct the target or permissions and retry. ABORTED and +PUBLISHED are terminal until the session is destroyed. `session save-script` in either state fails +loudly and never writes. + +Every existing arming entry point respects terminality. `open --save-script` on any existing session — +unarmed, ARMED, ABORTED, or PUBLISHED — is rejected before app dispatch; a plain later `open` is allowed, +causing ARMED to become ABORTED while leaving ABORTED/PUBLISHED unchanged. +`close --save-script[=]` in ABORTED or PUBLISHED is rejected before platform close or filesystem +work, so the caller can retry with plain `close`. Plain `close` tears down ABORTED/PUBLISHED without +writing; closing an unpublished ARMED recording retains the existing close-time publication behavior. A +fresh session is the only re-arming boundary. This lifecycle is distinct from ADR 0012's repair transaction. `session save-script` rejects a session with `saveScriptBoundary` set and directs the caller to finish or abort the repair through its existing @@ -68,19 +80,29 @@ commits a healed slice, writes `# agent-device:heal-complete`, or changes repair ### Destination readiness and replay handoff The destination is an authored postcondition, not the last navigation action. Before publication, the -recorded suffix after the last mutating action must contain a target-bearing `wait` for a landmark that -identifies the ready destination screen. A duration wait or `wait stable` alone does not qualify, though -`wait stable` may follow the landmark wait. `session save-script` refuses publication without this -**destination guard** and tells the author to record one. V1 does not infer a screen identity from a -snapshot or synthesize an implicit guard. - -The initial guard is selector-level. `wait` does not yet carry ADR 0012 `target-v1` evidence through -recording and replay verification, so the guard proves that an element matching its selector exists, not -that it is the same landmark element observed while authoring. Authors must choose a selective -destination-specific landmark; a reshuffled screen containing the same weak label elsewhere can -false-pass. [#1349](https://github.com/callstack/agent-device/issues/1349) extends identity annotations -and replay-time refusal to target-bearing waits, at which point the same guard gains recorded-landmark -identity verification without changing the ADR 0016 artifact shape. +recorded suffix after the last mutating action must contain a `wait` whose target is a portable selector +or selector chain identifying a landmark on the ready destination screen. A duration wait, `wait stable`, +or `wait @ref` does not qualify, though `wait stable` may follow the landmark wait. The publisher validates +the serialized guard before filesystem work and never relies on repair-only bare-ref rejection. +`session save-script` refuses publication without this **destination guard** and tells the author to +record one. V1 does not infer a screen identity from a snapshot or synthesize an implicit guard. + +The initial guard is selector-level. `wait` does not yet carry recorded-landmark identity through its +polling resolution, so the guard proves that an element matching its selector exists, not that it is the +same landmark element observed while authoring. Authors must choose a selective destination-specific +landmark; a reshuffled screen containing the same weak label elsewhere can false-pass. +[#1349](https://github.com/callstack/agent-device/issues/1349) owns the identity design for waits and +remaining read-only steps. It must preserve polling: ADR 0012's current pre-action `target-v1` +verification cannot be attached to a wait unchanged because a not-yet-present landmark is the expected +starting condition. Any identity check for a wait happens after its selector resolves, before the wait +reports success, or uses a distinct guard-specific mechanism. + +The phrase "last mutating action" is derived from a request-sensitive recording-effect trait on the +central `CommandDescriptor`, required for every command that records session actions and guarded by a +completeness test. It is not a publisher-local command-name set. The trait distinguishes app-state +mutation from observation for subcommands such as read-only versus mutating `find`, `keyboard`, and +`alert` actions; the existing conservative `refFrameEffect: may-invalidate` classification is not precise +enough for this boundary. On consumption, a script without `close` preserves the existing replay behavior: the named session stays active and the successful `ReplayCommandResult` returns its `session` id. The caller binds subsequent @@ -103,8 +125,10 @@ credentials that are safe to persist. CLI help must state this warning next to t The published `.ad`: -- contains one recorded `open` and every recordable action through the publication request; -- contains a destination guard after its last mutating action; +- contains exactly one recorded `open` as its first action and every recordable action through the + publication request; +- contains a portable selector/selector-chain destination guard after its last descriptor-classified + mutating action; - does not append or serialize `session save-script` or `close`; - uses the ordinary session context header, selector-chain optimization, and canonical `target-v1` annotations captured while ARMED; @@ -114,9 +138,10 @@ The published `.ad`: `--force` authorizes atomic replacement. The success response identifies the final path and session and reports the number of serialized actions. -The command must fail before writing when there is no active session, recording was not armed, no -recorded `open` or destination guard exists, or a repair transaction owns the session. Every failure -explains the recovery action; none degrades to `{ written: false }` success. +The command must fail before writing when there is no active session, recording was not armed, the +recording is ABORTED or PUBLISHED, the history does not contain exactly one initial `open`, no portable +destination guard exists, or a repair transaction owns the session. Every failure explains the recovery +action; none degrades to `{ written: false }` success. ### Surface and naming @@ -138,6 +163,8 @@ executing that script, not the artifact being saved. the destination is known only when the caller publishes. - Normal unarmed interactions keep their current fast paths and retention behavior. - A successful active-session publication cannot collide with a later close-time auto-save. +- A second successful open abandons the in-flight artifact instead of silently publishing a multi-open + bootstrap; authoring resumes only in a fresh session. - Intermediate lifecycle-free fragments, entry guards, include semantics, composed digests, and shared fragment pinning remain entirely under #1336. - Secret-bearing authoring remains unsafe until #1348; the initial workflow is limited to journeys that @@ -167,7 +194,10 @@ executing that script, not the artifact being saved. - An unarmed session refuses publication before filesystem work and names `open --save-script` as the recovery. - An armed session without a destination guard refuses publication before filesystem work and names a - target-bearing `wait` as the recovery. + selector-targeted `wait` as the recovery; `wait @ref`, duration waits, and `wait stable` are covered + refusal cases. +- A second successful plain `open` transitions ARMED to ABORTED and disables all publication, while + `open --save-script` on an existing session is rejected before app dispatch. - An armed session publishes `open` plus target-annotated actions without `close`, returns the final path, remains active, and can continue accepting commands. - The artifact replays from a cold start, completes its destination guard, returns the live session id, @@ -176,8 +206,14 @@ executing that script, not the artifact being saved. unresolved `@ref` reaches disk; the destination guard remains selector-level until #1349 lands. - Existing-target refusal preserves the original bytes; `--force` replaces atomically; a failed publish remains retryable. -- Closing after successful publication performs no second write, while closing an unpublished ARMED - ordinary recording preserves current close-time publication behavior. +- After PUBLISHED, later ordinary actions remain usable, repeated `session save-script` fails, plain + `open --relaunch` cannot re-arm, and `open --save-script` is rejected before app dispatch. +- In ABORTED/PUBLISHED, `close --save-script[=]` is rejected before platform close and plain + `close` tears down without writing; closing an unpublished ARMED recording preserves current + close-time publication behavior. +- Descriptor completeness tests classify every recordable request's mutation effect, including + request-sensitive read-only/mutating subcommands, and destination-guard ordering consumes only that + trait. - Repair-armed sessions refuse this action without changing repair state. - CLI help warns that literal `fill` inputs are persisted and tells authors not to record secret-bearing journeys until #1348's parameterized-input mechanism is available.