Summary
v0.43.0 shipped #1056, which made file resolution independent of the process working directory. Its changelog entry names the migration exactly: "consumers using root-relative paths from subdirectory files need an eigs.json at their project root."
Nobody ran that migration on the fleet. Three sibling repos have had unrunnable test suites since v0.43.0, and nothing anywhere reported it.
Measured, 2026-09-08, with the v0.43.0 release binary
| repo |
manifest |
affected files |
what broke |
| EigenMiniSat |
none |
6 |
tests/test_solver.eigs, tests/test_tseitin_invariant.eigs, lib/bench.eigs, all three benchmarks |
| Tidepool |
none |
5 |
every entry point, because game.eigs is loaded by all of them |
| eddy |
none |
3 |
src/dst.eigs, src/rw_dst.eigs, test/test_explorer_ui.eigs |
$ cd EigenMiniSat && eigenscript tests/test_solver.eigs
Error line 6: load_file: cannot read 'lib/dimacs.eigs' (not found or unreadable);
tried containing directory '/home/user/EigenMiniSat/tests', eigs_modules walk,
no eigs.json above /home/user/EigenMiniSat/tests; ...
The diagnostic is excellent. It names every root it tried and the fix is one file. That is not the problem.
Fixed in each repo by adding eigs.json, which is the documented migration and what ouroboros did at its own pin bump. Verified after: EigenMiniSat reports "All EigenMiniSat solver tests passed", Tidepool "All regressions passed", eddy's script runs to completion.
Why nobody noticed, and why that is the actual finding
EigenMiniSat's root-level minisat.eigs was unaffected. A smoke test of the solver itself passed the whole time. What broke was the correctness suite and the entire benchmark surface — the parts that only run when someone is measuring something.
The consumer repos are pinned to older releases, which is deliberate and correct: they are post-release coverage, not a pre-merge gate, and a pin is what keeps them stable. But a pinned repo does not exercise the new release, so the breakage is invisible until someone bumps the pin, and the bump is exactly when a green suite is assumed rather than checked.
So the forcing-function model has a hole where its own design puts one. Consumers find gaps by real use, and a consumer that cannot start is not using anything. The property that makes them useful is the property that hid this.
I found it by accident, while setting up an unrelated benchmark. Time-to-discovery for that path is unbounded; this instance ran a full release cycle.
Direction
The cheap version is not a suite. It is a start-check: for each sibling repo present on the box, run its declared entry point against a candidate binary and assert it gets past module resolution. Seconds per repo, and it would have caught all three.
Where it has to live is the part worth deciding carefully:
- It cannot be an EigenScript CI gate. CI has no sibling repos, and vendoring them is the wrong trade.
- It fits the release path, which already has a doc-drift gate and a tag/dispatch checklist. A pin bump or a release cut is the moment the question "does the fleet still start" is both answerable and load-bearing.
- It must fail loudly when it can measure nothing. A check that finds no sibling repos and reports success is worse than no check, and this repo has been bitten by exactly that shape before (the empty
git tag case in the drift gate, the vacuous enrollment in the werror gate).
Each repo needs one declared entry point. Most already have one in their README or Makefile; recording it in the repo rather than in the checker keeps the checker from rotting when a consumer reorganises.
Not proposed
Making consumers track the tip. The pins are right, and this is not an argument against them — it is an argument that the pin bump needs a gate it currently does not have.
Summary
v0.43.0 shipped #1056, which made file resolution independent of the process working directory. Its changelog entry names the migration exactly: "consumers using root-relative paths from subdirectory files need an
eigs.jsonat their project root."Nobody ran that migration on the fleet. Three sibling repos have had unrunnable test suites since v0.43.0, and nothing anywhere reported it.
Measured, 2026-09-08, with the v0.43.0 release binary
tests/test_solver.eigs,tests/test_tseitin_invariant.eigs,lib/bench.eigs, all three benchmarksgame.eigsis loaded by all of themsrc/dst.eigs,src/rw_dst.eigs,test/test_explorer_ui.eigsThe diagnostic is excellent. It names every root it tried and the fix is one file. That is not the problem.
Fixed in each repo by adding
eigs.json, which is the documented migration and what ouroboros did at its own pin bump. Verified after: EigenMiniSat reports "All EigenMiniSat solver tests passed", Tidepool "All regressions passed", eddy's script runs to completion.Why nobody noticed, and why that is the actual finding
EigenMiniSat's root-level
minisat.eigswas unaffected. A smoke test of the solver itself passed the whole time. What broke was the correctness suite and the entire benchmark surface — the parts that only run when someone is measuring something.The consumer repos are pinned to older releases, which is deliberate and correct: they are post-release coverage, not a pre-merge gate, and a pin is what keeps them stable. But a pinned repo does not exercise the new release, so the breakage is invisible until someone bumps the pin, and the bump is exactly when a green suite is assumed rather than checked.
So the forcing-function model has a hole where its own design puts one. Consumers find gaps by real use, and a consumer that cannot start is not using anything. The property that makes them useful is the property that hid this.
I found it by accident, while setting up an unrelated benchmark. Time-to-discovery for that path is unbounded; this instance ran a full release cycle.
Direction
The cheap version is not a suite. It is a start-check: for each sibling repo present on the box, run its declared entry point against a candidate binary and assert it gets past module resolution. Seconds per repo, and it would have caught all three.
Where it has to live is the part worth deciding carefully:
git tagcase in the drift gate, the vacuous enrollment in the werror gate).Each repo needs one declared entry point. Most already have one in their README or Makefile; recording it in the repo rather than in the checker keeps the checker from rotting when a consumer reorganises.
Not proposed
Making consumers track the tip. The pins are right, and this is not an argument against them — it is an argument that the pin bump needs a gate it currently does not have.