Skip to content

feat(tui): bind Next approvals to the file as it stood when asked - #452

Open
BunsDev wants to merge 3 commits into
test/411-approval-edgesfrom
feat/411-review-binding
Open

BunsDev wants to merge 3 commits into
test/411-approval-edgesfrom
feat/411-review-binding

Conversation

@BunsDev

@BunsDev BunsDev commented Nov 4, 2026

Copy link
Copy Markdown
Member

Summary

In Next, an approval now covers a file as it stood when the approval was asked for. If the file changes while the panel is open, Write, Edit, BatchEdit, ApplyPatch and NotebookEdit write nothing and tell the model to read it again.

Before this, on a real binary: a Write approved after someone else changed the file replaced their change with the model's text, and the model was told it had succeeded. An Edit whose old_string still matched applied on top of content nobody had reviewed.

This covers the engine-local half of #411's "a changed file invalidates stale review". Nothing here touches protected, daemon-backed approvals.

Context

How it works

Each tool notes the file's size and modified time before asking, and compares them after the answer.

It takes metadata rather than content on purpose:

  • Nothing is read before approval, which ApplyPatch already promises for path-scoped rules.
  • Looking at /dev/zero, a FIFO or a huge file costs nothing and can't hang before the panel opens. Reading content first would.

The limits are documented in docs/tools.md:

  • A touch also counts as a change. That fails safe: the model reads again and asks again.
  • A same-size rewrite on a filesystem with coarse timestamps can be missed.
  • A change landing after the check, in the moment before the write, isn't caught.

Worth a look

While checking this for false refusals, I found a separate bug that is already on main: two Edit calls to one file in one response lose one of them, while both report success. That reproduces in both renderers and isn't caused or fixed by this PR, which adds no false refusal in that flow (0 refusals across the runs). It's filed as #451 and gets its own PR, because the fix belongs in the shared engine.

Validation

  • Failing first on a real binary: the Write journey ended with from the model, and approval_binding failed 4 of its 6 tests before the tools were wired up.
  • crates/tools/tests/approval_binding.rs: all five tools refuse a changed or deleted file, a Write refuses a file created during the wait, an unchanged file is written as approved, and handlers that don't opt in keep today's behaviour.
  • cargo fmt --all --check, cargo clippy --workspace --all-targets --locked -- -D warnings.
  • cargo test --workspace --locked: 2917 passed, 0 failed.
  • Full tmux suite: 128 passed, 0 failed.
  • Test scripts' unit tests pass, and git diff --check is clean.

Not tested: Windows, Linux, network filesystems.

An approval could outlive what it was about: while the panel was open, a
file could change, and approving then overwrote that change. In Next,
Write, Edit, BatchEdit, ApplyPatch and NotebookEdit now note the file's
size and modified time before asking. If either differs after the answer,
they write nothing and tell the model to read the file again.

Only metadata is taken before approval, so nothing is read before it is
allowed and devices or huge files cost nothing to look at. The handler opts
in through ContentBoundApprovals, which only the Next path applies, so
Classic, headless and ACP keep their existing behavior.

Refs #411
@vercel

vercel Bot commented Nov 4, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
docs Ready Ready Preview Nov 4, 2026 5:39pm UTC

Request Review

The journey waited for "Write", which the typed prompt also contains. On a
slower host the other writer's change could then land before the tool noted
the file, so the approval really was current and the Write went through.

Refs #411

This branch was successfully deployed

1 active deployment
Preview — 6be97a98 Deployed Nov 4, 2026 by vercel[bot]
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