--optimization, and whether its proofs belong in nikaia.proofsOpen and answer this issue on GitHub.
compiler, ready · opened 2026-10-03 · updated 2026-10-07 · 7 comments
Two questions ADR-271 §6 leaves open:
remove-bounds-checks and remove-overflow-checks are off unless a build asks.aggressive costs about +3 % over the 46 standalone files and +16 ms on solver-kernels.nika (solver-workload.md §8.3).basic, or aggressive, once the compile time is measured over the whole corpus, std included.nikaia.proofs.1. nikaia.proofs - decided: the walk’s proofs go into it. This does not wait for the solver’s search to become free. ADR-306 §6’s “when the solver’s search is free” is superseded.
2. The default - not decidable yet. The owner’s view: aggressive is dangerous today. A proved index is emitted through proven-index’s get_unchecked, so a wrong proof means undefined behaviour, where a wrong overflow proof only means a wrong value. The certificate checker (ADR-270 D5) covers the solver’s arithmetic, not whether the query the walk built says what the program does. The questions are now:
A decision basis is being prepared first:
std, and sites proved per budget.Generated by Claude Code
Budget, as the owner decided in another session: the prover’s effort is a step budget, never time. Only the expensive steps are counted. That makes counting nearly free, and in any case cheaper than reading the clock. nikaia-logic’s Budget already has this shape: cases, where each case is one elimination, and bounds, the bounds one elimination may hold. It is not counted per operation.
What remains open for #390:
Generated by Claude Code
What makes aggressive dangerous (owner, 2026-10-04). A wrong proof that removes a run-time check turns a defect into a bomb. Where the program would have stopped cleanly, it reads out of bounds through get_unchecked (undefined behaviour) or computes a silently wrong value through wrapping_*. A proof used to find errors is not dangerous: a wrong refutation is a visible build error, and --refuted-claims warn bypasses it.
So the decision basis treats the prover’s two uses separately:
Generated by Claude Code
Today’s problem: aggressive is one switch for two things. It means search harder and remove what was proved at once, and nothing lets a build set the budget. Checked in the code:
prove.rs (written claims: requires, ensures, assert) always runs, and a refuted claim is an error (ADR-269 D8).bounds.rs (the checks nobody writes: index and overflow) runs only at aggressive. Its only effect is to remove checks: a check it could show can fail is not reported.Budget::default() (1024 cases, 4096 bounds), and no switch reaches it.1. How hard the compiler proves, e.g. --proving=LEVEL / [build] proving.
bounds.rs therefore runs whatever the optimization.nikaia.proofs (decided).2. What happens to a proved check: --optimization=remove-bounds-checks and remove-overflow-checks, now on/off.
off: a wrong proof that removes a check turns a defect into a bomb (get_unchecked, wrapping_*, and debug-assertions = false in the generated profile).Example: an index shown outside the list for a value the program reaches. The run-time check would only stop the program; it would not rescue it. So this is reported as a refused claim is, an error, with --refuted-claims warn as the bypass (ADR-269 D8).
prove.rs::refutes already demands, the model has to be checked, and the values have to be ones the program reaches; the facts are true but not all that is true. A refusal on a guess would refuse a correct program (Part III C.4).proving option: command line and [build], levels mapped to a Budget. Pass it to prove.rs and to the walk. Add it to the cache key and to the nikaia.proofs key.bounds.rs at every build, independent of --optimization, and record its answers in nikaia.proofs.remove-bounds-checks / remove-overflow-checks to on/off. basic either becomes a proving level or goes; decide when building, and say which. Removal reads the recorded proofs.NK1202’s family), naming the value that fails, and honouring --refuted-claims warn.[build] table.warn;proving level proves a site the default does not;nikaia.proofs entries);off emits every check even where it is proved.std per proving level, and the sites proved and refuted per level. These numbers pick the default proving level.Generated by Claude Code
Written into the records in 8fd9b3f (0.0.451):
optimization is on/off), the refuted-claims paragraph, --bounds and the NK1207 row say what;proving.Step 5 of To build is done.
Still open: proving’s level names, budgets and default, which wait for step 7’s measurements, and whether D3’s walk without a solver stays a level (ADR-306 §6). Part III gets the proving option once those are decided.
Generated by Claude Code
Steps 2 and 3 of To build are built in 97b883c (0.0.460).
bounds::proven no longer takes a level. The emitter writes a proved check without it only where remove-bounds-checks:on or remove-overflow-checks:on.remove-*-checks are on/off. Both are off by default.basic goes as a word of the option. Its one shape is proved at every build. Whether a level without the solver is worth offering belongs to proving’s levels (ADR-306 §6), which step 7’s measurements decide. basic and aggressive are refused with the word they now are (on). ADR-306 has an as built note under D14.--bounds (#389) reports the same proofs at either setting, and says in its first line what this build writes.Still open:
proving option and its budget. Levels and default wait for the measurements.nikaia.proofs.Generated by Claude Code
.nika file (nikaia lower --no-cache), plus std’s self-hosted tools (nikaia lower-std).proofs::search takes its Budget from an environment variable, with cases = N and bounds = 4·N (the default’s ratio, 1024/4096);nikaia.proofs are ignored, so that every query is searched.cases (bounds = 4×) |
queries | proved | refuted | unknown: budget (cases / bounds) | unknown: no contradiction | unknown: overflow |
|---|---|---|---|---|---|---|
| 1 | 984 | 52 | 18 | 484 / 419 | 2 | 9 |
| 4 | 989 | 489 | 287 | 63 / 122 | 19 | 9 |
| 16 | 995 | 664 | 303 | 0 / 0 | 19 | 9 |
| 64 | 995 | 664 | 303 | 0 / 0 | 19 | 9 |
| 256 | 995 | 664 | 303 | 0 / 0 | 19 | 9 |
| 1024 (today’s default) | 995 | 664 | 303 | 0 / 0 | 19 | 9 |
| 4096 | 995 | 664 | 303 | 0 / 0 | 19 | 9 |
| 16384 | 995 | 664 | 303 | 0 / 0 | 19 | 9 |
There are 472 distinct queries; the same query is asked from several places. At budgets 1 and 4 the walk asks fewer follow-up queries, because earlier ones end unknown.
The measurements do not separate any levels above 16, so they cannot choose a default among them. There are two honest readings:
Budget::default() and let nikaia.proofs carry the answers. A proving option then only matters once a program exists that reaches the limit.Not measured: queries from programs that do not exist yet. The corpus has 995 queries; its hardest is answered within 16 cases.
The 303 refuted queries are what step 4 (a refuted implicit check is an error) would read. Most are the walk asking whether a check is needed in a case it then rules out another way. They are not 303 defects. Step 4 still has to tell a refutation the program reaches from one under incomplete facts, as the decision says.
The status stays ready: steps 1 and 4 remain to build. A level design waits for a program that reaches the budget, or for the owner to choose reading 1 or 2.
Generated by Claude Code