Skip to content

tech-debt(ledger): a review stamp pins the head SHA, so a PR body edited after approval is invisible to it #16874

Description

@mrveiss

A ledger stamp pins a head SHA. A PR body is editable without moving it.

Found while sweeping the merge queue: three PRs had bodies missing content a reviewer had confirmed
present at approval time
— #16328, #16854 and #16856. In each case the ledger stamp still matched the
head SHA, so every SHA-based check said the approval was current.

A ledger review approve@<sha> verdict covers the tree at that SHA. It covers nothing about the PR
body, which GitHub lets anyone — or any bot — edit without producing a new commit. So the two can
diverge silently, and the gates that read the body (Validate PR template sections,
Check same-scope batching, Check PR links to its issue) fail on a PR whose stamp looks perfectly
current.

The mechanism, caught in the act

On #16856 the body ends with ## Summary by CodeRabbit and ## Model Used is gone. The bot
appended its section and the required heading did not survive. The PR is 42's fix to the batching
gate's wording — so a bot edit silently removed the heading from the very PR whose commit message
describes that heading being missed twice already.

#16854 lost its issue-linking keyword the same way: #16843 appears in prose with no
Refs/Closes, so Check PR links to its issue fails while the work itself is fine.

Why this is not just "re-read the body"

A reviewer did read these bodies and confirm them. The verdict was correct when made. What is missing
is any mechanism for the verdict to notice it has been invalidated — the same shape as a green CI run
that describes a tree the PR no longer has, except here nothing moves at all, so no freshness check
fires.

It also defeats the cheap re-verification the fleet has been relying on: "the PR's own files are
sha256-identical to what I reviewed, so this is a diff-verify"
is sound about the tree and says nothing
about the body.

Options

  1. Record a body hash alongside the head SHA in ledger review, so ledger pr <n> can report
    body-changed-since-approval the way it already reports a stale head. Cheapest and it makes the gap
    visible rather than requiring vigilance.
  2. Have reviewers re-read the body on every re-stamp, regardless of SHA match. Works, relies on
    memory, and is exactly what failed here — the reviewer had already read it.
  3. Treat a body edit as a head move — heavier, and bot edits would then invalidate stamps
    constantly, which trades a silent failure for a noisy one.

Option 1 looks right: the ledger already distinguishes stamped from stale, and this is the same
distinction applied to a second surface. Worth noting the failure is not hypothetical — it cost
three PRs a red apiece today, and two of them were blocked on the gate that the third PR exists to fix.

Bodies restored on #16856 and #16328; #16854's keyword is still missing and belongs to its author.

Found during the v0.9.0 merge-queue sweep. Refs #16855, #16859.

Activity

  1. github-actions commented on Sep 17, 2026

    @github-actions
    Contributor

    👋 Auto-triage could not confidently classify this issue (no confident signal). Could a maintainer add the appropriate labels (frontend, backend, infrastructure, docs, testing and good-first-issue, intermediate, advanced)?

  2. mrveiss commented on Sep 18, 2026

    @mrveiss
    OwnerAuthor

    Root cause of the recurring ## Model Used loss, found on #16853 — the PR it has now regressed on three times. It is more specific than this issue's framing, and it has a one-line fix.

    CodeRabbit rewrites the region between its markers wholesale, and anything placed inside that region is deleted on its next update.

    The PR body's edit history (userContentEdits) shows the pattern with no ambiguity — every human edit contains the heading, and every CodeRabbit edit that follows drops it:

    Edited Editor ## Model Used in body
    09-17 04:37 mrveiss no (original, never had it)
    09-17 05:17 mrveiss yes (restored)
    09-17 11:34 coderabbitai no
    09-17 13:34 mrveiss yes (restored again)
    09-17 16:49 coderabbitai no

    The 13:34 body shows exactly why:

    59: <!-- This is an auto-generated comment: release notes by coderabbit.ai -->
    60: ## Model Used                                  <- inside CodeRabbit's region
    64: ## Summary by CodeRabbit
    77: <!-- end of auto-generated comment: release notes by coderabbit.ai -->
    

    The bug was mine. My restore script inserted the heading immediately before ## Summary by CodeRabbit — an anchor that looks like the start of CodeRabbit's section but sits two lines inside it, after the opening marker. So each restore placed the heading where CodeRabbit would regenerate over it, and each "fix" armed the next regression.

    The fix is to anchor on the marker, not the heading: insert before <!-- This is an auto-generated comment: release notes by coderabbit.ai -->. Applied to #16853 — heading now at line 57, marker at 61 — and the template gate passes locally against the live body.

    I audited my other open PRs (#16854, #16444, #16911, #16916, #16922): all five have the heading outside the region. #16853 was the only one where the restore ran after CodeRabbit's region already existed.

    What this means for this issue

    This issue's premise — a stamp pins the head SHA, so a body edited after approval is invisible — is still true and still worth fixing; a stamp should not survive a body change. But it is not what drove the recurrence. The body was not edited after approval by a person; it was rewritten by a bot, deterministically, every time CodeRabbit updated its summary. Even a stamp that tracked body content would have been invalidated three times without anyone making a decision.

    Two things worth taking from it:

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

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions