Skip to content

Clear the comment half of the prose backlog, exemption first - #604

Merged
ptr727 merged 3 commits into
developfrom
prose-comment-batch
Aug 7, 2026
Merged

Clear the comment half of the prose backlog, exemption first#604
ptr727 merged 3 commits into
developfrom
prose-comment-batch

Conversation

@ptr727

@ptr727 ptr727 commented Aug 7, 2026

Copy link
Copy Markdown
Owner

Takes the The Prose Content Backlog cluster from TODO.md. Its Checked
anchor was re-verified against develop at c64e3e0 before anything was
written: prose_lint.py --summary reported the same 373 violations across 26
files, catalog/snippets the same 0, and the three largest files the same
99, 52 and 41.

The exemption came first, and it was wrong

RUN_ON read the closing dot of an ellipsis as a sentence terminator, so
scripts/repo_gate.py:204 was reported as two sentences:

# .editorconfig: [glob] ... end_of_line = lf

That is one schematic line, and the split the rule asked for would have broken
the fragment the comment exists to show. Fixing the prose first would have
damaged a correct comment on the checker's say-so, which is the failure this
repository has now recorded six times. The guard is that a dot preceded by a
dot never terminates, it carries two tests, and the verdict diff was run in
both directions: exactly one finding disappeared and none appeared.

The batch is the comment surface, all of it

The backlog splits by surface rather than by file. comment-wrap and
comment-case lived entirely in non-Markdown comments, dash and semicolon
live entirely in Markdown prose, so the comment half is one reviewable change
that owes no re-vendor: of the eleven files, four are workflows carried at
interface fidelity, where the contract is job keys and tokens rather than
comments, and the rest are hub-only.

241 findings are cleared by restructuring, never by widening an exemption. A
sentence per comment line, and a comment that opened on a lowercase identifier
reworded to open on a word, which is what the rule asks for and what the
snippets sweep established.

The tree goes from 373 across 26 files to 131 across 15, all of them dash
and semicolon in Markdown. Six of those files are carried, so the next batch
splits again at that line: the carried half rewrites byte-locked sections and
owes a re-vendor, the hub-only half owes nothing.

Both rules now gate

A rule swept clean but left warn-only regresses on the next edit with nothing
reporting it, so comment-wrap and comment-case move to the gating CI step
and out of the warn-only one. Every claim about which rules gate moved with
them, in OPERATIONS.md, scripts/README.md and the two workflow comments,
including the one in scripts/README.md saying gating still needs --diff
because the tree carries several hundred of them.

One gap found while counting, not fixed here

home-path is in DEFAULT_RULES and is named by neither CI list, so the
pattern-detectable half of the representative-data rule runs on every bare
local run and gates nothing in CI. It is clean tree-wide, so it is a hole
rather than a backlog. It is recorded in OPERATIONS.md beside the two gaps
already named there and in TODO.md, rather than closed here, because gating
a third rule is its own decision.

The write guard and both installer wrappers are among the eleven files, so
every installed copy is now behind the hub by that much. The divergence is
comment-only, the self-test passes unchanged, and TODO.md carries it under
the host rollout as a re-run at the next visit.

Verified with the full OPERATIONS.md local list: five self-tests, the repo
gate, both prose invocations, jq, spec/validate.py, editorconfig-checker,
markdownlint and actionlint.

Takes the `The Prose Content Backlog` cluster from `TODO.md`. Its `Checked`
anchor was re-verified against `develop` at `c64e3e0` before anything was
written: `prose_lint.py --summary` reported the same 373 violations across 26
files, `catalog/snippets` the same 0, and the three largest files the same
99, 52 and 41.

## The exemption came first, and it was wrong

`RUN_ON` read the closing dot of an ellipsis as a sentence terminator, so
`scripts/repo_gate.py:204` was reported as two sentences:

```text
# .editorconfig: [glob] ... end_of_line = lf
```

That is one schematic line, and the split the rule asked for would have broken
the fragment the comment exists to show. Fixing the prose first would have
damaged a correct comment on the checker's say-so, which is the failure this
repository has now recorded six times. The guard is that a dot preceded by a
dot never terminates, it carries two tests, and the verdict diff was run in
both directions: exactly one finding disappeared and none appeared.

## The batch is the comment surface, all of it

