docs(okf-v0.2): requirement 1 re-sized -- the emitter does not change at all
U4 escalated tag requirement 1: real v0.2 frontmatter is block YAML, and "emit
accepts a block-list value" had been scoped against a list of strings when
upstream's canonical sources is a list of five-key mappings. Re-sized against
the code rather than against its tests, and the answer runs the other way.
Measured: emit (profiles.py:197) takes Mapping[str, str] and renders one
"key: value" line per key through _render (:211), verbatim but for
source_query's whitespace collapse; _is_legal_value (:97) admits a string or a
non-empty list of strings, never mappings; the only production call sites are
materialize.py:166 and inbox.py:129, both passing dict[str, str].
The earlier "discharged" note read the validator correctly and drew too much
from it. But the gap it left is not U4's either: A-E4 owes at least a resource,
not upstream's five keys, and a manifest source has no author, no last_modified,
and no bundle-internal resource in upstream's sense. A field with no reader is
not written. So the question was never how large a YAML emitter we need -- it
was which form sources takes.
Chosen: sources: [{ id: <id>, resource: <ref> }], the same inline flow form A-E3
already chose for generated. It keeps parse_frontmatter line-oriented, which
matters because a block list pollutes that parser with false keys (- id,
resource) -- the failure mode documented for po-claude -- and _is_ingest_owned,
the one piece of this tag we called risky, reads THROUGH that parser. The block
form would force the collision gate and the parser to be hardened in the same
session. It also satisfies commons' section 5 single-line MUST, so the DEFAULT
seam U4 opened stays closed, and section 11 asks for parseable YAML, not block
YAML. The deviation from upstream's emitted shape is real and is recorded as a
decision, like U1, not as compliance we may claim.
Consequences: requirement 1 is smaller than both earlier sizings -- zero emitter
work -- and D1a drops off the critical path to v0.5.0a1. A-E4 is corrected from
"block list" to the flow form before the pilot receives it; a test spec that
outlives its decision is the A-E6 defect class.
Swept the same defect class while here: the TDD order and three other passages
wrote okf_version: "0.2" QUOTED, contradicting A-E6, which is precisely the form
catalog measured as exit 1. All five now unquoted.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A2aKJxLejT9S8jYwoZ9fut
This commit is contained in:
parent
bec5608106
commit
2d0328aaa7
2 changed files with 82 additions and 23 deletions
|
|
@ -127,8 +127,8 @@ repo rather than by a second consumer, which is the better test of it.
|
|||
**Order inside the track.** Characterization and audit first — they add tests and
|
||||
change no behavior. Then the frontmatter model that can carry block lists
|
||||
(`sources`, multi-verifier `verified`), then the profile, then the golden
|
||||
fixtures that make the support claim testable. The `okf_version: "0.2"`
|
||||
declaration comes last, once the shape has earned it. Commons' answer on
|
||||
fixtures that make the support claim testable. The `okf_version: 0.2`
|
||||
declaration comes last — **unquoted**, per A-E6 — once the shape has earned it. Commons' answer on
|
||||
`DEFAULT`'s `generated` runs in parallel and no longer blocks: the additive
|
||||
design took it off the critical path.
|
||||
|
||||
|
|
|
|||
|
|
@ -133,9 +133,12 @@ bare dates. §11 requires "a parseable **YAML** frontmatter block".
|
|||
|
||||
So requirement 1 — "`emit` accepts a block-list value (`sources`)" — was scoped
|
||||
against a list of *strings*. The canonical shape is a list of *mappings with five
|
||||
keys*. The plan's own rule applies to itself here: measure, do not reason. The
|
||||
tag's emitter work is larger than one value type, and the sizing must be redone
|
||||
before committing to a one-session estimate.
|
||||
keys*. The plan's own rule applies to itself here: measure, do not reason.
|
||||
|
||||
**Re-sized 2026-07-26**, and the answer runs the other way. What upstream
|
||||
*emits* does not settle what we *owe*: the re-sizing is recorded under
|
||||
requirement 1 below, and the emitter does not change at all, because the form we
|
||||
owe A-E4 is not upstream's five-key block list.
|
||||
|
||||
It also sharpens the §5 tension for commons: their ingest-spec requires "All
|
||||
values MUST be single-line" (`:158`). `generated` as an inline flow mapping
|
||||
|
|
@ -180,7 +183,9 @@ mistaken for something already checked. It touches nothing in the tag.
|
|||
|
||||
### What this changes
|
||||
|
||||
Tag requirement 1 needs re-sizing (U4). Requirement 5's fixture must respect U2
|
||||
Tag requirement 1 was re-sized (U4 → requirement 1): the emitter turns out not to
|
||||
change at all, and A-E4 is corrected to the flow form to match. Requirement 5's
|
||||
fixture must respect U2
|
||||
and take U1 as an explicit decision. `_is_ingest_owned` gets U3 as its governing
|
||||
fact. D1's reader gets the §5.2 bare-mapping MUST from U6. V1's option set gets
|
||||
U5. None of it blocks steps 1–3, which change no behavior.
|
||||
|
|
@ -285,7 +290,7 @@ be expressed in it. Three directions:
|
|||
**D2 — An `OKF_V0_2` profile.** Frontmatter schema covering the §5 families;
|
||||
`generated: { by: "llm-ingestion-okf/<version>", at: <ingested_at> }` using the
|
||||
§7 actor convention; `sources` emission from the manifest's source; `status` and
|
||||
`stale_after` expressible; root-index `okf_version: "0.2"`.
|
||||
`stale_after` expressible; root-index `okf_version: 0.2`.
|
||||
|
||||
Also an `OKF_LATEST` alias, which is how the standing policy becomes visible in
|
||||
code. **Document the tradeoff rather than hiding it:** an alias that moves means
|
||||
|
|
@ -348,7 +353,7 @@ Three repos, each exercising a different axis, and deliberately cheap for them:
|
|||
| Repo | Axis under test | What we ask of them | Their cost |
|
||||
|---|---|---|---|
|
||||
| `portfolio-optimiser-claude` | **Producer path.** Real Door A ingestion emitting v0.2 from a real manifest | Pin the pre-release tag, run one real manifest, report | one run |
|
||||
| `catalog` | **Gate acceptance.** Does a bundle *declaring* `okf_version: "0.2"` actually pass their gate — measured against their gate, not our reading of its regex | Run their existing gate on our fixture. No code change | one gate run |
|
||||
| `catalog` | **Gate acceptance.** Does a bundle *declaring* `okf_version: 0.2` actually pass their gate — measured against their gate, not our reading of its regex | Run their existing gate on our fixture. No code change | one gate run |
|
||||
| `claude-code-llm-wiki` | **Expressiveness.** Can the profile object express a strict consumer at v0.2, over 524 real documents | Nothing. We read their bundle and send them the report | zero |
|
||||
|
||||
The wiki entry is the important design detail: **most of the pilot is read-only
|
||||
|
|
@ -482,9 +487,9 @@ Walking each pilot test back to what it actually exercises produces a result the
|
|||
deliverable ordering had obscured: **none of the three tests needs D1's
|
||||
structured reader.**
|
||||
|
||||
- Test A **emits** v0.2. `generated` is an inline flow mapping — a formatted
|
||||
string — and `sources` needs the frontmatter *emitter* to accept a block-list
|
||||
value. Neither requires reading one back.
|
||||
- Test A **emits** v0.2. Both `generated` and — after requirement 1's re-sizing —
|
||||
`sources` are inline flow constructs, so both are formatted strings the
|
||||
existing emitter renders verbatim. Neither requires reading one back.
|
||||
- Test B runs catalog's gate on a fixture. That fixture is Test A's output.
|
||||
- Test C validates the wiki's **v0.1-shaped** documents (scalar frontmatter,
|
||||
`timestamp`, no `generated`) against a v0.2 *variant profile*. Their documents
|
||||
|
|
@ -493,11 +498,47 @@ structured reader.**
|
|||
The structured reader exists to **consume third-party v0.2 bundles** — that is
|
||||
D3/Door C, and no pilot repo sends us one. So the tag needs:
|
||||
|
||||
1. `FrontmatterSchema.emit` accepts a block-list value (`sources`). Verified
|
||||
2026-07-26: `emit` + `_render` are a single formatting path rendering values
|
||||
verbatim, and the *validator* already admits "a string or a non-empty list of
|
||||
strings" (`_is_legal_value`). Only the emitter side is missing, so the sizing
|
||||
caveat below is discharged — the estimate holds.
|
||||
1. **`sources` emits as an inline flow sequence, and the emitter does not
|
||||
change.** Re-sized 2026-07-26 against the code rather than against its tests.
|
||||
|
||||
Measured: `emit` (`profiles.py:197`) takes `Mapping[str, str]` and renders one
|
||||
`f"{key}: {value}"` line per key through `_render` (`:211`), verbatim but for
|
||||
`source_query`'s whitespace collapse; `_is_legal_value` (`:97`) admits a
|
||||
string or a non-empty list of **strings**; the only production call sites are
|
||||
`materialize.py:166` and `inbox.py:129`, both passing `dict[str, str]`.
|
||||
|
||||
The earlier "discharged" note read the validator correctly and drew too much
|
||||
from it — a list of strings is admitted, a list of *mappings* is not — so it
|
||||
did not cover the shape U4 measured upstream. But the gap it left is not the
|
||||
gap U4 predicted either. A-E4 owes "at least a `resource`", not upstream's
|
||||
five keys, and a manifest source has no `author`, no `last_modified`, and no
|
||||
bundle-internal `resource` in upstream's sense. "A field with no reader is not
|
||||
written" forbids inventing them. The question was therefore never how large a
|
||||
YAML emitter we need; it was which form `sources` takes.
|
||||
|
||||
Chosen: `sources: [{ id: <source id>, resource: <source reference> }]` — the
|
||||
same inline flow form A-E3 already chose for `generated`. Three reasons beyond
|
||||
not carrying two shapes for two fields in one frontmatter block:
|
||||
|
||||
- **It keeps `parse_frontmatter` line-oriented and correct.** A real block
|
||||
list pollutes that parser with false keys (`- id`, `resource`) — the failure
|
||||
mode documented for po-claude at `:444-448` — and `_is_ingest_owned`, the
|
||||
one piece of this tag we called risky, reads *through* that parser. The
|
||||
block form would force the collision gate and the parser to be hardened in
|
||||
the same session; the flow form lets requirement 3 be done in isolation.
|
||||
- **It satisfies commons' §5 "all values MUST be single-line"**, so the
|
||||
`DEFAULT` seam U4 opened stays closed and the tag does not wait on commons.
|
||||
- **§11 requires "a parseable YAML frontmatter block", not a block one.** A
|
||||
flow sequence of flow mappings is parseable YAML. Conform first, claim after.
|
||||
|
||||
The cost is real and is stated rather than glossed: this deviates from the
|
||||
shape upstream's own tooling emits (U4). Like U1, it is a **decision**, not
|
||||
compliance we may claim — and it is cheap to reverse, since moving to the
|
||||
block form later is a new profile, not a migration.
|
||||
|
||||
Consequence for the estimate: requirement 1 is *smaller* than both the
|
||||
original sizing and U4's escalation of it — zero emitter work, the change is
|
||||
in the materializer's value construction — and A-E4 is corrected to match.
|
||||
2. An `OKF_V0_2` profile constant — same shape as the existing `STRICT_V1` work.
|
||||
3. `_is_ingest_owned` becomes profile-aware, since the ownership marker now
|
||||
differs per profile.
|
||||
|
|
@ -523,9 +564,11 @@ in the same way. The tag is not waiting on a decision.
|
|||
|
||||
**Risk concentrates in item 3**, not in volume: `_is_ingest_owned` is the
|
||||
pre-mutation collision gate, so a defect there is expensive and quiet. Its
|
||||
characterization test (TDD step 1) is written before it is touched. Item 1 also
|
||||
runs through `DEFAULT`'s code path, so V-A6 must be *proven* by the golden suite,
|
||||
not assumed.
|
||||
characterization test (TDD step 1) is written before it is touched. After the
|
||||
re-sizing above, item 1 no longer touches shared code at all, which concentrates
|
||||
the risk rather than spreading it: V-A6 is still *proven* by the golden suite and
|
||||
not assumed, but now as a regression check over unchanged code rather than as a
|
||||
hypothesis about a changed formatting path.
|
||||
|
||||
**Caveat on the estimate — discharged 2026-07-26.** It treated
|
||||
`FrontmatterSchema.emit` as a single formatting function, inferred from its tests
|
||||
|
|
@ -535,6 +578,12 @@ whitespace collapse. The premise held, and the sizing stands. Recorded rather
|
|||
than deleted, because a premise that survives verification is only known to have
|
||||
survived if the check is visible.
|
||||
|
||||
**Second pass, same day.** U4 re-opened the sizing from the other side — not "is
|
||||
`emit` one function?" but "what shape must it emit?" — and requirement 1 records
|
||||
how it closed. Both checks stay visible: the first premise survived measurement,
|
||||
the second was re-opened by evidence and settled by a decision. Neither is an
|
||||
estimate that was merely asserted twice.
|
||||
|
||||
### Shipping a provisional surface without owing stability
|
||||
|
||||
The install channel is a direct git reference, so consumers pin a tag explicitly
|
||||
|
|
@ -583,8 +632,13 @@ output is the baseline. Materialize it twice with the *same* explicit
|
|||
- **A-E3** `generated` is an inline flow mapping
|
||||
`{ by: llm-ingestion-okf/<version>, at: <ingested_at> }` — not the string
|
||||
`true` — and `at` equals the `ingested_at` argument **exactly**.
|
||||
- **A-E4** `sources` is present as a block list carrying at least a `resource`
|
||||
derived from the manifest's source.
|
||||
- **A-E4** `sources` is present as an inline flow sequence of one flow mapping —
|
||||
`[{ id: <source id>, resource: <source reference> }]` — carrying at least a
|
||||
`resource` derived from the manifest's source. **Corrected 2026-07-26** from
|
||||
"a block list": requirement 1's re-sizing chose the flow form, for the reasons
|
||||
recorded there. A test specification that outlives the decision it encoded is
|
||||
the A-E6 defect class — a pilot failing our own expectation while the code is
|
||||
right — so the correction lands before the pilot receives it, not after.
|
||||
- **A-E5** Re-running with the same `ingested_at` is byte-identical.
|
||||
- **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:
|
||||
|
|
@ -880,10 +934,15 @@ everything after.
|
|||
the no-`timestamp`-plus-malformed-`generated` construction invariant (V-A7).
|
||||
4. **D1a — the emitter half only.** `FrontmatterSchema.emit` accepts a block-list
|
||||
value. Runs through `DEFAULT`'s path, so the golden suite proves V-A6 here.
|
||||
**No longer tag-blocking** after requirement 1's re-sizing — the flow form
|
||||
needs no emitter change — so this stays here for D1/Door C and drops out of
|
||||
the critical path to `v0.5.0a1`.
|
||||
5. **D2** — the `OKF_V0_2` profile, profile-aware `_is_ingest_owned`, `sources`
|
||||
derivation. `OKF_LATEST` stays on v0.1 semantics.
|
||||
6. **D5** — the v0.2 golden fixture, generated by the code. `okf_version: "0.2"`
|
||||
6. **D5** — the v0.2 golden fixture, generated by the code. `okf_version: 0.2`
|
||||
is declared only here, once 1–5 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`.
|
||||
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).
|
||||
|
|
@ -921,7 +980,7 @@ with ourselves.
|
|||
- Migrating `DEFAULT` or `STRICT_V1` onto v0.2. Support is additive; those two
|
||||
contracts belong to commons and the wiki.
|
||||
- Editing commons' ingest-spec locally. V1 is raised there.
|
||||
- Declaring `okf_version: "0.2"` on any bundle whose shape has not earned it.
|
||||
- Declaring `okf_version: 0.2` on any bundle whose shape has not earned it.
|
||||
- Config-file/DSL profile loading — still a phase-3 extension point.
|
||||
|
||||
## Coordination — who needs to know what
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue