Skip to content

Promote the Prose Gate's Vocabulary Check and Its Review Fix to Main - #3476

Merged
ptr727 merged 3 commits into
mainfrom
develop
Oct 25, 2026
Merged

ptr727 merged 3 commits into
mainfrom
develop

Conversation

@ptr727

@ptr727 ptr727 commented Oct 25, 2026 •

Copy link
Copy Markdown
Owner

Summary

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.

Closes #3441

Constituent coverage

Each change this promotion carries had coverage on its own pull request into develop, at its merged head:

Pull request Merged head Coverage
#3474 9d631289 attested local pass at the merged head, base develop, and a Copilot table on the merged head naming exactly its one changed file, no open findings
#3477 0999fc81 attested local pass at the merged head, base develop, and a Copilot table on the merged head naming exactly its one changed file, no open findings
#3478 93ba9d90 attested local pass at the merged head, base develop, and a Copilot table on the merged head naming exactly its one changed file, no open findings

🤖 Generated with Claude Code

)

## 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>
Copilot AI lite review requested due to automatic review settings October 25, 2026 17:51
@coderabbitai

coderabbitai Bot commented Oct 25, 2026 •

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

Run configuration
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

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.

🟢 Approval recommended

The change is test-only and its logic checks out, leaving only a docstring wording fix and two optional follow-ups.

1 open issue
What changed in this PR

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.

Review effort: Lite

Comment thread tests/test_prose_lint.py Outdated
@codecov

codecov Bot commented Oct 25, 2026 •

Copy link
Copy Markdown

Codecov Report

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

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #3476   +/-   ##
=======================================
  Coverage   80.98%   80.98%           
=======================================
  Files          25       25           
  Lines        8895     8895           
=======================================
  Hits         7204     7204           
  Misses       1691     1691           
Flag Coverage Δ
python-3.13 80.98% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

## 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 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
@ptr727
ptr727 requested a lite review from Copilot October 25, 2026 18:10

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.

🟢 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

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

1 resolved since last review

Review effort: Lite

## 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>
@ptr727

ptr727 commented Oct 25, 2026

Copy link
Copy Markdown
Owner Author

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.

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.

🔵 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

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

Review effort: Lite

@ptr727

ptr727 commented Oct 25, 2026

Copy link
Copy Markdown
Owner Author

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

@ptr727
ptr727 merged commit b056c7f into main Oct 25, 2026
14 checks passed
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.

Replace the Prose Gate's Line Ceiling with a Check for Vocabulary in the Script

2 participants