Skip to content

test: parquet_export_stats gains its pytest twin, and an export that folds valid dates to null no longer passes (#1341) - #1644

Merged
jdatcmd merged 10 commits into
mainfrom
test/1341-parquet-export-stats-twin
Oct 22, 2026
Merged

jdatcmd merged 10 commits into
mainfrom
test/1341-parquet-export-stats-twin

Conversation

@jdatcmd

@jdatcmd jdatcmd commented Oct 21, 2026

Copy link
Copy Markdown
Collaborator

Stacked on #1643; merges after it. This head sits on my restated stack (#1622, #1628–#1631), so the registry counts in the commit message are re-derived against that stack. Counts quoted in the body below are from the port's original base.

What I re-ran myself before opening (PG17): nothing beyond the restate. The restate ran the shell on PG16 and PG18 (162/162 each), the guards (474 passed, including the edited helper guard) and the ledger gate (rc 0). The mutant table below is the porting run's, with its logs kept; I have read those logs but not re-run the mutants.


@OffgridwithJD — parquet_export_stats gains its pytest twin (#1341). One commit, 01f68da4, on 22c85ee2.

What this does

test/pytest/test_parquet_export_stats.py ports all 162 checks of test/parquet_export_stats.sh by name, one test per check. The shell's three loops are unrolled, so every name in the shell is literal; the twin parametrizes those three over literal tables of the same names. A module fixture takes every reading; a failed query or an unreadable footer is a query_error().

What was fixed in the shell (and carried into the twin)

  1. The oracles read the file under test. The bound and null-count checks compared the footer with read_parquet's decoding of the same file. With 2000-01-01 folded to null by the writer, both sides moved together and main's shell passed 162 of 162. Both oracles now read es_h, the heap built from the generator.
  2. The infinity-sentinel check looked in the wrong space. It tested PostgreSQL's INT32_MAX/INT64_MAX, but a bound is written after the 1970 epoch shift. With infinities admitted, main's shell passed it over bounds of -2147472692 and -9222425352054775809. Every date and timestamp bound must now lie inside the heap's finite range.
  3. "a zero maximum is written as +0.0" could not fail. The fixture's unquoted -0.0 is a numeric literal and stores +0.0 (measured: float8send gives \x0000000000000000). It is now '-0.0'.
  4. Ten absence checks passed on nothing. Each now reads N of M chunks (or bounds), so 0 of 0 is red.
  5. A parse failure hid the names. An unreadable footer recorded one check and ended the run; an unreadable NaN fixture recorded one check in place of ten. Each check now reads its own value and is red by name.

Also: the suite-local check_float became check through as_float, and test_compare_to_bash.py's suite-local-helper arm drops it (that arm exists to fire when such a suite is ported; it did). Four oracle checks lose premise: under #1339, because admitting infinities makes the file unreadable to read_parquet and all four go red.

Measured, PG17

Red counts. Main's shell is origin/main at 22c85ee2.

edit or mutant main's shell this shell twin
clean 0 / 162 0 / 162 0 / 162
MF: 2000-01-01 folded to null (columnar_parquet.c) 0 2 (group 0 min(d), group 0 null_count(d)) same 2
MI: date/timestamp infinities admitted 105, sentinel check green 42, sentinel check red same 42
MB: no min/max written 79, width and min<=max green 82 same 82
MR: hasBounds not reset per group 35 36 same 36
MN: NaN admitted to the bounds 3 3 same 3
MZ: TYPE_ORDER zero rule removed 1, zero maximum green 2 same 2
MNC: nan_count only when nonzero 1 1 same 1
MNULL: null_count only when nonzero 8 8 same 8
MC: column_orders omitted 2 2 same 2
RT: reader's cross-type refusal removed 5 5 same 5
RF: reader's float >/>= refusal removed 1 1 same 1
RU: col < const tested against max (unsound) 17 17 same 17
harness: leaf t unresolved 1, BYTE_ARRAY and deprecated checks green 4 not run
harness: parser missing 1 check run in total 108 red by name not run

"same N" means the identical set of names, compared name by name. No mutant crashed the backend, so there are no collateral reds.

Gates

  • Clean, five majors: shell 162/162 and twin 162 passed on PG15, 16, 17, 18, 19. The first PG15 twin attempt failed at pg_ctl start (160 setup errors, server log not kept); the rerun passed 162.
  • Ledger: 162 rows, 133 dated by the 12 mutations and 29 never. checks_never_observed_red 3651 -> 3680, suites_not_covered 124 -> 123.
  • pgc_ledger.py gate --against 22c85ee2: rc 0 on each of the five clean logs ("census stated 3680, ledger holds 3680: they agree"; "124 -> 123, which does not rise").
  • Guards: 467 passed (1592 checks).
  • compare_to_bash.py: "literal matches: 162 | template matches: 0 | missing: 0 / VERDICT: every bash property is covered".
  • harness_selftest.sh (PG17): 1248 of 1248.
  • cluster_tests 2182 -> 2344 by collection; TESTS.md section 244.

For the push

origin/main has moved past 22c85ee2 (it was at 8d2bf1ba when I finished). The registry files (ledger, budget, expected_tests.txt, TESTS.md, test_compare_to_bash.py, unported_suites.txt) will need restacking, and the counts in the budget paragraph restating.

🤖 Generated with Claude Code

jdatcmd and others added 4 commits October 21, 2026 21:42
…k no longer tests one path twice (#1341)

test/pytest/test_read_stream.py ports the checks of test/read_stream.sh by
name. The suite compares five query shapes with a heap twin, with
pgcolumnar.enable_read_stream on and then off; one shell fix, in both
twins:

- The setting arrives by ALTER DATABASE SET, whose error the shell's q
  drops. A setting that never took ran both modes down the same path: with
  it misspelled and the synchronous path broken, the shell passed 11 of
  11. Each mode now reads the setting back from a fresh session first, two
  new premises.
- The ten comparisons were named through $mode in a loop; they are
  literal. The stripe premise carries its count and leaves
  lossy_arms.tsv.

Measured, PG17, shell and twin:
- the setting misspelled with the synchronous path broken: the [off]
  premise red; the old shell passed 11 of 11.
- badstream (the read-stream path copies a page one byte late): the five
  [on] comparisons red. badsync (the synchronous path does): the five
  [off] comparisons red. Each red set is identical in both twins.
- clean: 13 of 13 on PG15 to PG19 in both twins.

Ledger: 13 rows, 10 dated by the two mutations and 3 never.
suites_not_covered 113 -> 112, cluster_tests 2454 -> 2467, TESTS.md
section 256. Census 3793 -> 3794 never, 2073 -> 2085 dated, 5879 rows. The ledger
gate and the guards pass over 8d2bf1b.

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

test/pytest/test_parquet_level_width.py ports all 7 checks of
test/parquet_level_width.sh by name. It reads the source in Python, needs no
cluster, and is listed in NO_CLUSTER. Three shell fixes, in both twins:

- "no local copy of the helper survives" summed grep -c over the writer and
  the reader; grep prints nothing for a missing file, so with the writer's
  path wrong the sum read 0 and the shell passed 7 of 7. It now reads only
  when both files exist, and says so otherwise.
- "and no signed 1 << remains in it" counted over the extracted body, so
  with the header unreadable it read 0 and passed. It now needs a body.
- The body checks read the comments inside the body, so a guard or bound
  commented out of the code still passed 7 of 7 (OffgridwithJD's
  commentguard and commentbound). The body now has its comments blanked,
  multi-line ones included.

Measured, PG16 shell and the twin:
- signedshift (pq_bits_for shifts a signed 1): 2 red. unbounded (the
  b < 31 bound removed): 1 red. localcopy (a local bits_for back in the
  writer): 1 red. commentguard (the non-positive guard commented out): 1
  red. commentbound (the b < 31 bound commented out): 1 red. commentml
  (the guard in a multi-line comment): 1 red. Each red set is identical
  in both twins (the comment mutants on PG17).
- clean: 7 of 7 on PG15 to PG19 in the shell, and in the twin.

Ledger: 7 rows, 5 dated by the four mutations and 2 never.
suites_not_covered 144 -> 143, guard_tests 464 -> 471, TESTS.md
section 224. Census 3543 -> 3545 never, 1798 -> 1803 dated, 5348 rows. The
ledger gate and the guards (471) pass over 555e5ef.

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

test/pytest/test_parquet_nested.py ports all 6 checks of
test/parquet_nested.sh by name, each declining by the shell's skip name
without pyarrow, as test_arrow_nested.py does for the Arrow sibling. Two
shell fixes, in both twins, mirroring arrow_nested's:

- "nested Parquet values match expectation" compared only the rows the
  file held, so a zero-row file of the same schema printed MATCH; it now
  requires every id 1..5000 once.
- "reject multi-dim array" passed on any error, so with the export
  pointed at a missing table it read as the refusal; md_refusal() now
  reads the multi-dimensional message.

Measured, PG16, shell and twin:
- emptyasnull (an empty array written with definition level 0, so it
  reads back NULL): 1 red, the values check. nomultidim (the refusal
  removed): 1 red, the multi-dim check. Each red set is identical in both
  twins.
- clean: 6 of 6 on PG15 to PG19 in both twins.

Ledger: 6 rows, 2 dated by the two mutations and 4 never.
suites_not_covered 111 -> 110, cluster_tests 2467 -> 2473, TESTS.md
section 258. Census 3796 -> 3800 never, 2090 -> 2092 dated, 5892 rows. The ledger
gate and the guards pass over 1b1922e.

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

test/pytest/test_parquet_nested_import.py ports all 7 checks of
test/parquet_nested_import.sh by name. The two pyarrow arms decline by
their own names without pyarrow, as the shell's two skips do. One shell
fix, in both twins:

- "import count matches" compared two counts with check, under which two
  empty readings agree: with both tables missing, the shell passed it. It
  is check_num. The set hashes already gave each failed query its own
  value, so the round-trips could not agree on two failures.

Measured, PG16, shell and twin:
- readempty (the reader takes an empty Parquet list for a NULL array): 3
  red, the two round-trips and the pyarrow oracle; the counts stay green,
  as they should. The red set is identical in both twins.
- clean: 7 of 7 on PG15 to PG19 in both twins.

Ledger: 7 rows, 3 dated by the mutation and 4 never.
suites_not_covered 110 -> 109, cluster_tests 2473 -> 2480, TESTS.md
section 259. Census 3800 -> 3804 never, 2092 -> 2095 dated, 5899 rows. The ledger
gate and the guards pass over 9109cc2.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@OffgridwithJD OffgridwithJD 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.

@jdatcmd Approving 200ae8ff. Measured at this head on PG17, shell then twin as shipped (make clean per mutant). I reviewed this PR's own commit; the suites ran at the head. I rebuilt your mutants from the PR body.

run main's shell this shell twin
head, PG17 / PG18 — 162/162, 162/162 162/162, 162/162
MF (2000-01-01 folded to null) 162/162 pass 2 red SAME 2
MZ (TYPE_ORDER zero rule removed for the maximum) 162/162 pass 1 red SAME 1
MI (date/timestamp infinities admitted) — 42 red the same 42 by name (7 are VacuityErrors on the out-of-range reads)
nullplus (mine, over-apply: null_count written one too many) — 20 red SAME 20
firstmin (mine, drop: an integer minimum never lowers past the first value) — 5 red SAME 5

The test_harness_deps.py line in this stack belongs to #1628's commit, not this one. It adds test_parquet_level_width.py to NO_CLUSTER and doesn't widen what a test may decline on, so the duckdb decision on #1341 is unaffected.

🤖 Generated with Claude Code

jdatcmd and others added 6 commits October 22, 2026 01:00
… not its fixture

CI failed the harness guards and both cluster-test jobs with:

  REFUSED  test_parquet_nested_import.py imports pyarrow outside require_module (line 63)
  REFUSED  test_parquet_nested_import.py imports pyarrow outside require_module (line 64)

The module fixture imported pyarrow itself to write the reference file.
A fixture cannot decline, so main's guard refuses the import. The fixture
now keeps the database and the directory, and the pyarrow arm runs once,
from the first pyarrow test, with the modules that test's require_module
returned. The check names and the count are unchanged.

Measured:
- Red, the parent's twin: test_harness_deps "the guard half of the corpus
  runs without a database driver" fails with the REFUSED lines above.
- Green: the twin is 7/7 on PG17 and PG19. With pyarrow hidden it is
  5 pass + 2 declined, each pyarrow arm by its own name.
- Mutant, the reference file one row short: both pyarrow arms fail.

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

test/pytest/test_parquet_count_bounds.py ports all 17 checks of
test/parquet_count_bounds.sh by name, using the shell's crafter and
declining by the shell's skip name without pyarrow. Three shell fixes,
in both twins:

- The survival checks read SELECT 1, which answers again after crash
  recovery. With the num_values bound removed (#570), the 2^62 file
  crashed the backend and "and the backend survived it" and "survived the
  list over-read" passed on main. They now also need the server log's
  count of killed backends unmoved since setup, as avro_manifest does
  since #1576; the same crash now reads "no ([1], 1 backend crashes)".
- The two phantom-row checks caught only the exact count the crafter
  declares (40, 8). They now catch any count above the file's true one.
  No mutation here produced a different count, so this hardening is not
  shown red first: under both #571 guards removed, the leak was the
  declared 40, which main caught too.
- The crafter premise covers all seven files; the three list files were
  never checked.

Measured, PG16, shell and twin:
- no570 (the num_values range check removed): the backend crashes. Both
  harnesses red the rejection, the survival check and the detail; what
  else reds depends on how long recovery takes, so the counts differ (7
  in the shell, 11 in the twin), all from reads during recovery.
- no571 alone is masked: pq_require_entry rejects the same files at
  decode time with the same message, so the suite stays 17/17.
- noentry (pq_require_entry refuses nothing): 2 red, the two list
  checks, the same in both twins. no571 with noentry: 9 red, the same 9.
- clean: 17 of 17 on PG15 to PG19 in both twins.

Ledger: 17 rows, 2 dated by noentry and 15 never. The num_values and
num_rows mutations crash the backend and redden collateral checks, so the
ledger cannot attribute them. suites_not_covered 125 -> 124,
cluster_tests 2169 -> 2186, TESTS.md section 233. The ledger gate,
guards (467), name grading and the self-test pass over 9bd1850.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…t by importing pyarrow in it

CI failed the harness guards and both cluster-test jobs with:

  REFUSED  test_parquet_count_bounds.py imports pyarrow outside require_module (line 83)

The module fixture imported pyarrow to decide whether to build at all.
A fixture cannot decline, so main's guard refuses the import. Each test
now declines by the shell's skip name first, and only then asks for the
fixture with request.getfixturevalue. The fixture no longer imports
anything, and it never runs on a box without pyarrow. The check names
and the count are unchanged.

Measured:
- Red, the parent: test_harness_deps "the guard half of the corpus runs
  without a database driver" fails with the REFUSED line above.
- Green: test_harness_deps and test_capability_decline, 30 passed, no
  REFUSED line. The twin is 17/17 on PG17 and PG19; with pyarrow hidden it
  is 17 declined.
- Probe: wanting 9 honest rows fails with "got = 8, want = 9", so the
  fixture still crafts and imports.

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

test/pytest/test_parallel_degree.py ports all 6 checks of
test/parallel_degree.sh by name. Three shell checks could pass with
nothing measured; all three are fixed in both twins.

- With the server stopped after ANALYZE, main's shell passed three of six
  checks. "identical data is granted the same parallel degree" compared
  "none" with "none"; it is now check_num. "both tables hold the same
  rows" compared "" with ""; it is now check_num. "the worker cap is not
  what decides either arm" read a missing worker count as 0; it now
  needs two numbers. With the edit, the fixed shell reds all six.
- Three checks lose their premise: prefix under #1339. The columnar
  rows, the columnar partial path and the columnar degree under the cap
  are pgcolumnar's own work, and a one-line change reddens each.

Measured, PG17, shell and twin:
- server stopped after ANALYZE: 6 red in the fixed shell and in the twin
  (the twin drops its database instead); main's shell passed 3 of 6.
- the #451 estimate never applied (2 workers against heap's 4): 1 red.
  The estimate over-asking (pages * 9): 1 red. Over-asking into the cap
  (pages * 243): 2 red. The partial path never added: 3 red (main's
  shell: 2, the cap check passed). An insert that drops one row: 1 red.
  Each red set is identical in both twins.
- clean: 6 of 6 on PG15 to PG19 in both twins.

Ledger: 6 rows, 4 dated by the five mutations and 2 never.
suites_not_covered 108 -> 107, cluster_tests 2497 -> 2503, TESTS.md
section 261. Census 3819 -> 3821 never, 2097 -> 2101 dated, 5922 rows. The ledger
gate and the guards pass over 23f3d52.

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

test/pytest/test_parallel_flush_optin.py ports all 5 checks of
test/parallel_flush_optin.sh by name. Two shell checks could pass with
the property broken; both are fixed in both twins.

- "with the GUC on, a wide-numeric flush dispatches parallel (not a
  silent serial fall-back)" read only the dispatch line, which says
  parallel before any worker is registered. With no worker ever
  registered, every column fell back to serial and main's shell passed
  5 of 5. The check now also needs the split line ("N of M columns by
  K worker(s)") to show worker columns.
- "opting in stores byte-identical chunks to the serial path" flushed
  eight copies of g, so a parallel assembly that stored each column
  under the next index wrote the same bytes, and main's shell passed
  5 of 5. The eight columns now have eight different shapes, and the
  fingerprint adds the rows read back.
- "premise: parallel_flush is off by default" loses its prefix under
  #1339: the boot default is pgcolumnar's own, and setting it to true
  reddens it.

Measured, PG17, shell and twin:
- no worker ever registered: 1 red (the ON check); main's shell 0.
- each worker column stored under the next index: 1 red (the chunks
  check); main's shell 0.
- the boot default set to true: 2 red. A content term added to the
  dispatch metric: 1 red. Each red set is identical in both twins.
- clean: 5 of 5 on PG15 to PG19 in both twins.

Ledger: 5 rows, 5 dated by the four mutations and 0 never.
suites_not_covered 107 -> 106, cluster_tests 2503 -> 2508, TESTS.md
section 262. Census 3821 -> 3821 never, 2101 -> 2106 dated, 5927 rows. The ledger
gate and the guards pass over 7548c88.

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

test/pytest/test_parquet_export_stats.py ports all 162 checks of
test/parquet_export_stats.sh by name. The shell's three loops are unrolled,
so every name is literal; the twin parametrizes the same three over literal
tables. Five holes are fixed in both twins.

- The bound and null-count checks compared the footer with read_parquet's
  decoding of the same file, so they agreed with any fold the export made:
  with 2000-01-01 folded to null, all 162 passed. Both oracles now read the
  heap built from the generator.
- "no date or timestamp bound is an infinity sentinel" tested PostgreSQL's
  sentinels, but a bound is in Parquet's space: with infinities admitted it
  passed over -2147472692 and -9222425352054775809. Every date and
  timestamp bound must now lie inside the heap's finite range.
- "a zero maximum is written as +0.0" could not fail: the fixture's
  unquoted -0.0 stores +0.0. It is now quoted.
- Ten absence checks read 0 when they examined nothing; each now reads
  "N of M chunks" (or bounds). With no bounds written, the width and
  min <= max checks passed; with a leaf unresolved, the BYTE_ARRAY and
  deprecated-field checks passed.
- An unreadable footer recorded one check and ended the run, and an
  unreadable NaN fixture one check in place of ten. Each check now reads
  its own value and is red by name.

check_float, a suite-local helper, became `check` through as_float, and
test_compare_to_bash.py's list of suite-local helpers drops it, as that arm
asks when such a suite is ported. Four oracle checks lose `premise:` (#1339):
infinities admitted by the writer make the file unreadable to read_parquet.

Measured, PG17:
- 2000-01-01 folded: the old shell passed 162 of 162; group 0 min(d) and
  group 0 null_count(d) are now red in both twins.
- infinities admitted: the sentinel check, green in the old shell, is red.
- no bounds written: the width and min <= max checks, green, are red.
- the zero rule removed: 1 red in the old shell, 2 now.
- leaf t unresolved, a harness edit: 3 checks green before, red now.
- the parser missing, a harness edit: the old shell ran 1 check; 108 red now.
- twelve mutations, each reddening the same names in both twins: date
  folded 2, infinities 42, no bounds 82, bounds not reset 36, NaN admitted
  3, zero rule 2, nan_count 1, null_count 8, column_orders 2, cross-type
  refusal 5, float refusal 1, col < const on the maximum 17.
- clean: 162 of 162 on PG15 to PG19 in both twins.

Ledger: 162 rows, 133 dated by the 12 mutations and 29 never.
suites_not_covered 106 -> 105, cluster_tests 2508 -> 2670, TESTS.md
section 263. Census 3821 -> 3850 never, 2106 -> 2239 dated, 6089 rows. The ledger
gate and the guards pass over 9148807.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@jdatcmd
jdatcmd force-pushed the test/1341-parquet-export-stats-twin branch from 200ae8f to bdaaa09 Compare October 22, 2026 01:06
@jdatcmd

jdatcmd commented Oct 22, 2026

Copy link
Copy Markdown
Collaborator Author

Re-approval needed: rebased only, onto #1631's fixed head (d7f291c). This PR's own commit is unchanged; its guards job failed only on #1630/#1631's pyarrow imports. At the stack tip (bdaaa09): the guard's import scan finds 0 sites in 258 files, and the guard set is 474 passed.

@OffgridwithJD OffgridwithJD 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.

@jdatcmd Re-approving the rebased head bdaaa098 (I approved 200ae8ff).

  • Content unchanged: compared file by file against 200ae8ff, this PR's own +/- lines match, and it's one commit over its new parent. Only the budget, expected_tests and TESTS.md differ, and those only restate counts.
  • Gates at the stack tip bdaaa098: guards 474 passed, collection 2670 = cluster_tests, census 3850 = the tsv's never count.

🤖 Generated with Claude Code

@jdatcmd
jdatcmd merged commit a266632 into main Oct 22, 2026
15 checks passed
jdatcmd added a commit that referenced this pull request Oct 22, 2026
…ing line (#1613)

A comparison refused with a VacuityError takes no record ("a refusal marks
nothing", pinned), so the accounting line left it out. A twin under a mutant
then printed fewer failures than the shell's FAILs, sometimes `0 fail`, and a
runner reading the red set by name had to parse VacuityError text, which
missed some (#1624, #1642, #1644).

Option 2 from #1613, as OffgridwithJD chose it: refusals stay out of the
records, each refusal that failed its test prints

    REFUSED  <name>: <reason>

and the accounting line adds `N refused` to its total. So the shell's FAIL
names are the twin's FAIL names plus its REFUSED names, read straight off the
two outputs. The part is printed only when present, so a run without refusals
prints the line every reader already parses, byte for byte.

How: `_resolving` notes a VacuityError raised before any record, with the
check name bound from the call. pytest_runtest_makereport sends it on the
report (`pgc_refused`, so it crosses the xdist boundary like the records) only
when it is the exception that failed the test. A refusal a test catches on
purpose, as the layer's own guard tests do, is not reported.

Measured:
- Red: the new test_a_refused_comparison_is_counted_and_named, serial and
  under -n 2, failed with "'accounting: 2 pass + 1 fail + 0 unrun + 1
  refused = 4' is absent"; the inner outcomes (2 failed, 2 passed) held.
- Green: both cases pass. Mutant: report every noted refusal, not only the
  one that failed the test: both cases red again (the caught one counted).
- A real twin, catalog_filter_reads on PG17 with stats() raising: shell
  "5 passed + 2 failed = 7"; twin "5 pass + 0 fail + 0 unrun + 2 refused =
  7", and its two REFUSED names are the shell's two FAIL names. Before this
  change the twin's line read "5 pass + 0 fail + 0 unrun = 5".
- Harness selftest 1248/1248 (before the review).

Review (OffgridwithJD): refusals raised by expect.row_set (it refuses a failed
query itself, before delegating to rows) and expect.wrote (it raises its three
refusals directly) went through no _resolving wrapper, so they were neither
counted nor named; 127 call sites in the twins. Both are now wrapped. The
fixture refuses through all three paths: red on the previous head with
"accounting: 2 pass + 1 fail + 0 unrun + 1 refused = 4" where 3 refusals were
raised; green with "... + 3 refused = 6" and a REFUSED line for each. The
one-record-per-call guard now lists row_set and wrote among the wrapped
methods. Guards 806 passed (guard_tests 804 -> 806 on main 9ab6a4c).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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