The PR body hand-restates what `merge_pr.py --why` already computes, and drifts exactly when it matters most nf-tty ← Beads

open priority 2 task unassigned

Twinned from https://github.com/cfsh/ninefold/issues/783 by tools/beads/import_github.py's reconcile pass.

Unclaimed, unlabelled. Filed from the bork standing slot during a poll-loop strategic-uplift window. Sibling of #782 — same theme, different surface: a claim that a human relies on, checked only by a model re-reading prose.

The gap

For a PR outside the Tier-1 perimeter, the one thing @aedanpope must know before approving is what approval actually covers — which governance paths the diff touches, and why the perimeter refused it. That list already exists, computed exactly, by ./tools/pr/merge_pr.py --why <n>.

Nothing puts it in the PR body. Every session hand-writes a sentence about what it is asking for, and that sentence drifts from the diff as the branch grows during review.

Evidence

Two PRs, stated plainly because that is all I checked — I do not want to inflate it:

⚠️ The failure mode is specific to review-heavy PRs, which are exactly the ones that need a human. A PR that merges on the first pass has no time to drift. One that goes eight passes accumulates governance paths precisely *because* it is being reviewed hard — so the body is least accurate when the stakes are highest, and the reader is the one person who cannot re-derive it.

Why the reviewer catching it is not the fix

It works, and it is the only thing working. But:

  1. It costs a pass per catch — the most expensive gate in the loop spent on an arithmetic mismatch a tool already computes.
  2. It catches drift; it cannot catch absence. A body that never claimed a file list has nothing to be stale against, and reads as complete.
  3. A session can be honest, careful, and still wrong, because the perimeter's answer changes with every commit and nothing recomputes the sentence.
The shape I would try

Cheapest first:

  1. A merge_pr.py --why --markdown <n> mode emitting the denial list as a table, for a session to paste. I did this by hand on #772 (*"this table is --why verbatim, one row per line it prints"*) and it converted a paragraph that had been wrong three times into one derived from the gate. Ten minutes of work; no new machinery.
  2. ninefold-reviewer posting the block itself on any PR its authority check refuses, as a comment. Removes the paste step, and the reviewer already computes the refusal for its check run.
  3. A CLAUDE.md loop-step line telling sessions to include it — last, not first. This is the option that relies on remembering, and #782 is about how that class of guard fails.

⚠️ I would not do 3 alone. It adds prose to the file whose size is its cost, and it re-creates the same problem one level up: a session that forgets the line produces the same wrong sentence.

Not claimed

I am not taking this — #772 is my chunk and folding a merge_pr.py change into it would put a sixth governance file in a diff that already needs approval for five. Whoever takes it should note that tools/pr/merge_pr.py is itself GOVERNANCE-denied, so even option 1 needs @aedanpope's approval; bundle it with other governance-tier work.

Dependencies

None.

Comments

No comments.

Add a comment