Skip to content

test: audit gains its pytest twin, and a refusal must be its own guard's (#1341) - #1654

Merged
jdatcmd merged 13 commits into
mainfrom
test/1341-audit-twin
Oct 22, 2026
Merged

jdatcmd merged 13 commits into
mainfrom
test/1341-audit-twin

Conversation

@OffgridwithJD

Copy link
Copy Markdown
Collaborator

@jdatcmd The twin of test/audit.sh (#1341), one check per test, each by the shell's name, on a private database. Three holes in the shell, each measured red-first against main's shell on PG17 and fixed in both harnesses.

1. The refusal arms accepted any error. expect_error passed on any failure.

mutation (pgcolumnar--1.0-alpha5.sql) main's shell this shell twin
entryfail: set_options raises division_by_zero at entry, before any guard all five refusal arms PASS (and the suite stops under set -e 13 checks early) 9 red, each refusal refused for another reason: ERROR: division by zero same 9

expect_error now takes the guard's own message (chunk_group_row_limit must be at least 100, is not a columnar table, …). It keeps its name, because selftest 270 trusts an out-of-range set_options literal only inside expect_error. The two plain set_options calls no longer end the suite, so the checks that read them fail by name.

2. The parallel CREATE INDEX's outcome was discarded (>/dev/null 2>&1 || true).

mutation (columnar_tableam.c) main's shell this shell twin
buildfail: a parallel build participant raises an error "create index no relation leak" PASS (nothing built, nothing leaked); "create index rows indexed once" PASS (50000, by a Seq Scan); only premise: the indexed-once count reads idxleak_a_idx red 3 red: the new "the parallel CREATE INDEX completed", the plan check, and the count (not read through idxleak_a_idx) same 3

The plan check loses premise: (a product mutant reddens it, #1339), and the count is read only when that plan reads the index.

3. "AUDIT TEST ABORTED" never printed. A second trap cleanup EXIT replaced the trap that printed it. It now lives in cleanup(), and under entryfail on the old shape it printed.

And the suite records through lib.sh. Its local check() called pgc_record directly, which test_compare_to_bash's seed arm refuses for a twinned suite. It now uses lib.sh's check and ends in pgc_summary, so it prints an accounting line for the first time. PASS lines now carry the name only; FAIL lines still carry both sides.

Mutants, PG17, same red names in shell and twin:

mutant reds
partok (set_options accepts relkind p) 2
nochunk, nostripe, nolevel (a bound guard dropped) 1 each
noendscan (a participant does not end its scan) 1 (5 leaked references)
privreader (a private full-table reader per participant) 1 (150000 entries)
buildfail 3
nocollguard (another collation pushed into skipping) 2
nomissing (an added column reads NULL, not its default) 7
nowsinval (a buffer from before ADD COLUMN reused) 3
entryfail 9

Clean: 34/34 on 17, 18 and 19 in both harnesses. On 15 and 16 the shell records 30 + 1 skip (the partitioned-parent arm), and the twin's four partitioned tests decline UNSUPPORTED_MAJOR under that name; they're listed in expected_unrunnable.txt. Grader: literal matches: 35 | missing: 0.

Counts: 35 rows, the four partitioned ones on 17;18;19, plus the skip row on 15;16. 25 dated, 10 never. Census 3793 -> 3803 never, 2073 -> 2098 dated, 5901 rows; suites_not_covered 113 -> 112; cluster_tests 2454 -> 2488; TESTS.md §256. Gates: guards 467, collection 2488, ledger gate rc=0, self-test PASSED.

This will be restated onto the top of my stack (#1638 … #1650); the counts above are over a1b04eea.

🤖 Generated with Claude Code

@jdatcmd jdatcmd left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved at 19b2dc1. Its CI failures are inherited: the cluster jobs fail on #1653's ci.yml comment ("the job names no corpus file literally: got 'test_docs_style.py'"), and the suites jobs on the two skip-name orphans (#1640, #1647). This commit adds none of them.

Measured on PG17 at main a1b04ee with set_options failing at entry for none of its guards' reasons (PERFORM 1/0; first in its body):

run result
main's shell only the two arms that read the message fail; the five "reject …" arms pass on a division by zero: the hole
this head's shell 9 failed, for example "reject stripe_row_limit 5: got [refused for another reason: ERROR: division by zero] want [rejected]"
this head's twin the same 9 names
clean, PG15 shell 30 passed + 1 skipped (the partitioned-parent arm); twin 30 pass + 4 unrun
clean, PG19 shell 34/34, twin 34 pass

I did not rerun the other ten mutants.

Like the rest of the stack, this needs a restate onto main, after #1647's and #1653's fixes. I'll re-approve one whose non-registry files are unchanged.

@OffgridwithJD
OffgridwithJD force-pushed the test/1341-audit-twin branch 2 times, most recently from 86ec259 to 36b50b8 Compare October 22, 2026 05:46
jdatcmd and others added 13 commits October 22, 2026 14:14
… guard's (#1341)

iceberg_dv.sh gains test/pytest/test_iceberg_dv.py, one check per test under the
shell's names, and two holes in the shell are closed, each measured red-first on PG17:

- "a manifest/footer offset mismatch is refused (XX001)" read the SQLSTATE alone, and
  several guards raise XX001. With the offset match removed (the !found refusal
  skipped), a later XX001 refused the same file and main's shell passed 26/26. Every
  refusal now reads its own guard's message: refusal() answers the SQLSTATE only when
  the message carries the guard's phrase, and "<SQLSTATE> (another refusal: ...)"
  otherwise.
- The three "backend still up" checks read SELECT 1, which answers again after crash
  recovery. With the referenced_data_file check removed a NULL path reaches strcmp and
  the backend dies on that arm; main passed "backend still up after the DV refusals".
  They now also read the server log's count of killed backends (crashes/survived, as
  avro_manifest), and each refusal waits for the server to answer first (await_up),
  so a crash reddens its own arm and its section's survival check, the same names in
  both harnesses, and not a timing-dependent tail of collateral.

The eight survivor comparisons move to check_text, so an empty oracle no longer
matches an empty scan.

Measured on PG17, shell then twin as shipped: seventeen mutations, each with the same
reds in both (strictgt 7, anyfile/nodup/nov2/noflags/noref/nobigoff 2, the rest 1);
26/26 in each on 15-19. 25 rows dated, 1 never (the survival check after the DV
applications, which no mutation crashed). Census 3884 -> 3885 never, 2286 -> 2311
dated, 6196 rows; suites_not_covered 102 -> 101; cluster_tests 2753 -> 2779; TESTS.md
§268.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…d slot no longer passes (#1341)

The pytest twin of test/logical_decoding_source.sh (#754), one check per test,
every check by the shell's name, on a private cluster at wal_level=logical.

The shell's second slot (pgc_754b) had no heap control. Created after the
5000-row insert, it decoded nothing, and both "and it too emits no decodable
change of its own" and "100x the rows does not give 100x the internal records"
(0 < anything) passed: main 11/11. A heap table now writes through that slot in
the same window and must decode its 5000 inserts ("CONTROL: the second slot
decodes the heap table's 5000 inserts"), and the ratio check refuses a slot
that carried no internal records. Under that harness mutation both are red in
both twins (SAME 2).

The pgc_skip message for a missing test_decoding.so was built from
pg_config --pkglibdir, so the grader could not read it; it is now a literal,
and the twin records the same name (with the plugin moved away on PG17: shell
1 FAIL, twin the same 1 FAIL and 12 declined).

No source mutation reddens a check: the claim is that pgcolumnar writes nothing
logical decoding can read. A default chunk group of 100 rows still keeps the
internal records following groups (114 for 50 groups, under the 10x bound), so
it dates nothing. 12/12 shell and 13/13 twin on each of 15-19.

Census 3885 -> 3897 never, 2311 dated, 6208 rows (the skip name has no ledger row: it is not a check); suites_not_covered
101 -> 100; cluster_tests 2779 -> 2792; TESTS.md §269.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… longer passes as up (#1341)

The twin of test/iceberg_deletes.sh, one test per check under the shell's name.

Fixed in both harnesses, measured red-first against main's shell on PG17:
- The six "backend still up" checks read SELECT 1, which answers again after
  crash recovery. With abort() in place of the null-pos refusal, or the partition
  spec's fields guard removed (a NULL deref, signal 11), main passed the survival
  check once recovery had finished. They now also read the server log's count of
  backends killed by a signal, and every reading waits (bounded) for the server,
  so a crash reddens its own arm and its section's survival check rather than
  the two arms after it.
- Every refusal now also reads its own guard's message: XX001 and 22023 are each
  raised by several guards here. The oracle comparisons use check_text, so an
  empty side is not agreement.
- "the data file has 5 rows before deletes" reads pgcolumnar's own read_parquet,
  so it is not a premise (#1339).

Twenty-three mutations each redden the same names in shell and twin on PG17; head
is 49/49 in both on each of 15-19. The equal-sequence equality check is dated only
with both layered guards relaxed: the per-file strict < alone passed, because the
pre-skip at the oldest data file's sequence number drops that delete first.
Census 3897 -> 3903 never, 2311 -> 2354 dated, 6257 rows; suites_not_covered
100 -> 99; cluster_tests 2792 -> 2841; TESTS.md §270.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…at applied nothing no longer passes (#1341)

The twin of test/logical_subscriber.sh, on two private clusters (a heap publisher at
wal_level=logical, a columnar subscriber), one check per test, each by the shell's name.

Four holes, each measured red-first on main's shell (PG17):
- With the insert-only subscription disabled before its UPDATE, main passed 20/20:
  "skipped by design" and "no apply errors" read after a fixed sleep. An INSERT after
  the UPDATE must now arrive before either is read.
- With no UPDATE sent to the wedge publication, "the error does NOT say only 'row
  locking'" and "the wedge is real" passed. The first now needs the replication error
  in the subscriber's log; the second reads "publisher -> subscriber".
- With every lock statement failing on a missing table, the three "is not told about
  replication" checks passed. They now need the generic row-locking error first.
- "premise: the second subscription synced before the UPDATE" is reddened by COPY
  failing in a logical worker, so it is not a premise (#1339) and is dated.
The lock loop is unrolled into literal checks so the grader reads each name.

Five mutations (hintnoname, nogate, overgate, applyins, syncins) redden the same names
in both twins; clean 20/20 on 15-19 in both, and 8/8 head repeats on 17. 20 rows,
16 dated, 4 never. Census 3903 -> 3907 never, 2354 -> 2370 dated, 6277 rows;
suites_not_covered 99 -> 98; cluster_tests 2841 -> 2861; TESTS.md §271.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ck that undid nothing no longer passes (#1341)

The twin of test/logical_decoding_cdc_recipe.sh, every check by the shell's name, one
check per test, on a private cluster at wal_level=logical. Like the shell, it extracts
the CDC recipe from docs/user-guide.md at run time and executes it.

Two holes, both measured on PG17 against main's shell, fixed in both harnesses:
- The rollback arm read the capture count and the row only after ROLLBACK, so a
  rolled-back INSERT that never ran passed both arms. With it made a duplicate key, so
  the block stopped there, main passed 35/35. A reading inside the transaction, before
  the rollback, now requires the row both captured and present ("1:1"). Under the same
  mutation it is red in shell and twin.
- "and it saw the same values the table holds" compared two readings with check. With
  the server stopped before it, "" = "" passed. It is check_text now.
The missing-plugin pgc_skip name is a literal the twin records, and the seven
recipe-contains premises are unrolled into literal names the grader can read.

Five mutations date seven checks, the same names red in both twins on PG17:
pgcolumnar_tuple_delete marking nothing (1), the update writing its new version with
every column null (1), the update leaving the old version live (3), and two mutations
of the guide's recipe: the trigger not firing on DELETE (2), and every non-DELETE
captured as an INSERT (4). 36/36 shell and 37/37 twin on each of 15-19. Census 3907 ->
3936 never, 2370 -> 2377 dated, 6313 rows (the skip name has no ledger row: it is not a check); suites_not_covered 98 -> 97;
cluster_tests 2861 -> 2898; TESTS.md §272.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…wn guard's (#1341)

The twin of test/iceberg_rest.sh, every check by the shell's name, one check per
test. It runs a private cluster restarted with the bearer token in the postmaster
environment (a good one, a wrong one, none) against the shell's own REST fixture.

Two holes in the shell, each measured red-first on PG17 and fixed in both harnesses:
- The 42501 arms read the SQLSTATE alone, and two guards raise 42501. With the
  allow-list refusing everything and the link-local refusal gone (denyall_noll),
  "a link-local catalog host is refused even if allow-listed" passed on the
  allow-list's 42501; with every address read as link-local and the allow-list gone
  (llall_noallow), "a catalog endpoint off the allow-list is refused" passed on the
  link-local guard's. Every refusal now reads its own guard's message.
- "backend still up after the REST refusals" read SELECT 1, which answers again
  after crash recovery. With the backend aborting on the catalog's 404 (crash404),
  main passed it. It now also reads the server log's count of killed backends.

Mutants (PG17), SAME reds in shell and twin: tokenlog 1, tokenurl 2, no401 2,
no404 1, nocfgobj 1, nocap 1, crash404 2, noauth 4, denyall_noll 8,
llall_noallow 8, denyall 8. Clean 14/14 in both on each of 15-19.
Seeded: 14 rows, 12 dated, 2 never (the premises). Census 3936 -> 3938 never,
2377 -> 2389 dated, 6327 rows; suites_not_covered 97 -> 96; cluster_tests
2898 -> 2912; TESTS.md §273.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…no longer passes as up (#1341)

The twin of test/iceberg_rest_scan.sh, every check by the shell's name, one check
per test. It runs a private cluster restarted with the bearer token in the
postmaster environment against the shell's own REST fixture, which points loadTable
at the committed warehouse relocated into a work directory.

One hole in the shell, measured red-first on PG17 and fixed in both harnesses:
"backend still up after the REST read" read SELECT 1, which answers again after
crash recovery. With the backend aborting on the catalog's 404 (crash404) and the
probe 3s later, main passed it. It now also reads the server log's count of killed
backends. The not-found arm now reads the catalog's own 404 message, not any 42P01.

Mutants (PG17), SAME reds in shell and twin: no404 1, nslast 1, crash404 2,
noauth 7, denyall 7. Clean 9/9 in both on each of 15-19.
Seeded: 9 rows, 8 dated, 1 never (the premise). Census 3938 -> 3939 never,
2389 -> 2397 dated, 6336 rows; suites_not_covered 96 -> 95; cluster_tests
2912 -> 2921; TESTS.md §274.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nores storage-credentials no longer passes (#1341)

The pytest twin of test/iceberg_rest_vended.sh, one check per test under the
shell's names, on a private cluster restarted with an explicit environment
(every AWS_* removed; the catalog token, and for the ambient arm the AWS_*
credentials, passed in), against the shell's own SigV4 S3 fixture and REST
catalog.

Three holes, each measured on main's shell on PG17 first:
- "storage-credentials longest-prefix match selects the right creds" passed
  with the storage-credentials array never read (nosc, 10/10): the catalog's
  flat config carried the same right credentials. The fixture catalog now
  vends a wrong flat config beside the array, so only the array's longest
  entry reads.
- "a wrong vended secret is refused by S3, not bypassed (28000)" read the
  SQLSTATE alone, and 28000 is also "no credentials": with vended credentials
  never built (novend) it passed. It now needs the 403 message; the 42501
  allow-list arm likewise needs the allow-list's own message.
- "backend still up after the vended-credential arms" read SELECT 1, which
  answers again after crash recovery: with abort() in place of the 403
  refusal (abort403) it passed. It now also reads the server log's count of
  killed backends, and each arm waits for the server first.

pg_restart_env also unsets every AWS_* before starting the postmaster, so
the "no ambient credentials" premise does not depend on the caller's
environment.

Six mutations each redden the same names in shell and twin on PG17:
nosc 1, shortest 1, novend 4, leak 1, noallow 1, abort403 2. Clean: 10/10
shell and twin on each of 15-19. Seeded: 10 rows, 6 dated, 4 never. Census
3939 -> 3943 never, 2397 -> 2403 dated, 6346 rows; suites_not_covered
95 -> 94; cluster_tests 2921 -> 2931; TESTS.md §275.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…d no longer passes as up (#1341)

The pytest twin of test/iceberg_rest_server.sh, one check per test under the
shell's names, on a private cluster restarted without and then with the
catalog token in its environment, against the shell's own REST fixture.

Four holes, each measured on main's shell on PG17 first:
- "backend still up" read SELECT 1, which answers again after crash
  recovery: with the half-credential guard removed (the mint runs with a NULL
  secret and the backend segfaults) or abort() in place of the catalog's 401
  refusal, it passed. It now also reads the server log's count of killed
  backends, and every reading waits for the server first (the segfault had
  also reddened the next arm as collateral).
- "a warehouse option on the SERVER is accepted (not HV00D)" and "oauth
  options are accepted on a USER MAPPING (no error)" wanted an empty
  SQLSTATE, which a stopped server also gives: with the server stopped before
  them, both passed. A statement that ran now reads "ok".
- "a link-local oauth_token_uri is refused by the allow-list (42501)" read
  the SQLSTATE alone, and the link-local guard raises 42501 too: with the
  allow-list removed it passed. Every refusal now needs its own message.
- "a half OAuth2 credential ... is refused before any request" now also
  counts the fixture's requests.

Five mutations each redden the same names in shell and twin on PG17:
nohalf 2, abort401 3, nowh 1, noallow 2, nomaptoken 3. Clean: 29/29 shell
and twin on each of 15-19. The suite already had its 29 rows: 10 more are
dated (12 dated, 17 never). Census 3943 -> 3933 never, 2403 -> 2413 dated,
6346 rows; suites_not_covered unchanged; cluster_tests 2931 -> 2960;
TESTS.md §276.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…reads what the last key left (#1341)

The twin compiles src/columnar_curve.c with a small driver into a shared
library and checks the curve in Python: the permutation test (C1), the
memcmp-order adjacency walk (C2), dyadic contiguity (C3), the
production-width bridge (C4a), a Python transcription of Skilling's
TransposetoAxes for the round trips (C4b), the goldens and the pack's
extent (C5), ncols == 1 (C6) and the frozen Z-order bytes (C7), plus the
serpentine, Z-order and truncating controls through the same checkers.
The driver only encodes and counts. 184 tests, one per check, all in
NO_CLUSTER: no connection and no cluster.

Two shell changes:
- The battery fills every key buffer with 0xa5 before the pack writes it.
  Under a memset one byte short, which goldens went red depended on what
  the previous call left in the buffer (121 red on main, a stable 124
  now), and a buffer's first use read uninitialized memory.
- arms_unrunnable/arms_failed are written out as loops at their call
  sites. They took a list of names in one argument, so compare_to_bash
  refused the whole suite; it now reads and grades it. The refusal arm in
  test_compare_to_bash.py keeps its coverage against a fixture holding
  the two helpers as they stood, and self-test 480's pinned refusal
  emitters lose arms_unrunnable (its check renamed from "the two" to "the
  ones the tree defines", ledger row with it).

Measured on PG17: seventeen mutations of src/columnar_curve.c each give
the SAME failing names in the shell and the twin, and between them redden
every arm except the six premises and the ten INSTRUMENT controls. Clean
184/184 in both harnesses on each of 15-19.

Seeded: 184 rows, 168 dated, 16 never. Census 3933 -> 3949 never,
2413 -> 2581 dated, 6530 rows; suites_not_covered 94 -> 93;
guard_tests 474 -> 658; TESTS.md §277.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…was gone no longer passes (#1341)

bench_guards.sh kept its own tally and emitted no RESULT records, so it could
not be seeded or twinned. It now goes through lib.sh's check with no cluster
(as wal_envelope.sh does), and test_bench_guards.py is its twin, in NO_CLUSTER.

The per-runner checks grepped each bench runner for words. Every header
mentions --enable-cassert, and the cannot-tell block's exit 1 sits within 24
lines of that mention, so main's suite passed all 84 checks with the assert
refusal warning past it, with the empty-capture refusal removed, with every
build refused, and with the override ignored, in every runner. Each runner's
own guard block is now RUN against a stand-in pg_config (assert, plain, or no
output), and a sixth check per runner wants a plain build let through. Only a
complete guard is run: without one, nothing in the runner is executed.

Also fixed, each measured red-first on main's shell:
- "and the verdict is asked of cb_verdict" counted a comment that kept the
  call's shape; it now reads code only.
- the ratio premise matched rr1= as r1=, so a rename left the printf check
  reading nothing; it now matches a whole name.
- "more than enough is accepted" named two checks; the worker-slot one is now
  "more than enough worker slots are accepted".
- two premises that a one-line change to bench/cb_guards.sh reddens lost the
  prefix: the cold tag's presence and the verdict helpers'.

selftest 320's premise required at least one suite keeping its own
PGC_CHECKS tally, and bench_guards.sh was the last; converting it reddened
the premise for the reason the rule exists. It now counts the files the
sweep read ("premise: the sweep read the corpus"; its never row renamed).

Twenty-two mutations of bench/ redden the same names in shell and twin on
PG17; 91/91 shell and twin on each of 15-19. Seeded: 91 rows, 49 dated, 42
never. Census 3949 -> 3991 never, 2581 -> 2630 dated, 6621 rows;
suites_not_covered 93 -> 92; guard_tests 658 -> 749; TESTS.md §278.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… no longer passes (#1341)

docs_style.sh kept a private tally and emitted no RESULT records, so no ledger row
could be seeded for it and nothing could grade a twin. It now records through
lib.sh's check() and pgc_summary, with no cluster, as wal_envelope.sh does.

Four arms read text a reader never sees. Measured on main, each left all 58 checks
green:
- the 1024-floor sentence kept only inside an HTML comment;
- "join key" kept only inside an HTML comment;
- a stale VERSION citation with the right one hidden in a comment beside it;
- the roadmap nav entry commented out with #.
Each arm now reads the page without its HTML comments (_md), and the nav check
ignores YAML comments. Each of the four now reddens its own check in both twins.

Two loops built check names from a variable, which the twin grader cannot read.
The per-version changelog checks are three fixed names that list the failing
versions in their reading, and the META.json pair is two literal checks. In a
checkout without release tags the changelog arms stay notes in the shell (the
nightly coverage job runs it tagless and counts an unrunnable suite as failed),
and decline UNMET_PRECONDITION by name in the twin. The guards job, where the
twin runs (it is in NO_CLUSTER), now fetches the release tags so they run there.
"premise: CHANGELOG.md still has the union merge driver" asserted the same fact
as the driver check above it and went red with it, so it is an ordinary check.

The twin, test_docs_style.py: 53 tests, one check each, by the shell's names,
read with Python's re and pathlib. 36 documentation mutations (pages, META.json,
the badge, .gitattributes, the post-tag allowance file, untracked install
scripts) each redden the same names in both twins, with no premise among them.
53/53 shell on each of 15-19, 53/53 twin. 36 rows dated, 17 never (the 16
premises and the checker's examined-count control). Census 3991 -> 4008 never,
2630 -> 2666 dated, 6674 rows; suites_not_covered 92 -> 91; guard_tests
749 -> 802 (cluster_tests unchanged); TESTS.md §279.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…d's (#1341)

The twin of test/audit.sh, one check per test, each by the shell's name, on a
private database. Three holes in the shell, each measured red-first against
main's shell on PG17, and fixed in both harnesses:

- The refusal arms accepted any error. With set_options failing at entry for a
  reason that is none of its guards (a division by zero), all five passed.
  expect_error now requires the guard's own message; under that mutation both
  harnesses red the same 9 checks.
- The parallel CREATE INDEX's outcome was discarded (>/dev/null 2>&1 || true).
  With a build participant raising an error, no index existed, and "create
  index no relation leak" and "create index rows indexed once" both passed, the
  count answered by a Seq Scan. The build is now its own check, the count is
  read only through the index, and the plan check is no longer a premise (a
  product mutant reddens it, #1339).
- The "AUDIT TEST ABORTED" notice never printed: a second `trap cleanup EXIT`
  replaced it. It now lives in cleanup(). The two plain set_options calls no
  longer end the suite under set -e, so the checks that read them fail by name.

The suite now records through lib.sh's check and ends in pgc_summary, so it
prints an accounting line for the first time: its local check() called
pgc_record directly, which test_compare_to_bash's seed arm refuses for a twinned
suite. A PASS line now carries the name only; a FAIL line still carries both
sides.

Eleven mutations (set_options guards, the parallel build's scan and reader, the
collation guard, missing values, the write buffer after ADD COLUMN, a failing
entry) each red the same names in shell and twin on PG17. Head: 34/34 on 17-19
and 31 records on 15-16 (the partitioned-parent arm is one skip there, and the
twin declines its four tests UNSUPPORTED_MAJOR under that name), shell and twin
alike. 35 rows: 25 dated, 10 never. Census 4008 -> 4018 never, 2666 -> 2691
dated, 6709 rows; suites_not_covered 91 -> 90; cluster_tests 2960 -> 2994;
TESTS.md §280.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@jdatcmd
jdatcmd merged commit d46ee99 into main Oct 22, 2026
14 of 15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants