Skip to content

Check curve, ablation and baseline completion in code - #11

Merged
drewOrc merged 3 commits into
mainfrom
feat/completion-checks
Sep 23, 2026
Merged

drewOrc merged 3 commits into
mainfrom
feat/completion-checks

Conversation

@drewOrc

@drewOrc drewOrc commented Sep 23, 2026 •

Copy link
Copy Markdown
Owner

Why

A shell loop once passed make a single argument, curve MODEL=bert. Make read it as a variable assignment, ran its default target (setup) and exited 0, so two curves were reported done with no point run. No result was affected, but exit 0 is not evidence that anything ran. This makes each index command prove its own completion.

What changes

New src/tinyrouter/completeness.py, used by curves.run_curve and baselines.run_all.

Command Before running (plan) After running (index read back from disk) Printed only when both pass
make curve MODEL=bert|modernbert exactly 6 k x 3 seeds of that model, OOS default, each once same 18 (k, seed), each once; every archive's SHA-256 equal on disk, in the manifest and in the index completed 18/18 encoder points (bert)
make oos-ablation ModernBERT, k=100, 0 OOS rows x 3 seeds, each once same 3 points, same SHA-256 check completed 3/3 ablation points
make baselines (and the first half of make curve) 2 baselines x 6 k x 3 seeds, each once same 36 (baseline, k, seed), same SHA-256 check completed 36/36 baseline points
  • The plan check runs before any training, fitting or data loading.
  • The index is written to <name>.json.tmp, read back and verified, and only then moved into place; on failure the temp file is removed, the previous index stays as it was, and the command raises (non-zero exit).
  • The expected sets are literals in completeness.py, not read from CURVE_KS / SEEDS, so a wrong loop cannot agree with its own check. A test pins that the two agree today.
  • make baselines no longer prints wrote N baseline runs; the completion line replaces it.

No change to docs/PLAN.md, the acceptance criteria, hyperparameters or any result file. No training or paid API call was run.

Tests

27 new tests in tests/test_completeness.py: for the curve, the ablation and the baselines, a missing point and a duplicated point at the plan check and at the index check (12), plus the completion message and its ordering, archive changed / manifest disagrees / archive deleted, wrong model, OOS rows in the ablation, and a failed rerun leaving the old index intact. Three existing tests in tests/test_curves.py ran a 3-point subset of a curve; they now run the full curve and filter to k=100.

Mutation check (first commit): each of 13 mutations (dropping each plan check, each index check, the manifest comparison, the missing-file check, duplicate / missing / unexpected detection, verifying after the move instead of before, printing the message before publishing) turned at least one test red; all restored.

Review follow-up (second commit)

  • No default target. The entry point of the incident still exited 0: make -n "curve MODEL=bert" expanded setup. The Makefile now stops with $(error ...) when MAKECMDGOALS is empty, before any target. Chosen over .DEFAULT_GOAL := help with a failing help target because it fails at parse time for every no-goal call, including make -n, and its message names the actual mistake. README and CI only ever call named targets. tests/test_makefile.py (dry runs only, inherited MAKEFLAGS dropped): the quoted form, a bare make and make MODEL=bert exit non-zero without reaching setup; make -n curve MODEL=bert and make -n setup still exit 0. Make versions differ on the quoted form: GNU make 3.81 (macOS, where the incident happened) reads it as an assignment and now hits the guard; make 4.x (CI) reads it as an unknown target and fails with No rule to make target. The quoted-form test accepts either refusal, and the guard itself is pinned by the bare and assignment-only calls, which every version treats the same.
  • Read-back is pinned. A test makes the write of the temporary index lose a point while the body in memory stays whole; the check must fail.
  • Shared archives. check_archives refuses two points listing the same archive file or SHA-256, so the baseline index gets the check the curve index already had.
  • Docs. Makefile comments and README: decide completion by matching the whole line completed 18/18 encoder points (<model>), since make curve prints the baselines' completed 36/36 baseline points before the curve runs.

Mutation check for these: removing the guard, making it fire on every call, verifying the in-memory body instead of the file, and dropping the shared-archive check each turned at least one test red; all restored.

make lint, make test (348 passed, 1 skipped, 7 network deselected) and make smoke pass locally.

A shell loop once passed `make` one argument, `curve MODEL=bert`; make
read it as a variable assignment, ran its default target and exited 0,
so two curves were reported done with no point run. Exit 0 is not
evidence of completion, so each index command now checks its own work.

- Before running: the plan must be exactly the expected points, each
  once (curve: 6 k x 3 seeds; ablation: ModernBERT, k=100, 0 OOS rows x
  3 seeds; baselines: 2 x 6 x 3). Otherwise it raises before any
  training, fitting or data loading.
- After running: the index is written to a temporary file, read back,
  and must hold the same points, each once, with every archive's SHA-256
  equal on disk, in the manifest and in the index. Only then is it moved
  into place and `completed 18/18 encoder points (bert)`,
  `completed 3/3 ablation points` or `completed 36/36 baseline points`
  printed. Any failure raises, exits non-zero and leaves the previous
  index untouched.

The expected sets are literals in completeness.py rather than read from
CURVE_KS / SEEDS, so a wrong loop cannot agree with its own check; a test
pins that the two agree today. Three curve tests that ran a subset of a
curve now run the full curve and filter to k=100.
… archives

A quoted `make "curve MODEL=bert"` is one argument that make reads as a
variable assignment; with no goal it ran the first target (setup) and
exited 0. The Makefile now stops with an error when no target is given,
so the 2026-09-23 failure is non-zero at its entry point, not only
missing a completion line. Nothing in the README or CI relied on a bare
make running setup. tests/test_makefile.py pins both the refused quoted
form and the working split form (`make -n` only, nothing runs).

Also:
- A test that the check reads the temporary index file from disk, not
  the body in memory: a write that loses a point must fail.
- check_archives refuses two points that list the same archive file or
  SHA-256, which the baseline index did not check before.
- Makefile comments and README say to match the whole completion line
  (`completed 18/18 encoder points (<model>)`), since `make curve`
  prints the baselines' `completed 36/36` line before the curve runs.
GNU make 4.x (the CI runner) reads the quoted "curve MODEL=bert" as a
target name and stops with "No rule to make target"; make 3.81 (macOS)
reads it as an assignment and now stops at the guard. The quoted-form
test accepts either refusal; the guard itself is pinned by a bare
`make` and by `make MODEL=bert`, which every version treats as an
assignment with no goal. The dry runs drop inherited MAKEFLAGS and
MAKELEVEL so they behave the same under `make test`.
@drewOrc
drewOrc merged commit d8c96bd into main Sep 23, 2026
4 of 5 checks passed
@drewOrc
drewOrc deleted the feat/completion-checks branch September 23, 2026 10:25
drewOrc added a commit that referenced this pull request Sep 23, 2026
Two baselines x six k x three seeds, sharing curve_sample(k, seed) with
the encoders. Re-verified with the completion checks from #11: completed
36/36 baseline points, 39 archives match the manifest. Archives were not
recomputed. Logits files will be attached to a release.
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