From e5d278ff82773b774d9778ced1e99b067fd97793 Mon Sep 17 00:00:00 2001 From: "Joshua D. Drake" Date: Thu, 10 Sep 2026 11:59:20 -0600 Subject: [PATCH 1/3] docs: the two test harnesses are parallel, not coupled Owner's rule, 2026-09-10: the shell tests and the python tests are parallel in functionality but must not call, import or reference each other. Documentation is the only exception. It follows from the twin rule already in this section. That rule's stated point is that "where they disagree, one of them is wrong". Two harnesses can only disagree if they are two measurements, and a pytest test that drives lib.sh by subprocess is not a second measurement -- it is the first one wearing a Python wrapper. It agrees with the shell by construction and can never report the shell wrong, which turns the twin from evidence into a mirror. The starting debt is measured rather than asserted, with comments and docstrings stripped so the counts are of executable references: 13 sites in 2 python files reach into shell, and 27 lines in 7 shell files take python as their subject. A fixture that merely resembles the other harness is not a reference to it, so test_build_refusal.py's fake lib.sh under tmp_path is excluded and said to be. The paragraph fixes none of that. It records which direction those files are expected to move, and makes the count falsifiable. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01Unuuvh3fRR67SceiGpfeeK --- CONTEXT.md | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/CONTEXT.md b/CONTEXT.md index f93a1d9e..231a8bc4 100644 --- a/CONTEXT.md +++ b/CONTEXT.md @@ -175,6 +175,48 @@ outside the gate. Writing both keeps the two harnesses honest about each other: where they disagree, one of them is wrong, and that is worth finding at the time rather than during a port. +**The two harnesses are parallel in functionality, and independent in +implementation. They must not call, import or reference each other.** Owner's +rule, 2026-09-10. Documentation is the only exception: prose, comments and +docstrings may name the other harness freely. + +This follows from the paragraph above. Two harnesses can only disagree if they +are two measurements. A pytest test that drives `test/lib.sh` by subprocess is +not a second measurement of the property -- it is the first measurement wearing a +Python wrapper, so it agrees with the shell by construction and can never report +the shell wrong. The coupling turns the twin from evidence into a mirror, and a +mirror is what the twin rule exists to avoid. + +So each harness asserts the property against **the product**, in its own terms, +never against the other harness's implementation. Building a throwaway fixture +that merely resembles the other side -- writing a fake `lib.sh` into a `tmp_path` +-- is not a reference to it; sourcing the real one is. If a property can only be +expressed by driving the other side, that is a signal it belongs to one harness +alone: say which, and say why, rather than reaching across. + +**The debt this starts with, measured on 2026-09-10** rather than assumed, with +comments and docstrings stripped so the count is of executable references: + +- python that drives shell, 13 executable sites in 2 files: + `test_suite_accounting.py` 12 (the real `lib.sh` and `run_all_versions.sh`) and + `pgc_cluster.py:357` 1 (sources the real `test/lib.sh`). A third arrives with + PR #923: `test_check_results_are_machine_readable.py`, sourcing `./lib.sh`. +- shell whose subject is python, 27 executable lines in 7 files: `lib.sh` 5, + `selftest/380` 8, `selftest/350` 5, `selftest/040` 3, `selftest/360` 3, + `selftest/370` 2, `selftest/030` 1. + +Not counted, because the rule permits them: `test_build_refusal.py` writes a fake +`lib.sh` into a `tmp_path` and drives that, which is a fixture rather than a +reference, and `conftest.py` names `lib.sh` only in a help string and a comment. + +None of that is fixed by this paragraph. It is written down so the next change to +any of those files knows which direction it is expected to move, and so the count +is falsifiable rather than a vague sense that some coupling exists. + +When you sweep for this yourself, do not write the pattern as `[a-z_]+\.sh`: it +matches `sharedir`, and reported `pg_config --sharedir` calls as violations the +first time this was counted. + **A sequencing note that will stop being true.** As of 2026-09-09 the pytest harness is PR #897 and is not on `main`, so this rule cannot be satisfied for a test written today. Until it lands, write the `.sh` suite, write the pytest twin From b82dd6b8fc68f6d025274f80b179f54e4b5279bc Mon Sep 17 00:00:00 2001 From: "Joshua D. Drake" Date: Thu, 10 Sep 2026 12:43:03 -0600 Subject: [PATCH 2/3] docs: the inventory named its largest item as the counter-example @OffgridwithJD checked the inventory instead of believing it and found the example chosen to show what is NOT debt is the biggest debt in the list. test_build_refusal.py does both: it writes a fake test/lib.sh under a tmp_path and drives that, and it also sources the REAL one in three helpers -- _sh at 377, _sh_fp at 486, _sh_fp_as at 538 -- behind 39 call sites. The first draft read the fake tree, stopped there, and called the file clean. So the section now states the counting rule beside the numbers, which is what the reviewer asked for: a total nobody can re-derive is an assertion, not a measurement. Three different patterns gave three different line totals while this was being settled, including one of mine that reported 24 sites in test_suite_accounting.py and another that reported 2. The unit is therefore the FILE, and each file is named with the mechanism that makes it a reference -- which is also what has to change for it to stop being one. The inventory is 4 python files and 7 shell files. test_build_refusal.py is kept as the worked example, correctly this time, because one file doing both is exactly where the fixture/reference line has to be drawn. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01Unuuvh3fRR67SceiGpfeeK --- CONTEXT.md | 63 +++++++++++++++++++++++++++++++++++------------------- 1 file changed, 41 insertions(+), 22 deletions(-) diff --git a/CONTEXT.md b/CONTEXT.md index 231a8bc4..f9667f7c 100644 --- a/CONTEXT.md +++ b/CONTEXT.md @@ -194,28 +194,47 @@ that merely resembles the other side -- writing a fake `lib.sh` into a `tmp_path expressed by driving the other side, that is a signal it belongs to one harness alone: say which, and say why, rather than reaching across. -**The debt this starts with, measured on 2026-09-10** rather than assumed, with -comments and docstrings stripped so the count is of executable references: - -- python that drives shell, 13 executable sites in 2 files: - `test_suite_accounting.py` 12 (the real `lib.sh` and `run_all_versions.sh`) and - `pgc_cluster.py:357` 1 (sources the real `test/lib.sh`). A third arrives with - PR #923: `test_check_results_are_machine_readable.py`, sourcing `./lib.sh`. -- shell whose subject is python, 27 executable lines in 7 files: `lib.sh` 5, - `selftest/380` 8, `selftest/350` 5, `selftest/040` 3, `selftest/360` 3, - `selftest/370` 2, `selftest/030` 1. - -Not counted, because the rule permits them: `test_build_refusal.py` writes a fake -`lib.sh` into a `tmp_path` and drives that, which is a fixture rather than a -reference, and `conftest.py` names `lib.sh` only in a help string and a comment. - -None of that is fixed by this paragraph. It is written down so the next change to -any of those files knows which direction it is expected to move, and so the count -is falsifiable rather than a vague sense that some coupling exists. - -When you sweep for this yourself, do not write the pattern as `[a-z_]+\.sh`: it -matches `sharedir`, and reported `pg_config --sharedir` calls as violations the -first time this was counted. +**The counting rule, so the inventory can be re-derived.** A **reference** is an +executable line that names a file belonging to the other harness *as it exists in +this tree* -- sourcing it, importing it, running it, or reading its text. Three +things are not references, in the order they get confused: + +1. prose. Comments, docstrings and help strings may name the other harness freely. +2. a file the test **builds itself** under a `tmp_path`, even with the same name. +3. a word that merely looks like a filename. `sharedir` is not a `.sh` file. + +**Count files, not lines.** A line total moves with any refactor and with the +exact pattern used, and three different patterns gave three different totals when +this was first counted. The file is the stable unit, so each file below is named +with the mechanism that makes it a reference -- which is also what has to change +for it to stop being one. + +**The debt this starts with, on 2026-09-10: 4 python files and 7 shell files.** + +Python that reaches into shell: + +- `test_build_refusal.py` -- sources the real `test/lib.sh` in three helpers + (`_sh`, `_sh_fp`, `_sh_fp_as`), behind 39 call sites. The largest of these. +- `test_suite_accounting.py` -- reads `run_all_versions.sh`'s text, sources the + real `lib.sh` from a suite it writes, and executes the real runner. +- `pgc_cluster.py` -- sources the real `test/lib.sh`. +- `test_check_results_are_machine_readable.py` -- sources `./lib.sh`. Arrives + with PR #923; not on `main` yet. + +Shell whose subject is python: `lib.sh`, and `selftest/030`, `040`, `350`, `360`, +`370`, `380`. + +**`test_build_refusal.py` is the example worth studying, because it does both.** +It writes a fake `test/lib.sh` into a `tmp_path` and drives that -- rule 2, not a +reference -- and it *also* sources the real one three times. The first draft of +this section read the fake tree, called the file "not debt", and used it as the +illustration of what the rule permits. It is in fact the largest single item in +the list. Reported by @OffgridwithJD, who checked the inventory instead of +believing it. Judge a file by what it executes, not by the fixture it builds. + +None of that is fixed by this section. It records which direction those files are +expected to move, and makes the inventory falsifiable rather than a vague sense +that some coupling exists. **A sequencing note that will stop being true.** As of 2026-09-09 the pytest harness is PR #897 and is not on `main`, so this rule cannot be satisfied for a From 142cd9fcf0e4af10acd267f2803c720be0a92dac Mon Sep 17 00:00:00 2001 From: "Joshua D. Drake" Date: Thu, 10 Sep 2026 13:14:04 -0600 Subject: [PATCH 3/3] docs: the entry stating the sweep trap was written by falling into it Two corrections from @OffgridwithJD's review, and the first one is the document's own rule 3 catching the document. "39 call sites" was 36 calls plus the 3 definitions. The pattern `[^_a-z]_sh(` matches `def _sh(` as readily as a call, which is the same class of error as `[a-z_]+\.sh` matching `sharedir` -- already written three lines above as the thing not to do. Counted with ast now, and the entry says how, because a number in this section has to be re-derivable or it does not belong here. The heading said 4 python files. Three are on main; the fourth arrives with PR #923. The entry always said so, the heading did not, and a reader who stops at the bold line gets a count that is wrong today. Recounted against main at aa53c1ba, after #927 and #931 landed: still 3 python files and 7 shell files. test_raises_sqlstate.py, new on main, adds neither -- it drives pytester, not the shell. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01Unuuvh3fRR67SceiGpfeeK --- CONTEXT.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/CONTEXT.md b/CONTEXT.md index f9667f7c..c43f816e 100644 --- a/CONTEXT.md +++ b/CONTEXT.md @@ -209,12 +209,16 @@ this was first counted. The file is the stable unit, so each file below is named with the mechanism that makes it a reference -- which is also what has to change for it to stop being one. -**The debt this starts with, on 2026-09-10: 4 python files and 7 shell files.** +**The debt this starts with, on 2026-09-10: 3 python files and 7 shell files**, +with a fourth python file arriving in PR #923. Python that reaches into shell: - `test_build_refusal.py` -- sources the real `test/lib.sh` in three helpers - (`_sh`, `_sh_fp`, `_sh_fp_as`), behind 39 call sites. The largest of these. + (`_sh`, `_sh_fp`, `_sh_fp_as`), behind 36 calls. The largest of these. Counted + with `ast`, not `grep`: the pattern `[^_a-z]_sh(` also matches `def _sh(`, which + is how the first draft said 39 -- 36 calls plus the 3 definitions. Reported by + @OffgridwithJD. Rule 3 above, caught in the very entry that states it. - `test_suite_accounting.py` -- reads `run_all_versions.sh`'s text, sources the real `lib.sh` from a suite it writes, and executes the real runner. - `pgc_cluster.py` -- sources the real `test/lib.sh`.