Repository navigation
Description
What the file does
tests/test_a_tree_reading_guard_names_its_tree.py holds one convention of this repository's scripts/check-*.py family: a guard that reads a working tree says which tree answered, and a guard pointed at a tree it read nothing from must refuse (rc 2) with a reason rather than print a clean verdict (rc 0).
Two tables say which guard can be pointed at a tree, and how:
POINTABLE_AT_A_TREE— seven members, each mapping a guard to the argument that points it at a tree:check-citation-resolves.py(()— a positional),check-staged-inverse.py(--repo), andcheck-retry-bounds.py,check_unbound_reads.py,check-undefined-names.py,check-workflows.py,check-gui-package-refs.py(all--root).TAKES_A_TREE_ARGUMENT— two members,check-citation-resolves.py(()) andcheck-staged-inverse.py(--repo): the guards whose tree is their argument, so the leg that asks about the default root can run them with no flags at all.
A row in POINTABLE_AT_A_TREE is what puts a guard in front of the empty-tree leg, which is the only place the family's "an empty tree is not a clean one" rule is asked of it.
What is observed
Deleting exactly one row from POINTABLE_AT_A_TREE — leaving the guard everywhere else it appears, including TAKES_A_TREE_ARGUMENT — and running the whole file:
row deleted from POINTABLE_AT_A_TREE |
its pointing argument | whole file |
|---|---|---|
check-staged-inverse.py |
--repo |
9 passed — every leg green |
check-citation-resolves.py |
positional | 9 passed — every leg green |
check-retry-bounds.py |
--root |
1 failed, 8 passed |
check-workflows.py |
--root |
1 failed, 8 passed |
| (nothing deleted) | — | 9 passed |
So two of the table's seven members can be removed from it and nothing fails. The two that survive belong to different eras — check-staged-inverse.py joined with the staged-inverse guard, check-citation-resolves.py predates it — which makes this the table's shape rather than one addition's mistake.
Why no leg misses them
test_every_guard_advertising_a_root_flag_is_accounted_for derives its subject by asking each guard's own --help and keeping the guards whose text matches --root. Both survivors' pointing argument is something else: a positional (usage: check-citation-resolves.py [-h] [root]) and --repo. The leg never looks at them, so it cannot miss what it never asks about.
The other legs cannot see a deletion either, and each for its own reason:
test_a_guard_pointed_at_a_tree_it_read_nothing_from_refuses_to_passiterates the table, so a deleted row is simply not asked — a shorter table is still a passing one. Its companion assertionset(POINTABLE_AT_A_TREE) - _tree_reading_buckets()also shrinks with the table, so the deletion makes less to check, never more.test_a_guard_that_takes_a_tree_defaults_to_the_tree_it_stands_inandtest_the_guard_that_takes_a_tree_names_the_tree_it_was_giveniterateTAKES_A_TREE_ARGUMENT, which still has both guards, so they keep asking the guards other questions and stay green.test_every_guard_in_the_family_is_classifiedasks about the four classification buckets, which do not includePOINTABLE_AT_A_TREE; the guard is still classified in its bucket, so nothing moves.
Both directions of the table are therefore unfenced for those two members: nothing fails when a row goes, and nothing fails when a guard that could be pointed at a tree is never added.
Expected
Removing a member from the pointable table is a change to what the file measures, so it must fail and name the guard it stopped measuring. Adding a guard that can be pointed at a tree, and not adding it, must fail too.
Acceptance
- Deleting any single row of
POINTABLE_AT_A_TREEfails exactly one leg, and the failure names the guard. All seven rows, one arm each, each followed by a byte-identical restore. - A guard that admits a pointing argument and is in neither table fails, naming it and saying which table to add it to. A legitimate guard added properly stays silent — a leg that pins today's seven names instead of deriving them is the literal-pin defect this repository has paid for six times and is not acceptable.
- A guard whose
--helpcannot be read, or that is not on disk, is a loud failure naming it — never a skip, and never read as a pass. - The two tables' member counts, before and after.
Fix shape
Derive the membership; do not write a second list. The file already owns both derivations it needs:
TAKES_A_TREE_ARGUMENT's members are pointable by the table's own docstring — their tree is their argument — soset(TAKES_A_TREE_ARGUMENT) <= set(POINTABLE_AT_A_TREE)is a fact about the file's own classification, needs no vocabulary, and is exactly the fence missing over the two survivors above.--helpalready answers the other half, and the account-for leg already runs it: a guard's pointing flag is a flag its own--helpadvertises. Today the table's second column is prose nothing measures — if a guard renames or drops the flag its row names, that is found by the empty-tree leg crashing into an argparse error, not by a leg asking the question.
Together they cover all seven rows: five are --root and already paid for by the account-for leg, and the two survivors are TAKES_A_TREE_ARGUMENT members. A metavar vocabulary (matching ROOT/TREE/PATH in the help text) is not the derivation to use: check-citation-resolves.py's positional is spelled lowercase root with no metavar, while check-issue-links.py advertises --repo REPO that takes a GitHub slug and no tree, so a word list would both miss the first and wrongly claim the second.
Activity
pm25coder commentedon Nov 4, 2026
Handled by #2705