@@ -47,18 +47,30 @@ or the existing generated default.
4747
4848### Recording lifecycle
4949
50- Ordinary script recording gains a two-state publication lifecycle:
51-
52- - ** ARMED** : established by ` open --save-script[=<path>] ` before target-bearing actions run. The session
53- records portable action inputs and fresh target identity evidence.
50+ An ordinary recording eligible for active-session publication has three states:
51+
52+ - ** ARMED** : established only when a new session's initial successful
53+ ` open --save-script[=<path>] ` is recorded as action zero. The session records portable action inputs
54+ and fresh target identity evidence.
55+ - ** ABORTED** : reached when another plain ` open ` succeeds while ARMED. The app operation may continue,
56+ but the recording is no longer a single-open bootstrap and close-time publication is disarmed. The
57+ successful ` open ` response warns that a fresh session is required to author another script.
5458- ** PUBLISHED** : reached only after ` session save-script ` atomically commits the complete history from
55- the recorded ` open ` through the current action. The session remains active at the destination, but
56- close-time script publication is disarmed.
57-
58- A publication failure leaves the recording ARMED, including its path and same-target ` --force `
59- authorization, so the caller can correct the target or permissions and retry. PUBLISHED is terminal for
60- that recording: later actions are ordinary session work and a later ` close ` performs teardown only. V1
61- does not re-arm or publish multiple open-to-destination artifacts from one session.
59+ the sole recorded ` open ` through the current action. The session remains active at the destination,
60+ but close-time script publication is disarmed.
61+
62+ A filesystem or target-collision failure leaves the recording ARMED, including its path and same-target
63+ ` --force ` authorization, so the caller can correct the target or permissions and retry. ABORTED and
64+ PUBLISHED are terminal until the session is destroyed. ` session save-script ` in either state fails
65+ loudly and never writes.
66+
67+ Every existing arming entry point respects terminality. ` open --save-script ` on any existing session —
68+ unarmed, ARMED, ABORTED, or PUBLISHED — is rejected before app dispatch; a plain later ` open ` is allowed,
69+ causing ARMED to become ABORTED while leaving ABORTED/PUBLISHED unchanged.
70+ ` close --save-script[=<path>] ` in ABORTED or PUBLISHED is rejected before platform close or filesystem
71+ work, so the caller can retry with plain ` close ` . Plain ` close ` tears down ABORTED/PUBLISHED without
72+ writing; closing an unpublished ARMED recording retains the existing close-time publication behavior. A
73+ fresh session is the only re-arming boundary.
6274
6375This lifecycle is distinct from ADR 0012's repair transaction. ` session save-script ` rejects a session
6476with ` 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
6880### Destination readiness and replay handoff
6981
7082The destination is an authored postcondition, not the last navigation action. Before publication, the
71- recorded suffix after the last mutating action must contain a target-bearing ` wait ` for a landmark that
72- identifies the ready destination screen. A duration wait or ` wait stable ` alone does not qualify, though
73- ` wait stable ` may follow the landmark wait. ` session save-script ` refuses publication without this
74- ** destination guard** and tells the author to record one. V1 does not infer a screen identity from a
75- snapshot or synthesize an implicit guard.
76-
77- The initial guard is selector-level. ` wait ` does not yet carry ADR 0012 ` target-v1 ` evidence through
78- recording and replay verification, so the guard proves that an element matching its selector exists, not
79- that it is the same landmark element observed while authoring. Authors must choose a selective
80- destination-specific landmark; a reshuffled screen containing the same weak label elsewhere can
81- false-pass. [ #1349 ] ( https://github.com/callstack/agent-device/issues/1349 ) extends identity annotations
82- and replay-time refusal to target-bearing waits, at which point the same guard gains recorded-landmark
83- identity verification without changing the ADR 0016 artifact shape.
83+ recorded suffix after the last mutating action must contain a ` wait ` whose target is a portable selector
84+ or selector chain identifying a landmark on the ready destination screen. A duration wait, ` wait stable ` ,
85+ or ` wait @ref ` does not qualify, though ` wait stable ` may follow the landmark wait. The publisher validates
86+ the serialized guard before filesystem work and never relies on repair-only bare-ref rejection.
87+ ` session save-script ` refuses publication without this ** destination guard** and tells the author to
88+ record one. V1 does not infer a screen identity from a snapshot or synthesize an implicit guard.
89+
90+ The initial guard is selector-level. ` wait ` does not yet carry recorded-landmark identity through its
91+ polling resolution, so the guard proves that an element matching its selector exists, not that it is the
92+ same landmark element observed while authoring. Authors must choose a selective destination-specific
93+ landmark; a reshuffled screen containing the same weak label elsewhere can false-pass.
94+ [ #1349 ] ( https://github.com/callstack/agent-device/issues/1349 ) owns the identity design for waits and
95+ remaining read-only steps. It must preserve polling: ADR 0012's current pre-action ` target-v1 `
96+ verification cannot be attached to a wait unchanged because a not-yet-present landmark is the expected
97+ starting condition. Any identity check for a wait happens after its selector resolves, before the wait
98+ reports success, or uses a distinct guard-specific mechanism.
99+
100+ The phrase "last mutating action" is derived from a request-sensitive recording-effect trait on the
101+ central ` CommandDescriptor ` , required for every command that records session actions and guarded by a
102+ completeness test. It is not a publisher-local command-name set. The trait distinguishes app-state
103+ mutation from observation for subcommands such as read-only versus mutating ` find ` , ` keyboard ` , and
104+ ` alert ` actions; the existing conservative ` refFrameEffect: may-invalidate ` classification is not precise
105+ enough for this boundary.
84106
85107On consumption, a script without ` close ` preserves the existing replay behavior: the named session stays
86108active 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
103125
104126The published ` .ad ` :
105127
106- - contains one recorded ` open ` and every recordable action through the publication request;
107- - contains a destination guard after its last mutating action;
128+ - contains exactly one recorded ` open ` as its first action and every recordable action through the
129+ publication request;
130+ - contains a portable selector/selector-chain destination guard after its last descriptor-classified
131+ mutating action;
108132- does not append or serialize ` session save-script ` or ` close ` ;
109133- uses the ordinary session context header, selector-chain optimization, and canonical ` target-v1 `
110134 annotations captured while ARMED;
@@ -114,9 +138,10 @@ The published `.ad`:
114138 ` --force ` authorizes atomic replacement.
115139
116140The success response identifies the final path and session and reports the number of serialized actions.
117- The command must fail before writing when there is no active session, recording was not armed, no
118- recorded ` open ` or destination guard exists, or a repair transaction owns the session. Every failure
119- explains the recovery action; none degrades to ` { written: false } ` success.
141+ The command must fail before writing when there is no active session, recording was not armed, the
142+ recording is ABORTED or PUBLISHED, the history does not contain exactly one initial ` open ` , no portable
143+ destination guard exists, or a repair transaction owns the session. Every failure explains the recovery
144+ action; none degrades to ` { written: false } ` success.
120145
121146### Surface and naming
122147
@@ -138,6 +163,8 @@ executing that script, not the artifact being saved.
138163 the destination is known only when the caller publishes.
139164- Normal unarmed interactions keep their current fast paths and retention behavior.
140165- A successful active-session publication cannot collide with a later close-time auto-save.
166+ - A second successful open abandons the in-flight artifact instead of silently publishing a multi-open
167+ bootstrap; authoring resumes only in a fresh session.
141168- Intermediate lifecycle-free fragments, entry guards, include semantics, composed digests, and shared
142169 fragment pinning remain entirely under #1336 .
143170- 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.
167194- An unarmed session refuses publication before filesystem work and names ` open --save-script ` as the
168195 recovery.
169196- An armed session without a destination guard refuses publication before filesystem work and names a
170- target-bearing ` wait ` as the recovery.
197+ selector-targeted ` wait ` as the recovery; ` wait @ref ` , duration waits, and ` wait stable ` are covered
198+ refusal cases.
199+ - A second successful plain ` open ` transitions ARMED to ABORTED and disables all publication, while
200+ ` open --save-script ` on an existing session is rejected before app dispatch.
171201- An armed session publishes ` open ` plus target-annotated actions without ` close ` , returns the final path,
172202 remains active, and can continue accepting commands.
173203- 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.
176206 unresolved ` @ref ` reaches disk; the destination guard remains selector-level until #1349 lands.
177207- Existing-target refusal preserves the original bytes; ` --force ` replaces atomically; a failed publish
178208 remains retryable.
179- - Closing after successful publication performs no second write, while closing an unpublished ARMED
180- ordinary recording preserves current close-time publication behavior.
209+ - After PUBLISHED, later ordinary actions remain usable, repeated ` session save-script ` fails, plain
210+ ` open --relaunch ` cannot re-arm, and ` open --save-script ` is rejected before app dispatch.
211+ - In ABORTED/PUBLISHED, ` close --save-script[=<other>] ` is rejected before platform close and plain
212+ ` close ` tears down without writing; closing an unpublished ARMED recording preserves current
213+ close-time publication behavior.
214+ - Descriptor completeness tests classify every recordable request's mutation effect, including
215+ request-sensitive read-only/mutating subcommands, and destination-guard ordering consumes only that
216+ trait.
181217- Repair-armed sessions refuse this action without changing repair state.
182218- CLI help warns that literal ` fill ` inputs are persisted and tells authors not to record secret-bearing
183219 journeys until #1348 's parameterized-input mechanism is available.
0 commit comments