The backlog splits by surface rather than by file. `comment-wrap` and
`comment-case` lived entirely in non-Markdown comments, `dash` and `semicolon`
live entirely in Markdown prose, so the comment half is one reviewable change
that owes no re-vendor: of the eleven files, four are workflows carried at
`interface` fidelity, where the contract is job keys and tokens rather than
comments, and the rest are hub-only.

241 findings are cleared by restructuring, never by widening an exemption. A
sentence per comment line, and a comment that opened on a lowercase identifier
reworded to open on a word, which is what the rule asks for and what the
snippets sweep established.

The tree goes from 373 across 26 files to 131 across 15, all of them `dash`
and `semicolon` in Markdown. Six of those files are carried, so the next batch
splits again at that line: the carried half rewrites byte-locked sections and
owes a re-vendor, the hub-only half owes nothing.

## Both rules now gate

A rule swept clean but left warn-only regresses on the next edit with nothing
reporting it, so `comment-wrap` and `comment-case` move to the gating CI step
and out of the warn-only one. Every claim about which rules gate moved with
them, in `OPERATIONS.md`, `scripts/README.md` and the two workflow comments,
including the one in `scripts/README.md` saying gating still needs `--diff`
because the tree carries several hundred of them.

## One gap found while counting, not fixed here

`home-path` is in `DEFAULT_RULES` and is named by neither CI list, so the
pattern-detectable half of the representative-data rule runs on every bare
local run and gates nothing in CI. It is clean tree-wide, so it is a hole
rather than a backlog. It is recorded in `OPERATIONS.md` beside the two gaps
already named there and in `TODO.md`, rather than closed here, because gating
a third rule is its own decision.

The write guard and both installer wrappers are among the eleven files, so
every installed copy is now behind the hub by that much. The divergence is
comment-only, the self-test passes unchanged, and `TODO.md` carries it under
the host rollout as a re-run at the next visit.

Verified with the full `OPERATIONS.md` local list: five self-tests, the repo
gate, both prose invocations, `jq`, `spec/validate.py`, editorconfig-checker,
markdownlint and actionlint.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings August 7, 2026 18:39

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR reduces the prose-lint backlog by rewriting non-Markdown comment prose to satisfy comment-wrap and comment-case, and then updates the documentation and CI wiring so those comment rules are enforced as gates going forward. It also adjusts the run-on sentence detector to treat ellipses correctly and adds regression tests.

Changes:

  • Update scripts/prose_lint.py run-on detection to avoid treating an ellipsis' closing dot as a sentence terminator, with added unit tests.
  • Reflow and rephrase comments across spec, safety tooling, and workflow files to clear comment-wrap and comment-case findings.
  • Move comment-wrap and comment-case from warn-only reporting into the gating prose step, and update operational documentation to match.

Reviewed changes

Copilot reviewed 16 out of 16 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
TODO.md Refresh prose backlog tracking notes and add missing link references.
spec/validate.py Comment rewording to satisfy comment prose rules.
spec/fidelity_honesty.py Comment rewording to satisfy comment prose rules.
spec/audit.py Comment rewording to satisfy comment prose rules.
scripts/test_prose_lint.py Add regression tests for ellipsis handling in run-on detection.
scripts/README.md Update prose-lint gating vs warn-only documentation.
scripts/prose_lint.py Adjust RUN_ON detection for ellipsis and document intent.
OPERATIONS.md Update local verification runbook and describe CI check coverage gaps.
host-setup/agent-safety/install.sh Comment reflow for comment prose compliance.
host-setup/agent-safety/install.ps1 Comment reflow for comment prose compliance.
host-setup/agent-safety/gh-write-guard.py Comment reflow for comment prose compliance.
.github/workflows/validate-task.yml Gate comment-wrap and comment-case, trim warn-only list.
.github/workflows/test-pull-request.yml Comment reflow for comment prose compliance.
.github/workflows/publish-release.yml Comment reflow for comment prose compliance.
.github/workflows/merge-bot-pull-request.yml Comment reflow for comment prose compliance.
.github/dependabot.yml Comment reflow for comment prose compliance.

Comment thread scripts/prose_lint.py Outdated
The guard sat in front of the whole `[.!?]` class, so it suppressed a
terminator that happens to follow an ellipsis. `Really...? Yes it does.` read
as one sentence, because the `?` is preceded by the ellipsis' closing dot,
and that is a genuine run-on the rule exists to catch.

Only a dot can end an ellipsis, so the guard belongs on the dot alternative
alone: `(?:(?<!\.)\.|[!?])`. A test covers both `...?` and `...!`.

