Everything built so far targets OKF v0.1. Upstream published v0.2 on 2026-07-25, so the plan records how the library relates to it and, more importantly, who owns each decision. Read from the spec itself rather than secondhand, which corrected two readings that a summary had gotten wrong: - STRICT_V1's `timestamp` is NOT a defect. Section 13.1 grants consumers a documented fallback to legacy `timestamp` precisely when `generated` is absent, and STRICT_V1 emits no `generated`. Nothing is asked of the wiki. - The one measured shape problem is DEFAULT's `generated: "true"`, because v0.2 requires `generated.by` within `generated`. That key was not reserved in v0.1, so it was legal when written; v0.2 claimed the name. DEFAULT states commons' ingest-spec section 5 layer, so the fix is commons' call and is raised there as open question V1 rather than patched locally. Two findings shrink the work. The canonical form for `generated` and a single `verified` is an inline flow mapping, which the existing scalar parser already round-trips as an opaque string, so block-list support is only needed for `sources` and multi-verifier `verified` -- and only if a named consumer asks. And the collision degrades safely: `_is_ingest_owned` returns False for a v0.2 mapping, so a foreign concept is refused rather than overwritten. The track sits between Phases 3 and 4 because Phase 4 freezes the cross-runtime contract. Freezing a v0.1 shape into two runtimes would let the shared fixture suite certify the drift instead of catching it. Self-imposed rule, since the spec does not require it: conform first, claim after. Declaring `okf_version: "0.2"` is a MAY with no conformance checkpoint, so claiming it early would be permitted -- and would be the same class of true-sounding misleading claim as reporting a 0.2.0 measurement under a 0.3.1 heading. Also moves the guard 0.3.1 measurement procedure out of session state and into execution-order.md, where it belongs. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A2aKJxLejT9S8jYwoZ9fut
12 KiB
OKF v0.2 alignment — plan
Status: decisions open, no code approved. This is a policy track, not a roadmap phase: it decides how the library relates to upstream OKF v0.2 and where each decision is owned. Everything built so far targets v0.1.
Read with: docs/plan/execution-order.md (where this track sits in the
sequence), docs/plan/phase-3-configurable-contract.md (the profile object that
carries the outcome), docs/phase-3-split-table.md (the profile/guard/consumer
column discipline this track reuses).
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 step of any implementation 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, and both carry 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 (§5): 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 thehuman: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 Computationconcept type, withruntime,parameters,computation,executor,attester. The receipt and verdict wire formats and the full runtime protocol are explicitly deferred to a future revision.okf_versionstays 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
typevalues, unknown frontmatter keys, broken cross-links, or missingindex.md. One additional MUST: a bareverifiedmapping must be read as a one-element list.
One YAML detail that decides the parser scope: the canonical form for generated
and a single verified is an inline flow mapping on one line
(generated: { by: …, at: … }). Only a multi-verifier verified and sources
are block lists.
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]) |
Reads an inline flow mapping as an opaque string — round-trip-safe. Cannot read block lists, so sources and multi-verifier verified are unreachable |
this repo |
Fixtures and tests pin okf_version: 0.1 |
Accurate today: a MAY-field target declaration | catalog (decision 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 worth stating plainly, because both cut against the first reading:
STRICT_V1'stimestampis not broken. It sits on the documented legacy path. The earlier framing of it as a required-field defect was wrong.DEFAULTis the only surface with a real shape problem, and it is the one surface this repo may not unilaterally change:DEFAULTstates the ingest-spec §5 layer, whose authorship is commons'. Patching it locally is an existing non-goal.
Note the interaction that makes (1) conditional: the timestamp fallback is
granted only when generated is absent. Any profile that emits 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 and STRICT_V1 has no generated — and no profile should be allowed
to reach that combination.
The sequencing rule we impose on ourselves
Conform first, claim after. Do not move a bundle's okf_version to "0.2"
before the bundle's shape satisfies what it claims.
The spec does not require this — §12 is a MAY with no conformance checkpoint
attached, 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.
Open questions — to answer before any code
Numbered like the phase-3 assumptions so they can be cited in review.
V1 — Does generated change shape in DEFAULT, and may it?
This is the central question and the answer is not ours alone: DEFAULT is
commons' §5 layer. Three directions to put to commons, with our recommendation:
- (a) Leave
generated: "true"; document the deviation. Cheapest; keeps emitting a v0.2 field with a v1-era value. - (b) Recommended. Migrate to
generated: { by: "llm-ingestion-okf/<version>", at: <ingested_at> }and derive the machine-generated predicate fromgenerated.byinstead of a bespoke boolean. This is the spec-native answer, removes a hand-rolled key, and yields a realgenerated.at.atbinds to the already-validated requiredingested_atargument, so no wall-clock default is introduced and determinism holds. Costs: a_is_ingest_ownedchange and a golden-fixture regeneration. - (c) Rename our marker to a non-reserved key and leave
generatedfree for v0.2 semantics. Preserves the predicate exactly; still a fixture regeneration.
V2 — How far does the frontmatter parser need to go? Two tiers, and only the first is cheap:
- Tier 1, inline flow mappings: already round-trip as opaque strings. This needs a characterization test, not a change.
- Tier 2, block lists (
sources, multi-verifierverified): real work.
The question is whether any named consumer requires Tier 2 today. If none does, Tier 2 stays an extension point — the inverse of fields without a reader are not written: do not build a parser for a field no profile reads.
V3 — Do we add v0.2 trust fields to any profile at all?
Default answer no, until a named consumer asks. Neither wiki nor catalog has.
status, stale_after, verified, and sources are all optional upstream, and
adding them speculatively is the speculative-abstraction anti-pattern that
phase 3 exists to avoid.
V4 — What value does okf_version carry, and who sets it?
Already recorded as policy-not-constant. E1 gives the decision to catalog. This
repo's obligation is narrower and testable: no profile may hard-code 0.1, and
the root-frontmatter policy must express any value.
V5 — Is Attested Computation in scope?
Recommended out, on two independent grounds. Upstream has deferred the
receipt and verdict wire formats, so building now means porting an unfrozen
contract — the precise failure mode execution-order.md warns about for the Node
half. And attestation ("did this run produce the value the sanctioned way") sits
against the guard's boundary rather than this library's plumbing, so the boundary
question goes to the operator before any design.
TDD order — when quota returns
Steps 1–3 are characterization and audit only: they add tests, change no behavior, and are safe to run before V1 is answered.
- Characterize what already holds.
parse_frontmatterround-trips an inline flow mapping opaquely;_is_ingest_ownedreturns False for a v0.2generatedmapping. The second test converts today's fail-safe from an accident into a documented guarantee. - Audit Door C against the tolerance rules — one test per consumer MUST NOT
(unknown
type, unknown key, broken cross-link, missingindex.md) plus the bare-verified-as-one-element-list MUST. Measured, not reasoned: Door C is a consumer in the OKF sense and either honours these or does not. - Assert no hard-coded
0.1anywhere a profile should decide (V4). - Only after commons answers V1: the
generatedmigration, its_is_ingest_ownedchange, and the golden-fixture regeneration — as its own release with its own CHANGELOG entry, never folded into another change.
Key assumptions, each with its test
| # | Assumption | Test |
|---|---|---|
| V-A1 | The spec enumeration above 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 our parser unmodified | Step 1 characterization test; byte-exact round-trip |
| V-A3 | v0.2 input cannot cause an unowned overwrite | Step 1 test: _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 — a failure here is a real defect, not a plan revision |
| V-A5 | No profile hard-codes an upstream version | Step 3 |
| V-A6 | The generated migration is byte-neutral outside frontmatter |
Golden diff confined to the frontmatter block; every other byte identical |
Non-goals
- Implementing
Attested Computation, executors, attesters, receipts, or verdicts (V5). - Adding any optional v0.2 family to a profile without a named consumer requesting it (V3).
- Editing commons' ingest-spec locally. V1 is raised in commons; if commons declines, the deviation is documented rather than patched here.
- Moving
okf_versionto"0.2"in any fixture or profile before the shape earns it, and never without catalog. - Any change to the wiki's
timestamp. It is on the documented legacy path; the wiki is informed, not asked.
Coordination — who needs to know what
Notification set, not an approval queue. Each entry names the one thing that repo owns:
- commons — V1. The only blocking dependency:
generated's shape in the ingest-spec §5 layer. - catalog — form owner. That upstream moved to v0.2, that its form-not- membership gate absorbs the bump with no change, and that V4 is theirs.
- wiki — informational. Their
timestampis legacy-but-readable under §13.1; no action 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 is not ported against a v0.1-shaped contract.
Verification
- Spec re-read at a pinned commit; commit hash recorded in this doc.
pytestgreen;mypy --strict src/;ruff check .andruff format --check .clean.- Steps 1–3 add tests and change zero production behavior: golden suite
byte-for-byte,
git diff --stat examples/empty. - V1 answered in writing by commons before step 4 begins.
- If step 4 lands: golden diff confined to the frontmatter block; boundary grep-gate still empty; its own release and CHANGELOG entry.