22
33## Status
44
5- Accepted (2026-07-10); partially implemented (last updated 2026-07-13 ). See [ Migration progress] ( #migration-progress ) for the per-step landing record.
5+ Accepted (2026-07-10); partially implemented (last updated 2026-07-16 ). See [ Migration progress] ( #migration-progress ) for the per-step landing record.
66
77** Implemented and merged to ` main ` :**
88
@@ -12,6 +12,10 @@ Accepted (2026-07-10); partially implemented (last updated 2026-07-13). See [Mig
1212 ` REPLAY_DIVERGENCE ` repair-loop fix (#1223 ).
1313- Decision 6, the base agent-supervised re-record repair — ` replay --save-script ` arming, the
1414 post-watermark healed slice, ` repairHint ` , and the writer's bare-` @ref ` fail-loud guard (#1228 ).
15+ - Decision 6, R7 + commit state machine (repair-transaction lifecycle) — the ARMED → COMPLETE → COMMITTED
16+ commit state machine, keep-alive keyed off the persisted transaction (` resume.repairSessionHeld ` , never
17+ the per-request ` --save-script ` flag), terminal-source-` close ` skipping, the ` REPAIR_SESSION_EXPIRED `
18+ tombstone, and race-safe atomic publication via exclusive ` linkSync ` (#1235 ).
1519- Decision 4 amendment, ` screen ` 's capture scope and ref selection — the divergence ` screen ` capture runs
1620 through the same ` captureSnapshot ` wrapper as plain ` snapshot ` (full-window scope + Android freshness /
1721 post-action retry parity) under a clean, fixed capture-flags policy (a failed raw/scoped/` -d ` action can
@@ -31,6 +35,9 @@ Accepted (2026-07-10); partially implemented (last updated 2026-07-13). See [Mig
3135 end-to-end; a container whose subtree holds a competing interactive node (canonical classification or
3236 hittable flag), or whose selected descendant's center falls outside the container rect, is recorded
3337 unchanged; ` fill ` is excluded (#1280 ).
38+ - Decision 6 amendment, repair-segment default exclusion of observation-only commands and the ` --record `
39+ opt-in — stage 1 (interim guidance only, #1287 ) and stage 2 (the semantic default-exclusion fix,
40+ this amendment) of #1271 . See the amendment under decision 6 below.
3441
3542** Accepted but NOT yet implemented** (this amendment; tracked by #1235 — repair-transaction lifecycle):
3643the R7 repair-transaction keep-alive and its distinct ` resume.repairSessionHeld ` signal, the ARMED →
@@ -976,6 +983,90 @@ re-run guidance to restart the repair from the original script), never a bare `S
976983` replay --save-script ` on the same key ** clears the tombstone and starts a fresh transaction** (` ARMED ` ).
977984The tombstone itself expires after its bounded window, after which the key is fully free.
978985
986+ > ** Amendment (#1271 stage 2): repair-segment default exclusion of observation-only commands, and the
987+ > ` --record ` opt-in.** #1271 reported that read-only diagnostics an agent runs mid-repair to LOCATE the
988+ > repair target — ` snapshot -i ` , ` get attrs ` , ` find ` , ` is ` — recorded into the healed script exactly like
989+ > the corrective action itself. The wave-3 E3 experiment measured 0/4 trials producing a clean healed
990+ > script hands-off; one recorded ` get attrs ` caused a second, self-inflicted ` identity-mismatch ` divergence
991+ > on fresh replay. Stage 1 (#1287 ) shipped interim guidance only — the divergence ` repairHint ` and `help
992+ > workflow` told the agent to pass ` --no-record` on those commands by hand. This amendment makes the safer
993+ > behavior the default, superseding stage 1's "opt out by hand" guidance with "opt in by hand" for the one
994+ > case that needs it.
995+ >
996+ > ** 1. Which read-only commands are excluded.** Exactly the enumerated set stage 1 already named:
997+ > ` snapshot ` , ` get ` , ` is ` , and a ** read-only** ` find ` sub-action (` exists ` , ` wait ` , ` get_text ` , ` get_attrs ` ).
998+ > A ** mutating** ` find … click|fill|focus|type ` is not in this set — it is a corrective action like ` press ` /
999+ > ` fill ` and always records. The top-level ` wait ` command is deliberately ** not** in this set either: it is
1000+ > flow timing/synchronisation (pacing the script), not observation, so it must keep recording unconditionally
1001+ > even inside a repair segment — a healed script that silently dropped its own timing steps would replay
1002+ > differently from what actually happened.
1003+ >
1004+ > ** 2. The corrective-read trap, and why blanket exclusion by command class is unsafe.** "Exclude every
1005+ > read from a repair segment" is not a safe rule on its own: a divergence's ** correction** can itself be a
1006+ > read. In the wave-3 E3 drift, the diverged step ** was** a ` get ` , so its corrective replacement is a
1007+ > recorded ` get ` that ** must** land in the heal — a blanket read-exclusion would silently drop the very
1008+ > step being repaired, producing an incomplete heal with no signal anything was missing (the same class of
1009+ > silent-failure mode decision 3 exists to close for target-binding, and decision 6 R4 exists to close for
1010+ > bare ` @ref ` exports). Distinguishing "the read that replaces the diverged step" from "reads used to locate
1011+ > the target" by POSITION (e.g., relative to the eventual ` --from ` resume) was considered and rejected: the
1012+ > daemon records each action as it happens and has no reliable way to know, at record time, which one the
1013+ > agent will later treat as the resume boundary — and a position-based rule would silently reinterpret
1014+ > intent after the fact rather than let the agent state it. The adopted rule instead makes this an ** explicit
1015+ > opt-in per action** : ` --record ` (below) marks the ONE read that must count, independent of its position in
1016+ > the repair segment.
1017+ >
1018+ > ** 3. Explicit recording remains possible.** ` --record ` is a new, standalone flag — distinct from
1019+ > ` saveScript ` , and the mirror image of ` --no-record ` — that forces the action it is passed on into the heal
1020+ > even though its command is observation-only. It is a no-op outside a repair-armed session (there is no
1021+ > default exclusion to override) and a no-op on any non-observation-only command (those already always
1022+ > record). ` --record ` and ` --no-record ` express opposite intents for the same action and are ** mutually
1023+ > exclusive** : passing both is rejected as ` INVALID_ARGS ` before the command runs, uniformly for every
1024+ > surface, rather than letting one silently win.
1025+ >
1026+ > ** 4. How the rule projects across CLI/Node/MCP surfaces.** The exclusion is enforced at a single
1027+ > daemon-side choke point — ` isExcludedRepairSegmentObservation ` in ` recordActionEntry `
1028+ > (` src/daemon/session-action-recorder.ts ` ), gated on ` session.saveScriptBoundary !== undefined ` (set ONLY
1029+ > by a repair-armed ` replay --save-script ` ; an ordinary, non-repair ` open --save-script ` /` close --save-script `
1030+ > authoring recording never sets it — see decision 6's "Scope" note above) and on the entry's
1031+ > ` observationOnly ` marker (set by the caller — ` get ` /` is ` /a read-only ` find ` in
1032+ > ` src/daemon/selector-recording.ts ` , ` snapshot ` in ` src/daemon/snapshot-runtime.ts ` — never inferred from
1033+ > ` command ` /` positionals ` , since a mutating ` find … click ` shares the literal string ` command: 'find' ` with
1034+ > its read-only sibling). Every surface reaches this same function because every surface's request reaches
1035+ > the same daemon: the CLI parses ` --record ` /` --no-record ` into ` CommandFlags ` (` src/contracts/cli-flags.ts ` ,
1036+ > ` src/commands/cli-grammar/flag-definitions-action.ts ` , ` src/commands/cli-grammar/flag-groups.ts ` 's
1037+ > ` COMMON_COMMAND_SUPPORTED_FLAG_KEYS ` — so both flags are syntactically valid on every command, exactly like
1038+ > ` --no-record ` already was); the Node client threads the same-named option through
1039+ > ` buildRequestFlags ` /` buildFlags ` (` src/commands/command-flags.ts ` ) from ` InternalRequestOptions ` and
1040+ > ` AppOpenOptions ` (` src/client/client-types.ts ` ) and from each typed per-command Options type
1041+ > (` GetOptions ` /` IsOptions ` /` FindOptions ` /` CaptureSnapshotOptions ` ); and the MCP tool schema exposes ` record ` /
1042+ > ` noRecord ` fields on the ` get ` /` is ` /` find ` /` snapshot ` tools (` src/commands/interaction/metadata.ts ` ,
1043+ > ` src/commands/capture/snapshot.ts ` ) that flow through the same ` CommandMetadata.inputSchema ` +
1044+ > ` readMetadataCommandFlags ` path every other typed field already uses. ` management/app.ts ` mirrors both
1045+ > flags onto ` open ` for symmetry with ` --no-record ` 's existing presence there, though neither has an effect
1046+ > on a command that is never observation-only. No surface has a separate recording contract; all of them
1047+ > hit the identical daemon-side default.
1048+ >
1049+ > ** 5. The fail-loud guard is the EXISTING resume watermark, not new state.** Because the exclusion happens
1050+ > at the same point ` --no-record ` is enforced — before an action ever reaches ` session.actions ` — an excluded
1051+ > diagnostic read never grows ` session.actions.length ` . That counter is exactly what
1052+ > ` describeUnperformedRecordAndHeal ` (` src/daemon/handlers/session-replay-runtime-plan.ts ` , R2/#1262 's
1053+ > ` pendingRecordAndHeal ` watermark) already compares against ` actionsCountAtDivergence ` to prove a corrective
1054+ > action happened before authorizing a ` record-and-heal ` -shaped ` --from ` resume. No additional bookkeeping is
1055+ > needed: a repair segment containing only excluded diagnostic reads is indistinguishable, at that check,
1056+ > from a repair segment containing no activity at all, so the existing guard already refuses it. This
1057+ > amendment only updates the guard's message to name ` --record ` alongside the existing ` --no-record `
1058+ > mention, since the missing corrective action may have been a read rather than a press: "no corrective
1059+ > action was recorded in this repair segment; press the correct control via a blessed ` @ref ` … — or, if your
1060+ > corrective action was a read, re-run it with ` --record ` … — before resuming."
1061+ >
1062+ > ** Regression coverage** proves: a diagnostic read inside an armed repair segment is omitted from the
1063+ > healed script by default; a corrective action remains for both a mutating press/fill (unaffected — never
1064+ > observation-only) and a ` --record ` ed read (the diverged-step-was-a-` get ` case); the resulting healed script
1065+ > replays cleanly in a fresh session; the empty-segment guard refuses a blind resume with the actionable
1066+ > ` --record ` hint; and ordinary, non-repair ` open --save-script ` authoring recording is completely unchanged
1067+ > — a read in a fresh authoring session still records with no flag needed, because ` session.saveScriptBoundary `
1068+ > is never set there.
1069+
9791070## Consequences
9801071
9811072- ` --from ` makes app state the caller's responsibility, and only accepts a resume when the planner can
@@ -1073,8 +1164,8 @@ each states its dependencies explicitly.
10731164 daemon-side ` repairHint ` computation over all four ` kind ` s (R3), ` --save-script ` arming plus the
10741165 repair-run boundary watermark on ` replay ` (R1/R6), the writer's post-watermark slice and bare-` @ref `
10751166 fail-loud guard (R4/R6) — reusing ` close --save-script ` 's existing ` session.actions ` serializer.
1076- 9 . ** Repair-transaction lifecycle** (decision 6, R7 + commit state machine) — ** NOT YET IMPLEMENTED,
1077- tracked by # 1235 ** ; depends on 8. Adds: the distinct ` resume.repairSessionHeld ` divergence signal and
1167+ 9 . ** Repair-transaction lifecycle** (decision 6, R7 + commit state machine) — ** SHIPPED ( # 1235 ) ** ;
1168+ depends on 8. Adds: the distinct ` resume.repairSessionHeld ` divergence signal and
10781169 R7 keep-alive keyed off the session's ** persisted** repair-transaction state (so ` --from ` continuations
10791170 need no repeat of ` --save-script ` ), with ` fail-fast-before-step-1 ` when keep-alive is impossible; the
10801171 ` ARMED → COMPLETE → COMMITTED ` commit state machine where ** any teardown** (explicit ` close ` ,
@@ -1087,10 +1178,19 @@ each states its dependencies explicitly.
10871178 pre-existing target — complete or partial, default or explicit path alike, and uniformly for an
10881179 ordinary non-repair recording's target too, since the publish primitive is shared — never overwriting
10891180 one; see "Scope" under Decision 6 above).
1181+ 10 . ** Repair-segment default exclusion of observation-only commands, and the ` --record ` opt-in**
1182+ (decision 6 amendment) — ** SHIPPED (#1271 stage 2)** ; depends on 9 (` session.saveScriptBoundary ` /
1183+ ` repairSessionHeld ` are the signals the exclusion and its guidance key off). Adds: default exclusion of
1184+ ` snapshot ` /` get ` /` is ` /a read-only ` find ` from ` session.actions ` while repair-armed
1185+ (` isExcludedRepairSegmentObservation ` , ` session-action-recorder.ts ` ); the ` --record ` opt-in, plumbed
1186+ across CLI/Node client/MCP identically to ` --no-record ` , and rejected together with it as
1187+ ` INVALID_ARGS ` ; and an updated ` describeUnperformedRecordAndHeal ` message naming ` --record ` for the
1188+ corrective-read case. Stage 1 (#1287 ) shipped interim ` --no-record ` guidance only, gated on this same
1189+ ` repairSessionHeld ` signal.
10901190
10911191## Migration progress
10921192
1093- Landing record for the plan above (main as of 2026-07-13 ). This section tracks progress only; it does
1193+ Landing record for the plan above (main as of 2026-07-16 ). This section tracks progress only; it does
10941194not restate or amend the decisions.
10951195
10961196| Step | Decision | Status | Landed in |
@@ -1103,11 +1203,13 @@ not restate or amend the decisions.
11031203| 6. ` --update ` retirement | 1 | Shipped | #1211 |
11041204| 7. Benchmark extension | 5 | Deferred | — |
11051205| 8. Agent-supervised re-record repair, base | 6 (R1-R6) | Shipped | #1228 |
1106- | 9. Repair-transaction lifecycle | 6 (R7) | Not yet | #1235 |
1206+ | 9. Repair-transaction lifecycle | 6 (R7) | Shipped | #1235 |
1207+ | 10. Repair-segment default exclusion + ` --record ` | 6 amendment | Shipped | #1271 stage 2 |
11071208
11081209Step 4 (#1209 ) added the ` selector-miss ` /` identity-mismatch ` /` identity-unverifiable ` divergence kinds
11091210and a post-resolution target guard that cross-checks the dispatched winner against the verified member.
11101211Issue #1221 ("Complete ADR 0012 replay target-binding verification") was closed as already implemented:
11111212its verification scope is covered by step 4. Step 7 remains deferred. Step 8 implements the repair
1112- design accepted in #1226 and shipped in #1228 ; step 9 (R7 + commit machine) remains unimplemented,
1113- tracked by #1235 .
1213+ design accepted in #1226 and shipped in #1228 ; step 9 (R7 + commit machine) shipped in #1235 . Step 10
1214+ is #1271 's two-stage arc: stage 1 (#1287 ) shipped interim ` --no-record ` guidance only; stage 2 (this
1215+ amendment) makes exclusion the default and adds the ` --record ` opt-in.
0 commit comments