You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Promote the Prose Gate's Vocabulary Check and Its Review Fix to Main - #3476
Promotes the pull requests merged into develop since release 2.0.1178.
#3474: replaces the prose gate's line ceiling with a test that fails where prose_lint.py holds a banned spelling, an exemption entry, or a collection literal of more than five words.
#3477: answers this promotion's review. The is_word docstring now says what its pattern accepts, and a word tuple keying a mapping beside a wordless value is reported.
#3478: answers this promotion's second review round, a case proving paths with a / or \ separator are not words.
)
## Summary
Replaces the line ceiling on `.github/actions/prose-gate/prose_lint.py`
with a check aimed at what the ceiling stood for, per the maintainer's
decision on
[#3441](#3441). The
ceiling's own docstring said vocabulary and exemptions live in
`prose-gate.json`, and a line count could not tell that defect from
parser logic, so each lexer fix moved the ceiling or compacted unrelated
code to fit.
`TestVocabularyLivesInTheData` in `tests/test_prose_lint.py` now fails
where the script holds either of these:
- A string equal to a banned spelling, as `prose_lint.british_spellings`
generates them, or to an entry of an exemption list. Stem and ending
fragments and the US forms are not entries, since they are ordinary
words a lexer may spell. Neither are `service_accounts`, names like
`root` an identifier can use.
- A collection literal of more than five words, counted through nested
literals and reported once at the outermost. A word is letters alone,
with inner spaces, hyphens, and apostrophes allowed, so flags, paths,
and environment variable names are not words. `GIT_LOCATION` therefore
settles without an exemption by name. A mapping's key counts only beside
a value holding words, so `RULE_TABLE`'s dispatch keys do not.
Each shape has a case on a constructed script, and the real script holds
none.
## Verification
- Full unit suite, `python -m unittest discover -s tests` with
`tests/requirements.txt`: 2947 tests, OK, 7 skipped.
- ruff format and check, mypy: clean. `prose_lint.py --diff` against the
merge base, with and without `--check sentence-length`: clean.
- On a copy of the real script with `"judgement"`, a six-word nested
tuple, and a `hub_hosted` path appended, the check reports all three.
Renaming `GIT_LOCATION` reports nothing, since its entries are not
words.
- Three local adversarial passes, at the frontier tier. The first found
stem fragments and US forms read as entries, nested literals uncounted,
flags counted as words, and an overclaiming docstring, each fixed. The
second found two cases that proved nothing, both fixed. The third found
nothing.
## Declined local findings
- **Path and directive lists are not counted toward the cap**
(introduced). Paths and directive names are also the script's own logic,
its argv and the file names it reads, so counting them false-fires the
way counting flags did. An exact copy of an existing entry is still
caught by equality.
- **Counting stops at a `frozenset(...)` call** (introduced). Counting
through calls false-fires on `RULE_TABLE`, the gate's own dispatch
idiom, whose hyphenated word keys sit over `frozenset` groups.
## Pre-existing findings, not filed
- `.github/actions/prose-gate/prose_lint.py` `TERMINATOR`: an
abbreviation list (`e.g`, `i.e`, `vs`, `etc`) sits inside a regex, a
shape no literal check reads (pre-existing).
- A word list written as one string split at runtime, `"a b c".split()`,
reads as one constant and is not counted (pre-existing, no instance in
the script).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This PR replaces the prose gate's line-count ceiling in tests/test_prose_lint.py with a vocabulary check. The old ceiling kept forcing unrelated code to be compacted or the cap raised each time the lexer grew. The new test fails when prose_lint.py holds a banned spelling, an exemption entry, or a collection literal of more than five words. It closes issue #3441, and the promotion carries PR #3474 from develop to main.
Changes:
Removes the 1862-line ceiling test.
Adds TestVocabularyLivesInTheData, with helpers that walk the script's AST, count words in literals (nested literals counted once at the outermost), and compare string literals with banned spellings and exemption entries.
Adds constructed-script cases for each shape, plus a check that the real script holds none.
File
Description
tests/test_prose_lint.py
Replaces the line-count test with an AST-based vocabulary check, its helpers, and cases proving each shape is caught.
✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 80.98%. Comparing base (412ce42) to head (18ee2fc). ⚠️ Report is 420 commits behind head on main.
## Summary
Answers the finding promotion
[#3476](#3476) raised on
the vocabulary check
[#3474](#3474) added to
`tests/test_prose_lint.py`.
- **The `is_word` docstring** said "letters alone", while its pattern
joins letter runs with single spaces, hyphens, or apostrophes. It now
says that. It also names which paths and environment variable names are
not words, those with a separator or an underscore, since an all-letter
name such as `HOME` is one. The test covering them is renamed to match.
- **A word tuple keying a mapping beside a value holding no words was
never reported** (class sweep, found by the local pass). The mapping
does not count such a key, yet marked it nested, so the key literal was
skipped as well. Only a key the mapping counts is nested now, and the
docstring says a literal reports at the outermost literal that counts
it. One case proves each half: `KEYED` reports at the tuple, and
`COUNTED` reports once, at the mapping.
## Verification
- `tests/test_prose_lint.py`: 629 tests, OK. ruff format and check,
mypy: clean. `prose_lint.py --diff` against the merge base, with and
without `--check sentence-length`: clean.
- Three local adversarial passes at the frontier tier. The first found
the remaining environment-name overclaim and the skipped key, the second
two unproven halves and two wording gaps, each fixed. The third found
nothing.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
ptr727
changed the title
Promote the Prose Gate's Vocabulary Check to Main
Promote the Prose Gate's Vocabulary Check and Its Review Fix to Main
Oct 25, 2026
The reason will be displayed to describe this comment to others. Learn more.
🟢 Approval recommended
The change is test-only, its cases match the regex and syntax-tree behavior they describe, and the only open findings are optional improvements.
No open issues 🎉
1 previously missed issue
In code that hasn't changed since last review
Add a path case to verify paths are excluded from word counts
tests/test_prose_lint.py:7615
This test's name and docstring promise that paths are not words, but its body has no path case, so the separator exclusion is untested. A change that starts counting paths as words would still pass this suite. Adding a path tuple longer than the cap, and renaming the test to match, proves the exclusion. This is optional and not a blocker.
## Summary
Answers the second-round finding promotion
[#3476](#3476) raised on
the vocabulary check: the `is_word` docstring says a path with a
separator is not a word, and no case held a path. The case covering
flags and underscored names, renamed
`test_flags_paths_and_underscored_names_are_not_words`, now carries six
slash paths and six backslash paths in a tuple apiece, so a pattern
accepting either separator pushes its tuple past the five-word cap.
## Verification
- `tests/test_prose_lint.py`: 629 tests, OK. ruff and mypy clean.
`prose_lint.py --diff` against the merge base, with and without `--check
sentence-length`: clean.
- With `/` replaced by a space in the case's source, it reports `PATHS`
at 6 words, and with `\` replaced, `WINDOWS` at 6 words.
- Two local adversarial passes at the frontier tier. The first found a
single backslash path could not cross the cap by itself, fixed with its
own tuple. The second found nothing.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Answering the round on 86f66b4, which raised one finding with no thread: "[Low severity] Add a path case to verify paths are excluded from word counts" at tests/test_prose_lint.py:7615. Fixed in 18ee2fc (#3478): test_flags_paths_and_underscored_names_are_not_words now holds six slash paths and six backslash paths in a tuple apiece, so a pattern accepting either separator pushes its tuple past the cap.
The reason will be displayed to describe this comment to others. Learn more.
🔵 Needs a closer look
It promotes changes into main, and the local adversarial passes and Copilot coverage it cites cannot be verified from the review environment, so a maintainer should confirm them.
No open issues 🎉
1 previously missed issue
In code that hasn't changed since last review
Rename test to cover nested lists and mapping keys
tests/test_prose_lint.py:7591
The test name test_nested_word_lists_count_once_at_the_outermost covers only nested lists, but the case table also checks mapping keys (KEYED and COUNTED). Rename it so it names both shapes, which helps a reader find the mapping rule.
Answering the round on 18ee2fc, which raised one finding with no thread: "[Low severity] Rename test to cover nested lists and mapping keys" at tests/test_prose_lint.py:7591. Declined as a preference. A mapping is itself a nested literal, so test_nested_word_lists_count_once_at_the_outermost already names the shape KEYED and COUNTED exercise, and vocabulary_in's docstring states the mapping-key rule, which is where a reader looks for it. fleet-code-review "Review the Change" item 5 omits a preference no rule or user-visible risk supports. The earlier round's path finding is answered above, fixed in 18ee2fc (#3478).
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
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.
Summary
Promotes the pull requests merged into develop since release 2.0.1178.
prose_lint.pyholds a banned spelling, an exemption entry, or a collection literal of more than five words.is_worddocstring now says what its pattern accepts, and a word tuple keying a mapping beside a wordless value is reported./or\separator are not words.Closes #3441
Constituent coverage
Each change this promotion carries had coverage on its own pull request into develop, at its merged head:
9d631289develop, and a Copilot table on the merged head naming exactly its one changed file, no open findings0999fc81develop, and a Copilot table on the merged head naming exactly its one changed file, no open findings93ba9d90develop, and a Copilot table on the merged head naming exactly its one changed file, no open findings🤖 Generated with Claude Code