Skip to content

fix(check-docs): examine every mention of a PR, not just the first (#516) - #520

Merged
jphein merged 3 commits into
mainfrom
fix/516-check-docs-all-mentions
Sep 18, 2026
Merged

jphein merged 3 commits into
mainfrom
fix/516-check-docs-all-mentions

Conversation

@jphein

@jphein jphein commented Sep 18, 2026 •

Copy link
Copy Markdown
Collaborator

check-docs step 4 answered "does the FIRST mention agree?", not "do all mentions agree?" — grep … | head -1 per document meant a drifted claim appearing after a correct one was invisible.

Reproduced before fixing

Appending PR #1377 is still open upstream. to the end of README.md, with MemPalace#1377 MERGED upstream:

▸ 4/7  upstream PR states match doc claims
  ✓ all 245 PR references match upstream state

The control was proven reachable before its silence was trusted

This is the part that makes the reproduction mean anything. A control that is present but never looked at produces "did not fire" for the wrong reason, and that reads identically to "no drift". So before believing the pass:

grep -nE "(#1377|/1377)" README.md   -> 5 matches; the appended line is #5 (L477)
                                     -> head -1 selects L30, which mentions
                                        #1377 and claims nothing at all

Predictions were pre-registered before any code was read for a fix (scratch/refuted-claims-provenance/lucid-516-prereg.md) and all five held, including the one flagged in advance as most likely to ruin the change — that iterating every line must not add findings on the unmodified tree.

Three defects, now one scan

defect consequence
only the first line was read drift after a correct mention invisible
no right boundary on the claim scan #45 matched #452; /45 matched /459efab. The commentary scan beside it used [^0-9], so the two loops could read different lines and reach a conclusion neither supported
state words matched as substrings "opencode", "openai-compat", "reopened" all contain "open" and read as a claim of OPEN

The second and third were latent while only one line per document was examined, and the fix for the first amplifies them — so all three had to move together.

Measured differentially over the whole repo

Every PR stubbed MERGED, so every clean claim becomes checkable. 7 findings before, 7 after — and five different ones in each direction:

removed (false positives)   #45    read off a line about #452
                            #56    "OpenCode adapter smoke test"
                            #463   "openai-compat embedding"
                            #1567  ".opencode/opencode.json"
                            #2062  the v3.8.0 sync line
found (never examined)      #23    "PR #23 is still OPEN but"
                            #168   "#168 itself stays open"
                            #665   "*Upstream:* [PR #665] (OPEN)"
                            #1087  "(OPEN)"
                            #1094  "(open upstream, jp-authored)"

The unchanged total is the trap. Anyone checking whether the count moved would conclude nothing had.

On the real tree with real states, before and after are both clean — this change surfaces no new drift today; it removes the blind spot that would hide tomorrow's.

Also removes both shellcheck errors in the file (SC1087 — $n[ read as array indexing) and adds none. The two remaining warnings are pre-existing.

A limit, asserted rather than implied

When one clean line claims the true state and another claims a different one, drift cannot be told from history, so the PR is skipped. I considered flagging disagreement — the issue's own sketch suggests it — and measured it instead: exactly one such pair exists in the repo, #1024 in FORK_CHANGELOG.md, reading "pushed to the open MemPalace#1024 PR branch (squash-merged upstream)" alongside an authoritative "(MERGED)". That is correct documentation of a merged PR, and it is textually the same shape as genuine drift. Flagging disagreement would fire on it, so the rule stays and a test records the cost.

Tests

11 new in tests/test_check_docs_pr_state.py, driving the real script over a fixture tree with a stubbed gh — no network and no shared API quota. Four guards mutation-verified, each mutation asserting its target string exists, so a mutation that fails to apply reports loudly instead of as "nothing to guard":

mutation tests killed
restore head -1 3
remove the word boundary 2
remove the commentary skip 1
drop the right boundary 1

Three of these tests were not evidence when first written, and were rebuilt rather than kept:

  1. The harness sliced output between the "4/7" and "5/7" headings — but findings go to stderr while headings go to stdout, so in the combined capture the slice was always empty and every "no finding" assertion passed vacuously. Caught because a test that should have passed pre-fix didn't.
  2. The commentary test claimed closed, a state the MERGED branch ignores, so it passed with the commentary skip removed.
  3. The boundary test used a number the check never queried (it only considers numbers the docs mention), then a number the commentary skip catches anyway. It is now built from the measured #45/459efab case.

test_drift_on_the_first_mention_still_detected is kept deliberately as the harness's own positive control: it must pass before the fix, which is only possible if the harness can see a finding at all.

check-docs passes on itself with the fixed step 4 running live. Full suite 7443 passed, 82 skipped. ruff check + ruff format --check clean.

Part of #516


Update: a state WORD is not a state CLAIM (nebula's measurement applied)

The first pass above fixed head -1 and matched state words as whole words. That was not enough, and I could only prove it by running the check with REAL upstream states.

Two corrections to my own verification, which are why this was nearly missed

  1. My "no new false positives" run was not a real-state check. The stub returned a state for one PR number and nothing for the rest, so every other PR was skipped and "clean" proved nothing. With real gh, the first pass added a warning:

    NEW script: 2 warnings   (#1377, #459)
    OLD script: 1 warning    (#1377)
    
  2. The control line nebula cited (FORK_CHANGELOG.md L246) is blank in this tree — that measurement was taken on fix/505b-check-docs-pr-matcher, so the line number did not transfer. Found by content instead: it is L358 here. And L356 is a worse case that the line-number coordinate could not point at.

What the two real warnings actually were

source why
#459 README.md:297, FORK_CHANGELOG.md:356 The heading "purge / prune / mined share one open-and-refuse sequence", linking commit 459efab. There is no #459 on either line — /459 matched inside the commit hash, and "open-and-refuse" supplied a whole-word "open". A right boundary cannot help: /459 is followed by e.
MemPalace#1377 FORK_CHANGELOG.md:81 My own changelog entry, quoting the control sentence verbatim. The entry documenting the defect reproduced it — the self-quoting-retraction shape from the #503 spec, exactly as #511 went green and then warned again once its own entry landed.

Three rules now

The requirement, measured

With real gh on the repo itself, both the base script and this one report zero PR-state warnings. Worth being precise about the base's: its cleanliness is incidental — head -1 happens to land on a line without a claim, and only moved there because #509, #512 and this entry changed the changelog. This one is clean for a reason.

shellcheck findings 4 → 2; both remaining are pre-existing warnings, and both former errors are gone.

Tests

5 more (state word without a claim; nebula's "open the drawers" with the PR alone on the line, since the real one is spared only incidentally by a second PR sharing it; a commit hash is not a PR reference; a /pull/ URL still counts; a parenthesised marker is still a claim). 16 total, with two more mutations verified via the target-exists assertion:

mutation tests killed
claim shape → bare word 3
/pull/N → /N 2

One test asserted something the check cannot do — a PR referenced only by URL is never examined, because the number list is harvested from #NNNN alone. Pre-existing and out of scope; the test now says so rather than pretending to cover it.

Full suite 7448 passed, 82 skipped. check-docs passes on itself.

Summary by CodeRabbit

  • Bug Fixes

    • Improved documentation checks to review every pull request mention, accurately recognize state wording, and avoid partial-number or substring matches.
    • Reduced false positives while detecting previously missed status inconsistencies.
    • Ambiguous historical references are now handled without reporting misleading errors.
  • Documentation

    • Added changelog and fork-queue entries describing the updated validation behavior and its known limitations.
  • Tests

    • Added broad regression coverage for pull request references, status wording, URLs, and historical claims.

Copilot AI lite review requested due to automatic review settings September 18, 2026 04:41

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@coderabbitai

coderabbitai Bot commented Sep 18, 2026 •

Copy link
Copy Markdown

Review Change StackReview Change Stack

Warning

Review limit reached

Next included review available in 39 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: ca69e1cd-b7ca-4ba6-bca1-b5f954db5934

📥 Commits

Reviewing files that changed from the base of the PR and between 71241eb and 53f05db.

📒 Files selected for processing (5)
  • FORK_CHANGELOG.md
  • README.md
  • docs/fork-changes/2026-09-17-check-docs-every-pr-mention.yaml
  • scripts/check-docs.sh
  • website/public/llms-full.txt

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: 786d726b-b104-4820-81c0-ed7aba2bfc03

📥 Commits

Reviewing files that changed from the base of the PR and between 95a9385 and 71241eb.

📒 Files selected for processing (6)
  • FORK_CHANGELOG.md
  • README.md
  • docs/fork-changes/2026-09-17-check-docs-every-pr-mention.yaml
  • scripts/check-docs.sh
  • tests/test_check_docs_pr_state.py
  • website/public/llms-full.txt

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.


📝 Walkthrough

Walkthrough

The PR updates check-docs.sh to inspect every bounded PR mention and recognize only explicit state claims. It adds regression tests and records the change in the changelog, README, and generated inventory.

Changes

PR-state validation

Layer / File(s) Summary
PR-state parser update
scripts/check-docs.sh, FORK_CHANGELOG.md
The checker evaluates every matching line, filters multi-PR commentary per line, enforces PR-reference boundaries, and recognizes explicit whole-word state claims.
Regression coverage and published records
tests/test_check_docs_pr_state.py, docs/fork-changes/..., README.md, website/public/llms-full.txt
Tests cover stale later claims, ambiguous history, malformed references, valid URLs, and claim boundaries. Changelog and inventory entries document the update.

Priority: ⬇️ Low

Estimated code review effort: 3 (Moderate) | ~25 minutes

Change: Bug fix

Suggested reviewers: igorls

Merge Risk: ⚪ Minimal · up to 71241

The parser, regression coverage, and documentation are consistent, with no concrete merge-blocking risk identified.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 84.21% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 19 functions across 2 files. (4 skipped: 4 …
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: updating check-docs to examine every PR mention instead of only the first.
✨ Finishing Touches 💡 1
⚔️ Resolve merge conflicts 💡
  • Resolve merge conflict in branch fix/516-check-docs-all-mentions
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@jphein
jphein force-pushed the fix/516-check-docs-all-mentions branch from 97a4796 to faf0b5a Compare September 18, 2026 04:43
Step 4 did `grep … | head -1` per document, so only the FIRST line mentioning
a PR number contributed to that document's claimed state. The check answered
"does the first mention agree?" rather than "do all mentions agree?", and a
drifted claim appearing after a correct one was invisible.

Reproduced on the real repo before fixing: appending

    PR MemPalace#1377 is still open upstream.

to the end of README.md, with MemPalace#1377 MERGED upstream, left check-docs
reporting "✓ all 245 PR references match upstream state".

The control was confirmed reachable BEFORE its silence was trusted: the
appended line is in the instrument's own match set (line 477 of 5 matches),
while `head -1` selects line 30 — which mentions MemPalace#1377 and claims nothing.
A control that is present but never looked at produces "did not fire" for the
wrong reason, and that reads identically to "no drift".

Three defects, now one scan:

- Only the first line was read. Every matching line is now, with the
  multi-PR commentary skip applied per LINE rather than zeroing a whole
  document's claims.
- No right boundary. The claim scan used (#$n|/$n), so `#45` matched `#452`
  and `/45` matched `/459efab`, while the commentary scan beside it used
  [^0-9] — the two loops could read different lines and reach a conclusion
  neither line supported. One scan, one regex, anchored on a non-digit or
  end of line.
- State words matched as substrings. "opencode", "openai-compat" and
  "reopened" all contain "open" and were read as a claim of OPEN. Latent
  while one line per doc was examined; amplified the moment every line is.

Measured differentially over the whole repo with every PR stubbed MERGED:
7 findings before, 7 after — and five different ones in each direction.

  removed (false positives):  #45   read off a line about #452
                              #56   "OpenCode adapter smoke test"
                              #463  "openai-compat embedding"
                              MemPalace#1567 ".opencode/opencode.json"
                              MemPalace#2062 v3.8.0 sync line
  found (never examined):     #23   "PR #23 is still OPEN but"
                              #168  "#168 itself stays open"
                              MemPalace#665  "*Upstream:* [PR MemPalace#665] (OPEN)"
                              MemPalace#1087 "(OPEN)"
                              MemPalace#1094 "(open upstream, jp-authored)"

The unchanged total is the trap: a reader checking whether the count moved
would conclude nothing had.

Also removes both shellcheck errors in the file (SC1087 — `$n[` read as
array indexing) and adds none; the two remaining warnings are pre-existing.

Known limit, asserted in a test rather than left implicit: when one clean
line claims the true state and another claims a different one, drift cannot
be distinguished from history, and the PR is skipped. MemPalace#1024 in
FORK_CHANGELOG.md is exactly that shape — "pushed to the open MemPalace#1024 PR
branch (squash-merged upstream)" alongside an authoritative "(MERGED)" — and
is correct documentation of a MERGED PR. It is the only such pair in the
repo, which is why flagging disagreement was rejected.

Tests: 11 new in tests/test_check_docs_pr_state.py, driving the real script
over a fixture tree with a stubbed `gh` (no network, no shared API quota).
Four guards mutation-verified, each mutation asserting its target exists so a
mutation that fails to apply reports loudly instead of as "nothing to guard":

    restore head -1            -> 3 tests
    remove the word boundary   -> 2
    remove the commentary skip -> 1
    drop the right boundary    -> 1

Three of those tests were not evidence when first written and were rebuilt:
the harness sliced output between step headings while findings go to stderr
(so every "no finding" assertion passed vacuously); the commentary test
claimed a state the MERGED branch ignores; and the boundary test used a
number the check never queried. Each now fails under the old behaviour.

check-docs passes on itself. Full suite 7443 passed, 82 skipped.

Part of #516
Fixes #516
Follow-up on the same PR, applying nebula's measurement from the issue
thread. The first pass fixed `head -1` and matched state words as whole
words; that was not enough, and I could prove it only after running the
check with REAL upstream states.

Two corrections to my own verification first, because they are why this
was nearly missed:

  * My "no new false positives" run used a stub that returned a state for
    ONE pr number and nothing for the rest, so every other PR was skipped.
    "Clean" there proved nothing. With real `gh` the fix ADDED a warning.
  * The control line nebula cited (FORK_CHANGELOG.md L246) is blank in this
    tree — the measurement was taken on another branch. Found by content
    instead: it is L358 here, and L356 is a worse case nebula predicted but
    could not see.

Measured with real states, before this commit:

    NEW script: 2 warnings   (MemPalace#1377, #459)
    OLD script: 1 warning    (MemPalace#1377)

Both causes are the same defect from the other end. `head -1` decided
WHICH lines are read; this decides WHAT counts as a claim on a line.

  #459  README.md:297 and FORK_CHANGELOG.md:356 are the heading "purge /
        prune / mined share one open-and-refuse sequence", linking commit
        `459efab`. There is no #459 on either line — `/459` matched inside
        the COMMIT HASH, and "open-and-refuse" supplied a whole-word "open".
        A right boundary does not help: `/459` is followed by `e`.
  MemPalace#1377 FORK_CHANGELOG.md:81 was MY OWN changelog entry, quoting the
        control sentence verbatim. The entry documenting the defect
        reproduced it — the `self-quoting-retraction` shape from the #503
        spec, which is how #511 went green and then warned again once its
        own entry landed.

So, three rules now:

  * `/pull/$n`, never a bare `/$n`. A commit hash is not a PR reference.
  * A state word must appear in a CLAIM SHAPE — a parenthesised marker
    "(OPEN)", or a copula "is/was/stays/remains/now [still] open". Checked
    against all 13 cases in this repo: every real claim kept (#23 "is still
    OPEN", #168 "stays open", MemPalace#665/MemPalace#1087 "(OPEN)", MemPalace#1094 "(open upstream"),
    every prose case dropped ("open-and-refuse", "open the drawers",
    "opencode", "openai-compat", "reopened").
  * The entry for this change does not quote its own control sentence. It
    states a MERGED claim for MemPalace#1377, which is what MemPalace#1377 is.

Result with real `gh` on the repo itself: both the base script and this one
report zero PR-state warnings. The base's cleanliness is incidental — its
`head -1` happens to land on a line without a claim, and moved there only
because #509/#512/this entry changed the changelog. This one is clean for a
reason.

shellcheck findings 4 -> 2 (both remaining are pre-existing warnings; both
former ERRORS are gone).

Tests: 5 more (state word without a claim; nebula's "open the drawers" with
the PR alone on the line, since the real one is spared only incidentally by
a second PR sharing it; a commit hash is not a PR reference; a /pull/ URL
still counts; a parenthesised marker is still a claim). 16 total, and two
more mutations verified with the target-exists assertion:

    claim shape -> bare word   -> 3 tests
    /pull/N     -> /N          -> 2 tests

One test asserted something the check cannot do — a PR referenced only by
URL is never examined, because the number list is harvested from `#NNNN`
alone. Pre-existing and out of scope; the test now says so rather than
pretending to cover it.

Full suite 7448 passed, 82 skipped. check-docs passes on itself.

Part of #516
Rebased onto 4c6a8d0. `--next-seq` re-run after the final fetch says 157;
taken from the tool rather than assumed. Generated artefacts re-rendered
from main's side, never hand-merged.

Part of #516
@jphein
jphein force-pushed the fix/516-check-docs-all-mentions branch from 71241eb to 53f05db Compare September 18, 2026 06:25
@jphein
jphein merged commit 7ed80f0 into main Sep 18, 2026
17 checks passed
@jphein
jphein deleted the fix/516-check-docs-all-mentions branch September 18, 2026 06:34
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.

2 participants