Scope the semicolon exemption to the sentence its list lives in - #573
Merged
Conversation
A colon anywhere before a bullet's first semicolon marked the whole bullet a list, so every semicolon after it was exempt however plainly it joined two independent clauses, and the two did not have to be related at all. Measured over this repo, that covered 62 spans holding 120 semicolons across 9 files while the rule reported none of them, which is the silent-narrowing shape GOVERNANCE.md "Verification Discipline" names. Judge the exemption per sentence rather than per span, since that is where a list lives. The boundary reuses the run-on rule's guards, so an initial or an abbreviation ends nothing, and it accepts a terminator closing inside emphasis or a bracket, since reading a bare `. ` left `.**` and `.)` joining a bullet's every sentence into one span. The colon arm was measured before being kept. Dropping it flagged 14 further lines, and those were genuine colon-introduced lists whose items carry commas, which is the standard use the rule names, so the arm is scoped rather than removed. Both spellings of the bullet-opener colon are now dropped, since matching `**Label**:` alone left `**Label:**` announcing a list it never announced. Old and new verdicts were diffed over the whole tree: 20 semicolon findings become 64, nothing previously reported went silent, and every other rule is byte-identical. The 44 newly reported are the existing backlog, corrected as each file is next edited per GOVERNANCE.md, not swept here. Also correct an OPERATIONS.md claim that `DEFAULT_RULES` omits `comment-wrap` and `comment-case`, which it has not since they were added, and refresh the TODO.md backlog figure, stale at 534 against a measured 520 before this change. Fixes #570 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
Pull request overview
This PR fixes an accuracy hole in the Markdown semicolon prose gate by scoping the “list exemption” to the sentence that contains the potential list, rather than applying it across an entire bullet/list-item span. This restores the intended behavior where a colon-introduced list in one sentence does not silently exempt unrelated clause-joining semicolons later in the same bullet.
Changes:
- Update
scripts/prose_lint.pyto split Markdown spans into sentences (with Markdown-friendly sentence boundaries) and apply the list-exemption logic per sentence. - Expand the bullet label-colon stripping to treat both
**Label**:and**Label:**as the same opener, preventing false “list announcement” detection. - Add targeted regression tests and update operational/docs text to reflect the corrected behavior and current gate semantics.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| TODO.md | Refreshes prose backlog metrics and adds a reference link for issue #570. |
| scripts/test_prose_lint.py | Adds regression tests covering sentence-scoped exemptions and both label-colon spellings. |
| scripts/README.md | Updates documentation of the semicolon rule to match the new sentence-scoped behavior and label-colon handling. |
| scripts/prose_lint.py | Implements sentence-level splitting for semicolon exemptions and broadens LABEL_COLON matching. |
| OPERATIONS.md | Corrects the description of DEFAULT_RULES vs CI invocation behavior and clarifies sentence-split invocation gaps. |
This was referenced Aug 6, 2026
Closed
ptr727
added a commit
that referenced
this pull request
Aug 6, 2026
…574) Promotes the nine commits `develop` has carried since the previous promotion (#555) to `main`. Merge commit only, no squash, and `develop` is not deleted. ## What lands - **#560** Standardize the static-site-deploy repo type and its destination, including the deploy-verification guarantee and a retention rule that records which side owns the prune. - **#561** Ask for a blocked decision instead of reporting it, and scope the clickable-link rule to a surface that renders markdown. - **#564** Read a suppressed section nested inside the review-details wrapper, so `scripts/pr_review.py` stops reporting a clean round over findings that reach no thread. - **#566** Settle Markdown capitalization in `CODESTYLE.md`, and fix the two `TODO.md` prose defects the digest defect had hidden. - **#568** Check a `driftNote` on every run rather than only on an otherwise clean audit, and state what the audit does not evaluate. - **#569** Name who trips the production ref gate, and state that a harness refusal is a different thing from the maintainer's permission and is not lifted by it. - **#571** Re-audit `ptr727/Blog` against the hugo type and bump the conformance matrix. - **#573** Scope the semicolon exemption to the sentence its list lives in, with the 44 newly reported occurrences recorded on #519 rather than swept here. - **#572** Exempt a verbatim section from the coordination-reference scan. ## Issues closed Fixes #562. Closes #565. The closing keywords sit on this promotion rather than on the feature pull requests, because GitHub fires them only on a merge into the default branch. #456, #558, #563, #567, and #570 are already closed against their merged fixes. ## Verification `git merge-tree` reports no conflict between `origin/main` and `origin/develop`, so this promotion needs no throwaway resolution branch. Every constituent pull request merged green with its review loop closed.
ptr727
added a commit
that referenced
this pull request
Aug 8, 2026
Closes the last piece of [#519](#519), which is now closed with its evidence quoted on the issue. ## Why now The prose backlog reached zero at `20916ad`. `semicolon` and `dash` were warn-only because a whole-tree gate on them would have failed every run while several hundred findings sat in the tree. That reason has expired, and a warn-only tier over a clean tree is how the backlog grows back. Measured on this branch before writing the change, and again after: ```text python3 scripts/prose_lint.py . --check charset --check semicolon --check dash --check dupword \ --check spelling --check comment-wrap --check comment-case --check home-path 0 violation(s) across 0 file(s) ``` ## `home-path` gated nothing anywhere, which is the substantive fix `home-path` has been in `DEFAULT_RULES` since it was written, so a bare local run has always included it. It was named by neither CI step, so the rule that catches an absolute home path naming a real account ran on a developer's machine and gated nothing in CI. That is the pattern-detectable sliver of [`GOVERNANCE.md`](https://github.com/ptr727/ProjectTemplate/blob/develop/GOVERNANCE.md) "Representative Data in Agent-Authored Text", the section that exists because real paths carrying real names reached a public comment. [`OPERATIONS.md`](https://github.com/ptr727/ProjectTemplate/blob/develop/OPERATIONS.md) already recorded the hole, correctly and in detail. This closes it and drops the record, rather than leaving a runbook describing a gap that no longer exists. ## What stays warn-only, and why that is not an oversight `charset-unknown` alone. A finding there names a character no tier covers, and classifying one is a fleet-law edit rather than something the change that happened to type it can fix. Blocking on it would make an un-tiered character an unmergeable change instead of a question for the maintainer. The step keeps `continue-on-error: true` and is renamed from "Report prose backlog step" to "Report unclassified characters step", since there is no backlog left for it to report. `sentence-split` remains named by no invocation and stays that way here. It is deliberately outside `DEFAULT_RULES`, so promoting it is a separate decision rather than a consequence of this one, and `OPERATIONS.md` still records it as a gap. ## The composite action is unaffected [`.github/actions/prose-gate`](https://github.com/ptr727/ProjectTemplate/blob/develop/.github/actions/prose-gate/action.yml) runs `python3 "$SCRIPT" --diff "$BASE"` with no `--check` list, so it already gates the full default set, `home-path` included, over the lines a change touches. A downstream caller sees no behavior change from this pull request. Only the hub's own whole-tree steps move. ## Documentation kept level with the change - [`OPERATIONS.md`](https://github.com/ptr727/ProjectTemplate/blob/develop/OPERATIONS.md) carries the two CI invocations verbatim so a local run matches CI rather than exceeding it, so both lines are updated, the three-gap paragraph becomes two, and the sentence describing the second invocation as a backlog report is corrected. - [`scripts/README.md`](https://github.com/ptr727/ProjectTemplate/blob/develop/scripts/README.md) claimed five rules gate and the rest report. It now states the tree is zero and every default rule gates but one. It also names `home-path` for the first time, including the limit that it closes a sliver of its section and nothing more, because the exposure that section exists for was name-shaped and no pattern finds a name. - `TODO.md` loses the #519 entry and its link definition. ## Three corrections the closing comment carries rather than this diff The `TODO.md` entry being removed held three wrong figures, each re-measured before the issue was closed: | Claim in the entry | Measured | | --- | --- | | Fixed by `f7a6a13` (snippets) | Not a valid object in this repository. The snippets batch is `b002fac` (#600). | | 557 across 45 to zero, 184 in snippets | 553 across 44 to zero, batches 181, 241, 90 and 41, with today's checker run at every point | | #573 accounts for 37 of the 38 carried findings | 38 of 38. Pre-#573 checker reports 0 and post-#573 reports 38 over the identical bytes at `69688ec`. | ## Verification | Check | Result | | --- | --- | | New gating invocation, whole tree | 0 violations across 0 files | | `charset-unknown`, whole tree | 0 violations across 0 files | | `python3 scripts/test_prose_lint.py` | 198 tests, OK | | `python3 scripts/repo_gate.py` | `eol` 0, `sha-pin` 0 | | `actionlint` | exit 0 | | `editorconfig-checker` | exit 0 | | `python3 spec/validate.py` | 22 cataloged, 0 backlog repos classify cleanly | The four edited files keep their declared line endings, CRLF for the three Markdown files and LF for the workflow, which `editorconfig-checker` confirms. --------- 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.
Fixes #570.
The defect
scripts/prose_lint.pyjudged the semicolon exemption over the whole span, which for a Markdown bullet is the entire list item:A colon anywhere before the first semicolon therefore marked the whole bullet a list, and every semicolon later in it was exempt however plainly it joined two independent clauses. The colon and the semicolon did not have to be near each other, or related at all.
Measured over the 32 Markdown files in the gate's scope, the exemption was covering 62 spans holding 120 semicolons across 9 files while the rule reported 0 of them, reproducing the issue's figures exactly. That is the shape
GOVERNANCE.md"Verification Discipline" names: a pattern that silently matches less still exits zero.The fix
The sentence is the unit, because that is where a list lives.
sentences()splits a span at its sentence boundaries and each one is judged on its own, so a genuine enumeration keeps its separators while an independent clause later in the same bullet is still examined.The boundary reuses the run-on rule's guards, so an initial, an abbreviation, and a dotted identifier end nothing. It also accepts a terminator that closes inside emphasis or a bracket: reading a bare
.left.**and.)joining a bullet's every sentence back into one span, which would have left the defect in place for exactly the bullets it appears in.The colon arm was measured before being kept. The issue asked whether a colon should confer the exemption at all. Dropping it reported 14 further lines, and reading them, they are genuine colon-introduced lists whose items carry commas, which is the standard use
GOVERNANCE.mdnames, for exampleMatch the heading style: title case with short bind words (a, an, the, of); hyphenated compounds capitalize both parts.So the arm is scoped rather than removed.Both spellings of the bullet-opener colon are now dropped.
LABEL_COLONmatched**Label**:only, so**Label:**still announced a list it never announced. One line tree-wide was affected, and it is a splice.Verdict diff, old against new on the same tree
semicolonfindings, whole treeThe 44 newly reported sit in 33 lines across 7 files. Per
GOVERNANCE.md, existing prose is corrected as each file is next edited rather than swept, and the issue itself asks that the exemption be fixed and proven before anything acts on the work list, so no prose is changed here. The measured list goes to #519.Tests
Six cases added to
TestSemicolon2, each one proving a decision rather than restating the code:.**and.)split- **D3:**and- **D3**:are one constructe.g.would cut a list in halfAll 253 script tests pass.
markdownlint,editorconfig-checker,repo_gate.py,spec/validate.pyand the diff-scoped prose gate are clean, andruffreports nothing new against the pre-change baseline.Two corrections found while auditing the surfaces that describe this gate
OPERATIONS.mdclaimedDEFAULT_RULESomitscomment-wrapandcomment-case, which it has not since they were added. The real difference is the exit code, not the coverage.TODO.mdbacklog figure read 534 against a measured 520 before this change, so it is refreshed to 559 with a note that a gate fix moves it as readily as a prose fix does.Fleet effect
.github/actions/prose-gatereadsprose_lint.pyfrom hubdevelopfor every non-mainrun, so once this merges, fleet PRs gate on the corrected rule against their changed lines immediately.🤖 Generated with Claude Code