fix(interaction): carry one post-gesture outcome on the tree and disclose it on every route - #2864
Conversation
|
Size Report
Startup median (7 runs, lower is better):
|
0b82302 to
56ce5c0
Compare
|
Review round addressed in 56ce5c0: (1) re-captures inherit the outcome at the recovery and full-tree-retry sites, with a find-recovery regression test; (2) one entry point: interactions call withCaptureDisclosures with the proof as the consumed tree; (3) failure sentences always go to error.hint, the unsettled wording holds on success, and a success-side assertion was added. #2868 is folded in: one postGestureOutcome field replaces unsettledGesture/gestureNoEffect. Production is net −7. |
|
Reviewed at 56ce5c0. The core change is sound: one discriminated One design question first. The two explicit re-capture sites (sparse recovery and full-tree retry) now own the outcome. The only smaller option I can see is to inherit the outcome through the request's capture binding, but that would also stamp post-action captures. Did you consider that, and is that the reason you kept the two sites? The no-effect warning lost its route-level test. The live iOS runs covered Not blocking: the stored-tree reuse check (selector-capture-runtime.ts#L283) now refuses any tree with Smoke Tests was still running. It runs scroll followed by selector reads, which this PR changes, so a failure there would need a look before it is called unrelated. |
…lose it on every route
A selector click, press, or fill right after a scroll whose surface never
settled missed on that moving tree and answered a plain selector_not_found,
and find lost the fact when a sparse capture was recovered by a re-capture.
A proven no-effect gesture reached only snapshot, through annotations that
selector reads and interactions drop.
Both facts are one field, SnapshotState.postGestureOutcome ({ kind:
'unsettled' | 'no-effect', gesture }), stamped by post-gesture
stabilization. A re-capture taken at once to recover or widen that tree
inherits it. Every route discloses it through withCaptureDisclosures;
interactions pass their request capture proof as the consumed tree. A
failure's disclosure sentence now lands in error.hint, the hint request
finalization keeps. withGestureNoEffectWarning is deleted.
Closes #2853
Closes #2856
56ce5c0 to
95daea0
Compare
|
Addressed in 95daea0, rebased on main after #2883 and #2869. Why two sites, not the capture binding. The request's capture binding is
If the binding inherited the outcome, those captures would be stamped with a gesture outcome they did not observe. No-effect route tests. One
Live, plain route. On my sim: fling, then immediately Reuse check. A no-effect tree passed an accepted verdict: two agreeing captures that match the baseline. Re-capturing it only reproduces the same tree. The check now refuses only
E2E helper. Only Counts. The PR body now splits the diff as production +165/−172 (19 files), tests +313/−83, and CHANGELOG and docs +12/−6. |
|
Reviewed at 95daea0. The code looks good. The design question from the last review is answered: only the two explicit re-capture sites own the outcome, because stamping it through the capture binding would make One small note, not blocking: a selector read that reuses a stored no-effect tree repeats the outcome, and the new All checks pass, and there are no conflicts. |
…ag change readFragments ignores dotfiles, so a stray .DS_Store no longer aborts npm version; every other entry still has to be a valid fragment. The #2864 fragment restated the unsettled-surface fix that v0.21.13 already shipped. It now names only what changed after the tag: the unsettledGesture field renamed to postGestureOutcome, the no-effect kind, and disclosure on interactions. The vitest lane comment now says the migration guard runs one git show.
Summary
Closes #2853
Closes #2856 (folded in from #2868)
After a
scroll/swipewhose surface never settled, a selectorclick/press/fillanswered a plainselector_not_found. So didfind … clickwhen a sparse capture was recovered by re-capturing. A proven no-effect gesture was reported only onsnapshot.SnapshotState.postGestureOutcome = { kind: 'unsettled' | 'no-effect', gesture }replacesunsettledGestureandgestureNoEffect, which were never released. One formatter builds the warning; the unsettled wording also holds when the command succeeds.inheritPostGestureOutcomeruns where a re-capture is taken at once, which is find's sparse recovery and the full-tree retry inresolveSelectorInteractionTarget.withCaptureDisclosures. Interactions pass their capture proof as the consumed tree. Failure sentences go toerror.hint.withGestureNoEffectWarning,withRequestCaptureDisclosures, andgesture-no-effect.ts.kind === 'unsettled'; a settled no-effect tree is reused.unsettledmaps to settle-and-re-read.Diff against main, 36 files:
Validation
Tested SHA
95daea082f.pnpm check:affected --run: passed (852 test files; two earlier runs under load average 400-900 timed out in unrelated files, which then passed in isolation).Each named mutation fails a test:
details.hintkind === 'unsettled'filter inbuildSnapshotWarningsorwithCaptureDisclosures, which breaks the new route-level no-effect cases onsnapshotandisLive on an iOS 26.2 sim, Catalog list. Each run was a fling followed immediately by the command.
click,press,fillon a missing selectorselector_not_foundwithpostGestureOutcome {kind:"unsettled"}and one warningpresson the tab barfind … click(earlier head)is/get(earlier head)kind: "no-effect"