feat(watch): weekly OKF upstream watch that can prove it found nothing
The operator asked for a job that checks at least weekly whether Google OKF has moved, and messages the right repo immediately when it has. It belongs here rather than in `.claude` because knowing what a meaningful spec change IS requires owning the pin, the runbook and the always-latest policy. `tools/okf_watch.py`, stdlib only, driving git against the local read-only mirror. It lives outside `src/` so it never enters a wheel; a new packaging test holds that as a promise rather than an accident of the build config. Three properties carry the design, and each closes a failure this repo has actually met: 1. A failed call is never an empty result. Every git invocation raises on a non-zero exit and carries stderr, so a caller reading "" knows the query ran. The precedent is `grep ... | head; echo $?` reporting head's exit status - a broken query read as a quiet upstream. 2. It proves it can find, every run. Before believing any zero it re-runs the full detect-and-classify path over `ad30107^1..ad30107`, a range known to have changed SPEC.md. An empty known-positive aborts loudly rather than reporting a clean sweep. Network failure likewise raises; it never degrades to "no change". 3. It reports on change, not on state. A pin-keyed state file records what has been announced; moving the pin resets it, because a pin move means everything behind it was absorbed. Quiet is the enumerated list, not signal. Enumerating what counts as normative can only match what upstream has already invented, so anything new would fall outside it and the watch would go silent - failing in the direction nobody notices. A small measured quiet list, everything else reports. README.md is deliberately not quiet: the repository move was announced in a README commit. Sixteen tests build their own git repository in tmp_path rather than skipping when the mirror is absent - a skipped test preserves nothing on the machine where the dependency exists. All four load-bearing behaviours were mutation- tested red before this landed. Two more tests exist because building this fired a real false alarm: running with `--pin` and without `--dry-run` delivered two live coord messages. The override now implies dry-run, enforced in argument parsing rather than remembered, and `.claude` has the correction. The runbook gains a section stating what the watch CANNOT do, because that is the part a future session will otherwise assume away: it sees commits, not meaning. It would have fired on the 2026-08 tightening because SPEC.md changed, but no commit list says a value that conformed last month no longer does, and none says is_stale reversed. Its output is "run the runbook", never "here is your exposure".
This commit is contained in:
parent
e286b5a173
commit
3233b19b30
4 changed files with 719 additions and 3 deletions
|
|
@ -42,6 +42,10 @@ notice. So the re-check is an item on the release checklist — run it at every
|
|||
release of this library, and record the result **even when unchanged**, because an
|
||||
unrecorded check is indistinguishable from a skipped one.
|
||||
|
||||
**Since 2026-08-23 the trigger also fires without a release**, weekly, from
|
||||
`tools/okf_watch.py`. See § The weekly watch below. The watch decides *whether*
|
||||
this procedure runs; it never substitutes for it.
|
||||
|
||||
Check `GoogleCloudPlatform/open-knowledge-format`. **That is the canonical home of
|
||||
the spec, the reference agent and the sample bundles as of 2026-08-21.** A version
|
||||
bump appears as a commit against `SPEC.md` §12 and, in the v0.2 round, as an
|
||||
|
|
@ -54,9 +58,14 @@ will drift out of date"), and this repo was pinned to it until the 2026-08-23 ro
|
|||
Two consequences, both measured that round and neither hypothetical:
|
||||
|
||||
- **The two trees have already diverged**, and not only in the direction you would
|
||||
expect: the frozen copy carries a fix (`38c713f`, eight `tags:` values written as
|
||||
sequences rather than as one plain scalar) that the canonical repo does not. The
|
||||
canonical tree is authoritative for the *spec*; it is not automatically a superset.
|
||||
expect: the frozen repository's *head* carries a fix (`38c713f`, eight `tags:`
|
||||
values written as sequences rather than as one plain scalar) that the canonical
|
||||
repo does not. The canonical tree is authoritative for the *spec*; it is not
|
||||
automatically a superset. Note the pin-level precision, measured 2026-08-23:
|
||||
`38c713f` is **not** an ancestor of the old pin `3fcbb9f` either, so moving the
|
||||
pin lost nothing — canonical simply ships a form its own frozen predecessor has
|
||||
already repaired. Enumerated in full in
|
||||
`docs/plan/okf-2026-08-timestamp-tightening.md` § Known divergence.
|
||||
- **A round run against the frozen tree reports "no change" truthfully and
|
||||
uselessly** — the exact shape of a negative result that is not a measurement.
|
||||
|
||||
|
|
@ -271,6 +280,84 @@ survived verification is only known to have survived if the check is recorded, a
|
|||
claim we withdrew is only safely withdrawn if the withdrawal is written where the
|
||||
claim was.
|
||||
|
||||
## The weekly watch — `tools/okf_watch.py`
|
||||
|
||||
Answers one question on a schedule: *has canonical moved past our pin, and does
|
||||
the move touch anything that bears the contract?* On a hit it sends a coord
|
||||
message to this repo and, as FYI, to `.claude`. On a miss it prints one line and
|
||||
exits 0.
|
||||
|
||||
It is deliberately **not** part of the package: it lives in `tools/`, outside
|
||||
`src/`, so it never enters a wheel and a consumer's install surface is unchanged.
|
||||
`tests/test_packaging.py` holds that as a promise rather than an accident.
|
||||
|
||||
**Run it:**
|
||||
|
||||
python3 tools/okf_watch.py # the real weekly run
|
||||
python3 tools/okf_watch.py --dry-run # print the messages, send nothing
|
||||
python3 tools/okf_watch.py --pin <sha> # demonstrate the hit path (implies --dry-run)
|
||||
|
||||
**Cadence: weekly is the floor.** It costs one `git fetch` against a
|
||||
`blob:none` mirror, so running it daily is not meaningfully more expensive.
|
||||
|
||||
### Three properties, and why each is load-bearing
|
||||
|
||||
1. **A failed call is never an empty result.** Every `git` invocation raises on a
|
||||
non-zero exit and carries stderr. The failure mode this closes is specific and
|
||||
has been met before: `grep … | head; echo $?` reports the exit status of
|
||||
`head`, and a query that failed then reads as a query that found nothing.
|
||||
2. **It proves it can find, on every run.** Before believing any zero, the watch
|
||||
re-runs its full detect-and-classify path over `ad30107^1..ad30107` — a range
|
||||
known to have changed `SPEC.md`. If that comes back empty the query is broken,
|
||||
and the run aborts loudly instead of reporting a clean sweep. This is
|
||||
Verification-law face 4 made executable rather than remembered.
|
||||
3. **It reports on change, not on state.** A JSON state file records which
|
||||
commits have already been announced, keyed on the pin. Moving the pin resets
|
||||
it, because a pin move means everything behind it was absorbed.
|
||||
|
||||
### Quiet is the enumerated list; signal is not
|
||||
|
||||
`QUIET_PREFIXES` names the paths measured *not* to bear the contract
|
||||
(`.github/`, `CONTRIBUTING.md`, `CODE_OF_CONDUCT.md`, `LICENSE.md`, the HTML
|
||||
viewer, generated `viz.html`). **Everything else reports.**
|
||||
|
||||
The inverse design — enumerate what counts as normative — can only match what
|
||||
upstream has already invented, so anything new falls outside the list and the
|
||||
watch goes quiet about it. That fails in the direction nobody notices.
|
||||
Over-firing is visible and fixable by widening the quiet list; under-firing is
|
||||
neither. **If the watch becomes noisy, widen `QUIET_PREFIXES`. Do not narrow the
|
||||
signal.**
|
||||
|
||||
`README.md` is deliberately not quiet: upstream announced the repository move in
|
||||
a README commit, and that move is the change with the longest reach this library
|
||||
has seen.
|
||||
|
||||
### What the watch cannot do — state this when reporting it
|
||||
|
||||
It sees commits. It cannot see meaning.
|
||||
|
||||
When upstream tightened v0.2 in place on 2026-08-21, the watch would have fired
|
||||
correctly, because `SPEC.md` changed. But **no commit list says "a value that
|
||||
conformed last month does not conform now"**, and none says `is_stale` has
|
||||
reversed for date-only inputs. Those were found by reading the diff and running
|
||||
both readers against the same input. So the watch's output is always *run the
|
||||
runbook*, never *here is your exposure* — and the message it sends says so in as
|
||||
many words.
|
||||
|
||||
Two further blind spots, named rather than left to be discovered:
|
||||
|
||||
- **A silent relocation.** The last move was caught only because upstream
|
||||
committed a notice to `README.md`. A move announced anywhere other than this
|
||||
git history is invisible here.
|
||||
- **A tightening with no commit at all** — a spec whose meaning is changed by an
|
||||
external document, an errata page, a changed reference implementation shipped
|
||||
under a different repository. Nothing local can see that. The release-checklist
|
||||
trigger, which reads rather than diffs, is the only cover.
|
||||
|
||||
The watch narrows the window between an upstream change and our noticing it. It
|
||||
does not close it, and a session that treats a quiet watch as proof that upstream
|
||||
is unchanged has made exactly the mistake the watch was built to prevent.
|
||||
|
||||
## Invariants this procedure protects
|
||||
|
||||
- No profile hard-codes an upstream version.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue