The pilot invitations asked for feedback without specifying what to run or what we expect to see. That returns a description, not a measurement: only a stated expectation lets someone else's run falsify our model instead of merely confirming it. Each of the three tests now carries a numbered procedure, numbered expected outcomes, and an explicit list of what would surprise us. Naming the surprises is the load-bearing part -- a consumer who sees something odd but passing otherwise has no reason to mention it. Test A (producer, portfolio-optimiser-claude) runs one already-working manifest twice with the same explicit ingested_at, once under DEFAULT as baseline and once under OKF_V0_2, and diffs. A-E1 is the stop condition: if the DEFAULT run is not byte-identical to their current pinned output, we have broken a v0.1 consumer and the pilot ends there. The named surprises include `at` differing from the ingested_at they passed, which would mean a wall-clock crept in, and a collision refusal on a file that IS theirs, which is the inverse of the fail-safe we predicted -- we expect foreign files to be refused, so a false refusal of their own is the defect. Test B (gate, catalog) runs their unmodified gate on the v0.2 fixture and on a 0.1 control. B-E3 -- that no gate other than the version gate behaves differently between the two -- is what converts our reading of their form regex into a measurement. A membership list or equality comparison anywhere in their chain is exactly what this is paid for. Test C (expressiveness, wiki) is run by us, read-only at a recorded commit, and the report goes to them. What we ask them to check is the part we cannot measure from outside: whether a field we called an optional addition is in fact load-bearing in their pipeline, and whether "no change required" holds operationally rather than only formally. Feedback gains a per-expectation verdict line so three independent runs are comparable and a disagreement is located rather than merely reported. The request also says outright that a wrong expectation is a better result than a clean run, since a clean run only confirms what we already believed. Inputs come from this repo at the pre-release tag; nothing is transported through the mailbox except the specification. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A2aKJxLejT9S8jYwoZ9fut
475 lines
27 KiB
Markdown
475 lines
27 KiB
Markdown
# OKF v0.2 support — plan
|
||
|
||
Status: **committed implementation track.** Standing policy (operator,
|
||
2026-07-26): *the library always supports the current latest version of Google
|
||
OKF.* v0.2 is out, so v0.2 support is committed work — not conditional on a
|
||
consumer requesting it.
|
||
|
||
Read with: `docs/plan/execution-order.md` (sequencing),
|
||
`docs/plan/phase-3-configurable-contract.md` (the profile object that carries
|
||
this), `docs/phase-3-split-table.md` (the profile/guard/consumer column
|
||
discipline).
|
||
|
||
## What "support the latest version" means here
|
||
|
||
It means **additive**: the library can read, validate, materialize, and index
|
||
v0.2-shaped bundles. It does **not** mean migrating existing profiles onto v0.2.
|
||
That distinction is the whole design, and it is what makes a standing
|
||
always-latest policy implementable rather than a recurring crisis:
|
||
|
||
- `DEFAULT` states commons' ingest-spec §5 layer. Its shape is commons'
|
||
authorship, and editing it locally is a standing non-goal.
|
||
- `STRICT_V1` mirrors the proving consumer's contract, ratified by their operator
|
||
on 2026-07-25. Changing another repo's contract from here violates O2.
|
||
- So v0.2 support arrives as **a new profile**, alongside the existing two. The
|
||
phase-3 profile object is exactly the seam that makes this possible; this is
|
||
the first time that abstraction pays for itself against an external forcing
|
||
function rather than a second consumer.
|
||
|
||
The upper bound is set upstream, not by ambition: v0.2 **defers the receipt and
|
||
verdict wire formats and the full attestation runtime protocol to a future
|
||
revision**. The *format* is fully supportable; an unspecified runtime is not.
|
||
See D4.
|
||
|
||
## Provenance of the facts below
|
||
|
||
Read from `GoogleCloudPlatform/knowledge-catalog`, `okf/SPEC.md` at **`main` on
|
||
2026-07-26** — a branch, not a tag. **First implementation step is to re-read the
|
||
spec at a pinned commit and record it**, exactly as assumption C2 pins the
|
||
proving consumer's validator to `9ee5a8e`. An enumeration read off a moving
|
||
branch is a premise, not a fact.
|
||
|
||
## What v0.2 actually changes
|
||
|
||
Two supersessions — the only backward-breaking changes, both with a documented
|
||
consumer fallback (§13.1):
|
||
|
||
| v0.1 | v0.2 | Fallback the spec grants |
|
||
|---|---|---|
|
||
| `timestamp` | `generated.at` | Consumers **MAY** fall back to legacy `timestamp` **when `generated` is absent** |
|
||
| body `# Citations` list | `sources` frontmatter | Consumers **SHOULD** read `sources`, **MAY** still parse legacy `# Citations` |
|
||
|
||
Additive, all optional upstream (§5) — and all in scope for us under the standing
|
||
policy: `sources` (per-source credibility signals — `author`, `usage_count`,
|
||
`last_modified`, framed by `usage_window`), `generated { by, at }`,
|
||
`verified [ { by, at } ]`, `status` (`draft`/`stable`/`deprecated`, absent means
|
||
`stable`), `stale_after` (absolute `YYYY-MM-DD`).
|
||
|
||
Also new:
|
||
|
||
- **Actor convention (§7).** `<producer>/<version>` for an agent or tool,
|
||
`human:<id>`, `process:<id>`. Consumers that classify trust key off the
|
||
`human:` prefix, so producers **MUST** use it for hand-authored or
|
||
human-confirmed content. Trust tiers (unverified / machine-confirmed /
|
||
human-reviewed) are *derived* advisory signals, never a stored field.
|
||
- **`Attested Computation`** concept type, with `runtime`, `parameters`,
|
||
`computation`, `executor`, `attester`.
|
||
- **`okf_version`** stays root-`index.md`-only and is a **MAY**: a declaration of
|
||
the version a bundle *targets*, with no conformance checkpoint tying the
|
||
declaration to compliance.
|
||
- **Consumer tolerance is unchanged and broad.** A consumer MUST NOT reject a
|
||
bundle for missing optional fields, unknown `type` values, unknown frontmatter
|
||
keys, broken cross-links, or missing `index.md`. One additional MUST: a bare
|
||
`verified` mapping must be read as a one-element list.
|
||
|
||
One YAML detail that sizes the parser work: the canonical form for `generated`
|
||
and a single `verified` is an **inline flow mapping** on one line
|
||
(`generated: { by: …, at: … }`), which the existing scalar parser already
|
||
round-trips as an opaque string. Only a multi-verifier `verified` and `sources`
|
||
are **block lists**, and those are what force a richer frontmatter model.
|
||
|
||
## Exposure audit — measured against the tree, not reasoned
|
||
|
||
| Surface | Status under v0.2 | Decision owner |
|
||
|---|---|---|
|
||
| `DEFAULT` emits `generated: "true"` (`materialize.py:164`, `inbox.py:127`) | **Malformed for a v0.2 field**: `generated.by` is REQUIRED within `generated`. `generated` was *not* reserved in v0.1, so this was legal when written — v0.2 claimed the name | **commons** (ingest-spec §5) |
|
||
| `_is_ingest_owned` requires `generated == "true"` (`materialize.py:143-149`) | **Fail-safe.** A v0.2 mapping is not the string `"true"`, so the predicate returns False → target not ours → collision → refuse to overwrite. No safety hole | this repo |
|
||
| `STRICT_V1` emits `timestamp`, and **no** `generated` | **Conformant** via the §13.1 legacy fallback, which applies precisely *because* `generated` is absent. No action is forced on the wiki | wiki |
|
||
| `parse_frontmatter` is scalar-only (`partition(":")` → `dict[str, str]`) | Inline flow mappings round-trip as opaque strings. Block lists are unreachable, so `sources` and multi-verifier `verified` need a richer model — **now in scope** (D1) | this repo |
|
||
| Fixtures and tests pin `okf_version: 0.1` | Accurate for the v0.1 profiles. The new v0.2 profile declares `"0.2"` | **catalog** (E1: `okf_version` tracks the upstream Google version) |
|
||
| catalog's gate checks `okf_version` on **form** (`/^\d+(\.\d+)*$/`), not membership | `"0.2"` passes unchanged — an upstream bump breaks no gate | catalog |
|
||
|
||
Two results that cut against the first reading of the change:
|
||
|
||
1. **`STRICT_V1`'s `timestamp` is not broken.** It sits on the documented legacy
|
||
path. An earlier framing of it as a required-field defect was wrong.
|
||
2. **`DEFAULT` is the only surface with a real shape problem**, and it is the one
|
||
surface this repo may not unilaterally change. Under the additive design this
|
||
stops being on our critical path (D2, V1).
|
||
|
||
The interaction that keeps (1) true and must never be violated: the `timestamp`
|
||
fallback is granted only when `generated` is **absent**. A profile emitting both
|
||
a `timestamp` and a malformed `generated` would have neither a valid
|
||
`generated.at` nor an eligible fallback. No current profile does this — `DEFAULT`
|
||
has no `timestamp`, `STRICT_V1` has no `generated` — and no profile may be
|
||
allowed to reach that combination. This becomes a profile-construction invariant
|
||
(V-A7).
|
||
|
||
## The sequencing rule we impose on ourselves
|
||
|
||
**Conform first, claim after.** No bundle declares an `okf_version` its shape has
|
||
not earned.
|
||
|
||
The spec does not require this — §12 is a MAY with no conformance checkpoint, so
|
||
declaring `"0.2"` early would be permitted. We adopt the rule anyway, for the
|
||
same reason as *measure first, widen after* on the guard pin: a version claim
|
||
that is literally permitted but not earned is the true-sounding misleading claim,
|
||
and that class of mistake has already cost this repo a session. Concretely, the
|
||
v0.2 profile declares `"0.2"` only once D1–D3 and their fixtures are green.
|
||
|
||
## Deliverables
|
||
|
||
**D1 — A frontmatter model that carries block lists.** The single largest piece,
|
||
and the one that must be designed rather than improvised: `parse_frontmatter`
|
||
returns `dict[str, str]` today, and `sources` / multi-verifier `verified` cannot
|
||
be expressed in it. Three directions:
|
||
|
||
- (a) Widen the value type in place (`str | Mapping | list`). Cheapest to write,
|
||
worst to consume: every existing caller must newly narrow, and `mypy --strict`
|
||
makes that cost visible everywhere at once.
|
||
- (b) A typed frontmatter model for all profiles. Cleanest end state, but it
|
||
rewrites the v0.1 read path and puts C1 (default-profile behavior neutrality)
|
||
at risk for no v0.1 benefit.
|
||
- (c) **Recommended.** Keep the scalar reader as the v0.1 path, untouched, and
|
||
add a structured reader selected by the profile. C1 stays trivially true
|
||
(`DEFAULT` and `STRICT_V1` do not change code paths at all), the new
|
||
complexity is reachable only from the new profile, and the two readers share
|
||
the same byte-level guarantees.
|
||
|
||
**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"`.
|
||
|
||
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
|
||
a consumer bound to `OKF_LATEST` inherits upstream's breaking changes on a
|
||
library upgrade. The versioned constants are the stable binding and are what
|
||
consumers should pin; `OKF_LATEST` is for callers who have explicitly opted into
|
||
tracking.
|
||
|
||
`OKF_LATEST` means *the latest version supported as stable*, not the latest
|
||
version present in the tree. During the pilot it therefore keeps pointing at the
|
||
v0.1 semantics, and **flipping it to v0.2 is the GA event** — a single, auditable
|
||
action rather than a side effect of a merge. This is what keeps the alias from
|
||
becoming the footgun that drags tracking consumers into a provisional surface.
|
||
|
||
`at` binds to the already-validated required `ingested_at` argument, so no
|
||
wall-clock default is introduced and determinism holds.
|
||
|
||
**D3 — Door C accepts any v0.2 bundle.** This is where "we support v0.2" is most
|
||
user-visible: the §14 consumer tolerance MUST NOTs, plus the bare-`verified`
|
||
coercion. Measured with one test per rule — Door C is a consumer in the OKF
|
||
sense and either honours these or does not.
|
||
|
||
**D4 — `Attested Computation`: format yes, runtime no.** Support the type and its
|
||
fields (`runtime`, `parameters`, `computation`, `executor`, `attester`) for
|
||
parse, validate, round-trip, and index. Do **not** implement execution or
|
||
attestation: the receipt and verdict wire formats are explicitly deferred
|
||
upstream, so there is no contract to build against, and "did this run produce the
|
||
value the sanctioned way" sits against the guard's boundary rather than this
|
||
library's plumbing. When upstream specifies the protocol, the standing policy
|
||
brings it back into scope — and the boundary question goes to the operator first.
|
||
|
||
**D5 — v0.2 golden fixtures.** A byte-exact v0.2 bundle in `examples/`. This is
|
||
what converts "we support v0.2" from a claim into a test, and in phase 4 it
|
||
becomes the cross-runtime conformance oracle for the Node half too.
|
||
|
||
**D6 — Keep the policy from decaying.** "Always latest" silently rots unless
|
||
something checks. Two mechanisms, both cheap: the spec commit is pinned and
|
||
recorded in this doc (V-A1), and the release checklist gains one item — re-check
|
||
upstream for a newer OKF version and record the result, even when unchanged. A
|
||
standing policy with no detection mechanism is an intention, not a policy.
|
||
|
||
## Rollout — pilot before general availability
|
||
|
||
Operator directive 2026-07-26: consumers are better served by getting the latest
|
||
version early, testing it, and reporting back than by us holding it until we
|
||
judge it finished. So v0.2 ships to a small pilot set first, and general
|
||
availability is what happens *after* the findings are addressed.
|
||
|
||
This closes a gap the plan otherwise had. Every test in the suite — including
|
||
V-A8 against upstream's reference implementation — asks whether the output is
|
||
*conformant*. None asks whether it is *usable*: whether a real bundle is awkward
|
||
to construct, whether a rejection message tells the consumer anything actionable,
|
||
whether the profile can express what their actual data needs. Only a consumer
|
||
running real data surfaces that.
|
||
|
||
### The pilot set, chosen for signal rather than convenience
|
||
|
||
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 |
|
||
| `claude-code-llm-wiki` | **Expressiveness.** Can the profile object express a strict consumer at v0.2, over 522 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
|
||
on our side.** We run the v0.2 reader over real consumer bundles and report what
|
||
we find. That needs no adoption, no writes into their tree (O2 holds), and no
|
||
change to a contract their operator ratified. Only the *producer* axis requires a
|
||
consumer to do anything.
|
||
|
||
Deliberately excluded, with reasons: `okr` (Node side, not yet lifted — wrong
|
||
time); `linkedin-studio` (their `ingest/published/` provenance grammar is
|
||
deliberately not normalized, and v0.2's provenance families would put implicit
|
||
pressure on exactly that carve-out); `portfolio-optimiser-commons` (they are
|
||
deciding V1 — a participant, not a test site); `llm-ingestion-pipeline-security`
|
||
(not an OKF consumer in this sense).
|
||
|
||
### Stages
|
||
|
||
- **P1 — read-only sweep (us).** Run the v0.2 reader over real bundles from the
|
||
pilot set. Finds parser gaps, tolerance-rule violations, and expressiveness
|
||
gaps at zero risk to anyone. Runs as soon as D1–D3 are green.
|
||
- **P2 — one producer, opt-in.** `portfolio-optimiser-claude` emits a v0.2 bundle
|
||
against the pre-release tag.
|
||
- **P3 — gate check.** `catalog` runs their real gate on P2's output.
|
||
- **P4 — fix, then GA.** Address findings, then flip `OKF_LATEST` as its own
|
||
release.
|
||
|
||
### Shipping a provisional surface without owing stability
|
||
|
||
The install channel is a direct git reference, so consumers pin a tag explicitly
|
||
and nothing reaches anyone implicitly. The pilot therefore ships as a
|
||
**pre-release tag** (`v0.5.0a1`), and the marker lives in the tag name so a pin
|
||
is self-documenting. Three rules make the provisional status real rather than
|
||
stated:
|
||
|
||
1. `OKF_LATEST` does not point at v0.2 until GA (above).
|
||
2. The v0.2 profile's docstring and the CHANGELOG entry both say the surface may
|
||
change on pilot feedback, and name the pilot repos.
|
||
3. Breaking changes during the pilot are expected and do **not** get a
|
||
deprecation cycle. Saying this up front is what buys the freedom to act on
|
||
feedback; discovering it later is what makes a pilot a de-facto release.
|
||
|
||
### Test specifications — stated before the run, with expected results
|
||
|
||
A pilot that asks "run it and tell us how it went" returns a description. A pilot
|
||
that states the expected result first returns a *measurement*, because only then
|
||
can the consumer see that something differed. So each test below ships with its
|
||
expected outcomes numbered, plus an explicit list of what would surprise us —
|
||
naming the surprises is what lets someone else's run falsify our model instead of
|
||
merely confirming it.
|
||
|
||
All three read their inputs from this repo at the pre-release tag (`v0.5.0a1`);
|
||
nothing is transported through the mailbox except the specification itself.
|
||
|
||
#### Test A — producer path (`portfolio-optimiser-claude`)
|
||
|
||
**Procedure.** In a scratch venv, install the package at the `v0.5.0a1` tag. Take
|
||
**one manifest they have already run under `DEFAULT`**, so the existing v0.1
|
||
output is the baseline. Materialize it twice with the *same* explicit
|
||
`ingested_at`: once with `profile=DEFAULT`, once with `profile=OKF_V0_2`. Diff.
|
||
|
||
**Expected:**
|
||
- **A-E1** The `DEFAULT` run is byte-identical to what their currently pinned
|
||
version produces. Support is additive; if this fails we have broken a v0.1
|
||
consumer and the pilot stops here.
|
||
- **A-E2** The v0.2 run differs from `DEFAULT` **only inside the frontmatter
|
||
block**. Body bytes identical, filenames identical.
|
||
- **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-E5** Re-running with the same `ingested_at` is byte-identical.
|
||
- **A-E6** The root `index.md` declares `okf_version: "0.2"`.
|
||
|
||
**What would surprise us — report immediately:** any diff outside the frontmatter
|
||
block (the profile seam leaks); `at` differing from the `ingested_at` they passed
|
||
(a wall-clock crept in — a determinism defect); a collision refusal on a target
|
||
file that **is** theirs (`_is_ingest_owned` mis-firing under the new `generated`
|
||
shape — we predict fail-safe refusal of *foreign* files, so a false refusal of
|
||
their *own* is the failure mode); and anything about constructing the profile that
|
||
required reading our source to work out.
|
||
|
||
#### Test B — gate acceptance (`catalog`)
|
||
|
||
**Procedure.** Run their **existing, unmodified** gate on two bundles from
|
||
`examples/` at the tag: the v0.2 fixture, and the same fixture with `okf_version`
|
||
set to `"0.1"` as a control. No pin, no code change, two gate runs.
|
||
|
||
**Expected:**
|
||
- **B-E1** Both pass. The form regex accepts both values, so the two runs should
|
||
be indistinguishable.
|
||
- **B-E2** No gate emits a WARNING or ERROR mentioning the version.
|
||
- **B-E3** No gate *other than* the version gate behaves differently between the
|
||
two runs.
|
||
|
||
**What would surprise us:** any gate anywhere in their chain that keys off the
|
||
version **value** rather than its form — a membership list, an equality
|
||
comparison, a switch. We asserted from reading their regex that none exists;
|
||
B-E3 is what converts that assertion into a measurement. Also: a gate requiring
|
||
`timestamp` on a concept (superseded in v0.2), or anything downstream of the gate
|
||
— their re-pin flow, the discovery mechanism — that assumes `0.1`.
|
||
|
||
#### Test C — expressiveness (`claude-code-llm-wiki`, run by us)
|
||
|
||
**Procedure (ours).** Construct a v0.2 variant of `STRICT_V1` here, read their
|
||
bundle **read-only at a recorded commit**, validate all 522 documents, send them
|
||
the report. They run nothing.
|
||
|
||
**Expected:**
|
||
- **C-E1** All 522 documents pass the v0.2 variant while keeping `timestamp` and
|
||
emitting no `generated` — the §13.1 legacy path.
|
||
- **C-E2** The variant requires **no** change to their eight required keys.
|
||
- **C-E3** Everything a v0.2 posture would add is optional: `generated`,
|
||
`verified`, `status`, `stale_after`, `sources`.
|
||
- **C-E4** Their per-directory index policy and root frontmatter
|
||
(`okf_version` / `bundle_profile` / `okf_spec_commit`) are expressible
|
||
unchanged.
|
||
|
||
**What we ask them to check in our report** — the parts we cannot measure from
|
||
outside: whether any field we classified as an optional addition is in fact
|
||
load-bearing in their pipeline; whether our reading of their contract still holds
|
||
at their current HEAD; and whether "no change required" is true *operationally*
|
||
and not just formally, since they know their consumers and we do not.
|
||
|
||
### Feedback shape
|
||
|
||
Unstructured feedback is not comparable across three repos, so ask for a
|
||
**per-expectation verdict plus five things by name**.
|
||
|
||
First, one line per numbered expectation — `A-E1: as expected` / `A-E3: differed,
|
||
<what we saw>`. That is the part that makes three independent runs comparable and
|
||
that turns a disagreement into a located one. Then:
|
||
|
||
1. What was run, against which tag.
|
||
2. What failed — with the bytes or the actual error, not a summary.
|
||
3. **What was awkward but worked.** The workarounds are the highest-value signal
|
||
and the one that never shows up as a failure.
|
||
4. What was needed that the profile could not express.
|
||
5. Whether they would adopt it as-is.
|
||
|
||
**An expectation we got wrong is a better result than a clean run**, and the
|
||
request says so explicitly — otherwise a consumer who sees something odd but
|
||
passing has no reason to mention it. A clean run confirms what we already believed;
|
||
a differed expectation is the only thing that can change the design.
|
||
|
||
### GA exit criteria
|
||
|
||
1. Every P1–P3 finding is either fixed or explicitly accepted with the reason
|
||
recorded in this doc.
|
||
2. Golden fixtures byte-exact; V-A8 green.
|
||
3. The two existing profiles unchanged byte-for-byte, existing tests unmodified
|
||
(V-A6).
|
||
4. `mypy --strict src/`, ruff, and the boundary grep-gate clean.
|
||
5. At least one consumer has *emitted* and one has *consumed* a v0.2 bundle
|
||
end-to-end.
|
||
6. `OKF_LATEST` flips to v0.2 in its own release, with a CHANGELOG entry stating
|
||
what changed for anyone bound to the alias.
|
||
|
||
**One honest limit on what GA can mean.** A three-repo pilot exercises only what
|
||
those three repos use. `sources` with `usage_window`, multi-verifier `verified`,
|
||
and `Attested Computation` will very likely go unexercised. Those stay marked
|
||
provisional at GA rather than being silently promoted: "stable" applies to the
|
||
surface the pilot actually covered, and claiming more would be the same
|
||
unearned-claim pattern that "conform first, claim after" exists to prevent.
|
||
|
||
## Open questions
|
||
|
||
**V1 — `generated`'s shape in `DEFAULT` (commons').** *No longer blocks our v0.2
|
||
support*, because D2 puts v0.2 in a new profile. Still raised with commons,
|
||
because `DEFAULT` keeps emitting a v0.2-reserved key with a v1-era value.
|
||
Recommendation to carry: `generated: { by: "llm-ingestion-okf/<v>", at:
|
||
<ingested_at> }`, deriving the machine-generated predicate from `generated.by`
|
||
instead of a bespoke boolean — spec-native, removes a hand-rolled key, costs a
|
||
`_is_ingest_owned` change and a fixture regeneration. If commons declines, the
|
||
deviation is documented, not patched here.
|
||
|
||
**V2 — Which D1 direction?** (c) recommended above; needs a decision before code
|
||
because it determines whether `mypy --strict` churn lands across the existing
|
||
call sites or stays confined to the new path.
|
||
|
||
**V3 — Do we add v0.2 families at all?** **Answered: yes**, by the standing
|
||
policy. Superseded — kept here to record that the earlier default answer ("no
|
||
until a named consumer asks") was overridden deliberately, not forgotten.
|
||
|
||
**V4 — `okf_version` values.** Policy, not constant. E1 gives the value to
|
||
catalog. Our obligation is narrower and testable: no profile hard-codes a
|
||
version, and the root-frontmatter policy expresses any value.
|
||
|
||
**V5 — Attestation scope.** Answered by D4: format in, runtime out, on upstream's
|
||
own deferral rather than our preference.
|
||
|
||
## TDD order
|
||
|
||
Steps 1–3 add tests and change no behavior, so they are safe first and de-risk
|
||
everything after.
|
||
|
||
1. **Characterize what already holds.** `parse_frontmatter` round-trips an inline
|
||
flow mapping opaquely; `_is_ingest_owned` returns False for a v0.2 `generated`
|
||
mapping. The second converts today's fail-safe from an accident into a
|
||
documented guarantee.
|
||
2. **Audit Door C against the tolerance rules** (D3): one test per MUST NOT plus
|
||
the bare-`verified` MUST. Failures here are real defects, not plan revisions.
|
||
3. **Assert no hard-coded version** anywhere a profile should decide (V4), and
|
||
the no-`timestamp`-plus-malformed-`generated` construction invariant (V-A7).
|
||
4. **D1** — the structured reader, behind the profile seam, with the v0.1 scalar
|
||
path untouched and the full existing suite as the net.
|
||
5. **D2** — the `OKF_V0_2` profile and `OKF_LATEST` alias.
|
||
6. **D5** — v0.2 golden fixtures; `okf_version: "0.2"` is declared only here,
|
||
once 1–5 are green (conform first, claim after).
|
||
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).
|
||
9. **D4** — `Attested Computation` round-trip. Placed after the pilot on purpose:
|
||
nothing in the pilot set uses it, so building it earlier would add surface the
|
||
feedback cannot reach.
|
||
10. **P4** — address findings, then GA: flip `OKF_LATEST`, its own release.
|
||
11. **V1 to commons** in parallel throughout; **D6** lands with the GA release.
|
||
|
||
## Key assumptions, each with its test
|
||
|
||
| # | Assumption | Test |
|
||
|---|---|---|
|
||
| V-A1 | The spec enumeration here is complete | Re-read `okf/SPEC.md` at a **pinned commit**, record the commit, diff against this doc before step 4 |
|
||
| V-A2 | Inline flow mappings survive the scalar parser unmodified | Step 1 characterization; byte-exact round-trip |
|
||
| V-A3 | v0.2 input cannot cause an unowned overwrite | Step 1: `_is_ingest_owned` False on a `generated` mapping |
|
||
| V-A4 | Door C meets the v0.2 consumer tolerance rules | Step 2, one test per MUST NOT |
|
||
| V-A5 | No profile hard-codes an upstream version | Step 3 |
|
||
| V-A6 | Adding v0.2 support is behavior-neutral for v0.1 profiles | Golden suite byte-for-byte under `DEFAULT` **and** `STRICT_V1`; existing tests unmodified and green (C1 extended) |
|
||
| V-A7 | No profile can emit `timestamp` together with a malformed `generated` | Named construction-time test, same shape as C3's verdict reservation |
|
||
| V-A8 | A v0.2 bundle we emit is accepted by an independent v0.2 consumer | Validate the D5 fixture against upstream's reference implementation, not only against our own reader |
|
||
|
||
V-A8 is the one that keeps this honest. Every other test asks whether we agree
|
||
with ourselves.
|
||
|
||
## Non-goals
|
||
|
||
- Implementing attestation execution — executors, attesters, receipts, verdicts
|
||
(D4, on upstream's deferral).
|
||
- 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.
|
||
- Config-file/DSL profile loading — still a phase-3 extension point.
|
||
|
||
## Coordination — who needs to know what
|
||
|
||
- **commons** — V1. `generated`'s shape in ingest-spec §5. No longer blocking us,
|
||
still theirs to decide.
|
||
- **catalog** — form owner. Upstream moved to v0.2; their form-not-membership
|
||
gate absorbs the bump with no change; V4 is theirs; and a new profile now
|
||
declares `"0.2"`, which is the first bundle shape in this repo to do so.
|
||
- **wiki** — informational. Their `timestamp` is legacy-but-readable under
|
||
§13.1; nothing is required of them, and the earlier reading that called it a
|
||
defect was wrong.
|
||
- **okr** — before the reference-implementation lift, so the Node half ports a
|
||
contract that already knows about v0.2 rather than a v0.1 shape.
|
||
|
||
## Verification
|
||
|
||
1. Spec re-read at a pinned commit; hash recorded in this doc.
|
||
2. `pytest` green; `mypy --strict src/`; `ruff check .` and
|
||
`ruff format --check .` clean.
|
||
3. V-A6: golden suite byte-for-byte under both existing profiles; zero fixture
|
||
changes outside the new v0.2 fixtures (`git diff --stat examples/` shows
|
||
additions only).
|
||
4. V-A7 and V-A5 named tests present and failing-by-construction if removed.
|
||
5. V-A8: the v0.2 fixture validates under upstream's reference implementation.
|
||
6. Boundary grep-gate still empty (`sanitize|quarantine|lexicon` absent outside
|
||
guard imports).
|
||
7. D6: release checklist contains the upstream-version re-check item.
|