feat(okf-v0.2): D5 — the v0.2 golden fixture, with okf_version in root frontmatter

Placement settled by catalog's own reading of upstream at the pinned commit
3fcbb9f: SS8:509-510 and SS12:773-775 both put `okf_version` in a bundle-root
`index.md` frontmatter block, and SS12 calls it the only place frontmatter is
permitted in an index. Catalog's spec says the opposite about the same file;
that divergence is theirs against upstream, and we conform to upstream.

The value never touches a profile. `OKF_V0_2.index.root_frontmatter` names the
key; the caller supplies the value through a new keyword-only
`root_frontmatter_values` mapping. That keeps V4/V-A5 intact - `okf_version`'s
value tracks the upstream Google version and belongs to catalog (E1), so a
constant here would claim a decision we do not own and would have to be chased
on every upstream release. In the fixture the value is fixture DATA
(`okf-version.txt`), not a literal in our source.

Ordering comes from the policy, not the caller's mapping: a dict preserves
insertion order, so two callers passing the same keys would otherwise emit
different bytes. A key the policy does not name is refused fail-fast, before
any disk mutation. Omitting the argument emits no block at all - SS12 is a MAY
and none of upstream's four reference bundles declares the key.

The block is written only when the index is CREATED, so a re-run into an
existing bundle stays byte-identical (A-E5).

Raw-byte assertions rather than parsed ones, on the committed fixture as well
as on fresh runs: catalog measured that a quoted value fails their shape regex
with exit 1 and that a BOM hides the marker while still exiting 0.
`yaml.safe_load` returns "0.2" either way and strips a BOM first, so a parsed
assertion masks exactly those two defects. Asserting the frozen fixture catches
what a self-comparison cannot - regenerating from a broken emitter moves both
sides together.

A-E6 is now placement-explicit (promised catalog in 99cf987), and separates the
two byte properties: BOM-free is a property of the file, unquoted is a property
of CATALOG'S GATE and not of OKF v0.2 - upstream's own SS12 example is quoted,
so their gate rejects the spec's canonical form.

README gains the upstream-version section it was missing; CLAUDE.md gains the
mechanism behind "no profile hard-codes an upstream version": a profile names a
key, a caller owns its value.

550 -> 559 tests. test_profile_threading's `OKF_V0_2.index is DEFAULT.index`
assertion is replaced rather than deleted: object identity was a proxy for "the
shipped profiles differ in no NAME-bearing field", which is what makes the
synthetic test profile necessary, so the guard now asserts that directly.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013dgkSPjkLpACjMayd9R5jx
This commit is contained in:
Kjell Tore Guttormsen 2026-07-31 17:27:48 +02:00
commit 2504011010
14 changed files with 470 additions and 34 deletions

View file

@ -752,24 +752,45 @@ output is the baseline. Materialize it twice with the *same* explicit
undecided: whether a `DEFAULT`-written bundle may be re-run **in place** under
`OKF_V0_2` — i.e. whether the predicate accepts both the v0.1 stamp and the
v0.2 form. That is a D2 requirement question standing with the operator.
- **A-E6** The root `index.md` declares `okf_version: 0.2`**unquoted**, and
the file carries **no UTF-8 BOM**. Both are load-bearing, not stylistic:
catalog measured that a quoted value fails their shape regex `exit 1`, and
that a BOM makes the marker invisible to them while still exiting `0`. The
emitter renders values verbatim, so this is a property of the value we hand
it. Verifying the raw bytes here is part of the expectation.
- **A-E6** The root `index.md` declares `okf_version: 0.2` in a **frontmatter
block opening the file** — the first bytes are `---\nokf_version: 0.2\n---\n\n`
— with the value **unquoted** and **no UTF-8 BOM**.
**What A-E6 does not yet say is WHERE**, and as of 2026-07-26 that silence is
load-bearing rather than cosmetic. Upstream §12 puts `okf_version` in the root
`index.md`'s frontmatter block; catalog's own spec (`:55`, `:95`) says an
`index.md` has no frontmatter and carries the marker as a body line. A bundle
conforming to one is non-conformant against the other. Catalog verifies against
upstream before touching their §6, and D5 is not frozen until they have. Their
gate cannot settle it either way — the shape regex is unanchored and matches in
both places — so a green TEST B measures the value, never the placement. A-E6
becomes placement-explicit before the pilot receives it, whichever way the
choice lands; an expectation that does not state what it expects is the defect
class this very expectation is named after.
**PLACEMENT — settled 2026-07-31, frontmatter.** Catalog read upstream
themselves at the pinned commit `3fcbb9f` and reported §8:509-510 ("Index files
contain no frontmatter, with one exception: a bundle-root `index.md` MAY carry
an `okf_version` key") and §12:773-775 (declared "in a bundle-root `index.md`
frontmatter block (the only place frontmatter is permitted in an `index.md`)").
Their own spec (`:55`, `:95`) says the opposite about the same file; the
divergence is real, it is **theirs against upstream**, and its resolution is
their operator's. We conform to upstream. D5 is frozen on that.
**The two byte properties are expectations about DIFFERENT things, and A-E6
must not conflate them** — doing so would be this expectation's own defect
class:
- **Unquoted** is a property of **catalog's gate**, NOT of OKF v0.2. Their
shape regex `/^\d+(\.\d+)*$/` reads the quotes as part of the value and
exits 1. Upstream's §12:773 is the only line in the whole spec showing the
key *with* a value, and it shows it **quoted** — so their gate rejects
upstream's own canonical example. That is catalog's defect to resolve, not
ours to work around, but while it stands, an unquoted value is what passes.
We emit unquoted, and A-E6 expects unquoted **of catalog's gate**.
- **BOM-free** is a property of the file we emit, full stop. A BOM makes the
marker invisible to their gate *while still exiting 0* — a failure that
reports success.
Both are asserted on **raw bytes**, never on a parsed value: `yaml.safe_load`
returns `"0.2"` whether or not it was quoted and strips a BOM before any caller
sees it, so a parsed assertion masks precisely these two defects.
**A green TEST B does not confirm the placement, and a red one does not
identify it.** The shape regex is unanchored, so it matches in the frontmatter
block and in a body line alike — green measures the value, never the position.
Catalog sharpened the other direction on 2026-07-31: moving the marker into
frontmatter *with* quoting, as §12 literally shows it, also yields FAIL. So a
red run cannot distinguish "wrong quoting" from "wrong placement". Read TEST B
as a statement about the value's shape only, in both directions.
**What would surprise us — report immediately:** any diff outside the frontmatter
block (the profile seam leaks); `at` differing from the `ingested_at` they passed
@ -1096,7 +1117,17 @@ everything after. **Done 2026-07-26** (`1215f98`, `7bc366b`).
6. **D5** — the v0.2 golden fixture, generated by the code. `okf_version: 0.2`
is declared only here, once 15 are green (conform first, claim after).
**Unquoted** — the quoted form written here previously contradicted A-E6 and
is exactly what catalog measured as `exit 1`.
is exactly what catalog measured as `exit 1`. **Done 2026-07-31** (559
tests): `examples/ingest-golden-okf-v0-2/`, emitted by the code and frozen
byte-exact, with the marker in the root `index.md`'s frontmatter block per
upstream §8/§12. The value reaches the emitter as
`materialize_bundle(..., root_frontmatter_values={"okf_version": ...})`
keyword-only, defaulting to none, so every existing call site is untouched.
The profile names the key (`OKF_V0_2.index.root_frontmatter`) and never
carries the value, which is V4/V-A5 held intact: the value is catalog's (E1),
and in the fixture it is fixture DATA (`okf-version.txt`), not a constant in
our source. Offering a key the policy does not name is refused fail-fast
before any disk mutation.
7. **P1** — read-only sweep over real pilot bundles. Cheap, zero-risk, and the
first point where a finding can come from outside our own assumptions.
8. **Pre-release tag `v0.5.0a1`**, then **P2** (producer) and **P3** (gate).