Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
efeae75
test: count a check and record it in one operation (#917)
jdatcmd Sep 10, 2026
8518505
Merge branch 'feat/916-reconcile-accounting' into feat/917-machine-re…
jdatcmd Sep 10, 2026
ad0ec53
Merge branch 'feat/916-reconcile-accounting' into feat/917-machine-re…
jdatcmd Sep 10, 2026
dd6a4d3
test: pgc_record paid four forks per check, and direct counter writes…
jdatcmd Sep 10, 2026
9260cea
test: a ledger of which checks have ever been red (#918)
jdatcmd Sep 10, 2026
5bf0a20
test: a record names WHICH PART asked the check, not just which suite…
jdatcmd Sep 10, 2026
ec25bdf
Merge branch 'feat/917-machine-readable-results' into feat/918-mutati…
jdatcmd Sep 10, 2026
5e1228c
test: the ledger keys on the part, because check names are a shared c…
jdatcmd Sep 10, 2026
671a946
test: two different premises were wearing one sentence, and the detec…
jdatcmd Sep 10, 2026
0a6b2d3
test: a skipped timing check is an outcome, so count it and record it…
jdatcmd Sep 10, 2026
2c03d3e
test: the records/checks mismatch must name its cause, and the shape …
jdatcmd Sep 10, 2026
dddee7f
docs: the CHANGELOG entry this change should have shipped with (#917)
jdatcmd Sep 10, 2026
2289f10
test: redesign the ledger so it is fed, so it can refuse, and so it c…
jdatcmd Sep 10, 2026
f34fee4
Merge remote-tracking branch 'origin/feat/917-machine-readable-result…
jdatcmd Sep 10, 2026
fb87237
test: the monotone ceiling was true of the tool and false of the wiri…
jdatcmd Sep 10, 2026
3d880f4
test: the ceiling's prior is resolved, never named after a remote (#918)
jdatcmd Sep 10, 2026
380f9f6
test: say how far behind the ceiling's prior is, because the route ch…
jdatcmd Sep 10, 2026
3c067de
test: a file absent at the prior has no ceiling to violate, and the t…
jdatcmd Sep 10, 2026
71a7c9b
test: an unresolvable ref is not the bootstrap case (#918)
jdatcmd Sep 10, 2026
51b73fd
test: selftest 330 ran a collect loop whose reconciliation could not …
jdatcmd Sep 10, 2026
ea28d2c
Merge main into the machine-readable results branch
jdatcmd Sep 10, 2026
ad16800
Merge main into the machine-readable results branch, and declare the …
jdatcmd Sep 10, 2026
595d701
test: the record count is not the record schema (#917)
jdatcmd Sep 10, 2026
5413053
test: a named SKIP is an outcome, so it is counted and recorded (#917)
jdatcmd Sep 10, 2026
ee49fe6
test: a check_skip must not read a name its own file never assigns (#…
jdatcmd Sep 10, 2026
89a0df3
test: braces, because $_v[ reads as an array expansion (#917)
jdatcmd Sep 10, 2026
272084e
test: say why the braces are there, since the arm is about exactly th…
jdatcmd Sep 10, 2026
72cd2e4
Merge the machine-readable results branch into the mutation ledger
jdatcmd Sep 10, 2026
aa07d47
test: drop the pytest twin that reaches into the shell harness (#917)
jdatcmd Sep 10, 2026
04c409a
Merge main into the machine-readable results branch
jdatcmd Sep 10, 2026
8e687fe
Merge the machine-readable results branch into the mutation ledger
jdatcmd Sep 10, 2026
c7334e4
test: the deleted entry left six orphaned comment lines (#917)
jdatcmd Sep 10, 2026
7dffd28
test: five integrity blockers on the ledger, and its base moved (#918)
jdatcmd Sep 10, 2026
96a594d
Merge main (#939, #940 and #941's siblings landed) into the machine-r…
jdatcmd Sep 11, 2026
87d60c7
Merge main (#941 landed) into the machine-readable branch
jdatcmd Sep 11, 2026
f5a908e
test: four string-compared grep -c counts this branch added (#929)
jdatcmd Sep 11, 2026
eeb3765
Merge commit 'f5a908e' into tmp925work
jdatcmd Sep 11, 2026
54cb94c
test: the manifest and one more grep -c the base move brought under n…
jdatcmd Sep 11, 2026
d012411
test: the ledger is the OTHER tree-wide artifact new parts invalidate…
jdatcmd Sep 11, 2026
f3fb7b6
test: the census is the THIRD artifact, and this time I looked for al…
jdatcmd Sep 11, 2026
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
20 changes: 19 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,21 @@ jobs:
steps:
- uses: actions/checkout@v4

# THE LEDGER GATE NEEDS THE BASE BRANCH TO EXIST (#918). It compares the
# tracked ceiling in `test/check_ledger_budget.txt` against the one already
# on the PR's target, and it FAILS CLOSED when no trustworthy prior can be
# read rather than quietly enforcing less.
#
# actions/checkout fetches one ref at depth 1, so without this the base is
# absent and the gate stops the run -- correctly, but for a reason that is
# this workflow's to fix rather than the author's. Depth 1 is enough: only
# the file at that commit is read.
- name: fetch the PR base, for the ledger ceiling comparison
if: github.base_ref != ''
run: |
git fetch --depth=1 origin \
"+refs/heads/${{ github.base_ref }}:refs/remotes/origin/${{ github.base_ref }}"

# The repository and the package lists first, on their own, because the
# cache key below is derived from the versions apt resolves and cannot be
# computed before the lists exist.
Expand Down Expand Up @@ -539,7 +554,10 @@ jobs:
- name: Collect logs on failure
if: failure()
run: |
for f in /tmp/pgcolumnar-matrix-*/*.log; do
# /tmp/pgcolumnar-logs, not the build directory: the runner removes
# each builddir at the end of its major, so this step used to search a
# path that no longer existed and print nothing.
for f in /tmp/pgcolumnar-logs/*.log /tmp/pgcolumnar-matrix-*/*.log; do
[ -e "$f" ] || continue
echo "===== $f ====="
tail -60 "$f"
Expand Down
72 changes: 72 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,78 @@ true until the next version shipped.

### Added

- Every check result is machine-readable, and counting a check is the same
operation as recording it (#917).

`check`, `check_num` and `check_text` printed `PASS` or `FAIL` and nothing
else, so proving that a mutation reddened one NAMED check meant grepping prose
and retyping the result. That is also how a reverted guard once reported plain
green while the check count fell from 190 to 186: the suite passed, and the only
evidence anything had changed was a number nobody compared.

`lib.sh` had eleven places that bumped `PGC_CHECKS`, each with its own outcome
line beside it, which is eleven chances to add a twelfth and forget the line.
`projections.sh` did exactly that with an `expect_fail` at ten call sites, for
as long as it existed. There is now one, `pgc_record`, so a helper cannot report
an outcome without being counted and cannot be counted without reporting one.
Every human line is byte-identical; 3,762 call sites is past what a careful
refactor can be trusted on, so both harnesses pin the exact strings.

Each record names the suite, the part, the check, the verdict and the reason.
The part matters because `harness_selftest` sources 40-odd parts into one shell
and phrases its premises to be copied, so a key of suite and name is a key of
check NAMES rather than of checks: 583 records give 579 distinct pairs against
582 distinct triples. It is derived from `BASH_SOURCE`, not from a convention.

A skipped wall-clock check is a fourth counted outcome. Under
`PGC_SKIP_TIMING`, `check_timing` and `check_ratio_needs_quiet_machine` printed
a `SKIP` line a reader sees while leaving the count at zero and emitting no
record, in branches no arm reached. `checks run:` now reports the checks a suite
encountered rather than the ones it evaluated, and the summary reconciles four
counters against it. A suite that skipped every check reports `SKIPPED` rather
than `PASSED`, which the old zero-check condition caught only by accident.

The matrix reconciles each suite's records against the count its log states, and
names the cause rather than the arithmetic: more records than counted is a check
that ran in a subshell, fewer is a counter bumped outside `pgc_record`.
- A ledger of which checks have ever been seen red, and under what (#918).

Nothing recorded it. That is the gap that let 39 checks across 35 suites ship
unable to fail, three of them inside the suite whose whole purpose is to stop
exactly that: the gate answered "did anything print FAIL" and had never
answered "could anything print FAIL".

It records that a named check WAS OBSERVED RED in a recorded run. It does not
claim the check is proven able to fail, which needs a named mutation applied
deliberately; conflating the two would put a claim in the ledger that nothing
measured. It is fed by every real failure, not only by deliberate mutation
runs.

`run_all_versions.sh` runs the gate before it removes the build directory,
which is the only place a matrix run can reach every suite's log. What the gate
refuses is a check the committed ledger has never seen, in a suite the ledger
covers. Regenerating the ledger is the intended fix and a reviewable diff.

The two tracked numbers are different kinds of thing, and the first design
treated both as ceilings and deadlocked. `checks_never_observed_red` is a
CENSUS: every new check enters as `never`, so bounding it means every added
check breaks the gate and the only way to land one is to raise a number the
design says may only fall. It shipped that way once, at 614 rows, 614 never,
ceiling 614. `suites_not_covered` IS a ceiling, because adding a check to a
covered suite does not move it, and the gate refuses to see it raised above its
previously committed value rather than leaving that to review.

The row is keyed on suite, part and check name. The part matters because
`harness_selftest` sources 40-odd parts into one shell and phrases its premises
to be copied, so a name-only key is a key of check NAMES: 583 records give 579
distinct pairs against 582 distinct triples. A rename is detected and named
rather than silently resetting a check's history to `never`, and the mutation
column accumulates a set rather than keeping only the most recent attack.

Bad input fails closed. An unreadable log, an empty one, and a record missing
its verdict each returned success before, which is worse than no gate because
it certifies.

- A write that wrote no rows no longer passes as a fixture that built something
(#432).

Expand Down
5 changes: 1 addition & 4 deletions CONTEXT.md
Original file line number Diff line number Diff line change
Expand Up @@ -209,8 +209,7 @@ 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: 3 python files and 7 shell files**,
with a fourth python file arriving in PR #923.
**The debt this starts with, on 2026-09-10: 3 python files and 7 shell files.**

Python that reaches into shell:

Expand All @@ -222,8 +221,6 @@ Python that reaches into shell:
- `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`.
Expand Down
2 changes: 1 addition & 1 deletion test/advisory_lock_class.sh
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ if [ -n "$LK_CLASSID" ] && [ "$LK_CLASSID" -le 2147483647 ] && [ "$LK_OBJID" -le
sleep 0.2
done
else
echo "SKIP classid $LK_CLASSID or objid $LK_OBJID exceeds int4, so the SQL form cannot address it"
check_skip "the SQL form of the advisory lock" "SKIP classid $LK_CLASSID or objid $LK_OBJID exceeds int4, so the SQL form cannot address it" "classid or objid exceeds int4"
fi

# ---------------------------------------------------------------------------
Expand Down
7 changes: 3 additions & 4 deletions test/analyze_differential.sh
Original file line number Diff line number Diff line change
Expand Up @@ -54,13 +54,12 @@ ROWS=${PGC_ANALYZE_DIFF_ROWS:-50000}
# The major is asserted first so an unreadable version is not mistaken for an old
# one and reported as "supported, skipped".
if ! pgc_is_number "${PGC_MAJOR:-}"; then
echo "FAIL could not read the server major, so the gate below cannot be trusted: got [${PGC_MAJOR:-<none>}]"
PGC_CHECKS=$((PGC_CHECKS + 1)); PGC_FAILED=$((PGC_FAILED + 1))
PGC_FAIL=1
pgc_fail "could not read the server major, so the gate below cannot be trusted" \
"got [${PGC_MAJOR:-<none>}]"
pgc_summary
fi
if [ "$PGC_MAJOR" -lt 18 ]; then
echo "SKIP pgcolumnar.analyze() needs pg_restore_attribute_stats (PG18+); this server is $PGC_MAJOR"
check_skip "the differential analyze path" "SKIP pgcolumnar.analyze() needs pg_restore_attribute_stats (PG18+); this server is $PGC_MAJOR" "needs pg_restore_attribute_stats, PG18+"
pgc_summary
fi

Expand Down
7 changes: 3 additions & 4 deletions test/analyze_function.sh
Original file line number Diff line number Diff line change
Expand Up @@ -63,13 +63,12 @@ ROWS=${PGC_ANALYZE_ROWS:-500000}
# The major is asserted first. An unreadable version must not be mistaken for an
# old one, or a broken environment would report SKIP and look supported.
if ! pgc_is_number "${PGC_MAJOR:-}"; then
echo "FAIL could not read the server major, so the gate below cannot be trusted: got [${PGC_MAJOR:-<none>}]"
PGC_CHECKS=$((PGC_CHECKS + 1)); PGC_FAILED=$((PGC_FAILED + 1))
PGC_FAIL=1
pgc_fail "could not read the server major, so the gate below cannot be trusted" \
"got [${PGC_MAJOR:-<none>}]"
pgc_summary
fi
if [ "$PGC_MAJOR" -lt 18 ]; then
echo "SKIP pgcolumnar.analyze() needs pg_restore_attribute_stats (PG18+); this server is $PGC_MAJOR"
check_skip "pgcolumnar.analyze()" "SKIP pgcolumnar.analyze() needs pg_restore_attribute_stats (PG18+); this server is $PGC_MAJOR" "needs pg_restore_attribute_stats, PG18+"
pgc_summary
fi

Expand Down
Loading
Loading