Skip to content

fix(pixel): keep a passing show/hide proof across later passing checks of the same snapshot - #6754

Open
Lightheartdevs wants to merge 5 commits into
mainfrom
claude/transition-proof-retention
Open

Lightheartdevs wants to merge 5 commits into
mainfrom
claude/transition-proof-retention

Conversation

@Lightheartdevs

@Lightheartdevs Lightheartdevs commented Sep 26, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

Fleet round 108, website_create journey, on main + #6741 + #6747; the retention code is the same on main.

  1. The model published snapshot f120c3cf and inspected it at 375 with assert-hidden(heading "Midnight sold-out concert"), click("#soldOutBtn"), assert-visible(heading). The inspection passed as a show/hide transition.
  2. After a read, the model inspected the same snapshot again with assert-visible("h1"), click("#soldOutBtn"), assert-visible(heading). This plan passed. It has a click but no transition.
  3. fix(pixel): report an untested requested show/hide change as incomplete at tool-result time #6740 did not mark the second result INCOMPLETE, because the earlier proof covered the snapshot.
  4. The run guard still dropped that proof, and the owner got: "The requested show/hide interaction has not passed browser inspection."

Root cause

The code is in tool-loop-guard.mjs, in afterToolCall. A prior proof survived a later inspection only if boundStaticPreviewInspection accepted it, and that check (#6626) required that no step is a click. The second plan passed, used the same snapshot and ran in the same session, but it contained a click. As a result:

  • retainInteraction was false.
  • boundVisibilityInspection returned no new proof, because the plan is not a transition.
  • state.workspaceVisibilityInspection was set to undefined.
  • Delivery checks workspaceVisibilityInspectionPassed, which now failed, so the delivery reported the interaction as unverified.

The later result was not classified as failed or incomplete. The retention rule simply discarded the proof. That rule also contradicted the prior-proof exemption in #6740.

Fix

  • A later passing inspection of the proof's own snapshot can keep the proof. It must be in the same session, its receipt must be bound to that snapshot, and it must have no page errors. A passing plan without a transition never creates a proof.
  • The proof records two things:
    • its viewport;
    • each change it saw after exactly one click: a locator asserted before the click and in the other state after it, with the state it ended in.
      A change seen only after a preparatory or second click is not recorded.
  • A later passing plan without a click keeps the proof at any viewport, as on main since fix(pixel): preserve interaction proof after static preview checks #6626.
  • A later passing plan with a click keeps the proof only if all of these hold:
    • it has exactly one click;
    • the proof recorded at least one one-click change;
    • it runs at exactly the proof's viewport (width and height);
    • before the click, it does not assert a proved target, by its exact proved locator, in the state that target's change produced;
    • after the click, every assertion names a proved target by its exact proved locator, in the state that target's change produced.
  • Anything else revokes the proof, as main does for every plan with a click. The model then has to take the proof again, as before retention existed.
  • These still revoke the proof, unchanged:
    • a failed receipt, including a failed transition
    • an incomplete result
    • an unavailable inspector
    • page errors
    • a blocked request
    • an isError result
    • a republished or different snapshot
    • another session or run
  • boundStaticPreviewInspection is replaced by preservesVisibilityInspection(proof, params, result, preview).

Why the viewport

Before the click, no locator rule can tell the proved target under another name from an unrelated element. The round-108 later plan asserts h1 before its click, and an alias such as p#details has the same shape.

At another width, the page may already show the target in its proved state at load. A plan that asserts it there under an alias passes, and its own receipt shows the change not happening. A click on the proved target works the same way, because the inspector clicks only rendered elements.

At the proof's viewport, the snapshot loads as the proof saw it. So these observations can pass only if they agree with the proof.

Review history

  • 93df24d kept the proof unless the later plan re-asserted the target with byte-identical locators. Two reviews found two gaps: locator aliasing, and proofs whose change followed a preparatory click. fa45a29 replaced the rule with a locator-independent state rule.

  • fa45a29 accepted any assertion after the click that was in the proved state, on any locator. A review found pages where the requested change failed at the later width and the proof survived: a cookie banner, a toggle, two targets, tabs, and a self-hiding button. 2af6b12 fixed this in three ways:

    • it records only one-click changes;
    • it revokes on more than one later click;
    • it requires exact proved locators after the click.
  • 2af6b12 still kept two shapes that main revokes. The review of 2af6b12 confirmed both on all three transports:

    • An alias of a proved target, asserted before the click in its proved state. The confirmed aliases were p#details, heading "Details", "#details ", .promo for #promo (the conceal mirror), and the second target of a two-change proof.
    • A click on the proved target or an alias of it. The inspector performs such a click only when the target is rendered at load.

    In each case the page showed the target at load at 375 but not at 800. The proof ran at 800 and the later plan at 375. The 2af6b12 comment and commit message also claimed a stricter before-click rule than the code applied.

  • 3968252 binds a later plan with a click to the proof's viewport.

    • Cost: an earlier review suggested this rule, and fa45a29 did not adopt it, because it also revokes a consistent later check at another width. That cost remains: such a plan now revokes as on main.
    • Round 108 is not affected: both of its inspections ran at 375x667.
  • 9748a99 (this head) is test-only. It removes host, run and session identifiers from the round-108 replay:

    • the fixture is renamed to transition-proof-round108.json, and the test names and comment no longer name a host;
    • the workspace directory is night-garden-site in the owner message, the tool arguments and the publication receipts;
    • the host workspace path is /home/user/...;
    • the run ID, session ID and session key are neutral values in their valid shapes;
    • the evidence text no longer carries the host, integration commit, run ID, session ID or timestamp.

    The snapshot digest and siteId hash file names relative to the published directory and their bytes. All three recorded publication receipts (sha256, siteId, entrySha256, bytes) still match the replayed files. Plugin code is unchanged.

Behavior compared with main

  • Keeps where main revokes: a later passing plan with one click, at the proof's viewport, whose assertions agree with the proof as described above. The round-108 later plan is this case.
  • Revokes as on main:
    • every plan with a click at another viewport
    • plans with two clicks
    • proofs without a one-click change
    • aliases and other elements asserted after the click
    • a proved target asserted before the click in its proved state
    • all lifecycle faults listed above
  • Keeps as on main: plans without a click (fix(pixel): preserve interaction proof after static preview checks #6626). Such a plan can make the same load-time observation at another width, for example assert-visible(p#details) alone at 375, and it keeps the proof on both main and this PR. This PR does not change that.

Evidence

The replay fixture is tests/transition-proof-round108.json, built from the recorded create turn. It contains:

  • the owner message
  • the model-authored files and edits
  • the host publication receipts
  • the four inspection calls with their recorded results
  • the exec receipt
  • the model's answer and the delivered text

The workspace directory name, the host home directory, the run ID and the session identity are neutral values. Everything else is as recorded.

On main 6cba572, the replay reproduces two things byte for byte: the recorded tool output of all four inspections, and the recorded delivery ("The requested show/hide interaction has not passed browser inspection. ..."). With this change, the same replay delivers the verified preview ("Browser inspection passed for the submitted show/hide checks only; ..."). Both inspections ran at 375x667.

A read-only scan of the recorded qualification evidence (the portal observation files of rounds 100–114, all hosts) found 10 sessions with 31 inspection calls.

  • The only later passing plan with a click, after a proof of the same snapshot, is round-108 create. It ran at the proof's viewport.
  • So the viewport rule changes no recorded outcome compared with 2af6b12.
  • The 19-session replay described in earlier revisions of this PR was run on fa45a29. It has not been rerun.

Tests

  • Round-108 replay (inspection_transition_coverage.test.mjs), through the real guard and the real inspection tool: the recorded turn now delivers verified.

  • Round-108 revocation replay: each of these still revokes the proof:

    • a failed card transition at 1280
    • the proved heading left hidden
    • the same failure under three other locators
    • a republished snapshot
    • another session
  • Simulated-page file (transition_proof_retention_multiclick.test.mjs): the real guard, the real registered inspection tool, and a page state machine, so a plan passes only if the page behaves as asserted. Every case runs on the direct, tool_call and nested Tool Search transports.

    • A–F: cookie banner, toggle, hide-then-show, self-hiding button, two targets, tabs. All revoke.
    • G: lifecycle faults after a preparatory-click proof. All revoke.
    • G2 (new): lifecycle faults after a one-click proof, against a round-108-shaped plan at the proof's viewport. The fault-free case keeps. Failed, unavailable, page errors, incomplete, blocked, isError, republish and another session all revoke.
    • H: the round-108 shape at the proof's viewport keeps, and a static plan at another width keeps.
    • H3 (new): the round-108 shape at another width revokes.
    • I (new): the 2af6b12 false passes. All revoke:
      • I1: CSS alias
      • I2: role/name alias
      • I3: click on the target, and click on an alias
      • I4: control with the exact locator
      • I5: conceal alias
      • I6: alias in a two-change proof
  • Unit retention matrix (preview_interaction_assurance.test.mjs, direct and Tool Search transports): the click faults now run at the proof's viewport, so the locator rules decide them. The new click-other-viewport fault revokes.

  • Proof-shape table (preview_interaction_assurance.test.mjs): 23 later plans × 2 transports × 3 viewports, which is 138 tests. The viewports are the proof's 800x600, 800x720 and 1280x720.

    • A plan with a click keeps the proof only at 800x600.
    • A later plan that is itself a transition replaces the proof at any viewport.
  • Red check (the updated test files run against 2af6b12): 36 of 301 fail.

    • 15 are section I (the review's false passes).
    • 3 are H3.
    • 2 are click-other-viewport.
    • 16 are cross-viewport keeps in the proof-shape table.

    With 3968252, the four affected files pass 339 of 339.

  • Reviewer files run against 3968252:

    • The review's failing test passes 18 of 18. On 2af6b12, 15 of 18 failed.
    • Its 14-shape probe and lifecycle matrix give the same result as main 5afd46c on every line.
  • Red check after the scrub: the updated test files run against main 0a63258's plugin code. The four recorded inspection outputs still replay byte for byte. Both round-108 tests still fail where they did before the scrub: after the later plan, verification is failed where the test expects passed. G2 and H fail too. preview_interaction_assurance.test.mjs and control_names_transition.test.mjs fail to import preservesVisibilityInspection.

  • Full Linux suite (remote-js-test.sh):

    • 9748a99: 3393 tests, 0 failures
    • 9748a99 merged with main 0a63258 (local commit ad6f233b, not pushed): 3421 tests, 0 failures
    • Earlier: 3968252 gave 3393 tests, 0 failures, and 3968252 merged with main 5afd46c gave 3419 tests, 0 failures.

Overlap with open PRs

Checked with git merge-tree against 3968252. 9748a99 edits only lines this PR adds:

🤖 Generated with Claude Code

Lightheartdevs and others added 4 commits September 25, 2026 23:40
…s of the same snapshot

Tower2 round 108 (website_create, integration e58f475): the transition
assert-hidden(heading), click("#soldOutBtn"), assert-visible(heading)
passed on snapshot f120c3cf. The model then inspected the same snapshot
with assert-visible("h1"), click("#soldOutBtn"), assert-visible(heading).
That plan passed, and #6740 did not mark it incomplete because the earlier
proof covered the snapshot. The run guard still revoked the proof: it kept
a proof only across passing plans with no click (#6626). The owner got
"The requested show/hide interaction has not passed browser inspection."

A passing inspection of the same snapshot now keeps the proof, with or
without a click. Each inspection loads the unchanged snapshot again, so
its passing steps cannot contradict the earlier transition. The proof now
records each change it observed: the target, the control and the state
after one click. A later passing plan that clicks that control once and
then asserts that target in the other state is a failed test of the
change, and it revokes the proof. A failed or incomplete receipt, page
errors, another snapshot and another session still revoke it as before.

The replay fixture holds the recorded create turn: owner message, model
files and edits, publication receipts, inspection arguments and results,
and the delivered text. On main every recorded inspection result and the
delivered text are reproduced byte for byte. With this change the same
replay delivers the verified preview.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…y contradict it

Review of #6754. The previous commit revoked a kept proof only when a later
passing plan named the proved target and control with the same locators,
clicked once from load, and asserted the other state. Two gaps followed:

- Another locator for the same element kept the proof. After the round-108
  proof, assert-hidden("#midnightCard"), click("#soldOutBtn"),
  assert-hidden("#midnightCard") passed at 1280 on a page where the click
  left the card hidden, and delivery said the check passed. Main said it
  had not passed. The model does switch locators between plans (round 108:
  role/name, then "#soldOutBtn").
- A proof whose change was seen only after a preparatory or second click
  recorded no changes, so no passing plan could revoke it.

The check no longer depends on locators. After any click, each passing
assertion must be in the state that every proved change produced, or name a
proved target exactly in its proved state. Otherwise it may be a proved
target left in the other state, and the proof is revoked. A proof without a
one-click change is kept only across plans without a click. Plans without a
click keep the proof as since #6626, and the round-108 later plan still
keeps it.

The proof now records only each changed locator and its result state; the
control is no longer needed. A later click on another control that leaves
an element hidden now revokes a reveal proof, as on main.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…exact targets

Review of #6754 found later passing plans that showed a requested change
failing while the proof survived:

- a proof whose change followed a preparatory click (cookie banner) or a
  second click (toggle) recorded that change as caused by one click;
- a later plan with two clicks compared a toggle's second-click state with
  the one-click proof;
- after one click, any assertion in the proved state was accepted on any
  locator, so a card a self-hiding button never revealed, a summary a
  reveal never hid, or a panel a tab never replaced kept the proof.

A proof now records a change only from a plan with exactly one click. A
later plan with more than one click keeps nothing. With one click, each
assertion must name a proved target exactly, before the click in its load
state and after it in its proved state; anything else revokes, as main does
for every clicked plan. Plans without a click keep the proof (#6626), and
the tower2 round-108 shape, which re-asserted the proved heading, still
keeps it.

The simulated-page regression file checks every reviewer shape across the
direct, tool_call and nested transports; against the previous head 53 of
its and the matrix's cases fail.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…iewport

Review of 2af6b12 found two false passes where the PR kept a proof that
main revokes. Before the click, only a proved target named by the exact
proved locator revoked, so an alias of that target already in its proved
state (p#details, a heading role/name, "#details ", a conceal mirror, the
second target of a two-change proof) kept the proof. The one click's
locator was never checked, and the inspector clicks only rendered
elements, so clicking the target of a reveal proof kept it too. In each
case the later plan ran at another width, where the page already showed
the target in its proved state at load, and its own receipt showed it.

No locator rule can tell such an alias from the unrelated "h1" that the
round 108 later plan asserted before its click. A proof now records its
viewport, and a later plan with a click keeps it only at exactly that
viewport, where the snapshot loads as the proof saw it; at any other
viewport it revokes like main. Plans without a click are unchanged
(#6626). Both round 108 inspections ran at 375x667, so the recorded
replay still keeps its proof.

Tests: section I of the multiclick file adds the review's false passes
(CSS, role/name and click aliases, the conceal mirror and a two-change
proof) on all three transports, and H3 shows a round-108-shaped plan at
another viewport revoking. G2 runs the lifecycle faults against a
one-click proof and a later plan that keeps it when nothing is wrong. The
proof-shape matrix now runs each later plan at the proof's viewport, at
the same width with another height, and at another width; the fault
matrix checks click plans at the proof's viewport and adds a click at
another viewport. The comments now state the rule the code applies.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…08 replay

The round-108 replay fixture and its tests carried identifiers of the
qualification host and run. Replace them with neutral values without
changing what the replay exercises:

- rename the fixture to transition-proof-round108.json and drop the host
  name from the test names and comment;
- rename the workspace directory to night-garden-site in the owner
  message, the exec, write, edit and read arguments and the publication
  receipts (the snapshot digest and siteId hash file names relative to the
  directory and their bytes, so every recorded receipt still validates);
- use /home/user for the host workspace path of the exec cwd and the read;
- use a neutral run ID, session ID and session key in the valid shapes;
- remove the host, integration commit, run ID, session ID and timestamp
  from the evidence text, and say which values are neutralized.

The recorded inspection results, the model-authored bytes, the answer and
the delivered text are unchanged. On main's plugin code the two round-108
tests still fail where they did before (the later passing plan drops the
proof); with this branch they pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.

1 participant