The shell harness records every check outcome as a machine-readable line, and counting a check is the same operation as recording it — pgc_record does both, and there is no code path that does either alone. pgc_reconcile_records then validates the schema and reconciles the record count against checks run:.
The pytest harness has no equivalent, and after #923 the gap is explicit rather than hidden.
What exists today
Expect._counted() increments a per-test counter. report.user_properties carries exactly one structured value, pgc_unrunnable, and pytest_terminal_summary prints the unrunnable count. Nothing emits a per-assertion record, so there is nothing to reconcile a count against.
Concretely, the pytest layer cannot answer any of these:
- which named assertion produced which outcome,
- what its verdict and reason were,
- whether a name carrying a tab or newline would corrupt the stream,
- whether a conditional SKIP was counted,
- whether the session's totals reconcile against the records.
Every one of those is answered on the shell side, and each was found to be answerable only because the shell has records to check.
Why this is filed rather than done in #923
#923 originally carried test_check_results_are_machine_readable.py, which reached the property by sourcing the real test/lib.sh and extracting pgc_reconcile_records from run_all_versions.sh. @linuxhikerpm blocked it under the harness-independence rule in CONTEXT.md, correctly: the rule is already on main, and a file that has not landed is a new violation rather than pre-existing debt. The owner's ruling on the same question was "neither rule wins. each harness is independent and should only parallel test functionality."
A twin that drives the other harness agrees with it by construction and can never report it wrong, so the file was deleted rather than repaired — the precedent being #927, where the shell part whose subject was a python module's source text was deleted for the same reason.
The remedy the review asks for is a feature, not a port. There is no per-assertion record stream in pytest to write a native test against; it has to be built first.
What "parallel in functionality" means here
Not a copy of the shell's format. The pytest harness should reach the same property through Python's own reporting interface — pytest_runtest_logreport, user_properties, and the junitxml those feed — so that:
- every counted assertion produces exactly one structured record, in one operation, with no path that does either alone;
- each record carries enough to identify the assertion, its verdict and its reason;
- field separators cannot be smuggled in through a name or a reason;
- an assertion that declined to run is counted like any other outcome;
- the session total reconciles against the records, and the reconciliation can fail.
Point 1 is the one that matters: it is the property #917 exists to establish, and on the shell side it was reached by making counting and recording literally the same call.
Acceptance
- No file under
test/pytest/ reads, sources, executes or derives from any file in test/*.sh or test/selftest/*.sh. The arm for this should be derived, not a filename list.
- Each of the five properties above has an arm with a removal proof: name the mutation, apply it, show which arm reddens.
- A reconciliation that cannot fail is not acceptance. The shell side shipped one twice —
inputs == sum(buckets) that could not go red — and both were caught only by mutating them.
Related
🤖 Generated with Claude Code
https://claude.ai/code/session_01Unuuvh3fRR67SceiGpfeeK
The shell harness records every check outcome as a machine-readable line, and counting a check is the same operation as recording it —
pgc_recorddoes both, and there is no code path that does either alone.pgc_reconcile_recordsthen validates the schema and reconciles the record count againstchecks run:.The pytest harness has no equivalent, and after #923 the gap is explicit rather than hidden.
What exists today
Expect._counted()increments a per-test counter.report.user_propertiescarries exactly one structured value,pgc_unrunnable, andpytest_terminal_summaryprints the unrunnable count. Nothing emits a per-assertion record, so there is nothing to reconcile a count against.Concretely, the pytest layer cannot answer any of these:
Every one of those is answered on the shell side, and each was found to be answerable only because the shell has records to check.
Why this is filed rather than done in #923
#923 originally carried
test_check_results_are_machine_readable.py, which reached the property by sourcing the realtest/lib.shand extractingpgc_reconcile_recordsfromrun_all_versions.sh. @linuxhikerpm blocked it under the harness-independence rule inCONTEXT.md, correctly: the rule is already onmain, and a file that has not landed is a new violation rather than pre-existing debt. The owner's ruling on the same question was "neither rule wins. each harness is independent and should only parallel test functionality."A twin that drives the other harness agrees with it by construction and can never report it wrong, so the file was deleted rather than repaired — the precedent being #927, where the shell part whose subject was a python module's source text was deleted for the same reason.
The remedy the review asks for is a feature, not a port. There is no per-assertion record stream in pytest to write a native test against; it has to be built first.
What "parallel in functionality" means here
Not a copy of the shell's format. The pytest harness should reach the same property through Python's own reporting interface —
pytest_runtest_logreport,user_properties, and the junitxml those feed — so that:Point 1 is the one that matters: it is the property #917 exists to establish, and on the shell side it was reached by making counting and recording literally the same call.
Acceptance
test/pytest/reads, sources, executes or derives from any file intest/*.shortest/selftest/*.sh. The arm for this should be derived, not a filename list.inputs == sum(buckets)that could not go red — and both were caught only by mutating them.Related
🤖 Generated with Claude Code
https://claude.ai/code/session_01Unuuvh3fRR67SceiGpfeeK