Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 16 additions & 3 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,14 @@ uv run sphinx-build -b html docs dist/docs
# bare compare.py measures against two minors back while reporting the
# previous one, and _allowlist_for hard-errors on the missing file.
# tests/v2/test_regex_sync.py sweeps every expected_since_*.toml (#333), so
# the new ledger's hand copies of _SCRIPT_RANGES and the honorific
# vocabulary are checked from the day the file lands -- but it enrolls
# itself in neither roster, and both failures are loud, not silent:
# the new ledger's hand copies of _SCRIPT_RANGES and of the honorific and
# Latin vocabularies are checked from the day the file lands. All three
# rosters find copies by their SYNTAX -- a span class or an alternation --
# so a copy spelled some other way is not undeclared but unseen. What
# covers those is _CORPUS_CLAIMS, which records what every rule claims
# -- its regex's corpus reach, its roles, and which names -- and so
# needs no notion of how a copy is spelled. Enrol the new ledger in
# _CORPUS_CLAIMS always, and in the others where they apply:
# - _SPAN_BEARING_RULES: add the filename, mapped to the set of issue
# tags whose rules carry a script-span class (empty set if none).
# - _HONORIFIC_SOURCES: if the ledger has a CJK honorific rule, add a
Expand All @@ -99,6 +104,14 @@ uv run sphinx-build -b html docs dist/docs
# that issue. A retroactive ledger can repeat an older one's rule
# verbatim (fix(#271/#272/#298) is in both today), and every rule
# must match exactly one key.
# - _CORPUS_CLAIMS: REQUIRED, not conditional -- a ledger with no
# entry hard-fails. Add the filename mapped to {} while the ledger
# is empty, then one _Claim per rule as rules land. The test prints
# the values to record.
# - _LATIN_ALTERNATION_SOURCES: same, for a rule copying a Latin
# vocabulary (maiden markers, ambiguous acronyms). An alternation
# matching no key fails as undeclared -- add it, or record it in
# _NOT_A_VOCABULARY_COPY if it copies nothing.
```

Enable debug logging to see the parser's internal decisions:
Expand Down
45 changes: 43 additions & 2 deletions tests/v2/test_differential.py
Original file line number Diff line number Diff line change
Expand Up @@ -298,15 +298,56 @@ def test_validate_rules_rejects_a_rule_that_would_silently_widen(
compare.validate_rules([rule], "expected_since_2.0.0.toml")


def test_default_baseline_has_a_ledger_and_nothing_else_in_it() -> None:
"""Two facts about the OPEN cycle's ledger, which the carve-out
below leans on and neither of us should have to derive.

It must exist: _allowlist_for treats a missing ledger as a hard
error, so a DEFAULT_BASELINE with no file makes a bare compare.py
run abort -- and it would also make the carve-out inert, since it
would name a file nothing iterates over.

And it must define nothing at the top level except `change`. This is
the one ledger allowed to be empty, so a mistyped table header --
`[[changes]]`, `[[rules]]` -- reads as a legitimately empty open
cycle everywhere instead of as a broken file: every sweep gets zero
rules and passes, while the author believes they shipped a rule.
The other ledgers are protected by having to be non-empty; this one
needs saying out loud.
"""
import tomllib
open_cycle = _TOOLS / f"expected_since_{compare.DEFAULT_BASELINE}.toml"
assert open_cycle.exists(), (
f"DEFAULT_BASELINE is {compare.DEFAULT_BASELINE!r} but "
f"{open_cycle.name} does not exist; a bare compare.py run would "
f"hard-error, and the empty-ledger carve-out would be inert")
keys = set(tomllib.loads(open_cycle.read_text(encoding="utf-8")))
assert keys <= {"change"}, (
f"{open_cycle.name} defines {sorted(keys - {'change'})} at the top "
f"level. Only `change` is read, so anything else is a typo that "
f"would read as an empty ledger rather than as a broken one")


def test_validate_rules_accepts_the_shipped_ledgers() -> None:
"""The guards above must not be so strict they reject real rules."""
"""The guards above must not be so strict they reject real rules.

Every ledger but one must carry rules. The exception is the OPEN
cycle's -- the one DEFAULT_BASELINE names -- which is created the day
its baseline is released and is legitimately empty until that
cycle's first behavior change lands. An older ledger is history, and
history is not empty, so emptying one is still a mistake this
catches.
"""
import tomllib
open_cycle = f"expected_since_{compare.DEFAULT_BASELINE}.toml"
ledgers = sorted(_TOOLS.glob("expected_since_*.toml"))
assert ledgers, "no ledgers found; this test would pass vacuously"
for ledger in ledgers:
rules = tomllib.loads(
ledger.read_text(encoding="utf-8")).get("change", [])
assert rules, f"{ledger.name} has no [[change]] rules"
assert rules or ledger.name == open_cycle, (
f"{ledger.name} has no [[change]] rules, and it is not the "
f"open cycle's ledger ({open_cycle})")
compare.validate_rules(rules, ledger.name)


Expand Down
Loading