test: the grader reads every check helper lib.sh defines (#432, #1040) - #1044
Conversation
The other half of #1040. #1041 fixed the two hilbert_locality names this was hiding; this makes the grader able to see that class at all. THE DEFECT. The bash side matched `check(?:_num|_ratio|_text|_timing)?`, so three of the eight helpers lib.sh defines were invisible: check check_num check_text check_ratio check_timing READ check_unrunnable check_skip check_ratio_needs_quiet_machine INVISIBLE A property asserted through one of the three was never reported MISSING and could not move rc, so A PAIR COULD GRADE ONE-FOR-ONE ON THE STRENGTH OF THE GRADER'S BLIND SPOT. It never drifted out of date either: 0cbf574 introduced that pattern and check_unrunnable already had 21 call sites that day. Same shape as #1036 and #1038 on the python side -- a rule true of MOST of a class taken for a property of the class -- sitting on the other side of the same tool for five days, unexamined because fixing one end thoroughly did not prompt anyone to look at the other. THREE ARMS, and two of them state what the tool does NOT cover: test_the_extractor_reads_every_check_helper_lib_sh_defines re-derives the list from lib.sh's DEFINITIONS and fails with the helper named. The list is hand-written so the tool stays standalone; pinned the way _NAME_ARG is. test_a_longer_helper_name_is_not_shadowed_by_a_shorter_one the pattern SHAPE, not the ordering -- see below. test_the_suite_local_helpers_are_known_and_excluded the four helpers one suite each defines (check_structure, check_reconstruct, check_split_happened, check_float), and that none of their suites has a pytest twin, so none is graded today. THE ORDERING CLAIM WAS OVERSTATED AND THE CODE SAID IT UNTIL IT WAS MEASURED. The entries are longest-first and I wrote that a shorter prefix would shadow a longer helper. Python's `re` BACKTRACKS across alternatives, so a pure reorder reads both names identically -- measured, arm stays green. What the OLD pattern could not do was read check_ratio_needs_quiet_machine at all: it matches check_ratio, wants whitespace, finds `_needs`, backtracks to the empty option, wants whitespace after `check`, fails. On a fixture holding both: pre-#1040 optional-suffix form ['short'] this form, either order ['short', 'long'] So the arm pins the SHAPE and the ordering is readability. Corrected in the code comment, the docstring and TESTS.md rather than left to read as load- bearing. POPULATION: `check` or `check_<something>`, not `check[a-z_]*`. The loose form also matches `checks_in` in decode_interrupts.sh, a counting utility returning a number that records nothing. It was in the suite-local list until the arm printed it and the definition was read. Removal proof, each mutation asserted applied by md5, tree restored after each: drop check_unrunnable from the list the drift guard, naming it drop check_skip from the list the drift guard, naming it add a duplicate entry the drift guard (size reconciliation) pure reorder, longest no longer first NOTHING -- which is the finding above Verified against main 0d17a87, not carried from the head it was written on: all seven pairs rc=0 missing=0, guard job 316 collected, 784 checks, 784 pass + 0 fail + 0 unrun, docs_style.sh 14 PASSED. guard_tests 313 -> 316 re-derived by collection. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NhwXKAgSmYDUjteWkfajHK
#432, #1040) CI's cluster leg failed on 57ed275 and the failure was MINE, not the environment. The drift guard reads the shell harness's helper library by a hard-coded path: lib = (HERE.parent / "lib.sh").read_text() which is a python file naming a shell path, so test_the_harness_independence_inventory_is_exactly_what_the_corpus_does reddened: four declared crossings in the tree, five found. Reproduced locally against a clean main as a control. On main only test_a_cluster_test_still_needs_the_driver fails here, which is this host having no driver; with the branch, that plus the inventory arm. So the inventory arm is the branch's and the driver arm is the box's. DECLARED RATHER THAN REDESIGNED, and this is a judgement about the owner's standing rule rather than a mechanical fix. The inventory is the designed mechanism for a crossing that earns itself: it is asserted in BOTH directions, so a declaration that stops being true reddens too, and each entry must carry the mechanism a reader can check by hand. The justification here is the same shape as pgc_cluster.py's -- crossing exists so there is one implementation rather than two. The alternative is a hand-written copy of the helper list inside pytest, which is exactly the value #1040 is about: five of eight helpers had been unreadable since the pattern shipped, and nothing said so. THE ALTERNATIVE READING, which I am not taking but a reviewer may prefer. The assertion "the grader's helper list matches the definitions" is arguably owned by the side that OWNS those definitions, and the shell selftest already reads repo files including python tools. Moving it there would avoid the python-to-shell crossing entirely rather than declaring it. That is a larger change and a different PR; @OffgridwithJD has offered to argue it and I would rather the decision were visible than quietly made. The entry names no file, deliberately: the comment above the declaration records that spelling the helper library's path in a description made the detector flag its own file, reporting four crossings where the tree had three. Guard half unchanged at 316 collected, 784 checks, 784 pass + 0 fail + 0 unrun. docs_style.sh 14 PASSED. No test added, so no count moves. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NhwXKAgSmYDUjteWkfajHK
…1040) The harness-independence inventory scans `test/pytest/*.py`. That population is a LOCATION, and nothing said whether the location was the point. @OffgridwithJD inferred from it that `.github/scripts/` is exempt by design -- two tools there read `test/*.sh` and appear in no inventory -- and concluded that MOVING compare_to_bash.py there would delete its crossing rather than declare it. The conclusion may be right; the inference was not available from the evidence. Absent-from-the-report and outside-the-scan produce identical output. jd ruled today: the scope IS deliberate. The inventory is the pytest corpus's self-guard, and tooling under `.github/scripts/` belongs to neither harness -- it is CI's, and a tool there reading both sides is what it is for. So those two tools are OUT OF SCOPE rather than exempt, and the ruling is now in the file instead of inferable from a glob. The comment also records the consequence, because it is the part a reader acts on: under this reading a move to `.github/scripts/` is defensible on design grounds, since that is where cross-harness tooling lives -- but never because it makes this arm stop reporting the file. "The guard stops mentioning it" is the worst available reason to move anything. Same defect shape as #1040 itself, one level up: a population named by where things live rather than by what they do. There it was `check*` against "calls pgc_record"; here it is `test/pytest/*.py` against "python that reaches into shell". The difference is that this one is now intentional and says so. Comment only. Guard half unchanged at 316 collected, 784 checks, 784 pass + 0 fail + 0 unrun. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NhwXKAgSmYDUjteWkfajHK
linuxhikerpm
left a comment
There was a problem hiding this comment.
The defect is real and the same shape as #1036/#1038 on the other end of the same tool: a pattern that described most of a class, treated as a property of the class. I checked origin/main's lib.sh — the eight helpers in _BASH_HELPERS are exactly the definitions, including check_skip.
The three new arms do the useful work. The drift guard re-derives from definitions, not from call sites, so a new check_whatever() in lib.sh goes red before a suite using it is silently ungraded. The shadowing arm pins the pattern shape (the old optional-suffix form could not read check_ratio_needs_quiet_machine at all) and the comment now says the longest-first order is not load-bearing, which matches the measurement. The suite-local exclusion names the four helpers and that none of their suites is graded — that is the day-one statement a port of parallel_copy will need.
hilbert_cluster growing from 124 names to 133, with the nine check_unrunnable names already twinned in the same file, is the first real exercise of the widening. Nothing here should redden an existing pair.
CI is green on this head, including the cluster job. I did not merge.
OffgridwithJD
left a comment
There was a problem hiding this comment.
Reviewed at ea5a6cb532c7e18dba90d3efdd2f324ca8a23de0, and CI read from the head sha's
check-runs in the same call as the head: 14/14 success, 0 other, MERGEABLE/CLEAN, 0 behind
main. Every claim below was re-run here rather than read; each mutation asserted it applied
and its restore was md5-asserted.
The four things you asked me to check rather than take
1. The null result reproduces. A pure reorder of _BASH_HELPERS to shortest-first --
the arrangement your original claim said would break it -- reddens nothing:
control 48 checks, 48 pass, 17 passed
shortest-first reorder 48 checks, 48 pass, 17 passed
md5 moved, restore asserted
So your correction stands and the ordering is not load-bearing. Python's re alternation
backtracks across branches. A mutation that reddens nothing is a finding about the claim,
not a gap in the mutation -- that is your sentence and it is the best thing in this PR.
2. The drift guard fires in both directions and names the helper.
drop "check_skip" from the tuple
FAILED test_the_extractor_reads_every_check_helper_lib_sh_defines
every check helper lib.sh defines is one the extractor reads: got 'check_skip' want 'none'
remove the test_compare_to_bash.py declaration from SHELL_REFERENCES
FAILED test_the_harness_independence_inventory_is_exactly_what_the_corpus_does
got [... 'test_compare_to_bash.py' ...] want [... without it ...]
My first attempt at the second was invalid and I am saying so rather than quietly
re-running it: my regex removed part of the multi-line entry and produced a SyntaxError,
so the arm never ran and "it failed" would have been a claim about my mutation. The valid
version asserts the mutated file still parses before drawing any conclusion from it.
3. The population is right and the checks_in trap is real. The strict class
check(_[a-z_]+)? finds exactly your four suite-local helpers -- check_structure,
check_reconstruct, check_split_happened in parallel_copy.sh and check_float in
parquet_export_stats.sh -- plus the 13 suites that define their own check(). The loose
check[a-z_]* adds exactly one file, decode_interrupts.sh, for checks_in(), whose body
runs an awk that counts COLUMNAR_DECODE_INTERRUPT( occurrences and prints a number. It
records nothing. And none of the three suites has a pytest twin, so none is graded today.
4. The entry says a true thing, and it is not the phrase you gave me in chat. You told
me the justification was "one implementation rather than two", which is pgc_cluster.py's
and would have been wrong here -- no implementation is shared, a list is re-derived. The
entry in the file says the list "cannot become a hand-maintained copy that rots apart from
it (#1040, where five of eight helpers had been unreadable since the pattern shipped)". That
is accurate, it names a mechanism a reader can check, and it is a better justification than
the one I was going to hold you to. The artefact got the care; I only found that by opening
it.
One thing recorded, not asked for
The hilbert_cluster measurement in the body is mine and reproduces: 124 bash checks as the
tool ships, 133 with this change, the gap being that suite's nine check_unrunnable sites
and zero check_skip. It is worth saying why that case matters beyond being a bigger number:
hilbert_locality was the BROKEN case, where the names had no twin and #1041 fixed them;
hilbert_cluster is the WORKING case, where nine correct names were simply invisible. A
change exercised only against the broken case shows that it stops hiding a defect and never
shows that it adds names correctly. My port of that suite grades 0 MISSING against both the
shipping extractor and this one.
Approving
The cluster leg -- the one you cannot run -- is green here on this sha at 219 passed / 622
checks / RC=0, which agrees with CI's. Merging stays yours.
…rompt#432) 45 collected tests, 184 checks, across the bash suite's eight arms: the SQL surface and its refusals by SQLSTATE, "it only reorders", the recorded sorted_kind, the self-gate in every direction, the single-column identity, the vacuum_sorted ruling, the daemon, and the enumerations. compare_to_bash.py reports 0 MISSING both as it ships (124 bash checks) and under commandprompt#1044's widened extractor (133). The difference is this suite's nine check_unrunnable sites, all twinned, and zero check_skip -- so it is the first exercise of that widening on a suite that USES those helpers correctly rather than on hilbert_locality, where they were broken. TWO PLACES THE PORT ASSERTS WHAT THE ORIGINAL GETS FOR FREE, and they are one class: wherever a port replaces a STRUCTURAL guarantee with a PROCEDURAL one, it owes an arm the original does not need. The bash suite gives the daemon's naptime and thresholds to the server through PGC_EXTRA_CONF, so they are in postgresql.conf before the postmaster starts and the suite cannot run without them. pgc_cluster.py has no such hook, so the port sets them with ALTER SYSTEM and a reload -- available because all three are PGC_SIGHUP -- and that can silently not take effect. Silent in the worst way: at the default naptime the daemon still acts, the poll still sees the tail fold, and every S7 arm passes while the values were never in force. So they are read back from the server. The arm failed on its first run with got '2s/0.2/0.05' want '2/0.2/0.05' because SHOW returns the unit, which is the cheapest demonstration that it reads the server rather than restating the ALTER SYSTEM above it. Same for max_parallel_workers_per_gather = 0, which the fixture SET and nothing read back until the parity tool reported the bash premise as MISSING. A RELOAD IS NOT A READ. ALTER SYSTEM SET pgcolumnar.autovacuum = on, then pg_reload_conf(), then SHOW on the same connection returned 'off': a reload signals the postmaster and an already-open backend absorbs it at its next command boundary, and this module runs everything through ONE connection by design. The bash suite never meets it because every q is a fresh psql. This is inherited by every later port that changes postmaster-level state. EVERY CONDITIONAL check_unrunnable IS ITS OWN TEST. expect.cannot_run() records under the REASON code, so two refusals in one test collapse onto a single UNMET_PRECONDITION record and neither can be told from the other. S1's fixture lives in its own schema, which adds a way to pass wrongly that the bash suite cannot have: without schema USAGE the probe raises 42P01, and granting it naively lets a SCHEMA 42501 satisfy an OWNERSHIP 42501 arm. Closed by a premise beside the existing EXECUTE one. _sqlstate distinguishes its three outcomes by TYPE rather than by scraping text -- psycopg raises OperationalError from connect() where psql needs a sentinel statement to tell "no error" from "never reached the server". Both controls are kept anyway: a port that dropped the NOLOGIN control because psycopg makes that shape impossible would be asserting its own implementation rather than the property. ONE CHANGE OUTSIDE THE PORT. test_compare_to_bash.py's standing arm iterates a HARD-CODED list of stems, so a new pair is not graded by it unless it is added -- and nothing fails when it is missing: the arm passes, grading the pairs it knows about, and reports a clean verdict for a tree it has not fully looked at. hilbert_cluster is now in that list and it grades eight pairs. Found by chasing a +1: cluster-half checks moved 805 -> 806 across the rebase, which is commandprompt#1044's inventory arm doing one more expect.at_least now that test_compare_to_bash.py is a declared crossing. Not this branch's, and correct. Verified: the file alone 45 passed, 184 checks, 184 pass + 0 fail + 0 unrun guard half CI-style 316 passed, 785 checks, RC=0 cluster half 264 passed, 806 checks, RC=0 parity 133 bash names (123 distinct), 0 MISSING docs_style 14/14 expected_tests.txt cluster_tests 219 -> 264, and BOTH keys re-derived by collection on the merged tree: guard 316 stated 316, cluster 264 stated 264. commandprompt#1044 moved guard_tests on a different line so git kept both with no conflict, which is not evidence -- the failure that file exists for produces no conflict either. guard_tests is unchanged: this file needs a database, so it is not in NO_CLUSTER. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012RSw4qMHS7ByE7PY8Ns4cs
…rompt#432) 45 collected tests, 184 checks, across the bash suite's eight arms: the SQL surface and its refusals by SQLSTATE, "it only reorders", the recorded sorted_kind, the self-gate in every direction, the single-column identity, the vacuum_sorted ruling, the daemon, and the enumerations. compare_to_bash.py reports 0 MISSING both as it ships (124 bash checks) and under commandprompt#1044's widened extractor (133). The difference is this suite's nine check_unrunnable sites, all twinned, and zero check_skip -- so it is the first exercise of that widening on a suite that USES those helpers correctly rather than on hilbert_locality, where they were broken. TWO PLACES THE PORT ASSERTS WHAT THE ORIGINAL GETS FOR FREE, and they are one class: wherever a port replaces a STRUCTURAL guarantee with a PROCEDURAL one, it owes an arm the original does not need. The bash suite gives the daemon's naptime and thresholds to the server through PGC_EXTRA_CONF, so they are in postgresql.conf before the postmaster starts and the suite cannot run without them. pgc_cluster.py has no such hook, so the port sets them with ALTER SYSTEM and a reload -- available because all three are PGC_SIGHUP -- and that can silently not take effect. Silent in the worst way: at the default naptime the daemon still acts, the poll still sees the tail fold, and every S7 arm passes while the values were never in force. So they are read back from the server. The arm failed on its first run with got '2s/0.2/0.05' want '2/0.2/0.05' because SHOW returns the unit, which is the cheapest demonstration that it reads the server rather than restating the ALTER SYSTEM above it. Same for max_parallel_workers_per_gather = 0, which the fixture SET and nothing read back until the parity tool reported the bash premise as MISSING. A RELOAD IS NOT A READ. ALTER SYSTEM SET pgcolumnar.autovacuum = on, then pg_reload_conf(), then SHOW on the same connection returned 'off': a reload signals the postmaster and an already-open backend absorbs it at its next command boundary, and this module runs everything through ONE connection by design. The bash suite never meets it because every q is a fresh psql. This is inherited by every later port that changes postmaster-level state. EVERY CONDITIONAL check_unrunnable IS ITS OWN TEST. expect.cannot_run() records under the REASON code, so two refusals in one test collapse onto a single UNMET_PRECONDITION record and neither can be told from the other. S1's fixture lives in its own schema, which adds a way to pass wrongly that the bash suite cannot have: without schema USAGE the probe raises 42P01, and granting it naively lets a SCHEMA 42501 satisfy an OWNERSHIP 42501 arm. Closed by a premise beside the existing EXECUTE one. _sqlstate distinguishes its three outcomes by TYPE rather than by scraping text -- psycopg raises OperationalError from connect() where psql needs a sentinel statement to tell "no error" from "never reached the server". Both controls are kept anyway: a port that dropped the NOLOGIN control because psycopg makes that shape impossible would be asserting its own implementation rather than the property. ONE CHANGE OUTSIDE THE PORT. test_compare_to_bash.py's standing arm iterates a HARD-CODED list of stems, so a new pair is not graded by it unless it is added -- and nothing fails when it is missing: the arm passes, grading the pairs it knows about, and reports a clean verdict for a tree it has not fully looked at. hilbert_cluster is now in that list and it grades eight pairs. Found by chasing a +1: cluster-half checks moved 805 -> 806 across the rebase, which is commandprompt#1044's inventory arm doing one more expect.at_least now that test_compare_to_bash.py is a declared crossing. Not this branch's, and correct. Verified: the file alone 45 passed, 184 checks, 184 pass + 0 fail + 0 unrun guard half CI-style 316 passed, 785 checks, RC=0 cluster half 264 passed, 806 checks, RC=0 parity 133 bash names (123 distinct), 0 MISSING docs_style 14/14 expected_tests.txt cluster_tests 219 -> 264, and BOTH keys re-derived by collection on the merged tree: guard 316 stated 316, cluster 264 stated 264. commandprompt#1044 moved guard_tests on a different line so git kept both with no conflict, which is not evidence -- the failure that file exists for produces no conflict either. guard_tests is unchanged: this file needs a database, so it is not in NO_CLUSTER. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012RSw4qMHS7ByE7PY8Ns4cs
…rompt#432) 45 collected tests, 184 checks, across the bash suite's eight arms: the SQL surface and its refusals by SQLSTATE, "it only reorders", the recorded sorted_kind, the self-gate in every direction, the single-column identity, the vacuum_sorted ruling, the daemon, and the enumerations. compare_to_bash.py reports 0 MISSING both as it ships (124 bash checks) and under commandprompt#1044's widened extractor (133). The difference is this suite's nine check_unrunnable sites, all twinned, and zero check_skip -- so it is the first exercise of that widening on a suite that USES those helpers correctly rather than on hilbert_locality, where they were broken. TWO PLACES THE PORT ASSERTS WHAT THE ORIGINAL GETS FOR FREE, and they are one class: wherever a port replaces a STRUCTURAL guarantee with a PROCEDURAL one, it owes an arm the original does not need. The bash suite gives the daemon's naptime and thresholds to the server through PGC_EXTRA_CONF, so they are in postgresql.conf before the postmaster starts and the suite cannot run without them. pgc_cluster.py has no such hook, so the port sets them with ALTER SYSTEM and a reload -- available because all three are PGC_SIGHUP -- and that can silently not take effect. Silent in the worst way: at the default naptime the daemon still acts, the poll still sees the tail fold, and every S7 arm passes while the values were never in force. So they are read back from the server. The arm failed on its first run with got '2s/0.2/0.05' want '2/0.2/0.05' because SHOW returns the unit, which is the cheapest demonstration that it reads the server rather than restating the ALTER SYSTEM above it. Same for max_parallel_workers_per_gather = 0, which the fixture SET and nothing read back until the parity tool reported the bash premise as MISSING. A RELOAD IS NOT A READ. ALTER SYSTEM SET pgcolumnar.autovacuum = on, then pg_reload_conf(), then SHOW on the same connection returned 'off': a reload signals the postmaster and an already-open backend absorbs it at its next command boundary, and this module runs everything through ONE connection by design. The bash suite never meets it because every q is a fresh psql. This is inherited by every later port that changes postmaster-level state. EVERY CONDITIONAL check_unrunnable IS ITS OWN TEST. expect.cannot_run() records under the REASON code, so two refusals in one test collapse onto a single UNMET_PRECONDITION record and neither can be told from the other. S1's fixture lives in its own schema, which adds a way to pass wrongly that the bash suite cannot have: without schema USAGE the probe raises 42P01, and granting it naively lets a SCHEMA 42501 satisfy an OWNERSHIP 42501 arm. Closed by a premise beside the existing EXECUTE one. _sqlstate distinguishes its three outcomes by TYPE rather than by scraping text -- psycopg raises OperationalError from connect() where psql needs a sentinel statement to tell "no error" from "never reached the server". Both controls are kept anyway: a port that dropped the NOLOGIN control because psycopg makes that shape impossible would be asserting its own implementation rather than the property. ONE CHANGE OUTSIDE THE PORT. test_compare_to_bash.py's standing arm iterates a HARD-CODED list of stems, so a new pair is not graded by it unless it is added -- and nothing fails when it is missing: the arm passes, grading the pairs it knows about, and reports a clean verdict for a tree it has not fully looked at. hilbert_cluster is now in that list and it grades eight pairs. Found by chasing a +1: cluster-half checks moved 805 -> 806 across the rebase, which is commandprompt#1044's inventory arm doing one more expect.at_least now that test_compare_to_bash.py is a declared crossing. Not this branch's, and correct. THE DAEMON ARM RECORDS HOW LONG IT WAITED. A fixture one poll from its window and one fourteen from it produce identical greens, so the count is the only thing that distinguishes them. Measured on all five assert builds: PG15 PG16 PG17 PG18 PG19 -> 1 poll each, of 15, at a 2s naptime The arm bounds it at 5, not 15: fourteen polls of headroom, and a run needing six has drifted rather than been unlucky. The bound comes from the measurement because a ceiling nothing can exceed asserts nothing. Raised by @jdatcmd, who has no PostgreSQL on their host, said so rather than reading the loop and offering a view, and proposed the shape; the measurement is this branch's and is the only one that exists. Verified: the file alone 45 passed, 185 checks, 185 pass + 0 fail + 0 unrun on all five majors 45 passed, 185 checks, each guard half CI-style 316 passed, 785 checks, RC=0 cluster half 264 passed, 807 checks, RC=0 parity 133 bash names (123 distinct), 0 MISSING docs_style 14/14 expected_tests.txt cluster_tests 219 -> 264, and BOTH keys re-derived by collection on the merged tree: guard 316 stated 316, cluster 264 stated 264. commandprompt#1044 moved guard_tests on a different line so git kept both with no conflict, which is not evidence -- the failure that file exists for produces no conflict either. guard_tests is unchanged: this file needs a database, so it is not in NO_CLUSTER. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012RSw4qMHS7ByE7PY8Ns4cs
…rompt#432) 45 collected tests, 184 checks, across the bash suite's eight arms: the SQL surface and its refusals by SQLSTATE, "it only reorders", the recorded sorted_kind, the self-gate in every direction, the single-column identity, the vacuum_sorted ruling, the daemon, and the enumerations. compare_to_bash.py reports 0 MISSING both as it ships (124 bash checks) and under commandprompt#1044's widened extractor (133). The difference is this suite's nine check_unrunnable sites, all twinned, and zero check_skip -- so it is the first exercise of that widening on a suite that USES those helpers correctly rather than on hilbert_locality, where they were broken. TWO PLACES THE PORT ASSERTS WHAT THE ORIGINAL GETS FOR FREE, and they are one class: wherever a port replaces a STRUCTURAL guarantee with a PROCEDURAL one, it owes an arm the original does not need. The bash suite gives the daemon's naptime and thresholds to the server through PGC_EXTRA_CONF, so they are in postgresql.conf before the postmaster starts and the suite cannot run without them. pgc_cluster.py has no such hook, so the port sets them with ALTER SYSTEM and a reload -- available because all three are PGC_SIGHUP -- and that can silently not take effect. Silent in the worst way: at the default naptime the daemon still acts, the poll still sees the tail fold, and every S7 arm passes while the values were never in force. So they are read back from the server. The arm failed on its first run with got '2s/0.2/0.05' want '2/0.2/0.05' because SHOW returns the unit, which is the cheapest demonstration that it reads the server rather than restating the ALTER SYSTEM above it. Same for max_parallel_workers_per_gather = 0, which the fixture SET and nothing read back until the parity tool reported the bash premise as MISSING. A RELOAD IS NOT A READ. ALTER SYSTEM SET pgcolumnar.autovacuum = on, then pg_reload_conf(), then SHOW on the same connection returned 'off': a reload signals the postmaster and an already-open backend absorbs it at its next command boundary, and this module runs everything through ONE connection by design. The bash suite never meets it because every q is a fresh psql. This is inherited by every later port that changes postmaster-level state. EVERY CONDITIONAL check_unrunnable IS ITS OWN TEST. expect.cannot_run() records under the REASON code, so two refusals in one test collapse onto a single UNMET_PRECONDITION record and neither can be told from the other. S1's fixture lives in its own schema, which adds a way to pass wrongly that the bash suite cannot have: without schema USAGE the probe raises 42P01, and granting it naively lets a SCHEMA 42501 satisfy an OWNERSHIP 42501 arm. Closed by a premise beside the existing EXECUTE one. _sqlstate distinguishes its three outcomes by TYPE rather than by scraping text -- psycopg raises OperationalError from connect() where psql needs a sentinel statement to tell "no error" from "never reached the server". Both controls are kept anyway: a port that dropped the NOLOGIN control because psycopg makes that shape impossible would be asserting its own implementation rather than the property. ONE CHANGE OUTSIDE THE PORT. test_compare_to_bash.py's standing arm iterates a HARD-CODED list of stems, so a new pair is not graded by it unless it is added -- and nothing fails when it is missing: the arm passes, grading the pairs it knows about, and reports a clean verdict for a tree it has not fully looked at. hilbert_cluster is now in that list and it grades eight pairs. Found by chasing a +1: cluster-half checks moved 805 -> 806 across the rebase, which is commandprompt#1044's inventory arm doing one more expect.at_least now that test_compare_to_bash.py is a declared crossing. Not this branch's, and correct. THE DAEMON ARM RECORDS HOW LONG IT WAITED. A fixture one poll from its window and one fourteen from it produce identical greens, so the count is the only thing that distinguishes them. Measured on all five assert builds: PG15 PG16 PG17 PG18 PG19 -> 1 poll each, of 15, at a 2s naptime READ THAT FOR WHAT IT DOES NOT SAY. One poll everywhere means the loop NEVER WAITED -- true on the first check each time -- so the distribution is a SINGLE POINT on an idle container, and a loop that always succeeds on poll one is indistinguishable from no loop. The bound of 5 is four above the only value ever observed and no loaded measurement exists; the case it will meet is a shared CI runner. A reader meeting a red at six is the first to see the loop work, not someone looking at a regression. (@jdatcmd, who would not let the artefact claim more than five idle single-point runs support.) Raised by @jdatcmd, who has no PostgreSQL on their host, said so rather than reading the loop and offering a view, and proposed the shape; the measurement is this branch's and is the only one that exists. Verified: the file alone 45 passed, 185 checks, 185 pass + 0 fail + 0 unrun on all five majors 45 passed, 185 checks, each guard half CI-style 316 passed, 785 checks, RC=0 cluster half 264 passed, 807 checks, RC=0 parity 133 bash names (123 distinct), 0 MISSING docs_style 14/14 expected_tests.txt cluster_tests 219 -> 264, and BOTH keys re-derived by collection on the merged tree: guard 316 stated 316, cluster 264 stated 264. commandprompt#1044 moved guard_tests on a different line so git kept both with no conflict, which is not evidence -- the failure that file exists for produces no conflict either. guard_tests is unchanged: this file needs a database, so it is not in NO_CLUSTER. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012RSw4qMHS7ByE7PY8Ns4cs
…rompt#432) 45 collected tests, 184 checks, across the bash suite's eight arms: the SQL surface and its refusals by SQLSTATE, "it only reorders", the recorded sorted_kind, the self-gate in every direction, the single-column identity, the vacuum_sorted ruling, the daemon, and the enumerations. compare_to_bash.py reports 0 MISSING both as it ships (124 bash checks) and under commandprompt#1044's widened extractor (133). The difference is this suite's nine check_unrunnable sites, all twinned, and zero check_skip -- so it is the first exercise of that widening on a suite that USES those helpers correctly rather than on hilbert_locality, where they were broken. TWO PLACES THE PORT ASSERTS WHAT THE ORIGINAL GETS FOR FREE, and they are one class: wherever a port replaces a STRUCTURAL guarantee with a PROCEDURAL one, it owes an arm the original does not need. The bash suite gives the daemon's naptime and thresholds to the server through PGC_EXTRA_CONF, so they are in postgresql.conf before the postmaster starts and the suite cannot run without them. pgc_cluster.py has no such hook, so the port sets them with ALTER SYSTEM and a reload -- available because all three are PGC_SIGHUP -- and that can silently not take effect. Silent in the worst way: at the default naptime the daemon still acts, the poll still sees the tail fold, and every S7 arm passes while the values were never in force. So they are read back from the server. The arm failed on its first run with got '2s/0.2/0.05' want '2/0.2/0.05' because SHOW returns the unit, which is the cheapest demonstration that it reads the server rather than restating the ALTER SYSTEM above it. Same for max_parallel_workers_per_gather = 0, which the fixture SET and nothing read back until the parity tool reported the bash premise as MISSING. A RELOAD IS NOT A READ. ALTER SYSTEM SET pgcolumnar.autovacuum = on, then pg_reload_conf(), then SHOW on the same connection returned 'off': a reload signals the postmaster and an already-open backend absorbs it at its next command boundary, and this module runs everything through ONE connection by design. The bash suite never meets it because every q is a fresh psql. This is inherited by every later port that changes postmaster-level state. EVERY CONDITIONAL check_unrunnable IS ITS OWN TEST. expect.cannot_run() records under the REASON code, so two refusals in one test collapse onto a single UNMET_PRECONDITION record and neither can be told from the other. S1's fixture lives in its own schema, which adds a way to pass wrongly that the bash suite cannot have: without schema USAGE the probe raises 42P01, and granting it naively lets a SCHEMA 42501 satisfy an OWNERSHIP 42501 arm. Closed by a premise beside the existing EXECUTE one. _sqlstate distinguishes its three outcomes by TYPE rather than by scraping text -- psycopg raises OperationalError from connect() where psql needs a sentinel statement to tell "no error" from "never reached the server". Both controls are kept anyway: a port that dropped the NOLOGIN control because psycopg makes that shape impossible would be asserting its own implementation rather than the property. ONE CHANGE OUTSIDE THE PORT. test_compare_to_bash.py's standing arm iterates a HARD-CODED list of stems, so a new pair is not graded by it unless it is added -- and nothing fails when it is missing: the arm passes, grading the pairs it knows about, and reports a clean verdict for a tree it has not fully looked at. hilbert_cluster is now in that list and it grades eight pairs. Found by chasing a +1: cluster-half checks moved 805 -> 806 across the rebase, which is commandprompt#1044's inventory arm doing one more expect.at_least now that test_compare_to_bash.py is a declared crossing. Not this branch's, and correct. THE DAEMON ARM RECORDS HOW LONG IT WAITED. A fixture one poll from its window and one fourteen from it produce identical greens, so the count is the only thing that distinguishes them. Measured on all five assert builds: PG15 PG16 PG17 PG18 PG19 -> 1 poll each, of 15, at a 2s naptime READ THAT FOR WHAT IT DOES NOT SAY. One poll everywhere means the loop NEVER WAITED -- true on the first check each time -- so the distribution is a SINGLE POINT on an idle container, and a loop that always succeeds on poll one is indistinguishable from no loop. The bound of 5 is four above the only value ever observed and no loaded measurement exists; the case it will meet is a shared CI runner. A reader meeting a red at six is the first to see the loop work, not someone looking at a regression. (@jdatcmd, who would not let the artefact claim more than five idle single-point runs support.) Raised by @jdatcmd, who has no PostgreSQL on their host, said so rather than reading the loop and offering a view, and proposed the shape; the measurement is this branch's and is the only one that exists. Verified: the file alone 45 passed, 185 checks, 185 pass + 0 fail + 0 unrun on all five majors 45 passed, 185 checks, each guard half CI-style 316 passed, 785 checks, RC=0 cluster half 264 passed, 807 checks, RC=0 parity 133 bash names (123 distinct), 0 MISSING docs_style 14/14 expected_tests.txt cluster_tests 219 -> 264, and BOTH keys re-derived by collection on the merged tree: guard 316 stated 316, cluster 264 stated 264. commandprompt#1044 moved guard_tests on a different line so git kept both with no conflict, which is not evidence -- the failure that file exists for produces no conflict either. guard_tests is unchanged: this file needs a database, so it is not in NO_CLUSTER. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012RSw4qMHS7ByE7PY8Ns4cs
The other half of #1040. #1041 fixed the two
hilbert_localitynames this was hiding; this makes the grader able to see the class at all.The defect
The bash side of the parity tool matched
check(?:_num|_ratio|_text|_timing)?, so three of the eight helperslib.shdefines were invisible:A property asserted through one of the three was never reported MISSING and could not move
rc. So a pair could grade one-for-one on the strength of the grader's blind spot — whichhilbert_localitywas.It never drifted out of date.
0cbf574is the commit that introduced that pattern, andcheck_unrunnablealready had 21 call sites that day. Same shape as #1036 and #1038 on the python side — a rule true of most of a class taken for a property of the class — sitting on the other side of the same tool, unexamined because fixing one end thoroughly did not prompt anyone to look at the other.Three arms, two of which state what the tool does NOT cover
test_the_extractor_reads_every_check_helper_lib_sh_defineslib.sh's DEFINITIONS, failing with the helper namedtest_a_longer_helper_name_is_not_shadowed_by_a_shorter_onetest_the_suite_local_helpers_are_known_and_excludedThe helper list is hand-written so the tool stays standalone (
ast,re,sys), and it is pinned exactly the way_NAME_ARGis.A claim of mine that was overstated, corrected after measuring
The entries are longest-first, and the code said a shorter prefix would otherwise shadow a longer helper. That is false in Python.
rebacktracks across alternatives, so a pure reorder reads both names identically — measured, and the arm stays green under it.What the OLD pattern could not do was read
check_ratio_needs_quiet_machineat all: it matchescheck_ratio, wants whitespace, finds_needs, backtracks to the empty option, wants whitespace aftercheck, and fails.So the arm pins the shape and the ordering is readability. Corrected in the code comment, the docstring and
TESTS.mdrather than left reading as load-bearing.Population
checkorcheck_<something>, notcheck[a-z_]*. The loose form also matcheschecks_inindecode_interrupts.sh, a counting utility that returns a number and records nothing. It was in the suite-local list until the arm printed it and I read the definition.Removal proof
Each mutation asserted to apply by md5, tree restored to baseline after each.
check_unrunnablefrom the listcheck_skipfrom the listWhat this changes for a suite that uses those helpers CORRECTLY
Found by @OffgridwithJD while porting
hilbert_cluster, because their port grades against this tool; re-measured here before quoting it.hilbert_localitywas the broken case — two unrunnable names with no runnable twin, which #1041 fixed.hilbert_clusteris the opposite, and it is the first real exercise of this widening:check_unrunnablesites, invisible until nowcheck_skipsitesAll nine of those names are twinned by a runnable check in the same file — confirmed two ways, by the merged sweep tool reporting no
MISMATCHfor that suite, and by matching each unrunnable name against the runnable names directly: 9 of 9 twinned, 0 orphans.So the widening adds nine names to that suite's bash side and should produce zero MISSING against a complete port. Nothing reddens here —
hilbert_clusterhas no pytest twin yet — but a port of it now has a definition of done nine names larger, and that number moves the moment this merges. @OffgridwithJD is targeting 133 rather than 124 for exactly that reason.The crossing is declared, and why the scan's scope is now written down
The drift guard reads the shell harness's helper library by a hard-coded path, which the harness-independence inventory reddens: a python file naming a shell path. Declared rather than redesigned. The inventory is asserted in both directions, so a declaration that stops being true reddens too, and each entry must carry a mechanism a reader can check. The justification is that the alternative is a hand-maintained copy of the helper list inside pytest — the exact value this issue is about.
While reviewing that, @OffgridwithJD argued that
.github/scripts/is exempt by design, since two tools there readtest/*.shand appear in no inventory — and therefore that movingcompare_to_bash.pythere would delete the crossing rather than declare it.The inventory's scan is
test/pytest/*.py, so that population is a LOCATION, and absent-from-the-report and outside-the-scan produce identical evidence. Two readings were live and the artefact did not say which.Both halves of that are worth stating precisely:
jd ruled: the scope is deliberate. The inventory is the pytest corpus's self-guard;
.github/scripts/is CI tooling belonging to neither harness, so a tool there reading both sides is what it is for. Those two tools are out of scope rather than exempt. Recorded in the declaration's comment, along with the consequence a reader acts on: a move to.github/scripts/is defensible on design grounds, never because it makes the arm stop reporting the file.Verification
Against
main0d17a87, re-derived there rather than carried from the head it was written on:Which half of the corpus that is, and which I did not run. Everything above is the
GUARD half — the database-free files, run exactly as
ci.yml'spytest (harness guards, no database)job runs them, in a venv pinned fromrequirements-test.txtwith the driver absent. I did not run the cluster half:there is no PostgreSQL on this host, so
pytest (cluster tests, with the driver)isCI's evidence and not mine. Stated because a review is more useful when it says which
leg belongs to whom, and because this PR's first CI run failed exactly there.
No
.shchanged.pgc_vacuity.pyuntouched — an earlier draft added a keyword-onlyname=tocannot_runso the port could mirror bash's four unrunnable records; @OffgridwithJD showed that replicates a bash inconsistency instead of fixing it, and #1041 fixed it at the source. That work is dropped, not parked in this PR.🤖 Generated with Claude Code
https://claude.ai/code/session_01NhwXKAgSmYDUjteWkfajHK