Skip to content

Sweep the spaced-hyphen prose class out of the carried docs - #476

Merged
ptr727 merged 2 commits into
developfrom
sweep-dash-prose
Jul 31, 2026
Merged

Sweep the spaced-hyphen prose class out of the carried docs#476
ptr727 merged 2 commits into
developfrom
sweep-dash-prose

Conversation

@ptr727

@ptr727 ptr727 commented Jul 31, 2026

Copy link
Copy Markdown
Owner

Sweeps the dash prose 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

dash reports 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:

File Instances
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 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 deletions across 11 files, with the extra lines all in scripts/), 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 Fixes
  • WORKFLOW.md ## 4. Behavioral Contract: Expected Outcomes

Neither is referenced by an anchor link, a spec section list, or spec/audit.py's heading checks (grepped; STANDUP.md cites AUDIT section 10 by number).

One rule fix rode along

LABEL_DASH exempted - **Label** - explanation as a definition separator, but not 1. **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 across WORKFLOW.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.py asserts the runbook's reviewer-login sentence verbatim to keep pr_review.REVIEWER and 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 dash is zero on all seven carried files.
  • Blocking prose gate (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.py all pass.
  • markdownlint-cli2 (39 files), cspell, editorconfig-checker, actionlint clean via the documented Docker one-liners.

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

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>
Copilot AI review requested due to automatic review settings July 31, 2026 17:00

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 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.

Comment thread CODESTYLE.md Outdated
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>
Copilot AI review requested due to automatic review settings July 31, 2026 17:12

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 10 out of 11 changed files in this pull request and generated no new comments.

@ptr727
ptr727 merged commit 2378e1e into develop Jul 31, 2026
7 checks passed
@ptr727
ptr727 deleted the sweep-dash-prose branch July 31, 2026 17:16
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)
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>
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>
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