Skip to content

[Phase 4] already-reviewed-at-head deadlocks on metadata-only findings — the demanded fix can never produce the new commit the re-review requires #1551

Closed

Description

@don-petry

Story

As a maintainer relying on pr-review's fix-requested → re-review loop to converge,
I want a metadata-only blocking finding (PR body, labels, linked issues) to be re-reviewable without a new commit,
so that the cascade cannot demand a fix that, once made, it is structurally unable to see.

Problem — a guaranteed deadlock, observed on PR #1531 (2026-08-19)

  1. 12:06 — pr-review posts fix requested (cycle 1/3) at head f3f52b22. The sole blocking finding is metadata-only: the PR body's Closes #1493 would auto-close an issue whose ACs 10–12 are staged follow-ups. The prescribed fix: "edit the PR body to 'Refs [Phase 2 follow-up] Complete the #1184 legacy→major-scoped pin migration in the reusable-pin compliance check (split from #687 ACs 7–13) #1493' / 'Part of [Phase 2 follow-up] Complete the #1184 legacy→major-scoped pin migration in the reusable-pin compliance check (split from #687 ACs 7–13) #1493'" — explicitly "not a code change."
  2. ~12:45 — the body is edited; closingIssuesReferences is confirmed empty. The demanded fix is complete.
  3. 12:41 — a dispatched re-review no-ops. Run 32253896963 log: {"pr":"…/pull/1531","sha":"f3f52b22…","decision":"noop","reason":"already-reviewed-at-head"}. Every subsequent dispatch does the same.
  4. The review's own footer says "The review cascade will automatically re-review after new commits are pushed" — but the required fix produces no commit. Deadlock: the verdict blocks the PR, the fix is invisible to the reviewer, and the reviewer refuses to look again.
  5. Broken only by pushing an empty commit (3bc26c32) to mint a new head SHA.

The skip logic lives in scripts/review-one-pr.sh (skip reasons: already-reviewed-at-head, ci-failing, ci-pending, changes-requested — see ~line 140) and is surfaced in scripts/review-batch.sh (~line 290). The "already reviewed" fingerprint is the head SHA alone, so PR-metadata changes never invalidate it.

Acceptance Criteria

  1. After a fix-requested verdict whose blocking findings are all metadata-level, a metadata change (PR body edit, label change, linked-issue change) makes the PR re-reviewable — no new commit and no human intervention required.
  2. Mechanism suggestion (not prescriptive): include a PR-metadata digest (body hash + closingIssuesReferences + labels) alongside the head SHA in the reviewed-state fingerprint, or treat pull_request: [edited] as invalidating already-reviewed-at-head when the last verdict was fix-requested.
  3. Same-SHA no-op behavior is unchanged for PRs whose last verdict was approval or whose findings required code changes — this must not create a re-review churn vector (an attacker or noisy bot editing the body must not burn review cycles; keep the churn breaker authoritative).
  4. The no-op log line states what would re-arm the review (new commit vs. metadata change), so a stranded PR is diagnosable from the run log.
  5. Regression test: fix-requested on metadata-only finding → body edit → next review run reviews instead of no-oping.

Tasks

  • Extend the reviewed-state fingerprint (or the trigger surface) per AC 1–3.
  • Update the verdict footer text to state the actual re-arm condition. (AC: 4)
  • Tests. (AC: 5)

Dev Notes

References

Filed from steering review of epic #1402, 2026-08-19.

