docs(okf-v0.2): the alignment track lands before the contract freezes
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
This commit is contained in:
parent
c5e6f083e6
commit
ca453b2895
4 changed files with 291 additions and 5 deletions
|
|
@ -63,6 +63,15 @@ are the library baseline. First consumer: `portfolio-optimiser-claude`.
|
|||
|
||||
The two halves share the OKF contract and fixture suite, **not code**.
|
||||
|
||||
**Upstream version: everything above targets OKF v0.1.** Google published v0.2
|
||||
on 2026-07-25 (two supersessions, both with documented consumer fallbacks; all
|
||||
new fields optional). Alignment is a policy track between Phases 3 and 4, not a
|
||||
phase: `docs/plan/okf-v0.2-alignment.md`. Two constraints bind here — the
|
||||
`generated` marker `DEFAULT` emits sits in commons' ingest-spec §5 layer, so its
|
||||
shape is commons' call, not ours; and `okf_version`'s value belongs to catalog
|
||||
(decision E1). No profile may hard-code an upstream version, and no bundle
|
||||
claims a version its shape has not earned.
|
||||
|
||||
Phase 4 preconditions (coordination, not unilateral moves):
|
||||
- Lifts okr's reference implementations (`okf-check.mjs`, `okf-index.mjs`,
|
||||
innboks libs) in agreement with okr and the marketplace catalog; the
|
||||
|
|
|
|||
|
|
@ -8,12 +8,14 @@ own doc:
|
|||
- `docs/plan/phase-1-door-a.md` — DONE, shipped at v0.3.2.
|
||||
- `docs/plan/phase-2-doors-b-c.md`
|
||||
- `docs/plan/phase-3-configurable-contract.md`
|
||||
- `docs/plan/okf-v0.2-alignment.md` — a policy track, not a phase; slots between
|
||||
Phases 3 and 4 for the reason given below.
|
||||
- `docs/plan/phase-4-node-half.md`
|
||||
|
||||
Headline order: **Stage 0 (guard gate) → Phase 2 → Phase 3 → Phase 4 code**,
|
||||
with Phase 2 split guard-independent-first and Phase 4's *coordination* started
|
||||
in parallel from the beginning. The quality argument for this order is below —
|
||||
it is not merely the roadmap numbering.
|
||||
Headline order: **Stage 0 (guard gate) → Phase 2 → Phase 3 → OKF v0.2 alignment
|
||||
decisions → Phase 4 code**, with Phase 2 split guard-independent-first and
|
||||
Phase 4's *coordination* started in parallel from the beginning. The quality
|
||||
argument for this order is below — it is not merely the roadmap numbering.
|
||||
|
||||
## Stage 0 — Guard readiness (gate; do first, cheap)
|
||||
|
||||
|
|
@ -33,6 +35,36 @@ persist-gate design must know now, not at the integration step.
|
|||
|
||||
Outcome recorded in STATE before Phase 2 code starts.
|
||||
|
||||
### Stage 0 revisited — measuring guard 0.3.1 before widening the pin
|
||||
|
||||
The pin held is `>=0.2,<0.3`, which can only ever resolve `v0.2.0`. The guard repo
|
||||
gates its own 1.0 on our fixture suite being measured against `v0.3.1`, so this
|
||||
measurement is owed outward and is that repo's only named blocker. Order is
|
||||
*measure first, widen after*: widening the range before measuring would ship a
|
||||
window admitting a version the fixtures never ran against.
|
||||
|
||||
Procedure — do not vary it, each step exists because of a specific failure mode:
|
||||
|
||||
1. Scratch venv **outside** `.venv`: install the package `--no-deps`, then the
|
||||
guard from the `v0.3.1` tag. Do not touch `pyproject.toml`.
|
||||
2. **Assert the resolved version** via `importlib.metadata` *before* running
|
||||
anything, and carry it in the reported result. An unmodified tree resolves
|
||||
`0.2.0` and runs green — a true claim about 0.2.0 published under a heading
|
||||
that says 0.3.1. This step is what makes the result honest.
|
||||
3. Run `tests/test_guard_adapter.py` (19 tests, real scanner) and the full suite.
|
||||
Watch percent-escapes specifically: the slugger builds filenames from titles,
|
||||
and the guard has recorded that as its most likely false positive. Report raw
|
||||
URLs, not counts.
|
||||
4. **Send the result whichever way it goes**, and state explicitly that 19
|
||||
fixture tests are not a corpus — their gate is "does 0.3.1 change our
|
||||
verdicts", not "how often does the false positive fire".
|
||||
5. Green → one release: `>=0.2,<0.4`, uv tag `v0.3.1`, CLAUDE.md and README
|
||||
updated together. Red → no bump, and the finding goes back to the guard.
|
||||
|
||||
Until this lands, consumers of v0.4.0 resolve guard 0.2.0 (`pyproject.toml:25`).
|
||||
That is a silent under-defend: no build fails, so nothing surfaces it. The
|
||||
release in step 5 is what closes it.
|
||||
|
||||
## Phase 2 — Doors B/C (split: guard-independent first)
|
||||
|
||||
Internal order follows the phase-2 doc's TDD steps, deliberately:
|
||||
|
|
@ -68,6 +100,31 @@ policy differs per consumer and becomes configurable in Phase 3." This is where
|
|||
the contract becomes explicit and frozen — the precondition for a clean Node
|
||||
port.
|
||||
|
||||
## OKF v0.2 alignment — decisions between 3 and 4, code possibly later
|
||||
|
||||
Everything through Phase 3 targets OKF v0.1. Upstream published v0.2 on
|
||||
2026-07-25. Detail and open questions: `docs/plan/okf-v0.2-alignment.md`.
|
||||
|
||||
**Why the decisions must land before Phase 4 code, even if the code does not.**
|
||||
Phase 4 ports a *frozen, explicit* contract and uses the shared fixture suite as
|
||||
the cross-runtime conformance oracle. Freezing that contract while its relation
|
||||
to upstream v0.2 is undecided means freezing a v0.1 shape into two runtimes
|
||||
instead of one, and the fixture suite would then certify the drift rather than
|
||||
catch it. The expensive version of this mistake is discovered after the Node half
|
||||
exists; the cheap version is a decision recorded now.
|
||||
|
||||
**Why it is not a phase.** v0.2's breaking surface is two supersessions, both
|
||||
with documented consumer fallbacks, and every new field is optional. The only
|
||||
measured shape problem is `DEFAULT`'s `generated` marker, which lives in commons'
|
||||
ingest-spec §5 layer and is therefore not this repo's to change. So the work is
|
||||
mostly *deciding and routing*, expressed through the Phase 3 profile object —
|
||||
not a new implementation stage.
|
||||
|
||||
**Order inside the track.** Characterization and audit first (they add tests and
|
||||
change no behavior, so they are safe while the central question is still open),
|
||||
then the one blocking dependency — commons' answer on `generated` — and only then
|
||||
any migration, as its own release.
|
||||
|
||||
## Phase 4 — Node half (two clocks)
|
||||
|
||||
**Coordination clock — start early, parallel to Stages 0–3, but only the
|
||||
|
|
@ -117,6 +174,8 @@ These are the objective checks that a stage is done and the next may begin:
|
|||
second-brain spec).
|
||||
4. **Phase 4 code start:** each shape-independent coordination step has a
|
||||
recorded sign-off; the contract-shape agreement is signed off *after* the
|
||||
Phase 3 split-table, not before.
|
||||
Phase 3 split-table, not before; and the OKF v0.2 open questions V1–V5 are
|
||||
each answered or explicitly deferred *in writing*, so the frozen contract
|
||||
states its relation to upstream on purpose rather than by omission.
|
||||
5. **Throughout:** `mypy --strict src/`, `ruff check .`, `ruff format --check .`
|
||||
clean; boundary grep-gate empty.
|
||||
|
|
|
|||
212
docs/plan/okf-v0.2-alignment.md
Normal file
212
docs/plan/okf-v0.2-alignment.md
Normal file
|
|
@ -0,0 +1,212 @@
|
|||
# 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.
|
||||
|
|
@ -104,6 +104,12 @@ columns and are decomposed there rather than forced into one.
|
|||
to block it (checked against the catalog's spec during the split-table step).
|
||||
- Any change to commons' ingest-spec; if profile work reveals spec friction, it
|
||||
is raised in commons, not patched locally.
|
||||
- Aligning the contract with upstream OKF v0.2. This phase targets v0.1, and the
|
||||
profile object is what makes the upstream version a *policy* rather than a
|
||||
constant: no profile may hard-code an `okf_version` value, and the
|
||||
frontmatter-set fields must be able to express either the legacy `timestamp`
|
||||
or v0.2's `generated`. The decisions themselves live in
|
||||
`docs/plan/okf-v0.2-alignment.md` and are sequenced after this phase.
|
||||
|
||||
## Verification
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue