Tier the charset rule, invert the prose rules, and gate comments in every language - #459
Conversation
…very language The flat non-ASCII ban could not express context. An audit report classified U+2264 and U+2265 as the scientific carve-out while the rule named both must-replace, which is not a wrong reading so much as a rule with no vocabulary for "the range it describes next to a figure, agent typography between two words". Three tiers give it that vocabulary. Tier 1 carries no meaning its ASCII form loses and always flags. Tier 2 is an operator, kept beside a number or another operator and replaced between words. Tier 3 is a unit symbol whose ASCII form would be a lie and never flags. A character in no tier is reported rather than passed, because a gate that allows whatever it does not recognize stops gating as the character set grows. The charset rule is clean tree-wide, so it gates. Two prose rules invert from detecting a subset to banning the construction. The pronoun-keyed splice pattern found 170 of 493 and missed every imperative one, so any prose semicolon now flags and the exception is the list the rule already names. The em-dash replacement becomes "restructure the sentence" rather than a spaced hyphen, and that hyphen is banned in its own right, since allowing the ASCII spelling would keep the shape and change only the character. Both are markdown-only until a comment can be told from code, and both are warn-only. Comments get a gate that reaches every syntax the fleet's types carry, not only the hash ones: `//` and `/* */`, `<!-- -->`, `<# #>`, `;`, and `#`. JSON is read as JSONC because that is what ships, and the VS Code task, devcontainer, and workspace files carrying comments under a plain `.json` name were invisible before. A documentation comment is left to CODESTYLE, which permits the paragraphs this rule forbids. Python uses `tokenize` so a trailing comment is seen exactly, and every other language masks quoted spans so a marker inside a string is not read as a comment. Two comment rules come out of it. One sentence per comment line, never wrapped and never two on a line, and a sentence that starts on a line starts with a capital. Where the first word is a tool whose own casing is lowercase, the fix is to restructure rather than to capitalize the name against the tooling-casing rule. The Verification Discipline mechanisms behind all of this land too: a gate has to be watched failing, driving the real table rather than a restatement of it, and a heredoc consumes a backslash escape and leaves an invisible control character behind. Fixed in passing: 37 malformed comments across eight JSON-family files, the task snippets among them, so the shape stops propagating to every repo that copies them. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR updates the repo’s governance-driven prose linting to support tiered charset rules, invert/reshape prose punctuation checks, and expand comment-format checks across the fleet’s declared language/file types, with CI updated to gate only the now-clean checks and surface the rest as warn-only backlog.
Changes:
- Replaces the prior flat ASCII/typography rule with a 3-tier charset policy (
charset,charset-unknown) and corresponding self-tests and governance coupling. - Makes
semicolonanddashchecks “construction-banning” (with named exceptions) and scopes them to markdown-only until code-comments can be cleanly separated. - Adds cross-language comment extraction and comment style checks (
comment-wrap,comment-case), plus CI reporting for backlog rules.
Reviewed changes
Copilot reviewed 14 out of 14 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| scripts/test_prose_lint.py | Expands self-tests to cover charset tiers, punctuation rules, and multi-language comment extraction behavior. |
| scripts/README.md | Updates documentation of prose_lint.py behavior, including tiering and new comment rules. |
| scripts/prose_lint.py | Implements tiered charset scanning, markdown-only punctuation rules, and multi-syntax comment extraction with wrap/case checks. |
| host-setup/agent-safety/.markdownlint-cli2.jsonc | Reflows/clarifies markdownlint configuration comments (no functional change). |
| GOVENANCE.md | Updates governance text for comment capitalization, tiered charset policy, and punctuation rules. |
| docs/token-cost.md | Updates analysis text to reflect which rules gate vs. warn. |
| catalog/snippets/vscode/docker.jsonc | Reflows comments to match comment style rules. |
| catalog/snippets/vscode/base.jsonc | Reflows comments to match comment style rules. |
| catalog/snippets/devcontainer/python/devcontainer.json | Reflows comments to match comment style rules. |
| catalog/snippets/devcontainer/dotnet/devcontainer.json | Reflows comments to match comment style rules. |
| catalog/snippets/configs/vscode-tasks.json | Reflows comments to match comment style rules. |
| catalog/snippets/configs/vscode-tasks-python.json | Reflows comments to match comment style rules. |
| .markdownlint-cli2.jsonc | Reflows/clarifies markdownlint configuration comments (no functional change). |
| .github/workflows/validate-task.yml | Updates CI to gate charset+dupword and add warn-only reporting for backlog rules. |
… than memory The comment rule took four corrections to cover what it always governed. It started at hash comments only, then Python only, then missed JSON because the specification says JSON carries no comments, then missed capitalization. Each time the cause was the same: the check was scoped to the file being edited rather than to what the project declares it ships. The durable form is a rule beside the other verification mechanisms, since host memory is the wrong home for something every agent on every machine needs. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…e fifth Every finding was reproduced before it was fixed, and each carries a case. CSS was mapped to the C-like syntax, so `//` inside a URL read as a comment marker and `url(http://x/y. Z)` reported a run-on. CSS has block comments only. The numeric-context test skipped literal spaces, so a tab between an operator and its figure made a legitimate range read as prose. Any whitespace separates them. The list exception let a comma anywhere earlier on the line excuse a semicolon, so "It runs on push, always; it gates the merge" went unreported. A list announces itself with an introducing colon or with more than one separator, and only then does a comma excuse it. The semicolon count rises from 323 to 388, which is the under-reporting this removes. Extending the capitalization rule to trailing comments is declined, with the rule text tightened instead. Of the 105 trailing lowercase comments in the tree, 104 are the `# vX.Y.Z` version pins the action-pinning rule requires on every SHA-pinned action, and exactly one ends in a period. Capitalizing a version pin would satisfy one rule by breaking another, so the rule now says it governs a line that opens prose, and a bare version pin is classified as machinery. Two doc corrections: the warn-first sentence now names several rules and takes plural pronouns, and the script README described the semicolon rule as it was before the inversion. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 14 out of 14 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (4)
docs/token-cost.md:92
- This bullet refers to multiple rules, but the follow-up sentence uses the singular pronouns "its" and "it", which reads ambiguously and is grammatically incorrect in context.
- **The semicolon and dash rules are warn-first, and the charset and duplicate-word rules gate, being clean tree-wide.** Compare their hits against the next batch of review findings before enforcing them, and do not claim the mechanical share until that comparison exists.
scripts/prose_lint.py:452
- Comment scanning runs before the markdown code-fence logic, so
comment-wrap/comment-casewill treat<!-- -->sequences inside fenced code blocks as real markdown comments. That can produce false positives when a fenced snippet contains HTML/XML (or literal<!-- ... -->) that is not a prose comment.
raw = path.read_bytes().decode('utf-8')
except (UnicodeDecodeError, OSError):
return out
lines = raw.split('\n')
scripts/prose_lint.py:477
- The semicolon exception currently skips any semicolon if there is any comma earlier on the line. That can miss real semicolon splices in sentences that happen to contain a comma before the splice (e.g. "When X, do Y; it gates the merge"). If the intent is to only exempt list-style semicolons, the heuristic should be constrained to list contexts (for example, after a colon that introduces the list).
# Both prose rules are markdown-only until a comment can be told from code.
# A shell script carries 78 statement separators that are not prose at all.
if path.suffix == '.md':
if 'semicolon' in rules:
listish = prose.count(';') > 1 or ':' in prose.split(';')[0]
scripts/prose_lint.py:404
extracted_commentsscans for block-comment openers before it considers line comments. In C-like syntaxes this can mis-parse a/* ... */sequence that appears inside a// ...line comment as the start of a real block comment, causing subsequent lines to be treated as "inside a block comment" until a closing token is found.
out.append((n, body, True))
closing = '' if end >= 0 else closing
continue
masked = strip_strings(line, spec['quotes'])
cut = len(line)
leading = True
for opener, closer in spec['block']:
at = masked.find(opener)
if 0 <= at < cut:
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 14 out of 14 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (4)
scripts/README.md:48
- This list of warn-only checks is incomplete: the workflow's warn-only step also runs
comment-wrapandcomment-case, so the README should include them to match CI behavior.
`charset` and `dupword` are clean tree-wide and gate CI. `charset-unknown`, `semicolon`, and `dash` run as a warn-only CI step so their backlog is visible without blocking, and are corrected as each file is next edited.
scripts/prose_lint.py:455
comment_wrap_findings()runs before fenced code blocks are detected, socomment-wrap/comment-casefindings can be reported from inside ```/~~~ fenced regions even though the rest ofcheck_file()skips fenced blocks. This makes markdown code examples and other fenced text eligible for comment findings while other rules intentionally ignore them.
lines = raw.split('\n')
if {'comment-wrap', 'comment-case'} & rules:
out.extend(f for f in comment_wrap_findings(path, raw, lines) if f[1] in rules)
in_fence = False
scripts/README.md:42
- This sentence says CSS uses
//line comments, but the implementation explicitly treats CSS as block-comment-only (seescripts/prose_lint.pyCSS: Syntax = {'line': (), ...}) and the tests assert that//in CSS is a URL scheme separator, not a comment marker.
The `comment-wrap` rule covers comments in every syntax the fleet's project types carry, not only the hash ones: `//` and `/* */` for C#, C, C++, JSONC and CSS, `<!-- -->` for XML, csproj and markdown, `<# #>` for PowerShell, `;` for INI, and `#` for Python, shell, YAML and TOML.
scripts/README.md:40
- The earlier paragraph about whole-tree runs still refers to the old
asciirule and CI gatingascii+dupword. With this PR, CI gatescharset+dupwordand reports the other checks warn-only, so that paragraph should be updated to avoid misleading local runs.
This issue also appears on line 48 of the same file.
The `semicolon` and `dash` rules ban a construction rather than a detectable subset of it, so each flags by default and the exceptions are the ones the rule names: a semicolon inside a list that already carries commas, and for the dash a compound word, a leading list marker, a range, and the `- **Label** - explanation` separator that opens a governed bullet.
**Both are markdown-only for now.** A shell script carries 78 statement separators that are not prose at all, so telling a comment from code is a precondition for reaching source files. Until then a semicolon or dash in a code comment is missed, which reading the diff by eye still catches.
…of the loop The review body carries a collapsed block of findings that appear in no review thread, so polling threads reports a clean pass while they stand. Four were waiting there, and all four held. The comment rules ran before the fence loop, so an HTML comment inside a fenced markdown block was reported while every other rule skipped fences. A fenced example is quoted code, and its comments belong to whatever is being shown. Reproduced, fixed, and pinned by a case. Three were stale claims in the script README: it still said CSS uses line comments, which the previous round disproved, it named the pre-tier rule in the whole-tree paragraph, and its warn-only list omitted the two comment rules the CI step actually runs. The loop itself is the durable fix. The runbook now requires reading the body of every review and investigating each suppressed finding on the same footing as an inline one, answering it in the PR conversation because there is no thread to resolve. The merge gate names them too, since closing out findings meant threads and issue-level comments and silently excluded these. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Worked the low-confidence findings from the review body, which I had not opened. All four held. 1. Comment rules ignored fenced blocks ( Prose.
```html
<!-- One thing. Another thing. -->-> flagged comment-wrap |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 15 out of 15 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (1)
scripts/prose_lint.py:461
comment-wrapcontinuation detection only tracksprev_bodyfor comments that start the line. That misses wrapped sentences that begin in a trailing inline comment and continue on the next (leading) comment line (still a cross-line wrapped sentence per the rule), becauseprev_bodyis cleared whenleadingis false.
# A continuation is the very next line: two comments with code between them are separate.
adjacent = n == prev_no + 1
continuation = (adjacent and leading and prev_body
and not SENT_END.search(prev_body) and body[:1].islower())
if continuation:
out.append((prev_no, 'comment-wrap',
'comment sentence wraps into the next line -> one sentence per line'))
# A lowercase opening that is not a continuation is a sentence that failed to start.
elif leading and body[:1].islower():
out.append((n, 'comment-case',
'comment sentence opens in lowercase -> capitalize, or restructure so it '
'does not open on a lowercase name'))
prev_body = body if leading else ''
prev_no = n
The predecessor was cleared whenever a comment did not start its line, so a sentence beginning in a trailing comment and continuing on the next full-line comment was not read as wrapped. It reported a capitalization error instead, whose advice was to capitalize the continuation rather than to un-wrap the sentence. Reporting the wrong rule is worse than reporting nothing, because the fix it asks for is wrong. A trailing comment is now remembered. The continuation still has to be a full-line comment, since a trailing one annotates its own line rather than continuing the line above, so two trailing annotations on consecutive lines stay separate. Found in the low-confidence block of the review, which is now part of the loop. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
The one low-confidence finding on the previous head held, and it was worse than a miss.
x = 1 # a sentence that keeps
# going onto the next line.It reported Fixed in
Reverting the fix fails the new case, so the behavior is pinned rather than incidental. 58 self-test cases. Whole gate green. That is two rounds in a row where the only real finding was in the suppressed block and the inline threads were empty, which is why the runbook and merge gate now name it. |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 15 out of 15 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (2)
scripts/prose_lint.py:279
comment-wrapis meant to catch any two sentences on one comment line, butRUN_ONonly triggers when the second sentence starts with an uppercase letter and also suppresses matches when the sentence-ending punctuation is preceded by any uppercase letter. This misses real run-ons likeOne thing. another thing.andCI. Another thing.
# Two sentences on one line, guarded against an abbreviation or a dotted identifier.
RUN_ON = re.compile(r'(?<![A-Z])(?<!\be\.g)(?<!\bi\.e)(?<!\bvs)(?<!\betc)[.!?]\s+(?=[A-Z])')
.github/workflows/validate-task.yml:66
- The workflow comment says only the semicolon rule is warn-only, but this job also runs other warn-only prose rules (
charset-unknown,dash,comment-wrap,comment-case) in the backlog step right below. Updating the comment avoids implying that only semicolon is non-gating.
# The charset and duplicate-word rules are clean tree-wide, so they gate.
# The semicolon rule stays warn-only, since going green needs a sweep the rule forbids.
… opens in lowercase The initial guard suppressed a match whenever any capital preceded the sentence-ending period, which was meant to keep `J. Smith` as one name. It also silenced every sentence ending in an acronym, and this codebase ends them in CI, PR, SHA, and JSON constantly. The guard now anchors on a word boundary, so an initial is still one name and an acronym ending is two sentences. The second sentence also had to open with a capital to count, so a lowercase opening escaped both rules: the run-on missed it for being lowercase, and the capitalization rule missed it for not starting a line. Either case now counts. The workflow comment named only the semicolon rule as warn-only while the step below reports four more. Found in the low-confidence block, which is two rounds running as the only place a real finding appeared. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Both low-confidence findings on 1. A sentence ending in an acronym was invisible. The initial guard was # The check runs in CI. Another thing happens. -> MISSED
# Pinned by SHA. Dependabot still bumps it. -> MISSEDThe guard now anchors on a word boundary, 2. A lowercase second sentence escaped both rules. The run-on required 3. The workflow comment named only the semicolon rule as warn-only while the step below reports four more. Every guard re-verified after widening:
Reverting the guard fails 3 cases. 61 self-test cases. Whole gate green. That is three rounds running where the inline threads were empty and the suppressed block held the only real findings, five of five across them. Worth saying plainly: had I merged on the first clean-looking round, the gate would have shipped silently under-reporting on exactly the sentence shapes this repo writes most. |
Copilot's finding on promotion PR #460, fixed at the source. That PR's head is `develop`, so it picks this up when this squashes in. ## The contradiction Two adjacent bullets in "Documentation Style Conventions" disagreed on the emoji case: > **Unicode the developer deliberately typed** stays regardless of tier [...] Never strip the developer's own characters. > **An unrecognized non-ASCII character is reported, not allowed.** An emoji is both developer-typed and un-tiered, so the two bullets pointed opposite ways. The implementation already had a coherent answer: ``` un-tiered emoji: [(1, 'charset-unknown', 'SNOWMAN (U+2603) is in no tier - classify it in GOVERNANCE.md')] ``` It is preserved in place and never rewritten, and it is still reported until classified. The doc simply never said the second half, so the first bullet read as an exemption from the gate rather than from rewriting. ## The fix The carve-out is about what an agent may rewrite, not about what the gate reports. Saying that leaves both bullets true and needs no change to either rule. `scripts/README.md` restated the same claim in one sentence, so it takes the same clause. The spaced hyphen in the edited bullet is recast at the same time, under the rule three bullets below it that existing prose is corrected as each file is next edited rather than swept. ## Why it blocks a promotion This is fleet law on its way to `main`, and `main` is what the audit reads as ground truth. Shipping a self-contradicting rule is what leaves downstream repositories arguing the point, and the contradiction already produced one real misreading: the audit report that classified U+2264 and U+2265 as the scientific carve-out, which is what motivated tiering the rule in #459 to begin with. No behavior change, so no test accompanies it. 62 self-tests and 19 repo_gate tests pass, `repo_gate` is clean, and `charset` plus `dupword` exit 0. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Promotes `d4c4085` (#459), `adaa068` (#461), `e52c68a` (#463), `7bf8a12` (#464), `269629a` (#465), and `036e468` (#467). Conflict-free, six commits ahead. ## What lands The charset rule gets three tiers instead of a flat non-ASCII ban, which could not express context - an audit report classified U+2264 and U+2265 as the scientific carve-out while the rule named both must-replace. Tier 1 never survives ASCII, tier 2 is an operator kept beside a number and replaced between words, tier 3 is a unit symbol whose ASCII form would be a lie. A character in no tier is reported rather than passed, and the rule is clean tree-wide, so it gates. Two prose rules invert from detecting a subset to banning the construction. The pronoun-keyed splice pattern found 170 of 493 and missed every imperative one; the em-dash rule now says restructure the sentence, and the spaced hyphen is banned in its own right. Comments get a gate covering every syntax the fleet types carry - `//`, `/* */`, `<!-- -->`, `<# #>`, `;`, `#` - with JSON read as JSONC because that is what ships. That recovered four files the old discovery never saw, including the VS Code task and devcontainer snippets downstream repos copy; their 37 malformed comments are fixed here. The Verification Discipline mechanisms land too: a gate has to be watched failing, and a check is scoped by what the project declares rather than by the file that prompted it. ## The comment-extractor fix (#461) Copilot's review of this promotion found a defect in the extractor #459 adds, so it was fixed at the source and this PR now carries it. Block openers were searched before line markers against a ceiling of the whole line, so a `/*` inside a `//` comment opened a real block: the line's own comment was truncated at the opener, the closer carried into the lines below, and the code there was handed to the comment rules as prose. PowerShell failed identically with `<#` inside a `#` comment. Those are the two fleet syntaxes carrying both marker kinds. Tree-wide counts are unchanged - dash 963, comment-wrap 454, semicolon 388, comment-case 56 - because nothing in this repository nests the markers that way. The exposure is downstream, in the C# and PowerShell repositories the extractor is aimed at, and it would surface the moment the comment rules gate rather than warn. That is the argument for fixing before promotion rather than after. ## The carve-out contradiction (#463) Copilot's second round found the same review's own subject matter contradicting itself. Two adjacent bullets disagreed on whether a developer-typed but un-tiered character is exempt from the gate or reported by it. The implementation already reported it as `charset-unknown` while the doc read as an exemption. The carve-out now states that it governs what an agent may rewrite rather than what the gate reports, which leaves both bullets true and changes neither rule. Worth blocking a promotion for, because `main` is what the audit reads as ground truth, and this contradiction had already produced a real misreading - the audit report that classified U+2264 and U+2265 as the scientific carve-out, which is what motivated tiering the rule in #459 to begin with. ## The scanner rewrite (#464) The third round found a fourth defect in the same function, so the structure went rather than the instance. The extractor searched each marker kind from column 0 against a ceiling, and a ceiling can only describe the first comment on a line. That one shape produced every extractor defect in this promotion: a marker quoted inside the first comment read as real, a doc marker excluded from the ceiling unbounded it, and anything after the first comment was unreachable. A single left-to-right pass replaces it. Several blocks on one line, a comment trailing the line where a block closes, and a multi-line documentation block that no longer leaks its prose into the scan all come with it. #462 is fixed there too rather than deferred, since a defect left in the promoted diff keeps being found and the loop cannot reach a clean round while it stands. C# verbatim strings read correctly in both directions and in all three spellings, and C# took its own syntax entry so the rest of the C-like family does not inherit a form it lacks. ## Multi-line strings (#465) The fourth round found that masking runs per line while a C# verbatim string spans them, so a marker on any later line of one was reported as a comment. A false positive, and the direction that is worse than a miss, since it asks a reader to edit text that is data. Reviewing that fix found two more in the same area: a quote in comment text opened a phantom string that blanked the markers after it, within a line and then across lines, and a line-skip meant as an optimization dropped a string that closed and reopened around real code. Masking now runs from the scan position and only code advances the string state. #466 records what is still not carried. C# is the only syntax whose strings are tracked across lines, and the README says so rather than leaving a reader to find out. ## The continuation asterisk (#467) The fifth round found the extractor damaging the prose it then judged. A leading `*` was taken off every block comment body, which is the `/* */` convention for continuing a line and ordinary text everywhere else, so `<!-- *emphasis* leads here -->` became `emphasis* leads here` and `comment-case` reported the lowercase opening it had just created. Worse than a plain false positive, since the prose reported is not what the file holds. Stripping is now one `*` against whitespace, on a line continuing a `/* */` block. This defect dates to `d4c4085` rather than to any fix made during this review, so the promotion is where it would first reach `main`. ## Why promote now The snippets fixed here are the ones a new or realigning repo copies, and the reviewer-facing rules - the merge gate and the runbook now require investigating the low-confidence block - only bind downstream once `main` carries them. That requirement earned itself twice more on #461. Round one's single inline comment read as a wording nit and was a live defect; round two had no inline comments at all and both findings sat in the suppressed block, one of them the reopened bug. A loop polling `reviewThreads` would have reported a clean pass on both rounds. ## Fidelity note `GOVERNANCE.md` and `.github/copilot-instructions.md` sections declared verbatim changed, so downstream copies are **stale** until re-vendored. `spec/fidelity_honesty.py` separates stale from modified by hash, so the audit reports it correctly. Warn-only backlog, reported but not gating: dash 963, comment-wrap 454, semicolon 388, comment-case 56. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
The flat non-ASCII ban could not express context.
reports/homeassistant-purpleair/audit.mdclassified U+2264 and U+2265 as the scientific carve-out while the rule named both must-replace. That is not a wrong reading so much as a rule with no vocabulary for the distinction.Three tiers
Measured behavior:
Clean tree-wide, so charset gates. The audit report is corrected in the same pass.
Two prose rules inverted
The pronoun-keyed splice pattern found 170 of 493 and missed every imperative one, so any prose semicolon now flags and the exception is the list the rule already names. The em-dash replacement becomes "restructure the sentence" rather than a spaced hyphen, and that hyphen is banned in its own right: allowing the ASCII spelling would keep the shape and change only the character.
Both are markdown-only until a comment can be told from code.
repo-config/configure.shalone carries 78 statement separators that are not prose. Both are warn-only.Comments, in every language the fleet declares
Driven by the declared project types (
csharp,cpp,python,docker,homeassistant,eda,docs,nuget,pypi) rather than by whichever file was open://,/* */.code-workspace, JS/TS, CSS#tokenize, shell, YAML, TOML,.gitattributes<!-- -->.csproj,.props, markdown<# #>;,#.editorconfig///,/**, docstringsJSON is read as JSONC, because that is what ships. Four tracked files were invisible before, including
catalog/snippets/configs/vscode-tasks.jsonand bothdevcontainer.jsonsnippets, which downstream repos copy. Their 37 malformed comments are fixed here so the shape stops propagating.A marker inside a string literal is not a comment, verified in C#, shell, JSON, and Python.
Two rules come out of it: one sentence per comment line, never wrapped and never two on a line, and a sentence that starts on a line starts with a capital. Where the first word is a tool whose own casing is lowercase, the fix is to restructure rather than to capitalize against CODESTYLE tooling-casing.
Verification Discipline
The mechanisms behind the above land as fleet law: a gate has to be watched failing, driving the real table rather than a restatement of it, and a heredoc consumes a backslash escape and leaves an invisible control character behind.
Watched failing
U+2264from tier 2 -> 2 cases fail, naming the codepoint and tier.U+2126from tier 3 -> 2 cases fail..jsonmapping -> 5 cases fail, naming the file type.repo_gateprints0 issue(s)and reads clean, while 3 cases object.51 self-test cases. Whole gate green: mypy, markdownlint, actionlint, editorconfig-checker, cspell.
Posture
Gating: self-tests, both
repo_gatechecks,charset,dupword.Warn-only in a
continue-on-errorstep so the backlog is visible without blocking:charset-unknown,semicolon323,dash963,comment-wrap432,comment-case56. Going green needs the sweep the prose rules themselves forbid, so each is corrected as its file is next edited.The 247-instance GOVERNANCE.md dash sweep is deliberately a separate PR, where the diff is reviewable as prose rather than buried under tooling.
🤖 Generated with Claude Code