Activity

  1. don-petry commented on Aug 20, 2026

    @don-petry
    CollaboratorAuthor

    Implementation plan — #1551 (already-reviewed-at-head deadlock on metadata-only findings)

    Root cause. The reviewed-state fingerprint in scripts/review-one-pr.sh is the head SHA alone (marker <!-- pr-review-agent v1 sha=<SHA> -->). A metadata-only fix-requested verdict demands a fix (PR body / labels / linked-issue edit) that produces no new commit, so the head SHA is unchanged and every re-review no-ops with already-reviewed-at-head. Deadlock.

    Approach (AC2 option a). Add a PR-metadata digest to the fingerprint, but only for fix-requested verdicts that are classified metadata-only. Approval markers and code-change fix-requests carry no digest and keep identical same-SHA no-op behavior (AC3). The churn breaker (cycle cap + per-PR automation budget) stays authoritative — a re-armed review still flows through both gates, so a body-editing attacker/bot cannot burn cycles.

    Mechanism (deterministic, unit-tested)

    • New lib scripts/lib/pr-metadata-digest.sh
      • compute_pr_metadata_digest <snapshot_json> → stable short hash of {body, sorted(closingIssuesReferences[].number), sorted(labels[].name)} via jq → sha256 → 16 hex. Both writer and reader compute it identically from the same fields.
      • marker_meta_digest <marker_body> → extracts meta=<hex> from a fix-request marker, else empty.
    • scripts/post-pr-review.sh (writer)
      • Read metadata_only from the verdict JSON (default false).
      • When the fix-request is metadata_only=true, compute the digest from the current PR (gh pr view --json body,closingIssuesReferences,labels) and stamp it into the marker's decision comment: <!-- pr-review-agent v1 sha=<SHA> --> <!-- decision=fix-requested risk=<R> meta=<digest> -->.
      • AC4 footer: metadata-only → "re-review automatically after a new commit or a change to the PR body, labels, or linked issues"; otherwise keep "after new commits are pushed."
    • scripts/review-one-pr.sh (reader)
      • Add body,closingIssuesReferences to the PR_SNAPSHOT gh pr view --json list (line ~127).
      • In the idempotency block (line ~450): capture the latest marker body (not just its SHA). When the marker SHA equals head SHA and the marker carries meta=<digest>, recompute the current digest; if it differs, re-arm (proceed to review) with an AC4 log line ("metadata changed since a metadata-only fix-request — re-reviewing"); if it matches, no-op.
      • AC4 no-op log lines state the re-arm condition: metadata-only markers → "re-arms on a new commit or a metadata change (PR body/labels/linked issues)"; all others → "re-arms on a new commit."

    Classification (metadata_only)

    metadata_only is set by the review prompts (prompts/single-review.md, prompts/cascade-action.md) — the only layer that can judge whether a finding's fix is a pure metadata edit. Additive optional boolean, defaults false (missing ⇒ current behavior, no digest, no churn). Kept out of the deterministic mechanism so the fingerprint/re-arm logic is fully unit-testable without an LLM.

    AC ↔ change map

    • AC1 — metadata change re-arms a metadata-only fix-request (reader re-arm on digest mismatch).
    • AC2 — metadata digest alongside head SHA in the fingerprint (option a).
    • AC3 — approvals & code-change fix-requests carry no meta= ⇒ unchanged; cycle cap + budget remain authoritative.
    • AC4 — no-op / re-arm log lines name the re-arm condition; verdict footer states it.
    • AC5 — regression test below.

    Tests (Phase 2, written first)

    • tests/test_pr_metadata_digest.bats — digest stability & sensitivity (body/label/closing-ref change flips it; unrelated fields don't); marker_meta_digest parsing.
    • tests/test_review_one_pr_metadata_rearm.bats — AC5 regression: fix-request marker with meta=<old> at head SHA + snapshot whose metadata yields a different digest ⇒ reviews (not already-reviewed-at-head); same digest ⇒ no-op; an approval marker + body edit ⇒ still no-op (AC3).
    • tests/test_post_pr_review_meta_stamp.bats — verdict metadata_only=true ⇒ marker carries meta= + metadata-change footer; false/absent ⇒ neither.

    Verify (Phase 4)

    bats tests/… (new + existing review suites) and bash .dev-lead/scripts/dev-lead-lint.sh.

    No git actions taken (CI owns commit/push).

  2. don-petry commented on Aug 20, 2026

    @don-petry
    CollaboratorAuthor

    Dev-Lead: Implementation Complete — PR #1559

    The implementation for issue #1551 is durable: commits are pushed and a pull request is open. This record is backed by verifiable artifacts you can confirm in one click.

    Review happens on the PR. Acceptance criteria and test results are evidenced by the PR's diff and CI checks — not asserted here. If a later stage fails, this claim is retracted in place.

  3. added 2 commits that reference this issue on Aug 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugBug reportsdev-leadFor dev-lead agent pickupinitiativeEpic / initiative tracking issue

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions