docs(k3): round 24, a reason that became false is struck and the emitter stays flow

`_render_sources` gave three measured reasons for refusing to emit the block
form. Two of them fell with `28f9a4b` and are struck rather than left
standing: a block list no longer round-trips to an empty value (4 609 of
4 609 concept files), and B6's acceptance test can now pass.

Reason 2 STANDS and now carries the emission rule alone, re-measured by
reading `portfolio-optimiser` at `6eb58e5`: `read_provenance` returns
`UnreadableProvenance(reason="block-sequence")` for a block value. It is not
the guard's objection -- guard 1.4.0 reads the block form on 4 609 of 4 609
of the same files -- so the answer to "should our own emitter write block" is
NO, with the number that carries it, and the measurement that would make it
decidable is named in STATE rather than deferred without one.

The README paragraph said this library "cannot read the block form" where one
reader could and one could not. It now separates the two by KEY: `sources` in
either form through every reader, every other block mapping still refused,
with the D1b pointer intact.

Report: 222 lines, S7 form. Five deviations first, including the acceptance
sentence naming THREE flat readers rather than one, a `title` denominator of
7 372 rather than 5 372, and the `okf check` rule answered NO with a
measurement (`contract_check` holds 0 occurrences of `sources`, and
`consume` already writes `sources_unreadable` for the state a rule would
restate; `len(RULES)` is 17). Honesty limits name the one-key-wide set, the
block `verified:` fixture still reading empty, and the rendering not being
byte-identical to the source.

Suite 1807 passed / 1 skipped, rc 0, 98 s.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Kjell Tore Guttormsen 2026-09-12 16:50:26 +02:00
commit 2d4f56dc6c
5 changed files with 326 additions and 27 deletions

View file

@ -7,6 +7,53 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
### Fixed
- **A block `sources:` sequence no longer loses the address in the flat
readers (K3-24).** `consume.read_sources` has always read both YAML forms;
the three copies of this library's line-oriented frontmatter grammar read
only the flow one and returned the key with an EMPTY value for the block
form -- not a `KeyError` a consumer can catch, an address that disappears.
Measured 2026-09-12 over four bundles a producer ships, denominator = files
carrying a frontmatter block: 2 756 of 2 757, 446 of 447, 1 133 of 1 134 and
270 of 271 concept files lost it, while PyYAML 6.0.3 and the pinned guard
1.4.0 both read it on 100 % of the same files. After: **0 of each**, and all
three flat readers return what BOTH reference readers return on 4 609 of
4 609 files, plus 2 762 of 2 762 in a flow-form bundle that is unchanged.
`materialize.parse_frontmatter` is public API, so the external consumer is
the one this repairs.
The value type was the choice and it was measured: widening the return type
from `dict[str, str]` costs 15 `mypy --strict` errors across four modules
plus a signature every outside caller follows; rendering the entries back
into the flow form those readers already round-trip costs 0. The rendering
is a READING projection, not a claim that the value is writable.
Narrow on purpose: `profiles.STRUCTURED_BLOCK_KEYS` is `{"sources"}`, the
key `read_sources` already knows how to read, and one grammar now serves all
four call sites (`profiles.read_block_mappings`). A shipped fixture carrying
a block `verified:` still reads as an empty value, pinned by a test so the
next widening is a decision rather than a side effect. The K3-20 guarantee
is unmoved and asserted per reader copy: a nested key never enters the
document's namespace.
### Changed
- **No bundle bytes move.** A five-document folder built before and after is
`diff -r`-identical (52 files), so no ranking measurement is owed. The
emission rule is untouched: this library still writes flow.
- **Two docstrings and one README paragraph corrected rather than left
standing.** `materialize._render_sources` gave three measured reasons for
refusing to emit the block form; reason 1 (a block list round-trips to an
empty value) and reason 3 (B6's acceptance test cannot pass) FELL with this
fix and are struck. Reason 2 STANDS and now carries the rule alone,
re-measured by reading `portfolio-optimiser` at `6eb58e5`: `read_provenance`
returns `UnreadableProvenance(reason="block-sequence")`. It is not the
guard's objection -- guard 1.4.0 reads the block form on 4 609 of 4 609
files. The README said this library "cannot read the block form" where one
reader could and one could not; it now separates the two by KEY.
## [0.8.4] — 2026-09-11
### Added