Repository navigation
fix(settle): compare post-action trees in one view across every route - #4500
Conversation
After a full snapshot (no -i), the settle diff compared that tree with the interactive view the settle loop reads, so every wrapper node the view omits read as removed while nothing changed. The baseline now carries whether it is a full view, and such a settle diffs from its own settled capture and says so in its hint. Later settles diff interactive against interactive. Fixes #4486 Co-Authored-By: Claude Opus 5.5 <[email protected]>
Size Report
Startup median (7 runs, lower is better):
|
There was a problem hiding this comment.
1 issue found across 5 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="packages/capture-kit/src/snapshot-baseline.ts">
<violation number="1" location="packages/capture-kit/src/snapshot-baseline.ts:31">
P2: This skips full-tree baselines when `SnapshotState.presentation` is absent, even though the snapshot contract defines absence as the default full tree. Preserve the distinction for `undefined` snapshots, but mark an existing snapshot with no presentation as `fullView` so legacy or metadata-poor full snapshots do not reintroduce the false removals.</violation>
</file>
Reply with feedback, questions, or to request a fix.
View guided diff | Turn on auto-fix | Re-trigger cubic
Apex Sentinel · ✅ ApproveSince the last review: 2 new · 0 still open · 1 no longer raised (1 marked fixed) 2 finding(s): 2 minor. 🟡 Minor (2)
Reviewed with this repository's own Sentinel configuration ( 💬 Talking to SentinelIn a finding's thread, reply with:
On this conversation, comment Earlier reviewsReview of
|
| Where | Finding |
|---|---|
packages/command-runtime/src/commands/capture/runtime/snapshot.ts:139 |
🆕 diff snapshot can now self-compare a ref frame and always report no change |
Reviewed with this repository's own Sentinel configuration (efe6bb9eece4).
Review of 0149a53b · 12.11.2026 21:16 UTC
Apex Sentinel
Verdict: approve
Since the last review: 0 new · 0 still open · 1 no longer raised (1 marked fixed)
No findings.
Reviewed with this repository's own Sentinel configuration (d2426fffdf78).
Review of 2e941c2a · 12.11.2026 21:01 UTC
Apex Sentinel
Verdict: approve
Since the last review: 0 new · 1 still open · 3 no longer raised (1 marked fixed)
1 finding(s): 1 minor.
🟡 Minor (1)
| Where | Finding |
|---|---|
packages/command-runtime/src/commands/interaction/runtime/settle.ts:270 |
fullView mark ignores customActions, so --actions ref frames still false-diff |
Reviewed with this repository's own Sentinel configuration (d2426fffdf78).
Review of 3b6a9f7d · 12.11.2026 20:43 UTC
Apex Sentinel
Verdict: approve
Since the last review: 4 new · 0 still open · 1 no longer raised
4 finding(s): 3 minor, 1 nit.
🟡 Minor (3)
| Where | Finding |
|---|---|
packages/command-runtime/src/commands/interaction/runtime/settle.ts:268 |
🆕 A ref frame with no presentation (documented as the default full tree) is not marked, so false removals remain |
packages/command-runtime/src/commands/interaction/runtime/settle.ts:281 |
🆕 scroll/back --settle now reads the full tree, contradicting the PR scope |
packages/command-runtime/src/commands/interaction/runtime/settle.ts:268 |
🆕 Any non-interactive ref frame is marked fullView, so scoped/depth-limited frames diff against the whole screen |
🔵 Nit (1)
| Where | Finding |
|---|---|
packages/command-runtime/src/commands/interaction/runtime/settle.ts:281 |
🆕 Session-snapshot branch does not carry the fullView marking |
1 finding below minor not shown (user_settings.min_severity).
Reviewed with this repository's own Sentinel configuration (0495ee1bb3ae).
Review of 6e5ada80 · 12.11.2026 19:06 UTC
Apex Sentinel
Verdict: approve
Since the last review: 1 new · 0 still open · 0 no longer raised
1 finding(s): 1 minor.
🟡 Minor (1)
| Where | Finding |
|---|---|
packages/command-runtime/src/commands/interaction/runtime/settle.ts:313 |
🆕 Truncation is measured after removals are dropped, so the line cap silently shrinks |
2 findings below minor not shown (user_settings.min_severity).
Reviewed with this repository's own Sentinel configuration (9676eedac5b0).
Review of 9a623dc6 · 12.11.2026 18:56 UTC
Apex Sentinel
Verdict: approve
Since the last review: 0 new · 0 still open · 1 no longer raised (1 marked fixed)
No findings.
1 new finding away from what changed since a79152d not shown. Comment @apex-sentinel-staging review for a full review.
Reviewed with this repository's own Sentinel configuration (9676eedac5b0).
Review of a79152d9 · 12.11.2026 18:45 UTC
Apex Sentinel
Verdict: approve
Since the last review: 1 new · 0 still open · 2 no longer raised (2 marked fixed)
1 finding(s): 1 minor.
🟡 Minor (1)
| Where | Finding |
|---|---|
packages/command-runtime/src/commands/interaction/runtime/settle.ts:199 |
🆕 A same-surface screen transition after a full snapshot now reports +0 -0 with no additions |
1 finding below minor not shown (user_settings.min_severity).
Reviewed with this repository's own Sentinel configuration (9676eedac5b0).
Review of a8995d3b · 12.11.2026 18:18 UTC
Apex Sentinel
Verdict: approve
2 finding(s): 2 minor.
🟡 Minor (2)
| Where | Finding |
|---|---|
packages/capture-kit/src/snapshot-baseline.ts:31 |
Selector-targeted --settle always reports +0 -0: preAction tree is a full capture |
packages/command-runtime/src/commands/interaction/runtime/settle.ts:221 |
Full-tree hint replaces the tiny-tree and loop hints on the fullView route |
Reviewed with this repository's own Sentinel configuration (9676eedac5b0).
There was a problem hiding this comment.
Apex Sentinel: 2 new findings on a8995d3. The verdict and the full list are in the summary comment.
Address review: the full-view hint replaced the loop hint (such as the tiny-tree readiness hint) instead of joining it, the wording could read as diffing against the full view, and the interactive-baseline test used a tree that held the full-view wrapper. Co-Authored-By: Claude Opus 5.5 <[email protected]>
There was a problem hiding this comment.
All reported issues were addressed across 3 files (changes from recent commits).
Reply with feedback, questions, or to request a fix.
View guided diff | Turn on auto-fix | Re-trigger cubic
Address review: selector resolution captures are full trees and fed the settle baseline through preAction, so marking every non-interactive presentation reset every selector-targeted settle to +0 -0. Only the ref frame the agent read as a full snapshot is marked now; selector and generic-route baselines keep their diff. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Address review: the new hint helper sat between resolveSettleHint and its JSDoc. Co-Authored-By: Claude Opus 5.5 <[email protected]>
There was a problem hiding this comment.
Apex Sentinel: 1 new finding on a79152d. The verdict and the full list are in the summary comment.
Address review: resetting the baseline to the settled capture made every settle after a full snapshot report +0 -0, even when the press navigated. The diff now runs against the full ref frame and lists only added lines: the interactive view adds no node of its own, so additions are evidence, while removals of nodes that view never reads are not. Co-Authored-By: Claude Opus 5.5 <[email protected]>
There was a problem hiding this comment.
All reported issues were addressed across 3 files (changes from recent commits).
Reply with feedback, questions, or to request a fix.
View guided diff | Turn on auto-fix | Re-trigger cubic
Address review: the every() check passed on an empty line list. The settle comment now states that only Android's interactive view adds no node of its own; an iOS row rewrite can still read as added, as before. Co-Authored-By: Claude Opus 5.5 <[email protected]>
There was a problem hiding this comment.
Apex Sentinel: 1 new finding on 6e5ada8. The verdict and the full list are in the summary comment.
|
CI is green on 6e5ada8 (22 checks, none failing), and there are no conflicts. The fix still needs a live Android run before merge. The round-4 algorithm lists Not blocking, take or leave: (1) the same full-vs-interactive mismatch has two more baseline producers that are not marked, selector For the open inline threads: the iOS row-rewrite one still applies as a non-blocking note (#4500 (comment)). The other review threads are fixed at this head or benign, so the author can resolve them: #4500 (comment) (production always sets This review read the code only. I did not run the unit tests or any device, and the live table in the PR body was not reproduced. The SHA attribution of its rows is taken from the PR text, and the iOS behavior is taken from the code comment and the thread. |
Address review: a forced removals count of 0 read as a fact. The settle diff now carries removalsOmitted, the text verdict prints "removals not listed", the hint no longer promises a baseline the ref frame may not advance to, and the docs note the iOS row-rewrite limit. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Address review: listing only additions against a full-tree baseline still reported the keyboard window as added, because the interactive view reads windows the full tree does not. A settle on refs from a full snapshot now captures the full tree too, so its diff compares one view and issues full-tree refs. The removals flag, hint and formatter change are gone. Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
Thanks, the live run was the right ask: it disproved round 4. Live run at New design at Live at head, same dialog with the keyboard up:
Non-blocking notes:
Tests: |
There was a problem hiding this comment.
All reported issues were addressed across 5 files (changes from recent commits).
Reply with feedback, questions, or to request a fix.
View guided diff | Turn on auto-fix | Re-trigger cubic
There was a problem hiding this comment.
Apex Sentinel: 4 new findings on 3b6a9f7. The verdict and the full list are in the summary comment.
|
I reviewed 3b6a9f7 against the earlier findings. The dropped settle hint, the Not blocking, and you can take or leave these: the sentence at commands.md:497 says a On the open inline threads: these three should be fixed before merge: the selector test premise, the settle tail comment and depth/scope/raw frames. The two comment-drift threads also still apply. These three no longer apply and can be resolved: row-rewrite caveat (full is now diffed against full), truncated count (removals are no longer dropped), and dropped hint (the hint is spread directly). I did not run a device or the tests. The Android live table, the 1837-test pass and the typecheck come from the PR body and author comment, and the regression reasoning comes from reading the base code. Nothing covers iOS, where the loop now captures a full tree per poll after a full snapshot, or CI shows 21 of 22 checks passing, and the remaining one is an external review check, still in progress. Before merge, please fix the three threads above and file the follow-up issue. |
Address review: a scoped, depth-limited or raw frame is a projection the settle capture does not reproduce, so only an unprojected full snapshot is marked fullView. Point taps keep the session baseline unmarked. A full-tree settle carries no tail, because the tail names interactive targets. Comments now describe the full-tree path. Co-Authored-By: Claude Opus 5.5 <[email protected]>
An unchanged full-tree settle without a tail handed back no refs, so the next @ref press hit an expired frame. The tail stays, listing addressable refs from the full-tree view the agent chose. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Address review: custom actions are another projection the settle capture does not reproduce, like scope, depth and raw. Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
This PR is ready at 0149a53. The two problems from the earlier review (#4500 (comment)) are fixed: the full-view check now excludes Not blocking, and you can take or leave it: the Of the earlier inline threads, the five earlier review threads and the two at #4500 (comment) and #4500 (comment) are fixed at this head, so you can resolve them. These two do not apply, so please resolve them too: #4500 (comment) describes an earlier head, and the stored snapshot baseline is now the plain one as on main. #4500 (comment) is a limit the PR body already documents (point taps and scroll/back I did not re-run the Pixel 9a live runs, and I did not run the new tests locally. The live runs in the PR body are at earlier commits, and this change only narrows which frames get the full view. |
…baseline * origin/main: ci: use smaller runners for control and publishing jobs (#4492) docs(help): point verification at read-only commands, not bare find (#4497) fix(android): let open --relaunch accept a package Android restarts at once (#4498) fix(snapshot): name the captured Android app in the snapshot header (#4499) feat(proxy): configure trusted public upload origins (#4501) fix(android): restore screenshot demo permission state (#4477) feat(harmonyos): support app uninstall beside deploy (#4292) fix(ios): confirm foldable rotation from the lit panel in either simulator set (#4343) (#4440) chore: mark 0.21.37 as released fix(ios): make back the verified left-edge swipe (#4460) fix(ios): mark zero-area nodes unmeasured and point their press refusal at scroll --until (#4495) fix(ios): never repeat a synthesized gesture that may have run (#4474) fix(capture): publish cropped screenshots atomically after scaling (#4478) ci(android): reuse cached system images across emulator workflows (#4490)
A settle diff compared whatever pre-action tree it found with an interactive settle capture, so a full snapshot, a selector fallback or a full tree a selector read stored showed up as false removals (#4486). One rule now owns every post-action comparison: two trees are compared only when they were read in the same presentation. capture-kit's postActionBaseline picks the baseline in the agent's view, the presentation of the ref frame it last read, and SurfaceScopedNodes carries that presentation. The settle loop captures in it for press, click, fill, longpress, scroll and back, on refs, selectors and coordinates. --verify captures in the pre-action tree's view, and diff snapshot starts a new baseline when the stored tree was read in another view. Fixes #4486 Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
I redid this PR as you suggested, so the view rule is consistent across every post-action route instead of only ref presses. Rule. Compare two trees only when they were read in the same presentation (
Routes.
Live, Pixel 9a API 37, at
Production diff: 10 files, +170 −60. The earlier ref-only marking, the tail and hint special cases, and the |
There was a problem hiding this comment.
All reported issues were addressed across 14 files (changes from recent commits).
Reply with feedback, questions, or to request a fix.
View guided diff | Turn on auto-fix | Re-trigger cubic
There was a problem hiding this comment.
Apex Sentinel: 1 new finding on 1f15eb0. The verdict and the full list are in the summary comment.
|
The review findings are fixed at
Independent review completed; all three findings are fixed. In an isolated checkout of The four live sequences in the PR body were repeated on exact base The previous Android smoke failure was Local validation at
|
There was a problem hiding this comment.
1 issue found across 15 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="packages/command-runtime/src/commands/interaction/runtime/post-action-surface.test.ts">
<violation number="1" location="packages/command-runtime/src/commands/interaction/runtime/post-action-surface.test.ts:122">
P2: This fixture does not actually exercise the claimed selector-versus-frame view mismatch: the first selector capture overwrites `session.snapshot`, and no `refFrameSnapshot` is initialized. Preserve `frame` as `refFrameSnapshot` (or configure the helper to do so) so the test verifies the frame fallback and presentation selection rather than only repeating the existing cross-surface case.</violation>
</file>
Reply with feedback, questions, or to request a fix.
View guided diff | Turn on auto-fix | Re-trigger cubic
| const sheet = webSignInSheetSnapshot(WEB_SIGN_IN_SHEET_LABELS); | ||
| test('press --settle --verify reports a surface change even when the selector and frame views differ', async () => { | ||
| const before = { ...buttonSnapshot(), presentation: { interactiveOnly: true } }; | ||
| const frame = { ...before, presentation: {} }; |
There was a problem hiding this comment.
P2: This fixture does not actually exercise the claimed selector-versus-frame view mismatch: the first selector capture overwrites session.snapshot, and no refFrameSnapshot is initialized. Preserve frame as refFrameSnapshot (or configure the helper to do so) so the test verifies the frame fallback and presentation selection rather than only repeating the existing cross-surface case.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At packages/command-runtime/src/commands/interaction/runtime/post-action-surface.test.ts, line 122:
<comment>This fixture does not actually exercise the claimed selector-versus-frame view mismatch: the first selector capture overwrites `session.snapshot`, and no `refFrameSnapshot` is initialized. Preserve `frame` as `refFrameSnapshot` (or configure the helper to do so) so the test verifies the frame fallback and presentation selection rather than only repeating the existing cross-surface case.</comment>
<file context>
@@ -117,12 +117,13 @@ test('press --settle attaches no diff across a sheet-to-app surface change and d
- const sheet = webSignInSheetSnapshot(WEB_SIGN_IN_SHEET_LABELS);
+test('press --settle --verify reports a surface change even when the selector and frame views differ', async () => {
+ const before = { ...buttonSnapshot(), presentation: { interactiveOnly: true } };
+ const frame = { ...before, presentation: {} };
+ const sheet = { ...webSignInSheetSnapshot(WEB_SIGN_IN_SHEET_LABELS), presentation: {} };
let captures = 0;
</file context>
There was a problem hiding this comment.
Apex Sentinel: 2 new findings on 5b2c4cd. The verdict and the full list are in the summary comment.
| // A surface transition is evidence in every view; node digests compare only within one view. | ||
| const baseline = | ||
| outcome.settledCapture && | ||
| (resolvePostActionSurfaceChange(resolved.preAction, outcome.settledCapture) || | ||
| isSameSnapshotPresentation( | ||
| resolved.preAction?.presentation, | ||
| outcome.settledCapture.presentation, | ||
| )) | ||
| ? resolved.preAction | ||
| : undefined; | ||
| const evidence = params.verify ? settleEvidence(outcome.settledCapture, baseline) : undefined; |
There was a problem hiding this comment.
🆕 🟡 Minor · --settle --verify loses changedFromBefore when preAction and frame views differ
press label="Back" --settle --verify after a full-frame snapshot produces no evidence at all (changedFromBefore, digest, nodeCount all absent) when selector resolution reads an interactive tree as resolved.preAction. The new gate at lines 92–100 compares resolved.preAction against the settled capture, but the settle engine diffed against a different, view-matched baseline (postActionBaseline's frame).
Suggested change
Feed settleEvidence the baseline the settle loop actually diffed against (the postActionBaseline result on outcome) rather than gating resolved.preAction on its view; keep the surface-change escape hatch.
Details
persona: senior-developer, javascript
Useful? Reply @apex-sentinel-staging like, or @apex-sentinel-staging dislike <why> if it is not.
Reply @apex-sentinel-staging note ... to add context, or @apex-sentinel-staging pivot ... if it is wrong — I will re-check the cited code.
There was a problem hiding this comment.
Partially valid at merged head 5b2c4cd: when selector preAction is interactive but settle substitutes a full ref-frame baseline, changedFromBefore is omitted. The claim that all evidence disappears is inaccurate: settleEvidence receives an undefined baseline and summarizePostActionEvidence still returns digest and node counts. Known surface transitions retain their verdict.
Local revision ed9ffd13a4277e507e73770621e82c23f3662e8f addresses the underlying substitution by keeping fresh selector pre-action captures and the latest stored observation for scroll/back, then reproducing that baseline presentation. It was not pushed before merge. The user then requested cleanup, which is complete. No worktree or follow-up PR has been recreated. Leaving this finding open for follow-up instead of marking an unmerged fix as resolved.
Summary
Switching full and interactive snapshots produced false removals. One presentation rule covers settle, verification and snapshot diffs, including depth, scope, raw and custom actions.
Both settle routes select a baseline through capture-kit and reproduce its view.
--verifyreproduces its pre-action view; combined verification preserves known surface transitions.diff snapshotstarts a baseline when the latest stored view differs. Missing presentation metadata means the default full tree.18 files; includes verify/diff routes and tests. Fixes #4486.
Validation
Head
5b2c4cd685, pristine baseefe6bb9eec. Independent findings fixed; seven new regression cases fail before these revisions and pass now. Focused: 962 passed; related integration: 406 passed; full unit: 19,726 passed, three skipped. Standalone smoke: 76 passed, six skipped.check:affected --run --head HEADreceipt: failed,exactCommit=true(CLI smoke cleanup timeout). Base and head standalone smoke pass; cause unproven. CI must validate the full head.Live Pixel 9a profile, Android 17/API 37, Settings Device name dialog; T3-bound command, source daemons,
android-helperversion0.21.38-dev:snapshot -i;snapshot;press @e3 --settle+0 -10+0 -0, 16 unchangedsnapshot -i;wait text "Device name";scroll down --settle+0 -10+0 -0, 6 unchangedsnapshot;diff snapshot -isnapshot -i;press @e1 --settle --verifySessions closed; emulator shut down. No live iOS run.