No verdict changes on this tree, since it carries no such line, which is what
makes this a correctness fix to the rule rather than a change to the sweep.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings August 7, 2026 18:43

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated no new comments.

Suppressed comments (2)

TODO.md:34

  • This Checked bullet anchors the measurement to develop at c64e3e0 but then reports an "after the comment batch" number that would be on this PR branch, not on that develop commit. Clarify which ref the post-batch measurement comes from to avoid misattributing the figures.
  - **Checked** - `develop` at `c64e3e0` on 2026-08-07, where `python3 scripts/prose_lint.py --summary` reported 373 violations across 26 files before the comment batch and 131 across 15 after it, and `catalog/snippets` reports 0. The snippets sweep took the tree from 557 across 45, of which 184 across 19 were snippets.

scripts/README.md:75

  • This sentence lists which rules are clean tree-wide and gate CI, but it omits spelling, which is still gated in the workflow/ops invocations. That makes the doc inconsistent with the actual gating set.
`charset`, `dupword`, `comment-wrap`, and `comment-case` are clean tree-wide and gate CI. `charset-unknown`, `semicolon`, and `dash` run as one warn-only CI step, so the remaining backlog is visible without blocking and is corrected as each file is next edited, or cleared in a deliberate batch.

…to the gate list

Two findings the review suppressed rather than threaded, both correct.

The `Checked` bullet anchored to `develop` at `c64e3e0` and then quoted a
post-batch figure that exists only on this branch, which reads as though the
anchor commit carries it. `Checked` is the freshness anchor, so a number under
it that was measured somewhere else is the one thing it must not do. Each
figure now names the ref it came from, and says the branch figure becomes the
anchor's own on merge.

`scripts/README.md` listed the gating rules twice and omitted `spelling` from
the second list. The omission predates this change, and adding the two comment
rules around it left the same file stating five gating rules in one place and
four in another.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings August 7, 2026 18:48
@ptr727

ptr727 commented Aug 7, 2026

Copy link
Copy Markdown
Owner Author

Answering the two suppressed findings from the round on 2e1a6e5, since neither has a thread to reply in. Both were correct and both are fixed in efa60be.

TODO.md:34"This Checked bullet anchors the measurement to develop at c64e3e0 but then reports an 'after the comment batch' number that would be on this PR branch, not on that develop commit."

Correct, and it is the specific thing a Checked bullet must not do: the file defines it as the freshness anchor naming the branch, the commit and the date a claim was last read against the tree, so a figure under it measured somewhere else misattributes itself. Each figure now names its own ref, and the bullet says the branch figure becomes the anchor's own number when this merges.

scripts/README.md:75"This sentence lists which rules are clean tree-wide and gate CI, but it omits spelling, which is still gated in the workflow/ops invocations."

Correct. Worth recording that the omission predates this pull request: the line already read "charset and dupword are clean tree-wide and gate CI" on develop. Adding the two comment rules around it is what made it visible, because the file then stated five gating rules in one place and four in another. spelling is restored, so both lists and both CI invocations now agree.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated no new comments.

@ptr727
ptr727 merged commit c9c92dd into develop Aug 7, 2026
7 checks passed
@ptr727
ptr727 deleted the prose-comment-batch branch August 7, 2026 19:07
ptr727 added a commit that referenced this pull request Aug 7, 2026
)

