Skip to content

fix(flow): keep the last trusted matches when the final poll is blind - #1363

Open
latekvo wants to merge 6 commits into
mainfrom
fix/flow-blind-read-keeps-matches
Open

latekvo wants to merge 6 commits into
mainfrom
fix/flow-blind-read-keeps-matches

Conversation

@latekvo

@latekvo latekvo commented Oct 12, 2026 •

Copy link
Copy Markdown
Member

Closes #1227

A blank final poll inside the dark-tail tolerance overwrote lastMatches in waitForCondition, so a flow assert/await on text or visible reported no element matched selector instead of the real mismatch. Matches are now kept from the last trusted read, the same as when a read throws, together with the orientation they were read in, so a text reason quotes the element the check read.

No docs update needed.

Verification

All cases are in test/flows/flow-hidden-blank-reads.test.ts. Every read returns the element until 950 ms into a 1000 ms assert, then screen([]).

case before after
text, equals "Total $42.00" no element matched selector id="total" element matched id="total" but its text was "Total $41.50" (wanted to equal "Total $42.00")
visible, zero-area frame no element matched selector id="total" element(s) matched id="total" but none was visible (zero-area frame)
text, 2 matches, trusted reads landscapeLeft, blank read portrait no element matched selector id="total" quotes "Left", the match the check read (keeping the blank read's orientation would quote "Top")

Mutants all killed: restoring the old lastMatches assignment (3 tests fail); passing readingOrientation(env), never recording the orientation, or recording it from blind reads (1 test each).

Checks: npx tsc --build, tsc -p tsconfig.test.json --noEmit, vitest run test/flows (84 files, 2303 tests; the one failure is e2e-tool-aliases, local-only and green in CI), prettier and eslint on both files.

Not in this change: await-ui-element's timeoutNote has the same bug, tracked in #1577.

A blank final poll within the dark-tail tolerance overwrote lastMatches
with its empty result, so a text or visible mismatch was reported as
"no element matched selector". A blind read now keeps the previous
read's matches, the same as a read that throws.

Closes #1227
@coderabbitai

coderabbitai Bot commented Oct 12, 2026

Copy link
Copy Markdown
Contributor

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

…d in

A blind final poll still wrote its own orientation to env.lastRead, so the
text-assert reason picked among the restored trusted matches in the wrong
reading order and could quote an element the check never read. Keep the
trusted read's orientation alongside its matches, and pin the visible
zero-area reason too.

@latekvo latekvo left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Diplomat, Opus 5.5]: I ran 5 review agents over this diff (claims vs code, nearest twin, non-happy paths and inputs, reachability and outliving state, tests and absence) and mutation-tested the restore line. Two in-scope findings are inline below. Out of scope: await-ui-element’s timeoutNote has the same blind-final-poll bug and is now tracked in #1577.

@latekvo
latekvo marked this pull request as ready for review October 24, 2026 17:23
assertReason now takes the orientation of the read its matches came from,
so no text-condition path reads ActionEnv.lastRead.

@latekvo latekvo left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Diplomat, Opus 5.5]: Second pass over head 9ef0ad13c.

What I ran:

  • I re-checked both earlier threads against the code. Reverting the orientation fix fails the orientation test. Letting blind reads overwrite lastMatches fails all 3 new tests.
  • 3 review agents covered claims vs code, nearest twin, and non-happy paths/inputs/reachability. The third scripted 14 read sequences against this branch and against merge base 9565991fb. Pass/fail/indeterminate outcomes matched on every sequence, and only the tier-3 text/visible reasons changed.
  • Mutation pass: dropping the orientation record, recording it from every read, passing undefined, and stamping lastTrustedReadAt on blind reads. Each one fails a test. The last is caught in flow-when.test.ts.
  • CI checks run locally: tsc --build, tsc -p tsconfig.test.json, vitest run test/flows (2302/2303; the one failure is e2e-tool-aliases, which fails locally on main too and passes in CI), and prettier and eslint on both files.

One in-scope finding is inline. Out of scope: an abandoned idle tree read can write a stale orientation after rotate/fold clears it, filed as #1688.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

flow assert/await: an empty final poll inside the dark-tail tolerance replaces a text mismatch with "no element matched selector"

1 participant