test(mutants,assets): a mutant is judged by the suite that owns it, 45 of 45
The runner could only run one test file, which is why PM's three
survivors from `43331fc` could not be added: two are held by the gate's
row 3 and one by the soft-hyphen door's suite. A mutant now names its
suite; the catalogue goes 39 to 45.
X3 and X4 rewritten against the code as it now stands -- a mutant table
is a copy of the code it mutates, and this round moved the lines both of
them quoted. X6 is the defeated state exactly, X7 cuts the ledger off at
its source, X8 removes the cursor rule, P6/P11/P12 are PM's three.
Two survivors on the first run, both findings, both closed:
- X4 survived because every forgery arm now fails on the ledger check
before the binding is reached. An arm was added where the run DID book
the pair and the block stating it points at another picture.
- X5 survived the WHOLE suite -- 2134 passed with the disarming removed
-- because a document-supplied field can no longer reach the gate. The
property is about the BUNDLE and not about one judge, so it is kept and
measured in `tests/test_assets.py`, with a known-positive counting the
run's own two fields on the same expression.
killed 45 of 45, exit 0. Report, CHANGELOG and CLAUDE.md written.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
6d7dc7a660
commit
44ad845e29
6 changed files with 431 additions and 17 deletions
174
docs/2026-09-19-pekerblokken-bindes-til-kjoringen.md
Normal file
174
docs/2026-09-19-pekerblokken-bindes-til-kjoringen.md
Normal file
|
|
@ -0,0 +1,174 @@
|
|||
# The pointer block, bound to the run
|
||||
|
||||
2026-09-19. PM's checkpoint on `ae441ab` judged the previous round PARTIAL
|
||||
with one load-bearing rest and two small ones. The rest is the same class the
|
||||
round before it: the judge's fasit came from the reader it judges. It had been
|
||||
narrowed, not closed. This closes it, closes the two small ones, and clears
|
||||
three further residuals PM had listed. No new format, no new dependency, no
|
||||
version bump and no tag.
|
||||
|
||||
## 1. A form is not a signature
|
||||
|
||||
`tools/okf_accounting_gate.py` read the conversion claim out of the bundle
|
||||
text. The round before bound it to a POINTER BLOCK, which is the two lines
|
||||
`assets.render_block` writes:
|
||||
|
||||
```
|
||||

|
||||
Image: <name> (<w>x<h> px) -- converted from <type> sha256:<A> to <type> sha256:<B>
|
||||
```
|
||||
|
||||
That closed the two routes PM had measured (an `alt` attribute and plain body
|
||||
text) and left the class open, because a document can write the whole block.
|
||||
Reproduced here through the real `okf build`, three ways, each a whole build:
|
||||
|
||||
| way | before | known-positive in the same build |
|
||||
|---|---|---|
|
||||
| two `<p>` elements in one HTML file | **forged** | held |
|
||||
| one `<p>` with a `<br>` | **forged** | held |
|
||||
| a markdown note beside the HTML carrier | **forged** | held |
|
||||
|
||||
"Forged" means `asset_holds` returned True for a BMP declaring 50 000 x
|
||||
50 000, refused `asset_too_large`, absent from `assets/`. The forger needs two
|
||||
digests: the one it wants vouched for, and one of a picture the bundle really
|
||||
carries — public in the bundle, and computable in advance for a PNG carried
|
||||
verbatim.
|
||||
|
||||
### What was chosen, and why
|
||||
|
||||
Two fixes were on the table. The claim is now read from the RUN's own ledger:
|
||||
|
||||
- `assets.conversion(image)` names the `(source digest, asset digest)` pair.
|
||||
- `DocumentAssets.conversions` carries it out of the run.
|
||||
- `DocumentAccount.conversions` books it, and `okf build --accounting` writes
|
||||
`conversions: [{from, to}]` per document.
|
||||
- `_declared_conversions` reads that file; `_conversions` believes a pair only
|
||||
when the ledger holds it AND a pointer block confirms it for the asset it
|
||||
points at.
|
||||
|
||||
The confirmation can be forged and the ledger cannot, which is why the ledger
|
||||
decides. The alternative — neutralising pointer-shaped text at extraction, the
|
||||
way the soft-hyphen door neutralises U+00AD — was felled: it changes what every
|
||||
document SAYS in order to defend a tool outside the build, a source quoting a
|
||||
bundle listing would come out altered, and the bytes of existing bundles would
|
||||
move. This reads a file the run already writes.
|
||||
|
||||
A build with no accounting door has no ledger. A converted image is then
|
||||
reported claimed-and-not-found rather than believed, which is the reading the
|
||||
gate had before the conversion route existed, and it is visible in the row.
|
||||
|
||||
### After
|
||||
|
||||
3 of 3 forged → refused, with the known-positive True in all three. The
|
||||
text-level regression guard goes from 3 arms to 13: PM's eleven ways that the
|
||||
shape anchoring already refused, kept so a later narrowing cannot widen one of
|
||||
them, plus the two forms that defeated it — a perfectly written pointer block
|
||||
the run never booked.
|
||||
|
||||
R761 rebuilt from the frozen delivery: bundle `diff -r`-identical to the build
|
||||
before the change, 50 assets (29 JPEG + 21 PNG, 0 BMP), 19 of 19 conversions
|
||||
confirmed against 19 declared, soft hyphens 71 → 0, u = 0, d = 0, exit 0.
|
||||
|
||||
## 2. A terminator is not a coverage proof
|
||||
|
||||
The previous round's terminator rule asks only that a stream SAY it is
|
||||
finished, and a stream may say so anywhere. Measured: a stream whose FIRST two
|
||||
bytes are the end-of-bitmap escape was carried, with 32 of 32 pixels never
|
||||
decoded and all of them palette index 0. Pillow refuses the same file.
|
||||
|
||||
`_bmp_rle8_rows` now also requires the cursor to stand at or past the end of
|
||||
the last row, refusing with the same `asset_samples_invalid` otherwise.
|
||||
|
||||
**The line is the cursor, not the pixels.** A delta escape and an end-of-line
|
||||
escape STATE their skip, so the pixels they pass over keep index 0 and every
|
||||
decoder produces the same picture; a pixel-coverage count would refuse both
|
||||
constructions the format defines. The corpus cannot choose between the two
|
||||
rules — over the 25 RLE8 BMPs the R761 delivery ships (24 distinct; the bundle
|
||||
carries 19), 25 of 25 paint every pixel, 25 of 25 reach the end of the frame
|
||||
and 0 of 25 use a delta. An independent decoder can:
|
||||
|
||||
| stream over one 8x4 frame | cursor reaches the frame | Pillow |
|
||||
|---|---|---|
|
||||
| end-of-bitmap before one pixel is decoded | no | refuses |
|
||||
| one 4-pixel run, then end-of-bitmap | no | refuses |
|
||||
| last row one pixel short, no end-of-line | no | refuses |
|
||||
| every row painted and closed | yes | reads |
|
||||
| last row one pixel short, then end-of-line | yes | reads |
|
||||
| a delta skipping a whole row | yes | reads |
|
||||
| last row painted to its end, no end-of-line | yes | reads |
|
||||
| the shipped fixture, which uses a delta | yes | reads |
|
||||
|
||||
Eight of eight agree with the new rule, and a second test holds Pillow to the
|
||||
table so it is not our own rule restated.
|
||||
|
||||
Both docstrings PM named are rewritten. The test no longer says "NO PIXEL MAY
|
||||
BE GUESSED: either every one of them is decoded from the stream, or the picture
|
||||
is refused" — that was false as measured. `_bmp_rle8_rows` no longer frames the
|
||||
delta argument as read off the corpus, which it never was: 0 of the 25 files
|
||||
use a delta, so the corpus had nothing to say about it.
|
||||
|
||||
## 3. One number, read from one place
|
||||
|
||||
`assert sum(tbx.values()) == 568` sat behind a `skipif` on a delivery only this
|
||||
machine has, so on a fresh clone the sentence five files publish was unguarded
|
||||
again — the state in which 574 survived in four docstrings until PM counted it.
|
||||
`N101_TBX_TAGS` is now the one place the number lives, and a second test reads
|
||||
the published sentence out of `CHANGELOG.md`, `CLAUDE.md`, `tools/okf_witness.py`,
|
||||
`tests/test_accounting_gate.py` and this round's predecessor report, holding all
|
||||
five to it. It needs no corpus and no clock.
|
||||
|
||||
It was red at birth, and for a reason worth keeping: the scan read the test
|
||||
file's own known-positive string (`574`) as a sixth publisher. That string is
|
||||
now assembled from pieces, and the failure is the demonstration that the scan
|
||||
reads what it is pointed at. What the guard does NOT prove is stated in its
|
||||
docstring: five files agreeing is agreement, not a count. The measurement stays
|
||||
where it was.
|
||||
|
||||
## 4. A clean extract is not a checkout
|
||||
|
||||
`test_the_four_existing_goldens_are_untouched` ran `git status` with
|
||||
`check=True` in a `git archive` extract and raised `CalledProcessError`. It was
|
||||
the single failure of the whole suite run from a clean extract, twice reported
|
||||
as a round's one failure by a round that had not touched the file. Both arms
|
||||
measure the checkout with `git`, so an extract has nothing to ask: it now skips
|
||||
with the reason stated, and a checkout still has to answer.
|
||||
|
||||
## 5. The mutant runner
|
||||
|
||||
`tools/okf_gate_mutants.py` could only run one test file, which is why PM's
|
||||
three survivors from `43331fc` could not be added: two are held by the gate's
|
||||
row 3 and one by the soft-hyphen door's own suite. A mutant now names the suite
|
||||
it is judged by, and the catalogue goes 39 to 45:
|
||||
|
||||
- **X3** and **X4** were rewritten against the code as it now stands. A mutant
|
||||
table is a copy of the code it mutates, and this round moved the lines both
|
||||
of them quoted.
|
||||
- **X6** is the defeated state exactly: a pointer block believed without the
|
||||
run having booked it.
|
||||
- **X7** cuts the ledger off at its source.
|
||||
- **X8** removes the cursor rule.
|
||||
- **P6**, **P11**, **P12** are PM's three, now held by what runs AS the gate.
|
||||
|
||||
Two survivors appeared on the first run and both were findings.
|
||||
|
||||
**X4** — "the claim need not be about the asset its block points at" — survived
|
||||
because every forgery arm now fails on the ledger check before the binding is
|
||||
reached. An arm was added where the run DID book the pair and the block stating
|
||||
it points at another picture; under the mutant that reads as a carry.
|
||||
|
||||
**X5** — "a document-supplied label may emit a checksum field" — survived the
|
||||
WHOLE suite, measured: 2134 passed with the disarming removed. The gate no
|
||||
longer reads its claim out of the bundle, so a document-supplied field cannot
|
||||
reach it. The property did not stop mattering — the line stands in every
|
||||
concept body, and a bundle must not state a conversion in a sentence the run
|
||||
did not write — so it is measured where it lives, in `tests/test_assets.py`,
|
||||
and the mutant is judged there. Deleting an unfellable guard was the other
|
||||
option and was felled: this guard is about the bundle, not about one judge.
|
||||
|
||||
## 6. What this round does not do
|
||||
|
||||
- It does not teach the gate FIDELITY. A converter writing a blank PNG is still
|
||||
accepted, for the reason `asset_holds` states: both routes ask whether a file
|
||||
holds the bytes the run names, and neither decodes a pixel.
|
||||
- It does not touch the guard, the goldens, the proposer, any profile, any
|
||||
version or any dependency.
|
||||
Loading…
Add table
Add a link
Reference in a new issue