Takes the next batch of the `The Prose Content Backlog` cluster from
[`TODO.md`](./TODO.md), following
[#600](#600) (snippets)
and [#604](#604)
(comments). Part of
[#519](#519).

## The anchor was re-verified first

`develop` at `c9c92dd`, where `python3 scripts/prose_lint.py --summary`
reports **131 violations across 15 files**. That is the figure the
comment batch left, and merging that batch made it the anchor's own
rather than a branch's, exactly as the cluster entry predicted.

## Scope, and a correction to it

The batch is the **hub-only Markdown files**: `docs/`, `spec/*.md` and
`catalog/README.md`. That is **90 findings across 9 files** (79 `dash`,
11 `semicolon`), not the ~104 the batch was scoped at. The carried six
hold 41, and 90 + 41 = 131.

The 6 carried files are left for a later batch, since they rewrite
byte-locked sections and owe a fleet re-vendor while these owe nothing.
Every `dash` finding in the tree turned out to be hub-only, so what the
carried half now holds is **41 semicolons and no dash at all**.

| File | Findings |
| --- | ---: |
| `docs/devcontainer.md` | 22 |
| `docs/ssh-signing.md` | 17 |
| `spec/type-model.md` | 17 |
| `spec/fidelity-model.md` | 8 |
| `docs/repo-config-carry.md` | 7 |
| `spec/scope-model.md` | 7 |
| `catalog/README.md` | 6 |
| `docs/token-cost.md` | 5 |
| `spec/readme-structure.md` | 1 |

## The exemption came first, and this time it was right

`LABEL_DASH` exempts `- **Label** - text` as a definition separator that
is structurally a colon. It reaches neither a code-span label nor a
paragraph-leading one, and both shapes flagged here. Widening it was the
obvious move, and the corpus rejected it twice.

**A code-span definition list** is already spelled with a colon in five
files, against four dash-spelled lines in one (`catalog/README.md`):

```text
STANDUP.md:91             - `ARCHITECTURE.md`: how a code repo is built
spec/section-model.md:68  - `ARCHITECTURE.md`: how a code repo is built
scripts/README.md:81      - `sha-pin`: every workflow `uses:` naming
repo-config/README.md:6   - `operational/develop.json`: the `develop`
CODESTYLE.md:79           - `.NET Build`: Build with diagnostic
```

**A paragraph-leading label** is already spelled `**Label.** Sentence`,
against nine dash-spelled lines confined to two troubleshooting
sections.

So both were a file that had not adopted the tree's own convention
rather than a construct the rule fails to describe. **The checker is
unchanged by this batch.** That is the same verdict the comment batch
reached on a comment opening on a lowercase identifier.

A bulleted list terminating its items with semicolons appeared in
exactly one place tree-wide, `spec/type-model.md:24-25`, and its own
last item already ended in a period. It was punctuation to make
consistent rather than the list construction the semicolon exemption
protects, which a line-based checker could not see across bullets in any
case.

## One adjacent fix, flagged because it widens scope

Rewriting `docs/devcontainer.md` and `docs/ssh-signing.md` with a script
rather than an editor took both from CRLF to LF wholesale. Every prose
and Markdown gate stayed green, and `editorconfig-checker` was the only
one that reported it.

`OPERATIONS.md` scoped that instruction to a *new* file, so it is
widened here to a scripted rewrite of an existing one. Hub-only,
`presence` fidelity, no re-vendor owed. Happy to split it out if it does
not belong in this batch.

## Verification

Run from the repository root:

```text
prose_lint.py gating list              exit 0
prose_lint.py backlog list             131/15 -> 41/6, the carried six exactly
test_prose_lint.py                     198 tests OK
test_repo_gate.py, test_pr_review.py   OK
audit.py --selftest, gh-write-guard    SELFTEST PASS
repo_gate.py, validate.py, jq          clean
markdownlint-cli2 (44 files)           0 issues
editorconfig-checker                   0 errors
```

Every prose file changed line for line, with no line-count change in any
of the 9, and the full word-diff was read for meaning drift.

`TODO.md` carries the updated `Checked` anchor and four new `Settled`
lines: the batch figures, the two rejected exemptions, the one-off
bullet list, and the CRLF gap.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
ptr727 added a commit that referenced this pull request Aug 8, 2026
…606)

Takes the last batch of the `The Prose Content Backlog` cluster from
[`TODO.md`](./TODO.md), following
[#600](#600) (snippets),
[#604](#604) (comments)
and [#605](#605)
(hub-only Markdown). Closes
[#519](#519) on the
prose, subject to the sweep below.

## The anchor was re-verified first

`develop` at `d791930`, where `python3 scripts/prose_lint.py --summary`
reports **41 violations across 6 files**, all `semicolon`. That is the
figure #605 predicted, and merging it made the figure develop's own
rather than a branch's.

| File | Findings | Fidelity |
| --- | ---: | --- |
| `GOVERNANCE.md` | 14 | `intent`, 5 sections `verbatim` |
| `WORKFLOW.md` | 14 | `intent`, whole |
| `CODESTYLE.md` | 6 | `intent`, whole |
| `.github/copilot-instructions.md` | 4 | `intent`, whole |
| `repo-config/README.md` | 2 | `intent`, whole |
| `HISTORY.md` | 1 | `presence` |

**The tree is now at 0 across 0**, from 557 across 45 when the backlog
opened.

## The exemption came first, and left the checker alone again

Two candidates, both rejected by the corpus. That is the third batch
running where the pass ends in "do not touch the checker".

**The `*Prevents: a; b.*` tail.** Flagged on 4 lines because the list
exemption requires the items to carry commas. The tree already spells
the same construct with a comma on **5** lines against **6**
semicolon-spelled, so it is a convention half-adopted rather than a
construct the rule fails to describe. Two of the six (`D1.6`, `D5.6`)
keep their semicolons untouched, because their items *do* carry commas —
the rule working, not an exception to it.

**A two-example parenthetical**, `(A does X; B does Y)`. The tree
already spells this with a comma where the items carry none,
`GOVERNANCE.md` "No-op republish guarantee" being the case in point.

## Only 14 of the 41 are actually byte-locked

The cluster entry said the carried half "rewrites byte-locked sections".
That is true of `GOVERNANCE.md` and of nothing else, which changes what
the sweep owes:

- **`verbatim`, hash-detected.** All 14 `GOVERNANCE.md` findings sit in
five `verbatim` sections: Branching Model, Release Model, Documentation
Style Conventions, PR Review Etiquette, Workflow YAML Conventions. Every
downstream copy is now byte-mismatched and `spec/audit.py` classifies it
**stale** (matches a past hub revision), which is the correct
disposition and reaches the fleet without anyone filing anything.
- **`intent`, detected by nothing.** `WORKFLOW.md`, `CODESTYLE.md`,
`.github/copilot-instructions.md` and `repo-config/README.md` are judged
by meaning, so a punctuation-only edit produces no hash and therefore no
audit finding at all. These are the half that would be silently missed,
which is why they are written down rather than left to the run.
- **`presence`.** `HISTORY.md` is each repo's own changelog. Its one fix
owes nothing downstream.

No rule changed meaning anywhere, so the re-vendor is a **hash refresh
rather than a propagation**. A repo still holding the old copy is
correct on the rule and wrong on the bytes.

The Fleet Sweeps entry "Re-vendor the changed `verbatim` content" grows
from **five files to seven** and carries both halves as `Detail` lines.
It does not trigger a dedicated sweep — Blog remains the pilot.

## What #519 got wrong, and why it is worth recording

The issue states that the governance files were clean and that this was
"not a carry problem". That was true of the checker of the day and false
of the tree:

| Content | Checker | Result |
| --- | --- | ---: |
| the six files at `69688ec` | that commit's own `prose_lint.py` | **0**
|
| the six files at `69688ec` | today's `prose_lint.py` | **38** |

`semicolon` was in `DEFAULT_RULES` the whole time. What changed is the
**unit** the list exemption is judged over: scoping it to a sentence
rather than a whole bullet accounts for **37 of the 38**, because a
colon anywhere ahead of the first semicolon had been exempting every
semicolon after it, however plainly one joined two clauses.

So the carry problem was real from the start and invisible — the
stale-exemption hazard running in the loose direction, on the
most-carried files in the fleet.

## One fix beyond the 41

`GOVERNANCE.md` "Orchestration vs. build" carried a splice the exemption
excused only because the **bold label** on that sentence happened to
contain a comma (`...globs it; do not switch a single-target repo...`).
Same line as a flagged finding, so it is fixed inline rather than
stepped around. That is 42 semicolons removed, not 41.

## Verification

Run from the repository root:

```text
prose_lint.py --summary            41/6 -> 0/0
scripts/ unittest (375 tests)      OK
spec/audit.py --selftest           SELFTEST PASS
spec/validate.py                   22 cataloged, 0 backlog
scripts/repo_gate.py               eol 0, sha-pin 0
markdownlint-cli2 (44 files)       0 issues
cspell (README.md, HISTORY.md)     0 issues
editorconfig-checker               0 on tracked files
```

Every edit is a **within-line** replacement, so all seven CRLF files
stayed CRLF — verified by an `\n` versus `\r\n` count per file, not by
`file`. The full word-diff was read for meaning drift; the change is 31
lines, 31 insertions and 31 deletions.

`editorconfig-checker` reports 221 errors, every one of them under
`.artifacts/` or `Tests/obj/` — untracked pre-repurpose build leftovers
in the working tree, not tracked files. Pre-existing and untouched here.

## `TODO.md`

- The `The Prose Content Backlog` cluster is deleted, per selection rule
9.
- [#519](#519) moves to
"Verified Complete, Awaiting Close" with both of its questions answered
and the checker-versus-tree finding as closing evidence.
- The Fleet Sweeps re-vendor entry grows to seven files and gains the
two `Detail` lines above.
- Four link reference definitions orphaned by the cluster deletion are
dropped.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
ptr727 added a commit that referenced this pull request Aug 8, 2026
Closes #607.

## What was wrong

A Copilot review body states how many of the pull request's changed
files it read, and nothing parsed that line. A round that read part of
the diff carries the correct `commit.oid`, raises no inline threads, and
reports "generated no comments", so it is the clean pass byte for byte
in everything the loop checks, and `status` printed `review_on_head=yes`
over it and exited `0`.

Measured over **332 Copilot review bodies** on this repository, five
rounds across three pull requests reported reading fewer files than were
changed, and all three merged. #592 is the sharpest: three changed
files, one never read, across *both* rounds, both reporting no comments.

## What changed, and why it is wider than the issue asked

Partial coverage is the third instance of a shape this script already
answers twice, and the generalization is the point. Every reader here
keys on a structural marker, so a marker that changes spelling is a
section the reader stops finding and reports as absent. All three
failures on record have that shape:

| Drift | Result |
| --- | --- |
| Suppressed heading reworded | `suppressed=0` over a body carrying
findings |
| Suppressed section moved inside another wrapper | `suppressed=0` again
|
| Coverage line never parsed | a partial round reported as a covered
head |

Each was found by the maintainer after it had landed, rather than by the
gate. So the digest now vets the reviewer's output as a whole and
**fails closed**:

- `coverage=full` / `PARTIAL` / `unstated`, with exit **42** on a
partial round.
- `shapes=ok` / `UNRECOGNIZED`, with exit **43** on any heading,
`<summary>`, metadata label, coverage wording or reviewer login the
script has no vetted spelling for. It outranks 42, because a reader that
does not understand the output cannot be believed about what it read of
the diff.

The `43` message states the remedy in two parts: **file an issue on the
repository hosting the reader**, quoting the body the shape came from,
and **the merge decision is the maintainer's**. An unrecognized shape
does not say the pull request is bad, only that nothing here can vouch
for the review of it.

## The inventory is measured, not imagined

With fenced blocks dropped and text reduced to ASCII, all 332 bodies
reduce to **7 headings, 6 `<summary>` texts and 3 metadata labels**, and
every body carries at least one. Counts normalize to `(N)` and the
verdict headings' colored circle is dropped before comparing, since both
change on every review without the section changing, and dropping the
emoji is also what keeps the source inside the charset rule.

Two exemptions, both required by the corpus:

- **A body stating no coverage** reads as `unstated`, never as pass or
failure. 28 of the 332 are an overview and a change list, that shape is
current and interleaves with the counted one, and one pull request
carries both across its two rounds. Failing on it would cry wolf on
about one review in twelve.
- **A refusal** is exempt, being a bare paragraph by design and already
classified. The exemption is the pattern rather than a carve-out, so a
refusal reworded stops being exempt and blocks, which is the refusal
check's own failure mode caught one rewording later.

The quietest reading is the reviewer **login**: a rename leaves every
filter here matching nothing, so a review that landed reads as
`rounds=0` and a wait polls out its timeout against it.

## Evidence

Both readers were swept over the full corpus before this was written,
and over it again after:

- Shape inventory: **0** items raised across all 332 bodies, and **0**
across every review and comment author on those 120 pull requests
(`copilot-pull-request-reviewer` and `ptr727`).
- Coverage: `full 299 / unstated 28 / partial 5`, the five being exactly
#476 (x2), #479 and #592 (x2).
- Live: #592 reports `coverage=PARTIAL shapes=ok` and exits `42`. #595,
#604 and #606 report `shapes=ok` and exit `0`.

166 tests pass, up from 132. The two fixtures the issue named as
unasserted filler are promoted to assertions, and a case reads the
vetted coverage spellings out of the runbook and hands them to the
script's own parser, so the pair fails in both directions on drift. The
old fixtures crafted review bodies with no heading, which no real body
has, so they were made realistic rather than the check loosened.

## Also carried

- `.github/copilot-instructions.md` - the verify step checked
`commit.oid` only, which is what this shows to be insufficient, plus a
new section stating that an unrecognized shape blocks and earns an
issue.
- `GOVERNANCE.md` - merge-gate precondition 4, and the "all four
preconditions" restatement swept to five.
- `scripts/README.md` - both readings.
- `TODO.md` - a `measure` cluster to survey whether GitHub publishes
anything structured about a Copilot review, since this whole design
rests on prose being the only surface. Introspection says it is:
`PullRequestReview` exposes `body`, `bodyText` and `bodyHTML` and no
field naming a finding or a file count, and the only Copilot-named
GraphQL types configure review-on-push in a ruleset.

## The accepted cost

The shape scan reads every round rather than the head's, so once Copilot
changes format, every open pull request blocks at once until the
inventory is updated. That is deliberate and the maintainer's call: it
is well balanced against a reviewer silently missing a raised finding.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
ptr727 added a commit that referenced this pull request Aug 8, 2026
… that blocks on what it cannot read (#609)

Promotes 14 commits from `develop`. Merge with a **merge commit** (`gh
pr merge --merge`), never a squash, and **without `--delete-branch`**,
since this pull request's head is `develop` itself.

Closes #607 through the closing keyword already carried in `530cf71`,
which is why it is not repeated here.

## The prose backlog, cleared end to end

`#600`, `#604`, `#605`, `#606` took the tree from **557 findings across
45 files to 0 across 0**, in four batches ordered by surface: snippets,
comments, hub-only Markdown, then the carried files. Each batch measured
the checker's own exemption against the live corpus *before* sweeping,
and twice the measured answer was **"do not change the checker"**, which
is a result of that pass rather than a skipped one.

`#594` added the floor that makes those numbers trustworthy: a
diff-scoped run now asserts what it actually scanned, since a check
whose scan matches nothing reports zero findings and reads exactly like
a pass.

One finding from that work is worth carrying up: an exemption that is
too **loose** produces silence rather than false positives. #519
recorded the governance files as clean; today's checker reports 38
findings against those same files as they stood at the commit that
measured them.

## A review loop that fails closed

`#599`, `#601`, `#602`, `#603` and `#608` are one arc on
`scripts/pr_review.py`, each removing a shape in which the loop reported
a clean pass over a review it had misread:

- **`#599`** removed the shape a reply kept failing in, by taking the
thread's *words* rather than an id, so there is no argument a hand-typed
`PRRT_...` fits in.
- **`#602`** made `claims` resolve what a description points at rather
than what it looks like.
- **`#603`** gave a disproved claim a home the next round reads.
- **`#608`** reads the file-coverage line, and then generalizes: every
reader keys on a structural marker, so a marker that changes spelling is
a section the reader stops finding and reports as absent. The digest now
vets headings, `<summary>` texts, metadata labels, coverage wordings and
the reviewer login against an inventory measured from **332 review
bodies**, and **blocks on anything outside it**, exit `43`, with the
remedy stated as filing an issue on the hub. Whether to merge regardless
is the maintainer's decision.

`GOVERNANCE.md` merge gate went from four preconditions to **five**
accordingly.

## Governance and tooling

- **`#593`** states which checkout an agent works in and what the hub
is, which is the host-wide routing the repositories that most need it
cannot carry.
- **`#596`** gates the pattern-detectable half of the
representative-data rule, honest that no pattern closes the name-shaped
case.
- **`#598`** declares where a repository states what CI cannot verify.
- **`#592`** regrouped `TODO.md` by what ships rather than by what it
touches, so a `###` heading is one pull request.
- **`#601`** ended a `gh push` argument list at a newline rather than
only at `&&`, fixing a write-guard over-block.

## Verification

Run on `develop` at `530cf71` immediately before opening this:
`test_pr_review.py` (174), `test_prose_lint.py`, `test_repo_gate.py`,
`spec/audit.py --selftest`, `gh-write-guard.py --selftest`,
`spec/validate.py`, `repo_gate.py`, the prose gate in both CI
invocations, markdownlint and editorconfig-checker. All clean.

## Not carried by this promotion

- **#519 is complete and still open.** `TODO.md` holds its closing
evidence under "Verified Complete, Awaiting Close". Closing it is the
maintainer's call, so no keyword for it appears here.
- **The re-vendor debt is now nine files.** `#606` queued seven, and
`#608` changed `GOVERNANCE.md` "PR Review Etiquette" (`verbatim`) and
`.github/copilot-instructions.md` (`intent`) on top. The `intent` half
produces no hash and therefore no audit finding, which is why the Fleet
Sweeps entry names those files by hand.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
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