Files
booth/persistent-memory.d/2026-09-22-a-mutation-harness-that-certified-a-broken-test.md
T
vh 2511aab3d6 memory: a third way an instrument goes blind — nth-child vs nth-of-type
Credited to design-dev. His R2 order check has a positive control — one tile
given order:-1 that the check must catch — and the control went blind when group
headers became grid children: nth-child(5) started landing on a header rather
than the fifth tile.

Same class as the two defects already in this file. A control that no longer
controls reads exactly like a passing test; nothing in the output distinguishes
'detected nothing because there was nothing' from 'detected nothing because I am
aimed at the wrong element'.

The rule: nth-of-type over nth-child wherever the assertion means the Nth TILE
rather than the Nth child element. They agree until somebody adds a sibling of a
different kind, and adding siblings is what a redesign is.
2026-09-23 09:14:32 -07:00

3.6 KiB

A mutation harness that certified a broken test, twice, for two reasons

2026-09-22 · booth

This repo already knows that an assertion which has never seen its own defeating change is not known to falsify anything — two prior entries say so (2026-09-22-vacuous-falsifiers, 2026-09-22-seven-of-seven-falsifiers). So U7's groups were built with a harness that applies each defeating change and asserts the named test goes red. The harness itself had two defects, and both produce the same lie: a falsifier certified without being run.

Defect 1 — no green baseline

A test that is already red reports RED for every mutation thrown at it. The escaping test had an arithmetic slip (counted < against <a/<nav/</ and forgot the two <b> elements), so it was failing for a reason unrelated to escaping — and the harness cheerfully reported RED ✓ the rail markup is emitted with |safe. Run the test unmutated first; a non-zero baseline is a harness failure, not a proven falsifier.

Defect 2 — the bytecode cache, which is the subtle one

if len(sizes) < 2 → if len(sizes) < 1 is byte-identical in size. CPython validates a .pyc against the source's (mtime, size) at one-second granularity — so a mutation that lands in the same second as the revert before it is invisible, the cached bytecode is reused, and the harness runs the unmutated code and reports the falsifier proven.

The tell was non-determinism with no cause: INV-3a certified RED on one run and GREEN on the next with neither the test nor the code changing, and reproduced by hand every time. Fix: delete __pycache__ and set PYTHONDONTWRITEBYTECODE=1 in the subprocess environment before every run.

⚠ This bites any same-size source mutation, which is most interesting ones: comparison flips, off-by-one constants, and↔or, <↔>. A mutation harness without cache defeat is biased toward exactly the mutations most worth running.

Result

12 falsifiers, 12 proved, stable across consecutive runs. Two of them only after these fixes — and one of the twelve (test_group_order_is_the_position_of _the_first_member) was genuinely vacuous on the first pass: its w, x, y fixture's positional order happened to be alphabetical, so it stayed green under the alphabetical-sort mutation it forbade. Rebuilt so all three plausible rules (position, alphabetical, count) disagree.

The harness lives in the session scratchpad and dies with the session. Whether it becomes scripts/ is an open question for the operator — this repo has now been bitten by vacuous falsifiers three times, and prose in a memory file is not an instrument.

A third way an instrument goes blind: nth-child vs nth-of-type

Added 2026-09-23, credited to design-dev, who hit it in his R2 order check.

His layout check has a positive control — one tile given order:-1 that the check must catch. The control went blind when group headers became grid children: nth-child(5) started landing on a header instead of the fifth tile, so the control stopped controlling and the check kept reporting clean.

Same class as this file's other two, and the reason it belongs here: a control that no longer controls reads exactly like a passing test. Nothing in the output distinguishes "detected nothing because there was nothing" from "detected nothing because I am aimed at the wrong element".

The rule worth having written down: use nth-of-type over nth-child for any assertion that means the Nth TILE rather than the Nth child element. The two agree right up until somebody adds a sibling of a different kind — and adding a sibling is what a redesign is.