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
212 lines
12 KiB
Markdown
212 lines
12 KiB
Markdown
# 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 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`. The receipt and verdict wire formats and
|
||
the full runtime protocol are **explicitly deferred to a future revision**.
|
||
- **`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 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:
|
||
|
||
1. **`STRICT_V1`'s `timestamp` is not broken.** It sits on the documented legacy
|
||
path. The 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: `DEFAULT` states 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 from `generated.by` instead of a bespoke
|
||
boolean. This is the spec-native answer, removes a hand-rolled key, and yields
|
||
a real `generated.at`. `at` binds to the already-validated required
|
||
`ingested_at` argument, so no wall-clock default is introduced and determinism
|
||
holds. Costs: a `_is_ingest_owned` change and a golden-fixture regeneration.
|
||
- (c) Rename our marker to a non-reserved key and leave `generated` free 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-verifier `verified`): 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.
|
||
|
||
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 test converts today's fail-safe from an accident into a
|
||
documented guarantee.
|
||
2. **Audit Door C against the tolerance rules** — one test per consumer MUST NOT
|
||
(unknown `type`, unknown key, broken cross-link, missing `index.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.
|
||
3. **Assert no hard-coded `0.1`** anywhere a profile should decide (V4).
|
||
4. **Only after commons answers V1:** the `generated` migration, its
|
||
`_is_ingest_owned` change, 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_version` to `"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 `timestamp` is 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
|
||
|
||
1. Spec re-read at a pinned commit; commit hash recorded in this doc.
|
||
2. `pytest` green; `mypy --strict src/`; `ruff check .` and
|
||
`ruff format --check .` clean.
|
||
3. Steps 1–3 add tests and change zero production behavior: golden suite
|
||
byte-for-byte, `git diff --stat examples/` empty.
|
||
4. V1 answered in writing by commons before step 4 begins.
|
||
5. If step 4 lands: golden diff confined to the frontmatter block; boundary
|
||
grep-gate still empty; its own release and CHANGELOG entry.
|