Conversation
The cspell gate reads README.md and HISTORY.md only, deliberately: gating every markdown file would mean endlessly padding cspell.json with technical terms. That left a British spelling anywhere else in the tree with nothing checking it, which is how `labelled`, `behaviour` and `judgement` reached main. The new `spelling` rule closes the gap and joins the blocking CI prose run, since all three are fixed here and the tree is clean. The banned words generate from stems rather than list one by one, since an inflected spelling is as wrong as its base and a hand-listed family drifts as soon as one form is added without the others. Two words are deliberately absent: `analyses` is the US plural of `analysis` as much as a British verb form, and `cancelled` is a GitHub Actions job status rather than prose. Outside markdown the rule reads the comments rather than the source lines, reusing the extraction comment-wrap already does. Judging a source line as prose had this script report its own table of banned words. Also leads YAML['quote_after'] with the dash. Membership is a set test, so the order carries no behavior, but ':-,' reads as a regex range and cost a review round on #474 when it was read as missing the dash. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The dash rule reports without gating, and a carried file has no correct-as-you-next-edit trigger downstream, so the class only leaves the fleet by being swept at the hub. Takes every carried document to zero: GOVERNANCE.md, CODESTYLE.md, WORKFLOW.md, AUDIT.md, AGENTS.md, .github/copilot-instructions.md, and repo-config/README.md. Each instance is rewritten by hand as a comma, a colon, parentheses, or two sentences. No meaning changes, and every file stays line-for-line balanced so no line endings moved. Two headings now read with a colon, neither referenced by an anchor, a spec section list, or audit.py. LABEL_DASH exempted the bulleted definition list but not the ordered form of the same construct, so it now accepts either marker, with a case for both halves. test_pr_review asserted the runbook sentence whose dash this changed, so it moves with the prose it reads. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The merge gate counts a review body's collapsed low-confidence block as an outstanding finding, but the digest read reviewThreads alone, where that block never appears, so the one command written to answer "is this PR clean" reported clean while findings stood. Q_FULL now carries each review's body, and the digest reports suppressed=N for the blocks on the current head. Q_LIVE is untouched, so a liveness poll still fetches two scalars. Each block prints whole where a thread body truncates at 160, since a thread can be re-read at its id and a suppressed finding has no thread. Two defects were found and fixed inside the review loop. Copilot caught that the field counted blocks rather than findings, so a heading reading "Suppressed comments (2)" reported 1; the count now comes from the heading's (N), summed across the rounds on the head and floored at one. Running the tool on its own PR caught the second: the phrase was matched against the whole body, so a review whose overview prose discusses suppressed findings while carrying none reported one. The match is now anchored to the block's summary heading, and the markup-moved fallback reads only the text outside the details blocks and requires a heading with a count. Verified against live review bodies rather than fixtures alone: #473 reads one finding from its real block and #477 reads zero, agreeing with the runbook's independent REST query. The scripts README documents the behavior, and its own spaced hyphens are corrected in passing. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The semicolon sweep only ever cleared GOVERNANCE.md. The other six carried files still ran the class, and they vendor downstream on the same no-trigger argument the dash sweep ran on. Two parts, because sweeping the corpus first meant restructuring lists the exemption exists to protect. The rule keeps a semicolon separating items in a list that already carries commas, but the check read the comma positionally, so an enumeration whose commas fall in a later item had its openers flagged and its tail exempted, splitting one series in two. The comma now qualifies the list as a whole. A table row is judged one cell at a time, since a row is a record of fields and a comma in one column cannot excuse a semicolon in another, and a bullet's `**Label**:` is dropped before the line is read, because it opens the bullet rather than announcing a list, the same construct the label dash is already exempted for. That leaves 44 genuine clause joins, now recast as a comma or two sentences: CODESTYLE.md 24, .github/copilot-instructions.md 13, AUDIT.md 10, WORKFLOW.md 12, repo-config/README.md 1. The 5A guarantees and the 5B trace table keep their semicolons, which is the standard series punctuation the rule already allows. All six files now report zero, the bar GOVERNANCE.md already meets. The paren-internal asides those files still carry are the same construction GOVERNANCE.md kept through its own sweep, so they stay. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
Pull request overview
This PR forward-promotes a set of governance/documentation prose sweeps and supporting tooling updates into main, including a new US-English spelling enforcement outside cspell’s limited CI scope and improved PR-review digesting of Copilot “suppressed / low confidence” findings.
Changes:
- Add a
spellingrule toscripts/prose_lint.py, gate it in CI, and add unit coverage to keep the banned-word table and behavior correct. - Extend
scripts/pr_review.py(and tests) to surface Copilot review-body suppressed findings that are invisible toreviewThreadspolling. - Apply prose-only rewrites across carried governance docs (dash/semicolon class reductions, punctuation/clarity adjustments) plus a few related doc/test updates.
Reviewed changes
Copilot reviewed 16 out of 17 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| WORKFLOW.md | Prose-only punctuation/clarity tweaks; heading punctuation normalization. |
| spec/validate.py | US spelling fix in a comment (“labeled”). |
| scripts/test_prose_lint.py | Adds tests for ordered label dash exemption, semicolon exemption fixes, and comprehensive spelling-rule coverage. |
| scripts/test_pr_review.py | Adds review-body body support and tests for suppressed-findings reporting; updates runbook-string assertions. |
| scripts/README.md | Documents the new spelling rule and the suppressed-findings digest behavior. |
| scripts/prose_lint.py | Implements spelling rule, improves semicolon exemption logic, expands label-dash exemption, and adjusts YAML quote_after. |
| scripts/pr_review.py | Adds suppressed-findings detection/printing in digest; keeps a minimal liveness query for polling. |
| reports/vscode-server-dotnetcore/audit.md | US spelling fix in report prose (“behavior”). |
| repo-config/README.md | Prose-only punctuation/clarity tweaks. |
| README.md | Removes now-completed backlog item for the dash-class sweep. |
| host-setup/agent-safety/gh-write-guard.py | US spelling fix in a comment (“judgment”). |
| CODESTYLE.md | Prose-only punctuation/clarity tweaks; consistent US spelling guidance wording. |
| AUDIT.md | Prose-only punctuation/clarity tweaks; heading punctuation normalization. |
| AGENTS.md | Prose-only punctuation/clarity tweaks. |
| .github/workflows/validate-task.yml | Gates spelling alongside existing charset and dupword. |
| .github/copilot-instructions.md | Prose-only punctuation/clarity tweaks; aligns phrasing relied on by tests/runbook parsing. |
Uh oh!
There was an error while loading. Please reload this page.
This was referenced Aug 7, 2026
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
Follows the #609 promotion, which went partial at 61 of 62 files and stayed there through two re-requests. That prompted measuring whether the remedy the gate recommends has ever worked. It has not. ## The measurement Over the **332 Copilot review bodies** on the newest 120 pull requests, read beside `gh pr list --json number,changedFiles,additions,deletions`: | Question | Answer | | --- | --- | | Pull requests that went partial | **4** - #476, #479, #592, and the #609 promotion | | Rounds those four produced | **7** | | Rounds that recovered | **0** - every later round repeated the identical ratio | | Files skipped, each time | exactly **1** | ``` PR 592: round 1 = 2/3 round 2 = 2/3 PR 476: round 1 = 10/11 round 2 = 10/11 PR 479: round 1 = 16/17 PR 609: round 1 = 61/62 round 2 = 61/62 (after a re-request on a new head) ``` So the digest was telling a reader to spend a round on a retry that nothing has ever retried out of. ## Two hypotheses tested and rejected, so they are not re-derived **Size does not predict it.** The partials changed 502, 629 and 961 lines. Fully covered pull requests on this repository reach **33 files and 2,219 changed lines**, well past all three. **The denominator is not itself lossy.** I suspected the reviewer might drop a file before counting, which would make `N out of N` a lie. It does not: the stated denominator equals the API's own `changedFiles` on **103 of 104** pull requests, and the single exception is one whose branch shrank between rounds. The reviewer counts the file and does not read it. ## What changed The three places that stated the remedy now state what the record supports, swept by grepping the phrasing rather than by fixing the one the finding pointed at: - `scripts/pr_review.py` - the `status=COVERAGE_IS_PARTIAL` line, the digest's `COVERAGE IS PARTIAL` block, and the `status` exit-code docstring. - `.github/copilot-instructions.md` - the "Counts unequal" bucket. - `scripts/README.md` - the coverage paragraph, carrying the measurements. The new wording says a re-request has never cleared one, that splitting is real for a feature branch and unavailable for a promotion whose head is `develop`, and that the merge decision is the maintainer's, taken knowing one file has no review. **No exit code or verdict changes**, since the gate was reporting the state correctly. Only the advice attached to it was wrong. ## What is left open `TODO.md` gains an entry under the programmatic-reading cluster, with the measurements attached: **which** file is skipped, and why. The reviewer names no file list in these rounds, so it cannot be recovered from the API, and the pull request page may be the only place it appears. Whether it is worth escalating to GitHub needs the file first. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This was referenced Aug 8, 2026
ptr727 added a commit
that referenced
this pull request
Aug 8, 2026
) Closes#623. **Disposition** `Amends "Find out which file a partial round skips, and why re-requesting never clears it."` in the "A Programmatic Reading of a Copilot Review" cluster. #623 was filed after this repository's last `TODO.md` edit and appeared nowhere in the file, so selection step 1 was false until this change. ## What was wrong The `COVERAGE_IS_PARTIAL` caveat, `scripts/README.md` and the runbook all said the reviewer names no file list in these rounds, so which file went unread cannot be read from the API. Measured over 348 Copilot review bodies on the newest 120 pull requests here and 121 on Blog, that is false. 91 bodies carry a `| File | Description |` table, and every table row in the corpus belongs to one of those tables. ## What the measurement says the table is worth The issue proposed reading a table that names every changed file as evidence that the count is a reporting artifact. It cannot carry that, and the corpus is what says so. | round | states | names | reading | | --- | --- | --- | --- | | #476 | 10 of 11 | all 11 | table contradicts the count | | #592 | 2 of 3 | all 3 | table contradicts the count | | #479 | 16 of 17 | 16, omitting `GOVERNANCE.md` | table corroborates the count and names the file | | #609 | 61 of 62 | 50 of 62 | short by 12 where the count is short by 1 | | #574 | 33 of 33, full | 32, omitting `TODO.md` | a short table on a fully covered round | | #606 | 7 of 7 | names `GOVENANCE.md` | a path no diff carries | On Blog every table names exactly the changed set, on all seven partial rounds and on every full round alike. A reading identical under both outcomes discriminates neither, so a full table is reported as corroborating nothing rather than as a miscount. #574 kills the converse, since a short table sits on a round that read everything. ## What ships - `Q_FULL` selects the pull request's own `files`, windowed at 100 with `hasNextPage` carried, since a path outside a short window reads exactly like a path the reviewer left out and the record holds a pull request of 301 changed files. - `file_table` reads the table, quotations dropped for the reason the coverage line's are. - `head_table` takes it from any round covering the current head and never from a round before a push. Three of the four partials here carry their table on the pre-push round, describing a diff that push replaced, and comparing that against the current changed files would name a file unreviewed on a stale list. Thirteen commits here carry more than one round, and on one of them a round with a table sits beside a round without, so which of the two the verdict reads must not decide whether a table is found. - `table_against_diff` prints one sentence and decides nothing. It names an omitted file only where the table is short by exactly what the counts leave unread **and** names nothing outside the diff, that second guard existing because #606's typo would otherwise push the real `GOVERNANCE.md` into the omissions and report it as the file nobody reviewed. - Exit `42` is unchanged in every arm, and the caveat keeps its maintainer-decision framing, which is the issue's third suggestion. ## Verified against live data #479 the reviewer's own file table omits exactly the 1 file the counts leave unread, naming GOVERNANCE.md Blog #60 the reviewer's own file table names all 6 changed files, ... so it corroborates nothing #476/#592/#609 no round covering this head carries a file table ## Gates 228 `test_pr_review` cases, plus `test_prose_lint`, `test_repo_gate`, `spec/audit.py --selftest`, `spec/validate.py`, both `prose_lint.py` invocations, `markdownlint-cli2` and `editorconfig-checker`, all green locally. 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
One commit, `a706ddb` from #625, promoting the answer to #623 to `main`. ## What is promoted `scripts/pr_review.py` reads the Copilot review's own `| File | Description |` table and reports it beside a partial-coverage verdict, and the docs drop a claim that was false. - **The removed claim.** "The reviewer names no file list in these rounds, so which file went unread cannot be read from the API" was wrong. Measured over 348 review bodies on the newest 120 pull requests here and 121 on Blog, 91 carry a table and every table row in either corpus belongs to one. - **The table decides nothing, and the measurement is why.** It names the whole changed set on partial and fully covered rounds alike, including all seven partials on Blog, so it is identical under both outcomes. One round here states 33 of 33 and names 32, another states 61 of 62 and names 50, and a third names `GOVENANCE.md`, a path no diff carries. - **The one arm that locates a file.** #479 states 16 of 17 and names 16, omitting `GOVERNANCE.md`, and that is the only evidence on record that the unread file is real rather than an artifact of counting. `status` names an omitted file only in that shape, where the table is short by exactly what the counts leave unread and names nothing outside the diff. - **Head-scoped like the counts.** Three of the four partials here carry their table on the round before a push, describing the diff that push replaced, so those report as no table rather than as a stale list of unreviewed files. Exit `42` is unchanged in every arm, and the merge decision stays the maintainer's. ## State #623 is already closed by hand, since the keyword sat on a pull request into `develop`. Review on #625 was one round, `coverage=full shapes=ok threads=0 unresolved=0 suppressed=0 checks=6/6`, and the body was read rather than the digest trusted. **Merge as a merge commit, never a squash**, so `develop` and `main` stay level. Generated with [Claude Code](https://claude.com/claude-code)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for freeto join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Forward promotion of four commits.
maincarries no content beyond the merge-base (f7c77d8, #473), so this is a clean forward merge with no conflicts.What promotes
spellingrule generates its banned words from stems rather than listing them one by one, since an inflected spelling is as wrong as its base and a hand-listed family drifts.GOVERNANCE.md219,CODESTYLE.md53,WORKFLOW.md51,.github/copilot-instructions.md40,AUDIT.md29,repo-config/README.md20,AGENTS.md3. The remaining ~538 are hub-only (reports/,docs/,spec/*.md,STANDUP.md,catalog/), stay warn-only, and are corrected as each file is next edited, which works because nothing vendors them.reviewThreadsalone, where that block never appears, so the one command written to answer "is this PR clean" reported clean while findings stood.GOVERNANCE.mdalready met.Verification on the merged
developBoth prose classes report zero across all seven carried documents. The full 180-case script suite,
scripts/repo_gate.py,spec/validate.py, andspec/audit.py --selftestpass. The blocking prose run (charset, dupword, spelling) is clean tree-wide, markdownlint reports 0 issues over 39 files, and editorconfig-checker is clean. cspell's gated scope (README + HISTORY) is clean; the tree-wide backlog is unchanged.Release
A human merge never auto-publishes, so this fires no release, as designed. The latest release stays
2.0.108.Re-vendor debt
This promotion changes prose in all seven carried documents, so every downstream repo goes further out of date against canonical. That debt is pre-existing and still untracked - no issue covers it and filing one has not been authorized.
spec/audit.py --branch <ref>checks a repo's convergence before it promotes.🤖 Generated with Claude Code