# The end-of-line stream that paints nothing, and four smaller rests 2026-09-19. PM's checkpoint on `44ad845` judged the previous round PARTIAL with no load-bearing rest and five small ones. Four are code and one is arithmetic in a closing message. This closes all five. No new format, no new dependency, no version bump, no tag, and not one byte of any bundle moves -- measured, not asserted. ## 1. The rest that was a rule and not a sentence The round before wrote a cursor rule into `_bmp_rle8_rows` and, beside it, a sentence: > a delta escape and an end-of-line escape both leave pixels at index 0 and > every decoder agrees on them, because the stream stated the skip PM measured it false for the end-of-line half: four end-of-line escapes and an end-of-bitmap carry an 8x4 frame with **32 of 32** pixels never decoded, and Pillow -- the independent decoder that file names -- refuses the same bytes. The order offered two options: refuse a stream that reaches the end of the frame without painting (recommended), or write the sentence true. **Both were measured before either was chosen**, and the recommendation does not close the class. ### 1.1 The class, swept rather than curated Twice now this defect has been closed one construction at a time -- a truncated stream, then an immediate end-of-bitmap -- and twice the class stayed open one step down. So it was enumerated: every opcode sequence of length 1 to 4 over twelve opcodes on a 4x3 frame, **22 620 streams** (encoded runs that fit and runs that do not, absolute blocks, end-of-line, five deltas), each decoded by this package and by Pillow. | rule | carried here, refused by Pillow | carried by both, drawn differently | real RLE8 files carried | |---|---|---|---| | before this round | **703** | **1 492** | 25 of 25 | | + refuse when nothing was painted (PM's recommendation) | 512 | 1 171 | 25 of 25 | | + refuse an end-of-line at column 0 | **0** | 1 118 | 25 of 25 | | + refuse a delta out of its row | **0** | **32** | 25 of 25 | | + refuse a run that overruns its row | 0 | 0 | **10 of 25** | The recommendation on its own would have narrowed the class for the third round running. The last row is the one that cannot ship. ### 1.2 Why the end-of-line, and why the delta Pillow's RLE8 decoder is a flat accumulator: an end-of-line pads its buffer to the next row boundary, so an end-of-line at column 0 adds NOTHING, while this reader advances a whole row. A delta appends `dx + dy * width` bytes, which is the same skip this reader makes -- as long as `dx` stays inside the row. So the two clauses are the two places where a stream can move this reader's cursor without stating a skip another reader follows: * **an end-of-line escape at column 0.** It closes no row, so the row it passes over is a row the stream never wrote. * **a delta whose horizontal offset would leave the row.** The format puts that offset inside the line; this reader keeps the cursor past the row end and a flat decoder rolls it into the next row. Two pictures, one stream. Both refuse with `asset_samples_invalid`, the code the path already uses. ### 1.3 What is NOT closed, and what closing it would cost All **32** residual disagreements are a run or absolute block that OVERRUNS its row -- clipped at the row end here, spilled into the next row by Pillow. Refusing those gives 0 and 0 and costs **15 of the 25** real RLE8 files: 15 real figures dropped and a pinned bundle's bytes moved. It is stated in the docstring, in the test and here, and it is not taken. ### 1.4 The corpus, measured first and measured again Over **11 441** files scanned across the four raw standard deliveries and the K2 reference corpus, the only BMPs on this machine are R761's **25** (24 distinct), all RLE8. Of those: ``` end-of-line at column 0 : 0 of 25 delta escape (any) : 0 of 25 run overruns its row : 15 of 25 ``` So both shipped clauses cost nothing measured, and after the change **25 of 25** still decode to Pillow's pixels exactly -- **3 117 220** pixels compared, byte for byte, with the two readers independent on both sides. ## 2. The arms the table could not see `CURSOR_CASES` goes 8 to 12. One arm per CLAUSE of the cursor rule: the ROW clause had none, because every short stream in the table stops on the last row, which is why PM's `P8` (`height - 1` -> `height - 2`) survived 51 tests. The new arm stops one row early with that row complete. Three more arms are the end-of-line class. The table is CURATED and stays that way -- every arm in it is one where the two readers agree -- and the general relation between them is now held by the sweep, which asserts the direction that matters (nothing carried here is a file the other decoder refuses) and requires every residual disagreement to be an overrunning run, computed in the test by a walker written there. `P8` and `P13` (the end-of-line clause removed) join the mutant runner: **killed 47 of 47**, exit 0. ## 3. The contract a consumer implements the door from `tools/okf_accounting_gate.py` publishes the `--accounting` JSON a consumer is expected to write. It did not name `conversions` -- which `asset_holds`' conversion route now DEPENDS on -- nor `normalised_soft_hyphen`, `unaccounted` or `double_booked`. A door built from that contract writes a ledger this gate reads as "nothing was converted", and every converted image comes out claimed-and-not-found: **19 of 50** on R761. Two tests hold the sketch, one per direction: * what the gate READS. The ledger is handed to `_declared_conversions` as a mapping that records every lookup at any depth, so the assertion is about lookups and not about grep -- grep reads a rejection code as a JSON key. * what the door WRITES, from `Accounting.to_json()` itself, so the fix cannot be "publish everything": the two sets must be equal. ## 4. A doorless bundle says why Measured by PM (N5): built without `--accounting` there is no ledger, so `asset_holds` falls back to its first route and a converted image is counted claimed-and-not-found. That is the honest reading and it was a silent one -- the count was printed, its cause was not, and a reader of such a report can only conclude the bundle lost the pictures. **Chosen: say it out loud, not only in a docstring.** A docstring is read by whoever edits the file; the reader who is misled is reading a REPORT. `_tally` now names the missing ledger when, and only when, something was claimed and not found -- two known-negatives hold that condition -- and `asset_holds` gets the sentence as well, since it costs nothing. The gate itself always passes the flag, so no row moves. ## 5. The count in a closing message The previous closing message miscounted the outstanding work items. The count here was measured at closing time against the queue itself, not from memory. ## 6. The bytes, and every gate | what | result | |---|---| | R761 built from a pinned pre-change tree and from this one, shared inbox | `diff -r` **IDENTICAL**, **5 551** files | | assets in that bundle | **50**, 29 JPEG + 21 PNG, **50 of 50** viewable | | conversions booked, and lossless against Pillow | **19 of 19**, 2 366 365 pixels | | soft hyphens | 71 removed, 0 in the bundle | | content-accounting gate | `GATE RED: rows 2, 3, 6`, exit 1 -- row 2 8 of 13, row 3 17 of 22 (u = 19, d = 2), row 6 110 of 111. **Unchanged** | | mutant runner | `killed 47 of 47`, exit 0 | | suite, clean extract of the commit | see the closing message | | `ruff check` / `ruff format --check` / `mypy --strict src/` | clean | The BEFORE bundle was built from `git archive 23588e5` with `PYTHONPATH` pointing at that tree, and the module path was printed in both runs, because an editable install would otherwise have measured the working tree twice. ## 7. What this round did not measure * The 43-document reference corpus. Unchanged since before the round and not rebuilt (cost); the argument for it is the same as R761's, since the two clauses can only fire inside an RLE8 BMP and 0 of that corpus's files are one. * Fidelity, which neither route of the judge proves. Unchanged. * Whether another decoder besides Pillow draws the residual 32 the way this reader does. One independent decoder is the measurement; it is not a vote.