Repository navigation
Conversation
There was a problem hiding this comment.
4 issues found across 34 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/daemon/src/core/interaction/internal/interaction-touch-direct-ios-eligibility.ts">
<violation number="1" location="packages/daemon/src/core/interaction/internal/interaction-touch-direct-ios-eligibility.ts:38">
P3: This new early return has no direct test. Add a case in the daemon eligibility tests asserting `readDirectIosSelectorTapTarget` returns `null` when `flags.expectScreenshotDiff` is `true` on an otherwise eligible `click` selector target, so the fused route cannot regress silently.</violation>
</file>
<file name="packages/contracts/src/cli-flags.ts">
<violation number="1" location="packages/contracts/src/cli-flags.ts:126">
P3: This doc comment repeats the option's prose, which the header of this file says must live only on the `FlagDefinition` (flag-definitions-action.ts already has it). Remove the block and keep the bare field; the '#1047 sibling' ticket pointer goes with it.</violation>
</file>
<file name="test/integration/interaction-contract/runtime-selector.contract.test.ts">
<violation number="1" location="test/integration/interaction-contract/runtime-selector.contract.test.ts:378">
P3: This scenario never checks that the tap was dispatched: `tap` discards its arguments and no `taps` array is asserted. Record the dispatched point and assert one tap, so the test proves the pixel pair bracketed a real dispatch.</violation>
</file>
<file name="packages/command-surface/src/interaction/metadata.ts">
<violation number="1" location="packages/command-surface/src/interaction/metadata.ts:90">
P3: The description promises that `screenshotDiff` is reported, but the runtime silently omits it when a capture fails or the backend has no screenshot support. Add that caveat so callers, including models reading this schema, do not treat a missing `screenshotDiff` as a comparison result.</violation>
</file>
Reply with feedback, questions, or to request a fix.
View guided diff | Re-trigger cubic
| maxPasses?: number; | ||
| doubleTap?: boolean; | ||
| verify?: boolean; | ||
| /** |
There was a problem hiding this comment.
P3: This doc comment repeats the option's prose, which the header of this file says must live only on the FlagDefinition (flag-definitions-action.ts already has it). Remove the block and keep the bare field; the '#1047 sibling' ticket pointer goes with it.
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/contracts/src/cli-flags.ts, line 126:
<comment>This doc comment repeats the option's prose, which the header of this file says must live only on the `FlagDefinition` (flag-definitions-action.ts already has it). Remove the block and keep the bare field; the '#1047 sibling' ticket pointer goes with it.</comment>
<file context>
@@ -123,6 +123,13 @@ export type CliFlags = CloudProviderProfileFields &
maxPasses?: number;
doubleTap?: boolean;
verify?: boolean;
+ /**
+ * Press/click opt-in (#1047 sibling): bracket the dispatch with two
+ * lightweight screenshots and compare them, so a tap on a surface the
</file context>
| test(scenario('screenshotDiffEvidence'), async () => { | ||
| const device = createContractDevice(continueButtonSnapshot(), { | ||
| ...screenshotFramePlanBackend([{ color: [0, 0, 0] }, { color: [255, 255, 255] }]), | ||
| tap: async () => ({ ok: true }), |
There was a problem hiding this comment.
P3: This scenario never checks that the tap was dispatched: tap discards its arguments and no taps array is asserted. Record the dispatched point and assert one tap, so the test proves the pixel pair bracketed a real dispatch.
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 test/integration/interaction-contract/runtime-selector.contract.test.ts, line 378:
<comment>This scenario never checks that the tap was dispatched: `tap` discards its arguments and no `taps` array is asserted. Record the dispatched point and assert one tap, so the test proves the pixel pair bracketed a real dispatch.</comment>
<file context>
@@ -367,6 +372,24 @@ test(scenario('settleObservation'), async () => {
+test(scenario('screenshotDiffEvidence'), async () => {
+ const device = createContractDevice(continueButtonSnapshot(), {
+ ...screenshotFramePlanBackend([{ color: [0, 0, 0] }, { color: [255, 255, 255] }]),
+ tap: async () => ({ ok: true }),
+ });
+
</file context>
|
|
||
| const expectScreenshotDiffField = () => | ||
| booleanField( | ||
| 'Compare lightweight screenshots taken right before the dispatch and after it settles briefly, and report the comparison as screenshotDiff in the response. Use it when the target lives outside the accessibility tree, such as a PIN keypad, where verify would compare empty trees. A matching pair adds a warning, because the dispatch may have had no visible effect.', |
There was a problem hiding this comment.
P3: The description promises that screenshotDiff is reported, but the runtime silently omits it when a capture fails or the backend has no screenshot support. Add that caveat so callers, including models reading this schema, do not treat a missing screenshotDiff as a comparison result.
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-surface/src/interaction/metadata.ts, line 90:
<comment>The description promises that `screenshotDiff` is reported, but the runtime silently omits it when a capture fails or the backend has no screenshot support. Add that caveat so callers, including models reading this schema, do not treat a missing `screenshotDiff` as a comparison result.</comment>
<file context>
@@ -85,16 +85,23 @@ const interactionCommandDescriptions = {
+const expectScreenshotDiffField = () =>
+ booleanField(
+ 'Compare lightweight screenshots taken right before the dispatch and after it settles briefly, and report the comparison as screenshotDiff in the response. Use it when the target lives outside the accessibility tree, such as a PIN keypad, where verify would compare empty trees. A matching pair adds a warning, because the dispatch may have had no visible effect.',
+ );
+
</file context>
| 'Compare lightweight screenshots taken right before the dispatch and after it settles briefly, and report the comparison as screenshotDiff in the response. Use it when the target lives outside the accessibility tree, such as a PIN keypad, where verify would compare empty trees. A matching pair adds a warning, because the dispatch may have had no visible effect.', | |
| 'Compare lightweight screenshots taken right before the dispatch and after it settles briefly, and report the comparison as screenshotDiff in the response; the field is omitted when a capture fails or the backend has no screenshots. Use it when the target lives outside the accessibility tree, such as a PIN keypad, where verify would compare empty trees. A matching pair adds a warning, because the dispatch may have had no visible effect.', |
|
Coverage failed on an eager-closure ratchet: the new evidence module joined the command-runtime entry closure as a static edge. It now loads behind a function-scoped dynamic import (type import kept separate, so types cost nothing), with a follow-up fixing an intermediate scope mistake — verified green locally (typecheck/lint, 135 tests). 🤖 Generated with Claude Code |
There was a problem hiding this comment.
All reported issues were addressed across 1 file (changes from recent commits).
Reply with feedback, questions, or to request a fix.
View guided diff | Re-trigger cubic
|
At 6606bb9, Right now
This change alters live dispatch: it bypasses the native ref fast path and the direct-iOS fused route, and brackets the tap with two captures and a 150 ms sleep. The PR body lists typecheck, lint, unit tests and progress checks at an older head (69032ca), with no device run. After the two fixes above, please run through the CLI on one iOS simulator and one Android emulator, against a screen with a static region or keypad. The run needs to show: Not blocking, take or leave: the flag could be a Could the readback be smaller? After splitting out b37da26, put The open review threads still apply, apart from one. P2: absent |
|
This now conflicts with main at 6606bb9. Please rebase onto main. The two defects and the live run from the earlier review still apply. |
|
Tracked in #4318, group C (new public surface). We're reviewing the six feature PRs together for coherence and for performance impact on paths that don't use them, and we'll post a direction there. Please hold changes here until then. |
|
Thanks @strzhao. We reviewed this with the rest of group C in #4318. It mixes two changes that should land separately.
What the visual feature needs:
Could you open the split PR for part 1 and an issue for part 2? |
…eadback A coordinate press on a surface the accessibility tree cannot describe — a PIN keypad whose keys never reach the tree — dispatches fine, leaves --verify's node digest flat whether or not the digit was entered, and reports success. Repeated attempts walk toward a device lockout with no signal in any response. The flag brackets the dispatch with two lightweight screenshots and compares them on pixels instead of nodes: screenshotDiff carries changedFromBefore and the changed-pixel ratio. A matching pair adds a response warning, because the dispatch may have had no visible effect; the action itself still succeeds. Best-effort like --verify evidence: a failed capture or a screenshot-less backend drops the field and claims nothing about the screen. The flag routes press/click through the runtime path (the native ref fast path and the fused direct-iOS route never reach the bracket), and the decode stack loads behind function-scoped dynamic imports so the flag-less fast path pays nothing. Co-Authored-By: Claude Code <[email protected]>
…nshot-diff The compact workflow card has a byte ratchet (cli-help.test.ts) with no headroom for a new sentence, and the flag's owning help already carries the full guidance: the flag definition's usage text renders in "Command flags:" for press/click, and the metadata field description documents the PIN-keypad case for the Node SDK and MCP schemas. Co-Authored-By: Claude Code <[email protected]>
…apCommand The before-frame capture, its dispatch-failure cleanup, and the pair outcome composition each added a branch to a function the complexity gate already watches. dispatchWithScreenshotPair owns the bracket lifecycle and attachScreenshotPairOutcome owns the result composition, so tapCommand reads as dispatch-plus-observation again; the reader gate's positional table flattens the same way. Co-Authored-By: Claude Code <[email protected]>
The bracket lifecycle moved behind dispatchWithScreenshotPair, so the two frame helpers no longer need exports; the dead-export gate reads them as unused module surface. Co-Authored-By: Claude Code <[email protected]>
…matrix, settle the after-frame, and document the out-of-tree readback Review fixes for the press/click --expect-screenshot-diff PR: - The guarantee matrix (ADR 0011) gains the screenshotDiffEvidence row: runtime cells on runtime-selector/runtime-ref/coordinate scoped to press/click, a delegated cell on native-ref, and inapplicable cells on the paths the flag cannot reach. The three waivers that named only --verify/--settle as the opt-in post-action evidence routes now name this flag too. Each enforced cell carries a contract scenario, and the shared harness replays a solid-color frame plan. - The after-frame waits 150ms past the dispatch reply before it is captured: the reaction the flag asks about can render frames after the transport round trip returns, and an immediate frame would read an effective tap as unchanged. The flag help text, field descriptions, and schema prose state the timing instead of leaving it blank. - commands.md names the flag where it teaches that a successful press is not proof of effect: a PIN keypad target is the case --verify cannot answer, since it compares empty trees. - Gate drift the PR introduced: the new capture-kit export joins the layering manifest baseline, and the flag descriptor gate moves to test/repo-guards/ (a repo-level guard on command descriptors, not a module's unit test — R84 test-colocation). Co-Authored-By: Claude Code <[email protected]>
Co-Authored-By: Claude Code <[email protected]>
Co-Authored-By: Claude Code <[email protected]>
Co-Authored-By: Claude Code <[email protected]>
6606bb9 to
7a61db9
Compare
|
Rebased onto main (through 0.21.37). The only conflict was the interaction metadata field list — the plan now spreads main's |
|
Two defects from the earlier review (6606bb9, #4297 (comment)) are still open at 7a61db9, and the PR has no device run yet. CI is green: 19 of 19 checks pass. It cannot catch the two defects below, because every readback test injects
Live dispatch also changed. Under the flag, the native ref fast path and the direct-iOS fused route are bypassed, and two captures plus a 150 ms wait bracket the tap (interactions.ts#L184). Nothing proves the readback works on a real backend, and the first defect shows that it currently does not. After both fixes, please run through the CLI on one iOS simulator and one Android emulator. Show Not blocking, take or leave: the before-frame capture now sits between the at-rest confirmation in The review threads on the missing inconclusive-when-absent note in the docs (r4497036453), the missing catch after dispatch (r4497036487), the tap stub that records nothing (r4497036494), the repo guard's Before merge, please open the design issue for the screenshot readback, since the baseline half shipped in #4338, then fix the two defects and post the live iOS and Android run. |
…ed tap Nothing after a resolved dispatch throws out of the evidence lane anymore: a decode, read, import or cleanup failure yields no evidence, because a throwing readback reported a delivered tap as failed and a PIN caller would re-enter the digit. The after-frame settle honors the request signal. Both frames decode in the codec worker, the guard audits a missing descriptor instead of skipping it, the docs say the readback is best-effort, and the delivered-tap tests record the tap. Co-Authored-By: Claude Code <[email protected]>
|
Progress at ffff797, with the decision carried to #4509. Defect 2 fixed. Nothing after a resolved dispatch throws out of the evidence lane: the comparison runs inside a catch that yields no evidence, cleanups are caught, the after-frame settle honors the request signal, and both frames decode in the codec worker ( Defect 1 is the design decision. #4509 carries it: admit a capture operation on touch routes, or refuse pre-dispatch where no binding exists. Per the 11-08 direction the design issue comes first; the binding follows its outcome, and the live iOS/Android runs follow the binding (before it lands, the flag cannot report on a shipped surface — which is defect 1 itself). Taken as well: the docs' inconclusive-when-absent sentence, and r4497036570 resolved as not applying. The remaining threads (r4497036453, r4497036487, r4497036494, r4497036501, r4497036514) are fixed at this head; r4497036508 routes to #4509. Title and body retitle to the readback once defect 1 lands and the flag reports for real. |
There was a problem hiding this comment.
1 issue found across 4 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/screenshot-diff-evidence.ts">
<violation number="1" location="packages/command-runtime/src/commands/interaction/runtime/screenshot-diff-evidence.ts:168">
P2: ` sleep` resolves normally on abort, so this continues into the after-frame capture instead of stopping readback. Check `options.signal ?? runtime.signal` after sleeping and return no evidence when it is aborted.</violation>
</file>
Reply with feedback, questions, or to request a fix.
View guided diff | Re-trigger cubic
| ): Promise<ScreenshotPairOutcome> { | ||
| try { | ||
| // A request canceled after the dispatch stops waiting for the after-frame. | ||
| await sleep(runtime, AFTER_FRAME_SETTLE_MS, options.signal); |
There was a problem hiding this comment.
P2: sleep resolves normally on abort, so this continues into the after-frame capture instead of stopping readback. Check options.signal ?? runtime.signal after sleeping and return no evidence when it is aborted.
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/screenshot-diff-evidence.ts, line 168:
<comment>` sleep` resolves normally on abort, so this continues into the after-frame capture instead of stopping readback. Check `options.signal ?? runtime.signal` after sleeping and return no evidence when it is aborted.</comment>
<file context>
@@ -145,23 +147,43 @@ async function compareScreenshotPair(
+): Promise<ScreenshotPairOutcome> {
+ try {
+ // A request canceled after the dispatch stops waiting for the after-frame.
+ await sleep(runtime, AFTER_FRAME_SETTLE_MS, options.signal);
const after = await capturePairScreenshotFrame(runtime, options, 'after');
if (!after) return {};
</file context>
| await sleep(runtime, AFTER_FRAME_SETTLE_MS, options.signal); | |
| const signal = options.signal ?? runtime.signal; | |
| await sleep(runtime, AFTER_FRAME_SETTLE_MS, signal); | |
| if (signal?.aborted) return {}; |
|
I reviewed ffff797 against my earlier review at #4297 (comment). The capture-failure, corrupted-frame and docs concerns are fixed. The silent no-op on daemon routes is not, and there is still no live device run. The daemon touch backend is built without Under the flag, the native ref fast path and the direct-iOS fused route are bypassed (interactions.ts:154). Two captures and a 150 ms wait now bracket the tap. This changes live dispatch, and no device run exists at this head. After the fix above, please run through the CLI on one iOS simulator and one Android emulator: Of the other open threads, the abort-handling thread still applies, and so do three lower-priority ones. The dynamic-import thread does not apply, because the import is evaluated once and then served from the module cache: #4297 (comment). The threads on the docs sentence, the capture-failure catch, the capture-failure test, the metadata CI is green with 19 of 19 checks passing, but no CI route exercises the daemon touch route with the flag, so green does not cover the first issue. Before merge, the daemon binding or refusal needs to land with its route test, and then the live iOS and Android runs need to be posted. |
…l a capture binds The touch routes admit the flag but bind no screenshot capture, so the flag was a silent no-op on every shipped surface. The refusal lives at the one place the daemon admits the flag: typed reason screenshot-readback-unavailable, dispatched no, and the callstack#4509 decision owns when the capture binds instead. Co-Authored-By: Claude Code <[email protected]>
|
Defect 1 is enforced at the one place the daemon admits the flag: The CLI live runs you specified are meaningful once the flag reports on a shipped surface — under the refusal that is the typed refusal JSON on the daemon route, which I can attach next, and the runtime-lane runs (effective key, dead region, runtime-ref click) follow the #4509 binding decision. Title/body retitle to the readback with the next push. |
|
Thanks for the update. At 434e4ad the earlier findings on the delivered-tap tests, the after-frame error handling, the missing-descriptor guard and the PNG decode are fixed. One problem remains, and it is a new one. All 19 checks pass, but no job drives the daemon press/click route with the flag and asserts Every daemon press and click with Not blocking, and you can take or leave these: the guarantee matrix still marks Could the PR avoid shipping an advertised flag whose only production behavior is a refusal? Landing the capture binding first would remove the refusal, the dead eligibility guard and the pass-through, and the existing bracket would work on the real route. Otherwise, removing the flag from every surface and the matrix and parking the runtime lane until #4509 decides would avoid carrying about 209 production lines that no production route reaches. Could the baseline also fold into the post-action observation plan as a third evidence kind, so it has one owner? Of the open inline threads, the abort/deadline thread still applies (it must be fixed when a capture binds), as do the unreachable direct-iOS guard, the Before merge, the advertised flag and the daemon route must agree, either by binding the capture or by removing the flag from every surface and the matrix. |
Summary
A coordinate press on a PIN pad reported success while the digit never entered and the remaining-attempts counter ticked down — the no-op guard's baseline had fallen back to an older tree, exactly the blind spot its own comment admits. Two changes: the coordinate lane now takes a guaranteed fresh pre-tree when the session baseline is stale, and
press --expect-screenshot-diffadds an out-of-tree readback that compares a lightweight before/after screenshot digest, so "tapped, nothing happened" is a reported effect instead of a discovery.22 files changed (baseline freshness, expect readback, output fields, tests).
Validation
pnpm run typecheck,pnpm run lint, post-action-observation and interaction tests, integration-progress--check, fallow, freerange: pass locally.🤖 Generated with Claude Code
Open in [code]smith · Need help on this PR? Tag
@codesmith-botwith what you need.