feat(agent): a refused value stays out of the trace - #462
Merged
Conversation
A user's trace read: type 1200, type 800 - appended by the old typing semantics into 1200800 - clear, type 500. Four steps memorialized where one is the truth, and every replay re-performed the whole fumble, driving the app through its error state on purpose, for ever. The repo already had the principle, applied everywhere except here. An occluded click is not recorded, because it records a success the page never saw. A walk-away from a rejected form is not recorded, because it records a success the page refused to give. But a VALUE the page rejected - same verdict, from the same page - was recorded, and then replayed. When the rejected-form guard fires and the correction sticks, the writes that produced the red fields are now dropped before the trace is minted, and the survivors renumbered. The trace reads as the one line a person would have left: the value that stuck. The live run is unchanged - the fumble still happened against the app - only its memorial goes. Three guards keep the drop honest, each from adversarial review: The recording spans join BEFORE the prune. The join is by positional id, so joining after the renumber handed every survivor the time window of a different executed action - and heal's before/after frames would show the wrong moment. An observer between the fumble and the correction blocks the prune. A capture or assert there read the page WITH the fumble in it; a trace without the fumble would replay a different observation than the one recording verified. The journal is kept, refused value and all. A positional token (`nth-of-type` paths) is never pruned. The prune keys on selector strings, and a positional path can name different elements before and after a DOM mutation - last-write-wins on the string could drop an accepted write to a different element. Stable tokens only; everything else fails open to the journal, which is the direction throughout. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A user's trace read:
Four steps memorialized where one is the truth — and every replay re-performed the whole fumble, driving the app through its error state on purpose, forever. Their words: "the worst is when executing again, it repeats all the steps instead of keeping the last thing it tried and worked."
The principle already existed — this closes its last gap
An occluded click is not recorded, because it records a success the page never saw. A walk-away from a rejected form is not recorded, because it records a success the page refused to give. But a value the page rejected — same verdict, from the same page — was recorded, and then replayed.
Now: when the rejected-form guard fires and the correction sticks, the writes that produced the red fields are dropped before the trace is minted, and the survivors renumbered. The trace reads as the one line a person would have left. The live run is unchanged — the fumble still happened against the app — only its memorial goes.
Three guards, each from adversarial review
The recording spans join before the prune. The join is by positional id; joining after the renumber handed every survivor the time window of a different executed action — and heal's before/after frames would have shown the wrong moment. This was a real bug in my first version, caught in review.
An observer between fumble and correction blocks the prune. A capture or assert there read the page with the fumble in it; a trace without the fumble would replay a different observation than the one recording verified. The journal is kept, refused value and all — there is a test where a capture sits mid-fumble and the refused value stays.
Positional tokens are never pruned. The prune keys on selector strings, and an
nth-of-typepath can name different elements before and after a DOM mutation — last-write-wins on the string could drop an accepted write to a different element. Stable tokens only; everything else fails open to the journal, which is the direction throughout.Review findings deliberately NOT fixed here, so they're on the record
Notes
replaceand step ids already exist); the occluded-click precedent is the controlling one. The review's condition — the observer guard — is implemented.cargo fmt,clippy -D warnings, full suite (918 tests) green.🤖 Generated with Claude Code