Skip to content

Keep the grand catalogs current as new hosts and runtimes record evidence - #130

Merged
seathatflowsinourveins merged 21 commits into
mainfrom
claude/evidence-loop-runtimes-20260923
Sep 23, 2026
Merged

seathatflowsinourveins merged 21 commits into
mainfrom
claude/evidence-loop-runtimes-20260923

Conversation

@seathatflowsinourveins

Copy link
Copy Markdown
Collaborator

This makes one documented loop take a new PC's or runtime's evidence all the way into the grand catalogs, and closes the gaps that a dry run of that loop found.

  • hardware_profile.py --record-host <name-yyyymmdd>: writes the measured profile to evidence/artifacts/hw-profiles/<id>/profile.json, registers it, and adds or updates a native_proven entry in adoption/hardware-profiles.json, keeping the entry's existing fields. It validates all of hosts[] before writing anything. It sanitizes only decoded string values, never keys or other entries. It refuses an id that contains the local user name. A hosts[] schema check is added.
  • qualified_models in host receipts (optional; schema, host_receipts.py record --qualified-model, validate): records which model weights passed which bars on which runtime and host, for example vLLM/CUDA or MLX. Each entry is validated against the subschema before any command runs. It is surfaced per platform by component_matrix.py and in a new grand-list section. It never changes platform_status: the flip rule still governs.
  • scripts/verdict_flip_candidates.py: read-only and report-only. It uses platform_status.py to list verdict rows that new receipts would qualify for a stronger status. A flip still needs a record_verdicts.py re-record. It is wired into validate.yml with pipefail.
  • CI wiring: gap_crosswalk.py build --check and gap_wave_ledger.py --check. The step names say they prove fixed-revision reproduction, not drift against current gaps.
  • new_host_grand_list.py: fails on a missing native_proven evidence file, and carries measured fields, including those from --record-host reports.
  • Docs:

Ownership: the hardware-profile and flip-candidate items were agreed with their owner. record_verdicts.py, landscape.py, lane_packets.py and build_verdicts.py are untouched. The session that owned host receipts and the crosswalk has ended.

Review: an independent Claude review ran two rounds and a Codex cross-family review one round. All findings are fixed. They were:

  • whole-document sanitization corrupting keys and ids;
  • the CI step swallowing failures;
  • measurements not reaching the grand list;
  • qualified-model input validated only after commands ran;
  • evidence written before validation;
  • wording.

A dry run in a disposable clone recorded a synthetic receipt with a qualified model and ran --record-host on a real host; both appeared in the regenerated grand list.

Checks: every validate.yml step passes locally. Full python3 -m unittest: 3136 tests, with 9 failures, all in tests/test_adoption_bootstrap_macos.py signal-timing tests. The same module also fails on unmodified main under this host's load (3 failures in isolated runs on both main and this branch), and this PR touches neither that module nor bootstrap-macos.sh. Guarded Gitleaks: no leaks in the 20 changed files.

🤖 Generated with Claude Code

adoption/bootstrap.md:16-17 pinned a literal commit (bdd04ca..., the
v2026.09.22.1 tag) next to v2026.09.23's release_tag in prose. Point at
adoption/manifest.json's own source.release_commit instead of a copy that
the next re-pin would leave stale.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Two related gaps in the new-host evidence loop:

- scripts/new_host_grand_list.py silently gave null measured_tiers when a
  native_proven hosts[] entry named a .json evidence file that did not
  exist. It now fails (exit 1) instead, and carries each host's own
  `measured` block (and nested fields such as `mlx_smoke`) through to the
  generated JSON and a compact Markdown column. A native_proven entry whose
  evidence is prose (a hosted-CI-run citation, not a report path) is not
  held to the file-exists check.

- There was no structured place to record which local model weights were
  qualified on which runtime and host. adoption/host-receipt.schema.json
  gains an optional qualified_models[] array (model_id, revision, runtime,
  runtime_version, bars, result); scripts/host_receipts.py record supports
  --qualified-model (repeatable JSON) and --qualified-models-file, and
  carries the field through build_summary(); scripts/component_matrix.py
  surfaces it per platform on the runtime component's winner row; and
  scripts/new_host_grand_list.py adds a "Qualified local models" section.
  This is informational only: it never marks anything accepted by itself,
  and the platform_status flip rule (scripts/platform_status.py) still
  governs.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
adoption/hardware-profiles.json hosts[] had no script writer and no
structural check; a measured host could only be added by hand. Add
scripts/hardware_profile.py --record-host <host-id>:

- writes the measured report to
  evidence/artifacts/hw-profiles/<host-id>/profile.json and registers it in
  manifests/evidence.json (the same host_receipts.register_file helper
  record/review use);
- adds or updates the hosts[] entry (evidence_class native_proven,
  evidence pointing at that file), keeping the entry's other fields across
  a re-record;
- enforces the <name>-<yyyymmdd> id convention host_receipts.py already
  uses for measured hosts (a labelled_projection host id still needs to end
  "-projected");
- sanitizes the report (scripts/host_receipts.py's sanitize(), which also
  drives the committed this-host.json's redaction) and refuses to write if
  it still matches PRIVATE_CONTENT afterward.

validate_hosts() is the new hosts[] schema check (required keys, a known
evidence_class, no duplicate id, the id conventions above, and --
when a root is given -- that a measured entry's evidence file actually
exists); it runs in tests/test_hardware_profile.py against both synthetic
fixtures and the real shipped hosts[].

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
…nder

Landscape winners[].platform_status only moves through a
record_verdicts.py re-record; nothing previously surfaced when recorded
evidence already supports more than a row declares. The macOS flip rule
(scripts/component_matrix.py's flip-rule check, via
scripts/landscape.ENFORCED_PLATFORMS) only rejects a declaration that
claims *too much*, only for macos-arm64, and only fails CI -- it never
lists what could now claim more, and never covers linux-wsl2-x86_64.

scripts/verdict_flip_candidates.py calls scripts/platform_status.py's
platform_status() -- the one function record_verdicts.py, landscape.py and
component_matrix.py all use -- for every winner and platform in
catalogs/landscape/{foundation,us-equities}.json, and lists rows whose
derived status outranks the declared one, with the qualifying receipts.
Read-only and report-only: it selects nothing, records nothing, and exits 0
whenever the input parses, regardless of what it finds; only malformed
input is an error.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Three gaps confirmed against the real validate.yml before wiring (per the
dry-run findings): the macOS-only flip rule never reported a flip
candidate; tools/sota-convergence/gap_crosswalk.py build --check and
gap_wave_ledger.py --check existed and were tested but never ran in CI, so
a stale crosswalk or wave ledger could pass silently.

- scripts/verdict_flip_candidates.py runs as a report-only step (piped to
  the job summary); it always exits 0 on well-formed input, so it cannot
  fail the build.
- `gap_crosswalk.py build --check` passes on main as it stands; wired as an
  ordinary blocking check like the other --check steps.
- `gap_wave_ledger.py --check` needs required --wave/--owner flags (the
  task's literal invocation has none); the combination that reproduces the
  checked-in combined ledger
  (gap-wave2-20260923+gap-wave3-20260923--gap-resolution.json) --
  `--wave gap-wave2-20260923 --wave gap-wave3-20260923 --owner
  gap-resolution` -- passes on main and is wired as that exact command.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
docs/contributing-evidence.md scattered the new-host steps (bootstrap,
measure, record, refresh derived views, validate, how a flip happens)
across several sections with no single ordered list. Add "0. The new-host
loop, in one place" naming, in order: bootstrap.md; hardware_profile.py
--record-host; host_receipts.py record (--qualified-model for local model
qualifications); component_matrix.py --write then new_host_grand_list.py
--write; verdict_flip_candidates.py; the validate commands plus
`python3 -m unittest` (this repository uses unittest, not pytest, which
is not installed); and that a platform_status flip is a separate step,
done by record_verdicts.py's owner through its own lane process.

docs/next-host-stages.md gets a pointer to that section, and its two
per-host step lists are updated to name --record-host and --qualified-model
instead of the older by-hand "add the measured entry" instruction.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
python3 scripts/component_matrix.py --write, then
python3 scripts/new_host_grand_list.py --write, then
python3 scripts/evidence_manifest.py --write, plus re-registering every
source file this branch touched that manifests/evidence.json already
tracked a hash for (host_receipts.register_file, the same helper --write
uses for its own outputs). flip_rule_violations stayed 0 throughout.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
… Windows-side evidence

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
…re writing

Two review findings (human + Codex, both against 48471ea) on
scripts/hardware_profile.py's cmd_record_host:

- It ran host_receipts.sanitize() over the whole serialized
  hardware-profiles.json document. sanitize() replaces every occurrence of
  $USER in the text it is given, and $USER is often a short, common word
  ("mac", "runner", "ram", "max", ...) that legitimately appears inside
  unrelated pre-existing content: an id like
  "github-macos-15-arm64-runner", or -- more seriously -- a config key like
  "max_fraction_of_ram" in the same document's own
  layers.ecosystem_bounded_run, which a whole-document sanitize renamed,
  making the *next* recommend() call raise KeyError. Now only the new/
  updated hosts[] entry's own serialized text is sanitized (and the fresh
  measured report, which has no such shared keys to corrupt); the whole
  document is only regex-scanned (never mutated) for a final defensive
  check.
- The evidence file was written and registered before validate_hosts() ran.
  Validation (over the whole hosts[] array, so a pre-existing malformed
  entry elsewhere also blocks the write) now runs first, with root=None
  (skipping the evidence-file-exists check, which cannot pass before that
  file is written) -- so a failure leaves no evidence file, no manifest
  registration and no changed hardware-profiles.json behind.

Tests cover USER in {runner, mac, ram, max} leaving every existing hosts[]
entry and the whole layers config byte-identical, a subsequent recommend()
call not raising, and a pre-existing bad entry blocking the write with
nothing written.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Codex review (against 48471ea): scripts/new_host_grand_list.py's
build_hosts() only took a host's `measured` block from the hosts[] entry
itself. scripts/hardware_profile.py --record-host never puts `measured` on
the entry (only `evidence`, a path to the report that holds it), so a
freshly recorded host's real measurements never reached the grand list --
`measured` stayed null even though the evidence file had them. Read
`measured` from the evidence file too when the entry has none of its own
(an entry's own inline `measured`, as the shipped
github-macos-15-arm64-runner entry has it, still wins). Tests cover the
fallback, the entry-wins-when-both-present case, and a full
record-then-build-hosts round trip.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
…ommands

Codex review (against 48471ea): the CLI's own pre-execution check for
--qualified-model only looked at required keys and whether result was in
{pass, fail} via `not in` against a set, which raises an uncaught TypeError
for an unhashable value such as `result: []`. It also never checked
anything else the schema constrains (for example `bars`'s maxLength 400),
so an over-length bars value passed this check, ran the qualification
command(s), and was rejected only afterward by the pre-write
validate_receipt_shape() call -- after the side effect. Replace the
hand-rolled subset check with the same schema-driven validate_against_schema()
this module already uses everywhere else, run over the qualified_models[]
item subschema before any command executes; its equality-based enum check
never raises on a non-hashable value. Tests cover both failure modes,
asserting exit 2 (no traceback) and that no command ran.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Two P2/P3 findings against the CI wiring added earlier this branch:

- The verdict_flip_candidates.py | tee step had no `shell: bash`, so
  without pipefail a malformed-input exit 1 from the reporter would be
  swallowed by tee's own exit 0. Add `shell: bash`, matching the
  release_due step's existing pattern.
- "Check the gap crosswalk is current" and "...wave ledger is current"
  overstated what these --check steps prove: gap_crosswalk.py's build
  reads catalogs/landscape/*.json at a fixed historical commit
  (CURRENT_REV, 92bb279), and the wave ledger reads the checked-in
  crosswalk file, not the current catalogs. Both steps only prove the
  checked-in file still reproduces from its own fixed inputs; neither can
  detect that a layer re-recorded since CURRENT_REV made the crosswalk
  stale. Renamed to say so.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
P3 finding: the reproducibility of gap_crosswalk.py build --check and
gap_wave_ledger.py --check from their own fixed inputs (CURRENT_REV
92bb279 and the checked-in crosswalk, respectively) is not the same as
detecting that either has gone stale against the *current*
catalogs/landscape/*.json open_gaps. Section 4 now says so explicitly, so
a reader does not read the CI wiring as more than it is.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
…ection

P3 finding: "until python3 scripts/hardware_profile.py runs on each host
and replaces them" implied the labelled-projection hosts[] entry
disappears once a host is measured. --record-host adds a separate, dated
native_proven entry alongside it; the projection entry is neither removed
nor overwritten. Reworded to say to prefer the measured entry once one
exists, not to expect the projection to vanish.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
…n't make

P3 finding: 48471ea's decision block set memory=112GB but the evidence
string still said "the two records need reconciling by their owner; this
manifest does not choose the value" -- stale now that a decision exists.
Reworded to state the decision and point at the hardware-profiles.json
owner's own record of it (catalog PR #125) without this manifest claiming
to have updated that entry itself.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
python3 scripts/component_matrix.py --write, then
python3 scripts/new_host_grand_list.py --write, then
python3 scripts/evidence_manifest.py --write, plus re-registering
evidence/artifacts/host-upgrade-20260923/upgrade-evidence.json (the only
source file this round's fixes touched that manifests/evidence.json
already tracked a hash for). component-evidence-matrix.json was already
byte-identical (no new receipts this round). flip_rule_violations stayed 0.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Discovered while regenerating after rebasing onto catalog PR #125:
adoption/hardware-profiles.json's wsl-workstation-128gb-projected
workflow_concurrency_cap is now "min(16, 60 - 2) = 16 (nproc inside WSL is
the .wslconfig processors=60)". tier_value()'s rsplit("=", 1) took the
*last* '=' unconditionally, which lands inside that trailing explanatory
note ("processors=60)"), rendering the grand list's concurrency-cap column
as "60)" instead of "16". Strip one trailing, balanced parenthetical note
first (matching parens from the end, so it only touches a string that
actually ends in ")"); a string with several '=' but no trailing note
(the existing ecosystem_job_memory_max_gb style) still takes the result
after the last '=', unchanged.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Round-2 review follow-up (P2): the previous fix still ran
host_receipts.sanitize() over the whole serialized report and entry text.
sanitize() replaces every occurrence of $USER in the text it is given,
which still corrupted the *keys* of the new report/entry themselves: with
USER=ram, "effective_ram_gb" and "ram_gb_proc_meminfo" became
"effective_<user>_gb"/"<user>_gb_proc_meminfo"; with USER=gpu or
USER=cores, the bare "gpu"/"cores" keys became "<user>"; a preserved field
kept from a re-recorded entry (for example "ecosystem_job_memory_max_gb")
was equally at risk under USER=max. The grand list then silently dropped
RAM, GPU or cores for that host.

Add sanitize_values(), which walks the decoded report/entry the way
host_receipts.iter_receipt_strings() walks a receipt for its read-only
privacy scan, but only sanitizes each dict *value*/list *item* that is a
string -- never a dict *key* -- so JSON structure survives regardless of
$USER. Use it for both the report and the entry (including whatever an
existing entry's preserved fields are) in place of the prior
serialize-then-sanitize-the-whole-text approach.

Tests cover USER in {ram, max, gpu, cores, runner, mac}: the written
profile.json's and entry's own keys stay unchanged, and a key preserved
from a prior entry across a re-record survives too.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Round-2 review follow-up (P3): text ending in "(...)" with no separate
trailing note -- e.g. "cap = min(16, 30)" -- was treated the same as a
genuine explanatory note and stripped, returning "min" instead of
"min(16, 30)". Only drop a trailing "(...)" when it is set off by a space
(a real note, like "16 (nproc inside WSL is ...)"); one glued directly
onto a preceding token with no space (a function call, part of the result
expression itself) now stays. The two compose correctly: a genuine
trailing note after a parenthesized result is still dropped, leaving the
parenthesized result intact.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
…lear reason

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 23, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-09-23T17:26:22.417794Z add56ea PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@seathatflowsinourveins
seathatflowsinourveins merged commit 95f8cb7 into main Sep 23, 2026
23 checks passed
@seathatflowsinourveins
seathatflowsinourveins deleted the claude/evidence-loop-runtimes-20260923 branch September 23, 2026 17:13
seathatflowsinourveins added a commit that referenced this pull request Sep 23, 2026
First real use of the qualified_models field (#130): a native_proven host receipt for the vllm
component on linux-wsl2-x86_64 (host rtx5090-laptop-20260923, hardware profile this-host-20260923).
Live checks on the resident service: health 200, 2048-dimension embeddings, max-model-len 8192.
Qualified models: Nemotron-3-Embed-1B-BF16 on vLLM 0.25.0 (the one local embedder for code and
memory retrieval; hybrid nDCG@10 +0.078) and Qwen3-8B-AWQ on vLLM 0.29.0 (20/20 tool calls, 4/5
tasks). Regenerated the component matrix and new-host grand list; no platform status changes.

Co-authored-by: seathatflowsinourveins <[email protected]>
Co-authored-by: Claude Opus 5.5 (1M context) <[email protected]>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: add56ea2bf

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread scripts/host_receipts.py
Comment on lines +593 to +597
qualified_model_schema = (load_receipt_schema(root).get("properties", {})
.get("qualified_models", {}).get("items", {}))
for index, entry in enumerate(qualified_models):
entry_errors: list[str] = []
validate_against_schema(entry, qualified_model_schema, f"qualified_models[{index}]", entry_errors)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Bind model qualifications to their receipt runtime

The schema-only validation accepts a qualification whose runtime/runtime_version is unrelated to the receipt's component_id and tool_versions (for example, a vllm receipt can declare mlx-lm, or an obsolete vLLM version). platform_qualified_models() then places every such entry under the receipt component's current winner row, so the generated matrix and grand list misattribute the qualification. Validate the runtime identity and version against the receipt component before recording, with any supported aliases made explicit.

AGENTS.md reference: AGENTS.md:L17-L20

Useful? React with 👍 / 👎.

Comment on lines +503 to +505
evidence_path.write_text(report_text, encoding="utf-8")
host_receipts.register_file(root, relative_evidence)
profiles_path.write_text(profiles_text, encoding="utf-8")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Make host-profile recording atomic

If manifest registration fails after the report write—for example because manifests/evidence.json is malformed or unwritable—the command raises after leaving an unregistered evidence file behind. If the subsequent profiles write fails, the manifest instead points at evidence that has no corresponding host entry; re-recording can also overwrite a previously valid report before either failure. Stage these writes and roll them back together, as the receipt recorder already does around registration.

Useful? React with 👍 / 👎.

Comment on lines +199 to +203
for qm in entry.get("qualified_models") or []:
out.append({
"model_id": qm.get("model_id"), "revision": qm.get("revision"), "runtime": qm.get("runtime"),
"runtime_version": qm.get("runtime_version"), "bars": qm.get("bars"), "result": qm.get("result"),
"host_id": entry.get("host_id"), "receipt_path": entry.get("path"),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve receipt evidence class in qualified-model rows

This projection copies qualifications from every shape-valid receipt but drops the receipt's evidence_class; consequently a synthetic or local_integration receipt is rendered in the matrix and grand list exactly like a native_proven model run. The added tests even construct these rows from a synthetic receipt. Either restrict this list to native executions or carry and display the evidence class so fixture-backed results are not promoted as indistinguishable locally run qualifications.

AGENTS.md reference: AGENTS.md:L10-L15

Useful? React with 👍 / 👎.

Comment on lines +184 to +187
evidence_doc = json.loads(path.read_text(encoding="utf-8"))
tiers = evidence_doc.get("recommended")
if measured is None:
measured = evidence_doc.get("measured")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Prefer the newly recorded measurement when rebuilding hosts

When a dated host entry already has an inline measured block, --record-host preserves that field while writing fresh measurements to the JSON evidence file, and this condition then keeps the old inline block instead of the new report's block. A re-measurement after hardware or configuration changes therefore emits fresh tiers alongside stale CPU/RAM details in the grand list. For JSON-backed measured hosts, use the evidence report as authoritative or update the inline block during recording.

AGENTS.md reference: AGENTS.md:L17-L20

Useful? React with 👍 / 👎.

Comment on lines +394 to +396
elif evidence_class == "native_proven":
evidence = host.get("evidence")
if isinstance(evidence, str) and evidence.endswith(".json"):

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Require evidence for native-proven host entries

A native_proven host with no evidence key, an empty value, or a non-string value passes this validator because checks only begin when the value is a string ending in .json. The real-catalog test therefore also accepts such an entry, and the grand-list builder publishes it as native-proven with no supporting measurement or citation. Require every native-proven host to carry a nonempty evidence reference before applying the JSON-file-specific checks.

AGENTS.md reference: AGENTS.md:L10-L15

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant