Operator GO 2026-08-23. Two findings outrank the change that triggered the round. OKF moved to GoogleCloudPlatform/open-knowledge-format (6265173). The tree this repo pinned, knowledge-catalog path okf/, is now a frozen snapshot by upstream's own notice, and the runbook's Trigger step named it. The two trees have already diverged: the frozen copy carries a fix (38c713f, eight tags: values as sequences rather than one plain scalar) that the canonical repo does not. The spec tightened without a version bump. SPEC.md still declares Version 0.2, but every timestamp-valued key is now an ISO 8601 datetime with an explicit offset, so a value that conformed in July does not conform now under the same version number. Step 4 gained a fifth diff class for it. Our exposure is zero, measured: 10 of 10 timestamp values across the four goldens already carry a Z offset, gated by _INGESTED_AT_RE. The three tightened keys (stale_after, last_modified, usage_window) are named but never written by any door. V-A8 at the new pin: 9/9 of our bundle documents parse, 78/78 of upstream's do. Two reference-reader behaviour changes recorded. A date-only stale_after now reads as never stale rather than as stale -- silent, fails open, and the class the black box cannot absorb. Timestamps now survive a parse as written, so our ingested_at arrives as str where it used to arrive as datetime. No new profile: there is no new version to express. The pin move and consumer notification are named as deliverables and deliberately not taken here. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013j8ZEcjiZQpz22fecNsyLZ
351 lines
18 KiB
Markdown
351 lines
18 KiB
Markdown
# Upstream OKF round, 2026-08-23 — the timestamp tightening, and the repository move
|
|
|
|
Runbook run of `docs/upstream-okf-upgrade-runbook.md`, executed 2026-08-23 on an
|
|
operator GO. Previous round: `docs/plan/okf-v0.2-alignment.md` (v0.1 → v0.2, pinned
|
|
`3fcbb9f`).
|
|
|
|
This round found two things the runbook was not shaped for, and both matter more
|
|
than the change that triggered it:
|
|
|
|
1. **OKF moved to its own repository.** The tree we had pinned is now a declared
|
|
frozen snapshot. The runbook's Trigger step pointed at it by name.
|
|
2. **The spec changed without a version bump.** `SPEC.md` still says Version 0.2.
|
|
Step 4's classification vocabulary had no bucket for that.
|
|
|
|
The triggering change itself — every timestamp becomes an ISO 8601 datetime with an
|
|
explicit offset — costs this library nothing. That conclusion is measured, not
|
|
assumed; the denominators are below.
|
|
|
|
---
|
|
|
|
## Step 1 — Pins
|
|
|
|
Everything below was read at a fixed commit, never at a branch.
|
|
|
|
| What | Commit | Date | Role |
|
|
|---|---|---|---|
|
|
| `GoogleCloudPlatform/open-knowledge-format` | `ad30107c31c06aec8a7d5636e0d1058118604e6f` | 2026-08-21 | **New canonical source.** Read at this pin |
|
|
| `GoogleCloudPlatform/knowledge-catalog` | `395a0c311979858dcecfed767810658baf3ca3f3` | 2026-08-22 | Frozen `okf/` snapshot, read for divergence |
|
|
| `GoogleCloudPlatform/knowledge-catalog` | `3fcbb9f828c2f23d109c855ee403c3a4c81f3a96` | 2026-07-24 | **Our current pin**, the v0.2-round baseline |
|
|
|
|
The order that authorized this run named `62432a09` as the target. That commit is
|
|
real (`okf: make every timestamp an ISO 8601 datetime with an explicit offset
|
|
(#323)`, 2026-08-20) but it is a commit in the *frozen* tree, and it was not the
|
|
head of anything by the time this ran. Verified before use rather than adopted:
|
|
`knowledge-catalog` had advanced 53 commits past our pin, and the canonical tree is
|
|
a different repository.
|
|
|
|
Local read-only mirrors: `~/repos/_okf-upstream` (knowledge-catalog, pre-existing)
|
|
and `~/repos/_okf-canonical` (new, cloned this round, push URL disabled).
|
|
|
|
## Step 2 — Enumeration, with denominators
|
|
|
|
**Canonical repo at `ad30107`: 132 tracked files.**
|
|
|
|
| Top level | Files |
|
|
|---|---|
|
|
| `bundles/` | 83 (acme_retail 19, crypto_bitcoin 16, ga4 15, stackoverflow 33) |
|
|
| `src/` | 27 |
|
|
| `tests/` | 8 |
|
|
| `samples/` | 6 |
|
|
| `connectors/` | 1 — **new**, not present in the tree we had pinned |
|
|
| root files | 7 (`SPEC.md`, `README.md`, `pyproject.toml`, `LICENSE.md`, `CONTRIBUTING.md`, `CODE_OF_CONDUCT.md`, `.gitignore`) |
|
|
|
|
By extension: 89 `md`, 31 `py`, 5 `html`, 3 `txt`, 1 each of `toml`, `js`, `css`,
|
|
`gitignore`.
|
|
|
|
Of the 53 `knowledge-catalog` commits between our pin and its current head, **3
|
|
touched `okf/`**. The other 50 are `toolbox/` work (`mdcode`, `enrichment`) and are
|
|
out of scope for this library — stated with the denominator, not as an impression.
|
|
|
|
## Step 3 — The move, read from the primary source
|
|
|
|
`6265173 okf: point readers at the dedicated OKF repository (#324)` adds a notice to
|
|
`okf/README.md`:
|
|
|
|
> **OKF now lives in its own repository:
|
|
> GoogleCloudPlatform/open-knowledge-format.** … **Stop using the copy under `okf/`
|
|
> in this repository.** It is a frozen snapshot, no longer maintained, and anything
|
|
> built against it will drift out of date.
|
|
|
|
This is the finding with the longest reach. "Always latest" is a promise about a
|
|
tree, and the tree changed identity. The runbook's Trigger step — *Check
|
|
`GoogleCloudPlatform/knowledge-catalog`, path `okf/`* — would have kept sending
|
|
future rounds at a snapshot that upstream says will drift, and each such round would
|
|
have reported "no change" truthfully and uselessly.
|
|
|
|
### The two trees have already diverged
|
|
|
|
The canonical repo does **not** carry `38c713f okf: write stackoverflow bundle tags
|
|
as YAML lists (#293)` (2026-08-14), which is present in the snapshot it declares
|
|
obsolete. Measured across the 8 affected files, each differing by exactly the `tags:`
|
|
line and nothing else:
|
|
|
|
```
|
|
canonical: tags: stackoverflow, posts, questions # one plain scalar
|
|
frozen kc: tags: [stackoverflow, posts, questions] # a sequence
|
|
```
|
|
|
|
Upstream's own commit message for that fix records what the scalar form cost them: a
|
|
JavaScript string is iterable, so each one expanded into one label *per character*.
|
|
The canonical repository currently ships the form upstream had already diagnosed as
|
|
corrupting.
|
|
|
|
We are not exposed: this library neither writes nor reads OKF `tags` (measured — one
|
|
occurrence of the substring across 11 modules, in `extract.py:86`, about HTML tags).
|
|
Door C is exposed only in the sense that it merges external concepts **verbatim** by
|
|
design, so a consumer importing the canonical `stackoverflow` bundle would carry the
|
|
scalar form through unchanged. That is the verbatim invariant working, not a defect.
|
|
|
|
## Step 4 — The diff, classified
|
|
|
|
Between our pin `3fcbb9f:okf/` (129 files) and canonical `ad30107` (132 files), every
|
|
`.py` file differs by a 14-line Apache licence header added at the move. Confirmed as
|
|
an artifact rather than assumed: stripping those 14 lines makes the files byte-
|
|
identical. Excluding it, the substantive set is small.
|
|
|
|
**Normative (`SPEC.md`), 22 changed lines, all one change.** Every timestamp-valued
|
|
key becomes an ISO 8601 datetime with an explicit UTC offset. New sentence in §5:
|
|
|
|
> Every timestamp-valued key in OKF is an ISO 8601 datetime with an explicit UTC
|
|
> offset, for example `2026-06-30T14:00:00Z`.
|
|
|
|
Three keys change form; the rest of the diff is the examples and prose following
|
|
them:
|
|
|
|
| Key | Was | Is |
|
|
|---|---|---|
|
|
| `stale_after` (§5.5) | `YYYY-MM-DD` | datetime with offset |
|
|
| `sources[].last_modified` (§5.1) | `YYYY-MM-DD` | datetime with offset |
|
|
| `usage_window.{from,to}` (§5.1) | date range | datetime range |
|
|
|
|
`generated.at` and `verified.at` were already datetimes with `Z` and are untouched.
|
|
|
|
**Not a version bump.** `SPEC.md` still declares Version 0.2, and §12's minor/major
|
|
rules are unchanged. This is a *tightening of the normative text in place*: a value
|
|
that conformed on 2026-07-24 does not conform now, under the same version number. The
|
|
runbook's Step 4 vocabulary (Breaking / Additive / Deferred upstream / Observed but
|
|
undocumented) has no bucket for it, which is why it is named here as a fifth class —
|
|
**tightened in place, no version bump** — and why declaring conformance to "v0.2"
|
|
without also naming a commit is now ambiguous. Our `okf_spec_commit` key already
|
|
anticipated this; this round is the first time it earns its keep.
|
|
|
|
**Reference reader (`src/reference_agent/bundle/document.py`), 34 substantive
|
|
lines, two behaviour changes.**
|
|
|
|
*Change 1 — a date-only `stale_after` is now silently ignored.* `is_stale` returns
|
|
`False` for any value without a `T`, on the stated reasoning that a date-only value
|
|
"names a different instant in every timezone, so it is ignored rather than guessed
|
|
at". Measured on identical input at both pins, with `now` set 17 months past the
|
|
date:
|
|
|
|
| `stale_after` | reader @ `3fcbb9f` | reader @ `ad30107` |
|
|
|---|---|---|
|
|
| `2026-12-31` | `is_stale=True` | **`is_stale=False`** |
|
|
| `2026-12-31T00:00:00Z` | `True` | `True` |
|
|
| `2026-12-31T00:00:00+00:00` | — | `True` |
|
|
|
|
This is the class the black-box promise explicitly cannot absorb. No library change
|
|
makes an already-authored date-only `stale_after` mean what its author meant; it now
|
|
reads as *never stale* rather than as *stale*, which fails open. Nothing in this
|
|
library emits `stale_after`, so our first-order exposure is zero — but any consumer
|
|
who authored one has content whose meaning reversed without their file changing.
|
|
|
|
*Change 2 — timestamps now survive a parse as the text the author wrote.* Upstream
|
|
subclasses `SafeLoader` and drops the `tag:yaml.org,2002:timestamp` implicit
|
|
resolver, because PyYAML implements YAML 1.1 and was rewriting frontmatter on a
|
|
round-trip. Measured against our own golden, same bytes, both pins:
|
|
|
|
| Key | type @ `3fcbb9f` | type @ `ad30107` |
|
|
|---|---|---|
|
|
| `ingested_at` | `datetime` | **`str`** (`'2026-07-16T12:00:00Z'`) |
|
|
| `generated.at` | `datetime` | **`str`** |
|
|
| `okf_version` | `float` (`0.2`) | `float` (`0.2`) — unchanged |
|
|
|
|
This is a change in our favour: the value an upstream consumer recovers from our
|
|
bundle is now the literal we wrote. It also narrows the A-E6 finding — the float
|
|
coercion on `okf_version` survives, so that one is still real and still upstream's.
|
|
|
|
**Additive:** `connectors/gcp-knowledge-catalog.md` (new file), `CONTRIBUTING.md` and
|
|
`CODE_OF_CONDUCT.md` (repository furniture that came with the move).
|
|
|
|
**Deferred upstream (unchanged):** §12 still defers the receipt and verdict wire
|
|
formats, the attester ABI, attestation caching, and semantic-layer templates. Our
|
|
`Attested Computation` support stays format-only on upstream's deferral, not on our
|
|
preference.
|
|
|
|
**Observed but undocumented:** none this round.
|
|
|
|
## Step 3a — V-A8, upstream's reader against our fixtures
|
|
|
|
Run with `/usr/local/bin/python3` (3.14.0, PyYAML 6.0.3) against the pinned clone.
|
|
The `.venv` was not touched and nothing was installed; the one-runtime-dependency
|
|
rule and its packaging test are intact.
|
|
|
|
| Bundle | `.md` files | parsed | parse failures |
|
|
|---|---|---|---|
|
|
| our `ingest-golden-file` | 3 | 3 | 0 |
|
|
| our `ingest-golden-http` | 2 | 2 | 0 |
|
|
| our `ingest-golden-okf-v0-2` | 2 | 2 | 0 |
|
|
| our `ingest-golden-sql` | 2 | 2 | 0 |
|
|
| upstream `acme_retail` | 17 | 17 | 0 |
|
|
| upstream `crypto_bitcoin` | 15 | 15 | 0 |
|
|
| upstream `ga4` | 14 | 14 | 0 |
|
|
| upstream `stackoverflow` | 32 | 32 | 0 |
|
|
|
|
**9 of 9 of our own bundle documents parse under the new reader**, and
|
|
`trust_tier`, `normalize_verified` and `is_stale` run over each without raising.
|
|
|
|
A clean sweep is a weak result unless the harness is shown to be able to fail, so it
|
|
was put to a known-positive: a document with `type: [unclosed` raises
|
|
`OKFDocumentError` as expected, while a well-formed control parses. The zeroes above
|
|
are measurements, not the absence of one.
|
|
|
|
Two notes on what V-A8 does *not* buy, both carried forward from the v0.2 round:
|
|
|
|
- `validate()` checks `REQUIRED_FRONTMATTER_KEYS = ("type",)` — one key. It fails on
|
|
our root `index.md`, correctly: an index is not a concept and carries no `type` by
|
|
§12. The runbook's recorded "13/13" counted concept documents; this run's harness
|
|
asserted `validate()` on index files too and so reports one expected failure per
|
|
bundle root. Not a regression — a stricter harness. Do not read the two counts as
|
|
comparable.
|
|
- Upstream's `serialize()` still reflows inline flow forms to block form. Unchanged,
|
|
one-directional, and still not a reason to emit block form (D4).
|
|
|
|
## Step 5 — Exposure
|
|
|
|
### Ours: measured zero
|
|
|
|
Every timestamp-valued byte this library emits, across all four golden bundles —
|
|
denominator 10 of 10, every one already conformant:
|
|
|
|
```
|
|
examples/ingest-golden-file/expected-bundle/ingest-orders.md:5 ingested_at: 2026-07-16T12:00:00Z
|
|
examples/ingest-golden-file/expected-bundle/ingest-orders.md:7 generated: { by: process:okf-ingest, at: 2026-07-16T12:00:00Z }
|
|
... 4 further bundles, same two keys, same form
|
|
```
|
|
|
|
The gate is `materialize.py:37`,
|
|
`_INGESTED_AT_RE = re.compile(r"^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$")`, enforced by
|
|
`validate_ingested_at` (`materialize.py:49`) and called on every Door A run
|
|
(`materialize.py:386`). `generated.at` derives from the same validated value via
|
|
`profile.ownership.stamp(ingested_at)` (`materialize.py:235`), so both keys are
|
|
covered by one check. The spec's own example of an explicit UTC offset is `Z`, so
|
|
this form conforms as written.
|
|
|
|
The three keys the tightening actually changes are not written by any door:
|
|
|
|
- `stale_after` is **named but not written** — `profiles.py:757` places it in
|
|
`_OKF_V0_2_KEY_ORDER` so a caller can emit it in canonical order and the schema can
|
|
judge a document carrying one. The comment at `profiles.py:736` says so directly:
|
|
*"Naming a family is not writing it … Door A writes none of them."*
|
|
- `last_modified`, `usage_window`, `usage_count` are not emitted at all.
|
|
`materialize.py:188` records the decision: a manifest source has no `author` and no
|
|
`last_modified`, *"and inventing them would be writing fields with no reader."*
|
|
|
|
Nothing in this library validates a timestamp value other than `ingested_at`
|
|
(measured across 11 modules; the query was validated against a known-positive control
|
|
before its empty result was believed).
|
|
|
|
One test fixture carries a now-non-conformant literal:
|
|
`tests/test_okf_v0_2_characterization.py:188` uses `stale_after: 2027-01-01` inside a
|
|
§14 *consumer-tolerance* case — a document a conformant consumer MUST NOT reject.
|
|
That is still exactly what it tests, and the date-only form arguably tests it better
|
|
now. **No change proposed.** Flagged so a later reader does not "fix" it.
|
|
|
|
### Consumers: framed for them to measure, not decided for them
|
|
|
|
The list was re-derived against disk rather than trusted from the runbook, and all
|
|
eight are present (two were initially recorded as absent from a guessed path — a
|
|
wrong-path negative, corrected before use):
|
|
|
|
`portfolio-optimiser-commons`, `portfolio-optimiser`, `portfolio-optimiser-claude`,
|
|
`claude-code-llm-wiki`, `catalog`, `okr`, `linkedin-studio`, `ms-ai-architect`.
|
|
|
|
What has to be true for the tightening to reach a consumer — one falsifiable question
|
|
each, for them to run against their real data:
|
|
|
|
1. **Do you author `stale_after`, `last_modified`, or `usage_window` anywhere?** If
|
|
no, your exposure is zero and this round costs you nothing. If yes, and the value
|
|
is date-only, an OKF v0.2 reader at the current spec now reads it as *never stale*
|
|
rather than as *stale*. It fails open, and it is silent.
|
|
2. **Do you declare conformance as "v0.2" without naming a commit?** That string no
|
|
longer identifies one document. Naming the commit alongside it is the fix.
|
|
3. **Do you read OKF from `GoogleCloudPlatform/knowledge-catalog`, path `okf/`?**
|
|
That copy is frozen by upstream's own notice, and has already diverged.
|
|
|
|
Nothing here asserts which consumers are affected. The v0.2 round showed the
|
|
falsifiable framing is what made the answers useful.
|
|
|
|
## Step 6 — The plan
|
|
|
|
**No new profile.** Support is additive by rule, but there is nothing additive to
|
|
express: the spec did not bump, the three tightened keys are ones we do not write,
|
|
and what we do write already conforms. Inventing an `OKF_V0_2_1` profile would
|
|
declare a version upstream has not minted. *Conform first, claim after.*
|
|
|
|
**Deliverables, in order:**
|
|
|
|
1. **Correct the runbook's Trigger step** to name
|
|
`GoogleCloudPlatform/open-knowledge-format` as canonical, record that
|
|
`knowledge-catalog/okf/` is frozen and already divergent, and add the fifth
|
|
diff class (*tightened in place, no version bump*) to Step 4. Done this round —
|
|
leaving a known-false instruction in the procedure would send the next run at a
|
|
dead tree.
|
|
2. **Move the pin** `3fcbb9f` → `ad30107`, and with it the repository. Cost, measured:
|
|
**zero bytes in our fixtures.** `okf_spec_commit` is a key the `STRICT_V1` profile
|
|
requires (`profiles.py:721,726`); its *value* is the caller's under D5, and no
|
|
shipped fixture carries one. The cost lands where D5 says it should — on the
|
|
caller that stamps it, as a re-run. This is a separate decision and is **not taken
|
|
here**, per the order.
|
|
3. **Re-record V-A8 at the new pin** in `docs/plan/okf-v0.2-alignment.md`'s
|
|
verification table, which currently reads *"DONE 2026-07-31 @ `3fcbb9f`: 13/13"*.
|
|
Pending the pin decision, so the two move together.
|
|
4. **Notify consumers** (Step 7) with the three questions above. Not sent this
|
|
round — the order scoped this to analysis and a plan document, and notification
|
|
is a sent action.
|
|
|
|
**No pilot, no GA event.** Pilot-first governs a *new upstream version* reaching a
|
|
new profile. There is no new version and no new profile, so `OKF_LATEST` does not
|
|
move and there is nothing to flip. Saying this explicitly because the standing
|
|
policy's machinery is easy to run out of habit.
|
|
|
|
**Guard seam:** nothing in this round touches it. The tightening is a value-format
|
|
question inside frontmatter we already gate; the persist gates are unchanged, and the
|
|
standing posture that everything entering a bundle passes the guard is unaffected.
|
|
Noted, not built.
|
|
|
|
## Step 8 — Verification log
|
|
|
|
Every claim above and the command class that produced it. Corrections stay visible.
|
|
|
|
| Claim | How verified |
|
|
|---|---|
|
|
| OKF moved; `okf/` is frozen | `git show 6265173` — primary source, upstream's own notice text |
|
|
| Canonical pin `ad30107`, 132 files | `git log -1`, `git ls-files \| wc -l` at the pin |
|
|
| 3 of 53 kc commits touched `okf/` | `git log --oneline 3fcbb9f..origin/main -- okf` |
|
|
| `.py` diffs are a licence header | Stripped 14 lines, files byte-identical (`diff` empty) |
|
|
| Spec change is timestamps only | `diff -u` of the two `SPEC.md` files, whole diff read |
|
|
| Still Version 0.2, no bump | `SPEC.md:3`, §12 read in full at the new pin |
|
|
| Canonical lacks `38c713f` | `diff -rq` frozen-vs-canonical; 8 files, each 2 lines, all `tags:` |
|
|
| We never write `tags` | `grep` across 11 modules — 1 unrelated hit (`extract.py:86`) |
|
|
| `is_stale` reversal | Same input run against both pinned readers, `now=2027-06-01` |
|
|
| `ingested_at` type change | V-A8 type dump, same golden bytes, both pins |
|
|
| Our exposure is 10/10 conformant | Frontmatter scan over all 10 `examples/**/*.md` |
|
|
| No other timestamp validation | `grep` over 11 modules, **validated against a known-positive control first** |
|
|
| `okf_spec_commit` has no fixture value | `grep examples/` exit 1, after `git grep` confirmed the key exists in 3 tracked places |
|
|
| V-A8 harness can detect failure | Deliberate `type: [unclosed` → `OKFDocumentError`; control parses |
|
|
| All 8 consumers present | `find ~/repos -maxdepth 3 -type d -name <n>`, with a known-positive control |
|
|
| Suite green | `.venv/bin/python -m pytest -q` → 596 passed |
|
|
|
|
**Corrections made during this run, kept visible:**
|
|
|
|
- The order named `62432a09` as the target. It is a commit in the frozen tree, not
|
|
the canonical head. Verified before it was used as a premise.
|
|
- `linkedin-studio` and `ms-ai-architect` were first recorded as absent, from a
|
|
guessed path (`~/repos/<name>`). A wider search found both under
|
|
`~/repos/ktg-plugin-marketplace/`. A negative from a guessed path is not a
|
|
measurement.
|
|
- An early `grep … | head; echo $?` reported the exit status of `head`, not of
|
|
`grep` — a negative that measured nothing. Re-run with the status of the actual
|
|
query before its empty result was used.
|