V-A8 was scheduled to be WEAKENED to parse-and-render on the standing claim
that "there is no validator in okf/". Enumerating the repository tree instead
of that one subdirectory falsified it: OKFDocument.validate() sits at
okf/src/reference_agent/bundle/document.py:58 inside a working v0.2 reader.
So V-A8 ran as written, and passed 13/13 against
examples/ingest-golden-okf-v0-2/ at pinned 3fcbb9f. It needs nothing installed
-- document.py imports only yaml -- so .venv and the one-runtime-dependency
rule are untouched. The load-bearing assertion is not validate() (which checks
a single key, `type`); it is that a REAL yaml parser recovers our inline flow
forms as structures: `generated` as a mapping, `sources` as a list of mappings.
Our own parser is line-oriented and reads both as opaque strings, so no test of
ours could ever have answered this.
Second falsified premise, in the A-E6 rationale itself: "yaml.safe_load returns
"0.2" whether or not it was quoted". Measured against PyYAML 6.0.3 (the version
upstream requires), unquoted loads as float 0.2 and quoted as str '0.2'. The
BOM half of the same sentence is true. Correcting it surfaces what it hid --
unquoted `0.10` loads as `0.1`, indistinguishable from v0.1, and the type is
not stable across version shapes (`0.2` float, `0.2.1` str). Upstream's only
written instance, SPEC.md:773, is quoted.
That changes nothing today and D5 is NOT requoted: at 0.2 both forms are
unambiguous, and neither consumer reading the key parses YAML. It is recorded
because okf_version's value belongs to catalog (E1), so it is a constraint we
owe them, not a choice we may make for them.
Runbook: Step 2 now enumerates the REPOSITORY root, not okf/ -- the same
mistake this step already warned about, repeated one level up (toolbox/ and
samples/ sit outside okf/; 265 tracked files, 48 py + 43 ts, none of it on any
list). New Step 3a carries the V-A8 procedure per upstream release, including
that it can never be a pytest test and why.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012gwLPe5TY5aN3o3tejv9Nh
Caught while verifying facts for a consumer notice: the runbook stated in the
past tense that `profile` landed as a keyword-only argument with a DEFAULT
default. Measured -- no door takes the argument at all. The decision is real and
stays; the claim that it is in the code was about to be repeated to a consumer
as a property they could build on.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A2aKJxLejT9S8jYwoZ9fut
The standing always-latest policy had no procedure, and v0.1 -> v0.2 showed what
that costs: the spec was read, the shipped example bundles were not, and five
findings a spec reading had settled wrongly surfaced only after someone asked
whether we had studied the examples. One of them re-sized the release itself.
docs/upstream-okf-upgrade-runbook.md is that procedure. Every step names the
concrete failure it prevents, and each is a failure that actually happened:
- Pin before reading. Two repos read main independently, which felt like two
confirmations and was one unstable reference read twice -- and SPEC.md was
edited after the migration commit, so neither alone was the whole answer.
- Enumerate the whole okf/ tree. bundles/ held the real examples and was on
nobody's list until the tree was listed.
- Read the shipped examples, not only the normative text. A directory named
samples/ turned out to be a runner config, not samples of the format. And
markdown-converting fetch tooling strips exactly the byte-level properties
(BOM, trailing newline, quoting) that break downstream gates.
- Name fixtures by repo AND path. Two repos had examples/ingest-golden-file/
holding different fixtures with zero content overlap, and a pilot baseline was
agreed by a name that pointed at two files.
The black-box commitment is now an invariant in CLAUDE.md rather than an
emergent property: a consumer's cost for an upstream release is a re-run.
Additive profiles, byte-stable existing profiles, keyword-only new parameters so
positional call sites survive, no churn in consumer fixtures. The boundary is
stated rather than glossed -- shape changes are absorbed, upstream changes to
consumer-authored content are not, and those get a measured exposure report per
consumer instead.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A2aKJxLejT9S8jYwoZ9fut