W12: make ontology/kko/PROVENANCE.md a gate instead of a record - #35
Merged
Conversation
The engine's KKO provenance is the best in the estate — source, licence, version IRI,
byte count, sha256 — and until now it was verified by NOTHING. The digest was a correct
line in a Markdown file, connected to no check.
That matters beyond this repo: prophet-platform vendors byte-identical copies of this
TBox and now asserts the SAME constant at import. The engine, the artifact's source of
truth, was the one copy where drift would have gone unnoticed.
scripts/check-kko-provenance.mjs (npm run check:kko, wired into ts-ci on every PR)
asserts three things:
1. kko-2.10.n3 still hashes to the SHA-256 the record declares, and is still the
declared byte length. The record is the source of the expectation, so record and
artifact cannot silently disagree.
2. The record pins a COMMIT. It previously cited "@ master" — a moving reference: the
same retrieval later can return different bytes while claiming this provenance.
Now pinned to 3f888b397255b69d1439fd95823e97011ed9440b (verified against upstream
2026-07-29; master has not moved since 2019-04-09, which is exactly why the
difference stays invisible until it isn't).
3. ts/src/kko-data.ts is exactly what scripts/gen-kko.mjs reproduces from that .n3 —
so the ontology the engine SHIPS derives from the file whose provenance we publish.
A hand-edit, or a re-vendor without regeneration, otherwise leaves the runtime
ontology diverged from its stated source. Same doctrine as the existing ts/dist
staleness gate.
Read-only with respect to ts/src: it regenerates into memory and compares, never writes.
Teeth verified — each check fails on a real tamper:
1. length-preserving :Monads -> :Places re-parent (valid Turtle, still 327,797 bytes) -> refused
2. commit pin reverted to "master" -> refused
3. hand-edited label in the generated kko-data.ts -> refused
clean tree -> exit 0
Note: the commit-pin check deliberately strips the sha256 from the document before
searching for a 40-hex commit — a naive scan matches a 40-char window INSIDE the 64-char
digest and would have been satisfied by the very value it is meant to be independent of.
…guards Two findings from review of #35. 1. CodeQL actions/missing-workflow-permissions fired on the new kko-provenance job (alert 36, ts-ci.yml:92). Accepted. Fixed at WORKFLOW level rather than on the new job, because the same rule was already open against this file on `main` for build-and-verify-dist (alert 33, ts-ci.yml:24) — a job-level block would have closed the alert this PR introduced and left the one it inherited. Declaring it once also means a job added later inherits least-privilege instead of silently getting the default write-scoped token. Both jobs only read the checkout and install from the public registry, so `contents: read` is sufficient; verified both still pass locally. 2. `on: push` was path-filtered to package.json / ts/** / the workflow itself. The kko-provenance job guards ontology/kko/** and scripts/**, so on a push that touched ONLY those paths the gate did not run at all. It always runs on pull_request (no path filter there), which is the merge path, so this was not a hole in practice — but the trigger should cover what the job guards. Added ontology/** and scripts/**. Gate-fires proof (all six failure branches, each restored afterwards): artifact digest drift -> rc=1 commit pin replaced by @master -> rc=1 kko-data.ts hand-edited -> rc=1 declared byte count wrong -> rc=1 declared sha256 removed -> rc=1 source repo no longer recorded -> rc=1 untouched tree -> rc=0
This was referenced Jul 30, 2026
Open
Closed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Companion to prophet-platform #1056 (W12 inventory hygiene, from the vendor-freshness inventory in sociosphere PR #492).
The finding
ontology/kko/PROVENANCE.mdis the best provenance record in the estate — source, licence, version IRI, byte count, sha256. It is also, until this PR, verified by nothing. The digest was a correct line in a Markdown file, connected to no check.That was worth fixing on its own, but it became load-bearing this week: prophet-platform vendors byte-identical copies of this TBox (
apps/owl-reasoner) and now asserts the same constant at import. The engine — the artifact's source of truth — was the one copy where drift would have gone unnoticed.I went looking for the "sha asserted at import" the engine was believed to have. It does not exist:
grepfor the digest across the repo returns exactly one hit,PROVENANCE.md:4. Worth knowing, since other work has been reasoning from the assumption that it was already enforced here.What this adds
scripts/check-kko-provenance.mjs—npm run check:kko, wired intots-cion every PR (cheap, no path filter, so a re-vendor can never land unverified). Three assertions:kko-2.10.n3still hashes to the SHA-256 the record declares, and is still the declared byte length. The record is the source of the expectation, so record and artifact cannot silently disagree.@ master— a moving reference: the same retrieval later can return different bytes while claiming this provenance. Now pinned to3f888b397255b69d1439fd95823e97011ed9440b, verified against upstream 2026-07-29. (masterhas not moved since 2019-04-09, which is exactly why the difference stays invisible until it isn't.)ts/src/kko-data.tsis exactly whatscripts/gen-kko.mjsreproduces from that.n3— so the ontology the engine actually ships derives from the file whose provenance we publish. A hand-edit, or a re-vendor without regeneration, otherwise leaves the runtime ontology diverged from its stated source with no signal. Same doctrine as this repo's existingts/diststaleness gate: a generated artifact must be reproducible from its source, or the source is not the source.Lane safety
ts/srcis not modified. Check 3 importsts/src/kko.tsto reuse the engine's own parser, regenerates into memory, and compares. Nothing is written.git statuson this branch touches only.github/workflows/ts-ci.yml,ontology/kko/PROVENANCE.md,package.json, and the new script.One consequence worth stating plainly for the active
ts/srclane: if a parser change alters whatgen-kko.mjswould emit, check 3 goes red untilkko-data.tsis regenerated and committed. That is the intended signal — it is the same contractcheck:distalready imposes — but it is a new red that lane could see.Teeth verified
Each check fails on a real tamper, then passes again on the clean tree:
:Monads→:Places— a length-preserving re-parent of a real class. Still valid Turtle, still parses, still exactly 327,797 bytesmasterkko-data.tsexit 0One trap worth calling out
The commit-pin check strips the sha256 from the document before searching for a 40-hex commit. A naive
/[0-9a-f]{40}/over the whole file matches a 40-char window inside the 64-char digest — I wrote it that way first, and it passed against aPROVENANCE.mdthat pinned no commit at all. The check was being satisfied by the very value it is meant to be independent of. A checker that validates itself validates nothing.Also noticed, deliberately not fixed here
package-lock.jsonis stale — it recordsversion: 0.4.2whilepackage.jsonsays0.4.45, andnpm installrewrites it. Reverted to keep this PR surgical and to avoid a conflict with the activets/srclane; worth its own change.allow_auto_merge=falseon this repo — do not merge, opened for review.