Skip to content

feat(artifact): model-level multi-run artifacts, append and merge#10

Merged
dutifulbob merged 6 commits into
mainfrom
feat/model-level-artifacts
Jul 2, 2026
Merged

feat(artifact): model-level multi-run artifacts, append and merge#10
dutifulbob merged 6 commits into
mainfrom
feat/model-level-artifacts

Conversation

@dutifulbob

Copy link
Copy Markdown
Member

Opened on behalf of Onur Solmaz (osolmaz).

Summary

The docs promised model-level artifacts (all runs for one model in runs/models/<model>.sqlite with one HTML report), but the writer deleted any existing file, the checker required exactly one run, and the report read a single run.
This change wires that promise end to end: runs append into a shared artifact, a new merge command combines existing ones, and the report renders every run with cross-run aggregation.
It implements phase 5 of docs/2026-07-02-reporting-completeness-plan.md.

What Changed

The blocker was that dimension primary keys were bare names (profile "8k" from two runs collides), so appending was structurally impossible.

  • Dimension ids are namespaced per run (<run_id>/<name>) in the artifact writer; name columns stay bare for display. Not a schema change.
  • artifact.CreateOrAppend opens an existing artifact for another run instead of deleting it; re-writing the same run directory replaces that run via cascade delete. bench run --artifact points batches at the model-level path.
  • artifact check accepts one or more runs, each required to carry exactly one original and one normalized spec.
  • New localperf artifact merge --into <dst> <src>...: text ids rescope with a prefix-if-missing rule (old single-run and new namespaced sources both merge), integer ids shift by the destination's max per referenced table, duplicate runs are skipped so merges are idempotent, and the destination is checked after every merge.
  • The report loads all runs (latest as header, Runs section lists every run with status and hardware), repeated points across runs aggregate with mean ± spread exactly like repeats, and per-repeat rows carry their run id.
  • Docs updated: sweep doc shows the --artifact and merge workflows, the artifact format doc documents multi-run validation and id conventions, and the plan doc gains the phase 5 section.

Testing

I ran the full gates plus a CLI-level acceptance of the exact documented workflow.

  • go test ./..., go vet, gofmt, simpledoc, slophammer check/dry/crap — all green.
  • New tests: two dry runs appending into one artifact (2 run rows, check passes, replace-on-rerun), merge of two single-run artifacts (4 measurements, namespaced profiles, duplicate-merge skipped), multi-run report (2 runs load, cross-run point aggregates with repeat count 2, per-repeat rows keep run provenance).
  • CLI acceptance: two bench run --dry-run --artifact batches appended plus one artifact merge of a separate single-run artifact produced a 3-run model artifact that passes artifact check and renders one HTML report listing all three batches.
  • Not tested: merging very old artifacts predating the claims work (cutover policy says re-run), and a live multi-batch vLLM sweep.

Risks

Append semantics change the writer's contract: the artifact at --artifact is no longer replaced wholesale.
Re-running the same run directory intentionally replaces that run's rows, which is the retry behavior the sweep doc asks for.

  • Merge validates both source and destination before and after copying, and runs inside one transaction per source, so a failed merge leaves the destination unchanged.
  • Reports over multi-run artifacts aggregate same-named points across runs by design; distinct experiments belong in distinct artifacts per the sweep doc.

🤖 Generated with Claude Code

Namespace dimension primary keys per run (run_id/name) so multiple runs
coexist in one model-level SQLite file. Writers append to an existing
artifact instead of deleting it, and re-writing the same run directory
replaces that run. bench run gains --artifact for pointing batches at
runs/models/<model>.sqlite. artifact check accepts one or more runs,
each with exactly one original and one normalized spec. New localperf
artifact merge --into combines artifacts: text ids rescope
(prefix-if-missing handles old single-run sources), integer ids shift
by the destination's max per referenced table, duplicate runs are
skipped, and the destination is checked after every merge. The report
loads every run (latest as header, all in a Runs section), aggregates
repeated points across runs like repeats, and keeps run provenance on
per-repeat rows. Implements phase 5 of the reporting completeness
plan.
profiles.engine_id now stores the namespaced engine id; keying the
served-model map by bare engine name silently disabled the
declared-vs-served mismatch check for newly written artifacts. Key by
engine id, which matches in both old and namespaced artifacts, and
cover the namespaced path in the multi-run report test.
Appending refuses to replace a run recorded from a different run
directory: run ids are directory basenames, and a basename collision is
not a retry. The run row records its absolute run directory in
labels_json for the comparison. Merge validates every source before
touching the destination and removes a freshly created destination on
failure, so a bad source cannot strand an empty artifact.
…ites

Merge classifies same-id runs by provenance (created_at plus recorded
run directory): matching provenance is an idempotent skip, differing
provenance is a refused collision instead of a silent drop. The writer
removes a freshly created artifact when its first write fails so
retries do not hit a stranded schema-only file.
Runs written before the provenance label carry no run_dir; re-running
the same run id over them keeps the old replace behavior instead of
refusing as a collision. Remove the now-unused insertMetadata helper
that failed lint.
No legacy allowance: a same-id run without a recorded run directory has
unknown provenance and is refused, not silently replaced. Pre-cutover
artifacts are out of scope per the plan; re-run them under the new
format.
@dutifulbob

Copy link
Copy Markdown
Member Author

Final report: phase 5 implemented with strict cutover. Codex review ran 5 rounds; 7 findings fixed (engine-id keying for model mismatch, merge provenance collisions, failed-write and failed-merge cleanup, basename collision guard). The last finding (allow replacing legacy runs without a run_dir label) is deliberately rejected: cutover policy, no backward compatibility — unknown-provenance runs are refused and pre-cutover artifacts get re-run. Gates green locally (test/vet/gofmt/dry/crap/golangci-lint/simpledoc) and CI green. Merging.

@dutifulbob dutifulbob merged commit 331e357 into main Jul 2, 2026
2 checks passed
@dutifulbob dutifulbob deleted the feat/model-level-artifacts branch July 2, 2026 15:29
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.

2 participants