Sweep the spaced-hyphen prose class out of the carried docs - #476
Merged
Conversation
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. This takes every carried document to zero: the four rule books named in the README backlog plus AGENTS.md, the Copilot runbook, and repo-config/README.md. Each instance is rewritten by hand as a comma, a colon, parentheses, or two sentences, per the rule's own remedy. No meaning changes, and every file stays line-for-line balanced so no line endings moved. Two headings lost a dash and now read with a colon, neither of them referenced by an anchor, a spec section list, or audit.py. The rule itself exempted `- **Label** - text` but not the ordered form of the same definition list, so `1. **Label** - text` was flagged as prose it would restructure. LABEL_DASH now accepts either marker, which covers 18 lines across four files, with a case for both halves. test_pr_review asserted the runbook's reviewer-login sentence verbatim, including its dash, so it moves with the prose it reads. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
Pull request overview
This PR removes the spaced-hyphen “dash” prose pattern from the repository’s carried governance/documentation set, ensuring the fleet-wide canonical docs no longer perpetuate that backlog class, and updates the prose-lint exemption logic to avoid restructuring definition-list formatting during the sweep.
Changes:
- Rewrites carried documentation sentences/headings to eliminate spaced-hyphen clause breaks while preserving meaning.
- Extends
prose_lint.py’s label-separator exemption to cover ordered definition-list markers (e.g.,1. **Label** - ...) and adds a targeted test. - Updates the PR review runbook assertion test and removes the now-completed README backlog item.
Reviewed changes
Copilot reviewed 10 out of 11 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| WORKFLOW.md | Prose rewrites to remove spaced-hyphen clause breaks; one heading updated to use a colon. |
| scripts/test_prose_lint.py | Adds coverage for ordered-list label-separator exemption behavior. |
| scripts/test_pr_review.py | Updates a runbook string assertion to match the revised reviewer-login wording. |
| scripts/prose_lint.py | Expands LABEL_DASH to treat ordered definition-list markers as exempt separators. |
| repo-config/README.md | Prose rewrites to remove spaced-hyphen usage while keeping semantics intact. |
| README.md | Removes the completed backlog item for the carried-docs dash sweep. |
| GOVERNANCE.md | Prose rewrites across multiple sections to remove spaced-hyphen usage while preserving contracts/rules. |
| CODESTYLE.md | Prose rewrites to remove spaced-hyphen usage while preserving guidance. |
| AUDIT.md | Prose rewrites and one heading updated to use a colon instead of a spaced hyphen. |
| AGENTS.md | Prose rewrite to remove spaced-hyphen usage while keeping the same guidance. |
| .github/copilot-instructions.md | Runbook prose rewrites to remove spaced-hyphen usage; updates wording relied on by tests. |
The sweep turned two clause-break dashes into connectives that repeat one already in the sentence: `TreatWarningsAsErrors` read "so ... so" and the blanket-delete rule read "which ... which". Both are the same rewrite habit, so the whole diff was re-scanned for a repeated conjunction inside one sentence and these are the two it found. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This was referenced Jul 31, 2026
ptr727
added a commit
that referenced
this pull request
Jul 31, 2026
…479) Forward promotion of four commits. `main` carries no content beyond the merge-base (`f7c77d8`, #473), so this is a clean forward merge with no conflicts. ## What promotes - **#475 - Gate US English spelling where cspell does not reach.** The cspell gate reads README and HISTORY only, so a British spelling anywhere else in the tree had nothing checking it. The `spelling` rule 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. - **#476 - Sweep the spaced-hyphen prose class out of the carried docs.** All seven carried documents to zero: `GOVERNANCE.md` 219, `CODESTYLE.md` 53, `WORKFLOW.md` 51, `.github/copilot-instructions.md` 40, `AUDIT.md` 29, `repo-config/README.md` 20, `AGENTS.md` 3. 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. - **#477 - Report the suppressed findings the thread poll cannot see.** 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. - **#478 - Sweep the semicolon prose class out of the carried docs.** The exemption is fixed first (list-wide comma, per-table-cell scoping, bullet-label colon), then 44 genuine clause joins recast across five files. All six remaining carried files reach zero, the bar `GOVERNANCE.md` already met. ## Verification on the merged `develop` Both prose classes report zero across all seven carried documents. The full 180-case script suite, `scripts/repo_gate.py`, `spec/validate.py`, and `spec/audit.py --selftest` pass. 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](https://claude.com/claude-code)
This was referenced Jul 31, 2026
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>
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 free
to 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.
Sweeps the
dashprose class (a spaced hyphen joining or interrupting a sentence) out of every carried document, and closes the README backlog item that asked for it.Why a sweep
dashreports in the warn-only prose step rather than gating, and the standing policy is to correct a backlog class as each file is next edited. That policy has no trigger for carried content: a downstream repo receives these files whole, so an instance only leaves the fleet when the hub removes it. Same argument the semicolon class was swept under.Scope
Every carried document, taken to zero:
GOVERNANCE.mdCODESTYLE.mdWORKFLOW.md.github/copilot-instructions.mdAUDIT.mdrepo-config/README.mdAGENTS.mdThe README backlog named the first four (~350). The other three carry by the same rule, so leaving them half-swept would file the same item again later.
Hub-only surfaces are deliberately out of scope and still report in the backlog step:
reports/*(audit snapshots, ~350),docs/,spec/*.md,STANDUP.md,README.md. Those are corrected as each is next edited, which works because nothing vendors them.How each instance was rewritten
By hand, into whichever of the rule's own remedies fit: a comma, a colon, parentheses for a paired aside, or two sentences. No rule, guarantee, or contract changes meaning. Every documentation file is line-for-line balanced (
254 insertions, 248 deletionsacross 11 files, with the extra lines all inscripts/), so no line endings moved and no paragraph reflowed.Two headings carried a dash and now read with a colon:
AUDIT.md## 10. Converge: Apply the FixesWORKFLOW.md## 4. Behavioral Contract: Expected OutcomesNeither is referenced by an anchor link, a spec section list, or
spec/audit.py's heading checks (grepped;STANDUP.mdcites AUDIT section 10 by number).One rule fix rode along
LABEL_DASHexempted- **Label** - explanationas a definition separator, but not1. **Label** - explanation, which is the same construct with an ordered marker. Sweeping those as prose would have restructured a definition list, which is exactly what the exemption exists to prevent, so the pattern now accepts either marker. It newly exempts 18 lines acrossWORKFLOW.md,spec/readme-structure.md,docs/host-setup.md, and one report. A case covers both halves: the ordered separator is exempt, a later dash on the same line still counts.test_pr_review.pyasserts the runbook's reviewer-login sentence verbatim to keeppr_review.REVIEWERand the prose in sync. That sentence lost its dash, so the assertion moves with it (the test still fails if the two drift).Verification
prose_lint.py --check dashis zero on all seven carried files.charset,dupword,spelling) clean tree-wide.test_prose_lint(128),test_repo_gate(23),test_pr_review(18),spec/audit.py --selftest,spec/validate.py,scripts/repo_gate.pyall pass.Follow-on
Every carried file changed here, so the fleet re-vendor debt grows by this commit. It is one prose-only wave on top of the 12 sections promotion #474 already left stale, not a new class of divergence.
🤖 Generated with Claude Code