From 88fa07965011b01891d78c9d69333b1e3c92fde5 Mon Sep 17 00:00:00 2001 From: khive Date: Mon, 17 Aug 2026 09:16:44 -0400 Subject: [PATCH 1/7] docs: add AGENTS.md agent guide, with CLAUDE.md as a pointer to it --- AGENTS.md | 120 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ CLAUDE.md | 12 ++++++ 2 files changed, 132 insertions(+) create mode 100644 AGENTS.md create mode 100644 CLAUDE.md diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..b0559c5 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,120 @@ +# Agent guide + +Instructions for coding agents (and new human contributors) working in this repository. +[`CONTRIBUTING.md`](CONTRIBUTING.md) carries the full build and test detail; this file is the +short contract an agent must not break. If the two ever disagree, one of them is stale, and +the fix is a pull request that makes them agree, not a silent local interpretation. + +## What this repository is + +Two artifacts, joined by a file. A Python engine under `moodboard/` builds a reference board +from images, ranks candidates against it, and writes a closed JSON report. A TypeScript/React +viewer under `viewer/` renders that report as one self-contained offline HTML file. The report +is the only boundary between them ([ADR-0001](docs/adr/0001-engine-and-viewer-split.md)); the +viewer never recomputes a score and never fetches anything at runtime. + +## Rules that bind every change + +1. **A claim lands in the README only after the artifact that reproduces it lands in the + repository.** This is the project's standing rule. Do not write "supports", "validated", + or a number into any document without the test, fixture, or recorded run that produces it. + +2. **Recorded measurements are immutable** ([ADR-0009](docs/adr/0009-measurement-and-evaluation-contract.md)). + A published value is bound to the revision that produced it. If the code changes, produce a + new measurement under the new revision; never edit an existing one to match. + +3. **Keep the numbers meaning what they mean.** Conformal p-values are board-relative + exchangeability statements, not aesthetic quality and not an approval probability. + Retrieval cosine is a retrieval similarity in `[-1, 1]`, never a substitute for the + conformal score. Preference-replay outputs are model snapshots on frozen probes, not human + preference. Do not collapse these into one number, in code or in prose. + +4. **Abstention is a first-class output, not an error path** + ([ADR-0004](docs/adr/0004-abstention.md)). When the engine cannot score, it says so in the + report. Do not convert an abstention into a default value to make a test pass. + +5. **Fail-closed stays fail-closed.** The Khive/Lattice path rejects malformed, partial, + drifting, non-finite, wrongly dimensioned, or non-unit results. Report readers refuse + unknown schema versions before interpreting content. Resource ceilings (report file size, + ingest byte budgets, pixel bounds) reject rather than truncate. A change that turns any + refusal into a warning or a fallback needs its own decision record, not a code review nit. + +6. **Frozen policy is frozen.** `build` writes the complete scoring policy into the board + artifact; `rank` consumes the stored policy. Editing `eval/thresholds.json` must never + move an existing board's scores. Do not add a path that lets it. + +## Build and test + +Engine (offline, no network, no model weights): + +```bash +uv sync --locked +uv run pytest -q -rA +uv run ruff check . +``` + +Viewer: + +```bash +npm --prefix viewer ci +npm --prefix viewer run test:ci +``` + +CI runs the lint job and the full pytest suite on Python 3.11 and 3.13, rebuilds the viewer +from locked inputs, and fails if the committed `viewer/dist-static` is not byte-identical to +the fresh build. So: **if your change affects viewer output, run `npm --prefix viewer run +build` and commit the resulting `viewer/dist-static` changes in the same pull request.** + +Real-integration tests (`tests/test_khive_real.py`) are opt-in via the environment gates +documented in `CONTRIBUTING.md`. The ordinary suite uses a fake executable; never make an +ordinary test depend on a real `kkernel`, a model checkpoint, or the network. + +## Generated files: regenerate, never hand-edit + +| Path | Regenerate with | +|---|---| +| `viewer/src/generated/report-validators.mjs` (+ `.d.mts`) | `npm --prefix viewer run validators:write` | +| `viewer/src/generated/*-bridge.json` | the matching `*:write` script in `viewer/package.json` | +| `viewer/dist-static/` | `npm --prefix viewer run build` | +| `moodboard/viewer_dist/` | staged by the viewer build; never edited directly | + +Each generated artifact has a `--check` twin that CI or the build chain runs; a hand edit will +be detected and rejected, so save yourself the round trip. + +## Contracts and where they are decided + +- [`INTERFACES.md`](INTERFACES.md) — the shared signatures between `moodboard/` modules. + Changing a signature means changing this document first, in the same pull request. +- `moodboard/schema/report_v1_0.schema.json` and `report_v1_1.schema.json` — the report + contract ([ADR-0002](docs/adr/0002-report-contract.md), + [ADR-0008](docs/adr/0008-report-contract-for-viewer.md)). Dispatch is by exact + `schema_version` string. A field change is a version change. +- `moodboard/preference.py` — the preference feature artifact version. Changing any feature + formula requires a new version even if field names stay the same. +- `viewer/artifact-manifest.schema.json` — the viewer package manifest shape. + +## Decision records + +`docs/adr/` holds one file per decision. Records are immutable once accepted; a later change +gets its own record and marks the earlier one superseded. A record that makes a measurable +claim names the dataset that measures it, as a row in [`DATASETS.md`](DATASETS.md), with +source, licence, and the exact reproduction command. If your change contradicts an accepted +record, the change is a new record plus the code, not just the code. + +## Style + +- Python is linted by ruff with the configuration in `pyproject.toml` (line length 100). + There is no separate formatter; match the surrounding code. +- Comments in this codebase explain *why*, often at paragraph length, and many of them are + load-bearing records of a decision. Preserve them unless you are removing the code they + describe, and write in the same register when you add your own. +- Tests are deterministic. No sleeps, no wall-clock dependence, no network, no randomness + without a fixed seed. +- Commit subjects are lowercase and type-prefixed (`docs:`, `feat:`, `fix:`, `ci:`), matching + the existing history. + +## Pull requests + +Work on a branch, open a pull request, and let CI finish. `main` blocks force-push and branch +deletion for every actor. Keep pull-request text technical and about the product change; +the diff and the tests are the argument. diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..8833f01 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,12 @@ +# CLAUDE.md + +Read [`AGENTS.md`](AGENTS.md). It is the single agent guide for this repository, and it is +deliberately the only copy: duplicating its content here would create a second version that +drifts, so this file stays a pointer. + +The two facts most often needed before the first edit: + +- Verify with `uv run pytest -q -rA` and `uv run ruff check .`; the suite is offline and + deterministic by design, and CI runs exactly these. +- If a change affects viewer output, rebuild with `npm --prefix viewer run build` and commit + the resulting `viewer/dist-static` diff, or CI's byte-identity check will fail. From cd0def97a7a95f1f10a04d6750ea03c83b0e8734 Mon Sep 17 00:00:00 2001 From: khive Date: Mon, 17 Aug 2026 09:25:40 -0400 Subject: [PATCH 2/7] docs: defer AGENTS.md style section to docs/STYLE.md and its terse-comment rule --- AGENTS.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index b0559c5..74c022d 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -103,11 +103,16 @@ record, the change is a new record plus the code, not just the code. ## Style +The style authority is [`docs/STYLE.md`](docs/STYLE.md). The points agents get wrong most: + +- New comments and docstrings are terse: one-line docstrings for most functions, comments only + where the why is not visible in the code. Rationale longer than two sentences goes to a + decision record, `INTERFACES.md`, or the commit message, with a citation left in the source. +- Much of the existing code predates that rule and carries paragraph-length load-bearing + comments. Do not delete them on style grounds; migrate their substance to the right home + when you touch the code they describe. - Python is linted by ruff with the configuration in `pyproject.toml` (line length 100). There is no separate formatter; match the surrounding code. -- Comments in this codebase explain *why*, often at paragraph length, and many of them are - load-bearing records of a decision. Preserve them unless you are removing the code they - describe, and write in the same register when you add your own. - Tests are deterministic. No sleeps, no wall-clock dependence, no network, no randomness without a fixed seed. - Commit subjects are lowercase and type-prefixed (`docs:`, `feat:`, `fix:`, `ci:`), matching From faa154e6809385470ff7157048b9f26dee26040c Mon Sep 17 00:00:00 2001 From: khive Date: Mon, 17 Aug 2026 12:46:50 -0400 Subject: [PATCH 3/7] docs: make the generated-file table name each artifact's real generator The table pointed every bridge at a matching *:write script, but the Firefly bridge has no such script, and validators:write emits only the .mjs, not the adjacent declaration. Each row now names the command that actually reproduces its artifact. Co-Authored-By: Claude Fable 5 --- AGENTS.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 74c022d..c84740f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -73,8 +73,11 @@ ordinary test depend on a real `kkernel`, a model checkpoint, or the network. | Path | Regenerate with | |---|---| -| `viewer/src/generated/report-validators.mjs` (+ `.d.mts`) | `npm --prefix viewer run validators:write` | -| `viewer/src/generated/*-bridge.json` | the matching `*:write` script in `viewer/package.json` | +| `viewer/src/generated/report-validators.mjs` | `npm --prefix viewer run validators:write` | +| `viewer/src/generated/report-validators.d.mts` | edited alongside the generator; `validators:write` does not emit it | +| `viewer/src/generated/pixel-rag-bridge.json` | `npm --prefix viewer run pixel-rag:write` | +| `viewer/src/generated/preference-replay-bridge.json` | `npm --prefix viewer run preference-replay:write` | +| `viewer/src/generated/firefly-bridge.json` | `uv run python -m moodboard.firefly_viewer` (no npm write script; `firefly:check` verifies) | | `viewer/dist-static/` | `npm --prefix viewer run build` | | `moodboard/viewer_dist/` | staged by the viewer build; never edited directly | From eaa221f1ffde5daa307b70cb475eb51b32a72cca Mon Sep 17 00:00:00 2001 From: khive Date: Mon, 17 Aug 2026 12:51:54 -0400 Subject: [PATCH 4/7] docs: state exactly which generated artifacts the build gates The build chain checks the validators module and two of the three bridges; the pixel-rag bridge check exists but is wired to nothing, and the validators declaration has no drift check. Saying every artifact is check-gated invited hand edits in exactly the two unprotected spots. Co-Authored-By: Claude Fable 5 --- AGENTS.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index c84740f..ed918d0 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -81,8 +81,12 @@ ordinary test depend on a real `kkernel`, a model checkpoint, or the network. | `viewer/dist-static/` | `npm --prefix viewer run build` | | `moodboard/viewer_dist/` | staged by the viewer build; never edited directly | -Each generated artifact has a `--check` twin that CI or the build chain runs; a hand edit will -be detected and rejected, so save yourself the round trip. +The viewer build (`npm --prefix viewer run build`, which CI runs) verifies +`report-validators.mjs`, the Firefly bridge, and the preference-replay bridge through their +`*:check` scripts, and CI separately rejects any drift in `viewer/dist-static`. Two artifacts +are not check-gated today: `pixel-rag-bridge.json` has a `pixel-rag:check` script that nothing +invokes, and `report-validators.d.mts` has no drift check at all — hand edits there are +unprotected, so verify those two yourself. ## Contracts and where they are decided From 93e75d9e60871d3e7d5927d42cc9ccfc3431ea72 Mon Sep 17 00:00:00 2001 From: khive Date: Mon, 17 Aug 2026 12:57:31 -0400 Subject: [PATCH 5/7] docs: generated-file table carries each generator's complete invocation The write scripts take required arguments and the Firefly viewer module is check-only; the bare commands in the table exited at argument parsing without writing anything. Each row now shows the working invocation and points at the doc that explains it. Co-Authored-By: Claude Fable 5 --- AGENTS.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index ed918d0..f365f36 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -75,9 +75,9 @@ ordinary test depend on a real `kkernel`, a model checkpoint, or the network. |---|---| | `viewer/src/generated/report-validators.mjs` | `npm --prefix viewer run validators:write` | | `viewer/src/generated/report-validators.d.mts` | edited alongside the generator; `validators:write` does not emit it | -| `viewer/src/generated/pixel-rag-bridge.json` | `npm --prefix viewer run pixel-rag:write` | -| `viewer/src/generated/preference-replay-bridge.json` | `npm --prefix viewer run preference-replay:write` | -| `viewer/src/generated/firefly-bridge.json` | `uv run python -m moodboard.firefly_viewer` (no npm write script; `firefly:check` verifies) | +| `viewer/src/generated/pixel-rag-bridge.json` | `npm --prefix viewer run pixel-rag:write -- --input --manifest --write src/generated/pixel-rag-bridge.json` (full form: `docs/pixel-rag.md`) | +| `viewer/src/generated/preference-replay-bridge.json` | `npm --prefix viewer run preference-replay:write -- --input --features --write src/generated/preference-replay-bridge.json` (full form: `docs/demo-preference.md`) | +| `viewer/src/generated/firefly-bridge.json` | `uv run python eval/showcase_firefly_projection.py --write viewer/src/generated/firefly-bridge.json` (`firefly:check` verifies; `moodboard.firefly_viewer` is check-only) | | `viewer/dist-static/` | `npm --prefix viewer run build` | | `moodboard/viewer_dist/` | staged by the viewer build; never edited directly | From 9805470046ff708788b99d19486cb66309669366 Mon Sep 17 00:00:00 2001 From: khive Date: Mon, 17 Aug 2026 13:08:26 -0400 Subject: [PATCH 6/7] docs: state that the firefly bridge is a recorded-run projection, not regenerable at will The projection script reads .cache measured-run inputs absent from the repository and refuses to overwrite an existing bridge file, so the regeneration row now points at a fresh --write path and a note explains when regeneration is possible at all. Co-Authored-By: Claude Fable 5 --- AGENTS.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/AGENTS.md b/AGENTS.md index f365f36..ba0b971 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -77,7 +77,7 @@ ordinary test depend on a real `kkernel`, a model checkpoint, or the network. | `viewer/src/generated/report-validators.d.mts` | edited alongside the generator; `validators:write` does not emit it | | `viewer/src/generated/pixel-rag-bridge.json` | `npm --prefix viewer run pixel-rag:write -- --input --manifest --write src/generated/pixel-rag-bridge.json` (full form: `docs/pixel-rag.md`) | | `viewer/src/generated/preference-replay-bridge.json` | `npm --prefix viewer run preference-replay:write -- --input --features --write src/generated/preference-replay-bridge.json` (full form: `docs/demo-preference.md`) | -| `viewer/src/generated/firefly-bridge.json` | `uv run python eval/showcase_firefly_projection.py --write viewer/src/generated/firefly-bridge.json` (`firefly:check` verifies; `moodboard.firefly_viewer` is check-only) | +| `viewer/src/generated/firefly-bridge.json` | `uv run python eval/showcase_firefly_projection.py --write ` — needs recorded-run inputs; see note below | | `viewer/dist-static/` | `npm --prefix viewer run build` | | `moodboard/viewer_dist/` | staged by the viewer build; never edited directly | @@ -88,6 +88,12 @@ are not check-gated today: `pixel-rag-bridge.json` has a `pixel-rag:check` scrip invokes, and `report-validators.d.mts` has no drift check at all — hand edits there are unprotected, so verify those two yourself. +`firefly-bridge.json` is a projection of a recorded measured run: the script reads +`.cache/showcase-firefly-v1*` inputs that are not in the repository and refuses to overwrite +an existing bridge file. Regenerating it requires that cache plus a fresh `--write` path moved +into place. Without the cache, the committed copy is the recorded artifact — verify it with +`firefly:check`; do not delete it expecting to rebuild it. + ## Contracts and where they are decided - [`INTERFACES.md`](INTERFACES.md) — the shared signatures between `moodboard/` modules. From a7ca8980c857657322a5425be652c5ae5362fd9f Mon Sep 17 00:00:00 2001 From: khive Date: Mon, 17 Aug 2026 13:28:57 -0400 Subject: [PATCH 7/7] docs: name both recorded-run cache roots the firefly projection reads The script reads .cache/showcase-firefly-v1/ and .cache/showcase-firefly-khive-v1/evidence/; the previous wording matched only the first. Co-Authored-By: Claude Fable 5 --- AGENTS.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index ba0b971..341170a 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -88,10 +88,11 @@ are not check-gated today: `pixel-rag-bridge.json` has a `pixel-rag:check` scrip invokes, and `report-validators.d.mts` has no drift check at all — hand edits there are unprotected, so verify those two yourself. -`firefly-bridge.json` is a projection of a recorded measured run: the script reads -`.cache/showcase-firefly-v1*` inputs that are not in the repository and refuses to overwrite -an existing bridge file. Regenerating it requires that cache plus a fresh `--write` path moved -into place. Without the cache, the committed copy is the recorded artifact — verify it with +`firefly-bridge.json` is a projection of a recorded measured run: the script reads inputs +under `.cache/showcase-firefly-v1/` and `.cache/showcase-firefly-khive-v1/evidence/` (neither +is in the repository; a fresh checkout has no `.cache/`) and refuses to overwrite an existing +bridge file. Regenerating it requires both cache roots plus a fresh `--write` path moved into +place. Without them, the committed copy is the recorded artifact — verify it with `firefly:check`; do not delete it expecting to rebuild it. ## Contracts and where they are decided