You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Integer arithmetic returns plain int/float. A fault — overflow, zero divisor — performs an operation of the compiler-declared Arith effect and is answered by a handler the region installed. No panic is introduced: halting is unrepresentable in the operation signatures, because a substituting arm's value is the operation's result.
This is the fix for #230. That issue reports the symptom; this one is the remedy, and it is a scheduling issue, not a design one — the design is already specified and the specs are already written to it.
Why this is urgent: the tax is compounding weekly
?: sites in .osp/.ospml/.ospo, measured with git grep at weekly checkpoints:
Date
?: sites
Files
2026-07-22
7
3
2026-07-29
1,507
178
2026-08-05
1,512
180
2026-08-12
1,512
180
2026-08-19
2,911
202
2026-08-26
6,344
231
HEAD
6,408
231
6,401 of the 6,408 sites appeared in the last five weeks. The last week alone added 3,433 — the corpus more than doubled. The inflection is [ARITH-CHECKED] landing (#188, 2026-07-28): the moment arithmetic started returning Result, every new program began paying the tax, and the rate is accelerating rather than settling.
The dominant fallbacks are ?: 0 (3,233), ?: 0.0 (231), ?: 99 (156), ?: -1 (106). Exactly 7 files in the whole tree ever inspect a MathError. The overwhelming majority of these sites do not handle failure — they discharge it with a fabricated number.
Why waiting makes it worse, not just later
Every week of delay adds thousands of sites to the conversion. The migration cost is a direct function of when this lands. At the current rate the corpus adds roughly its own size weekly.
The idiom is being taught.?: 0 is in tests/regressions/basics/osprey_mega_showcase.test.osp — the flagship "Osprey in one screen" example — seven times. Every new contributor and every generated example copies it. This is how a workaround becomes the house style.
The goldens make it invisible. Every one of these sites is green. The .expectedoutput files were recorded from runs where the fallback never fired, so the masked path has zero coverage. The differential harness structurally cannot catch this class, which means the corpus growing does not increase confidence — it increases exposure.
It blocks writing correct code today. A hash function cannot currently be expressed. fn djb2(bytes) = bytes |> fold(5381, fn(h, b) => (h * 33 + b) ?: 0) resets its accumulator to zero around the 11th byte; a 12-byte string hashes to 108, a 40-byte string to 121319, exit code 0. Checksums, PRNGs and any other modular arithmetic are in the same position.
The specs and the compiler already disagree. Specs 0001–0004, 0007, 0008, 0010–0013, 0017, 0024, 0025, 0034–0036 have been rewritten to the new model and carry the [ARITH-TOTAL] guarantee. They are exempt from the code-citation requirement until this lands (specs/README.md). That exemption is a known, recorded divergence and should be short-lived.
What lands
Phases are sequenced in the plan; phases 2–5 are one PR because the type change intentionally breaks every arithmetic ?: site.
Phase 1 — Default handle binder in → do (51 files). Separate, mechanical, lands before the rest so every handler the conversion writes uses do from birth.
Phase 3 — codegen: the cold fault branch dispatches to the active handler instead of constructing an error value. Hot path unchanged.
Phase 4 — total helpers (wrapAdd…satMul), constant folding, file-scope rule.
Phase 5 — corpus conversion. The detailed checklist is at the bottom of the plan: 8 mechanical recipes, per-directory passes with measured counts, reject-fixture and golden protocols.
Phase 6 — docs/website, make bench re-run, spec status flip, drop the README exemption.
Why the migration is tractable despite the number
The compiler classifies every site. After phase 2, an arithmetic ?: fails with the message already pinned by examples/failscompilation/result_default_on_plain_value.ospo:
`?:` needs a Result on its left, found int
A ?: whose scrutinee is a genuine Result — indexing, map lookup, HTTP, parsing — still compiles and stays. So the 6,408 sites are enumerated exactly, by the compiler, with no judgement call about which are which.
Acceptance
Every clause of [ARITH-TOTAL] has a rejection fixture or differential runtime test, exercised on native under all three memory backends and on wasm32.
crates/run_test_corpus.sh byte-exact under default, --memory=arc (zero leaks), --memory=gc, and OSPREY_TARGET=wasm32.
No MathError remains in any .osp/.ospml/.ospo, spec snippet, or diagnostic golden.
Phase 0's red tests are green, asserting correct mathematical answers through declared policies.
Summary
Land plan 0027 — Arithmetic as an Effect, which implements the normative model in spec 0037.
Integer arithmetic returns plain
int/float. A fault — overflow, zero divisor — performs an operation of the compiler-declaredAritheffect and is answered by a handler the region installed. No panic is introduced: halting is unrepresentable in the operation signatures, because a substituting arm's value is the operation's result.This is the fix for #230. That issue reports the symptom; this one is the remedy, and it is a scheduling issue, not a design one — the design is already specified and the specs are already written to it.
Why this is urgent: the tax is compounding weekly
?:sites in.osp/.ospml/.ospo, measured withgit grepat weekly checkpoints:?:sites6,401 of the 6,408 sites appeared in the last five weeks. The last week alone added 3,433 — the corpus more than doubled. The inflection is
[ARITH-CHECKED]landing (#188, 2026-07-28): the moment arithmetic started returningResult, every new program began paying the tax, and the rate is accelerating rather than settling.The dominant fallbacks are
?: 0(3,233),?: 0.0(231),?: 99(156),?: -1(106). Exactly 7 files in the whole tree ever inspect aMathError. The overwhelming majority of these sites do not handle failure — they discharge it with a fabricated number.Why waiting makes it worse, not just later
?: 0is intests/regressions/basics/osprey_mega_showcase.test.osp— the flagship "Osprey in one screen" example — seven times. Every new contributor and every generated example copies it. This is how a workaround becomes the house style..expectedoutputfiles were recorded from runs where the fallback never fired, so the masked path has zero coverage. The differential harness structurally cannot catch this class, which means the corpus growing does not increase confidence — it increases exposure.fn djb2(bytes) = bytes |> fold(5381, fn(h, b) => (h * 33 + b) ?: 0)resets its accumulator to zero around the 11th byte; a 12-byte string hashes to108, a 40-byte string to121319, exit code 0. Checksums, PRNGs and any other modular arithmetic are in the same position.[ARITH-TOTAL]guarantee. They are exempt from the code-citation requirement until this lands (specs/README.md). That exemption is a known, recorded divergence and should be short-lived.What lands
Phases are sequenced in the plan; phases 2–5 are one PR because the type change intentionally breaks every arithmetic
?:site.?: 0fabricates a value on arithmetic overflow — silent wrong answers, taught by the flagship showcase (3,405 sites) #230's three silent wrong answers, intests/core/arithmetic/effect_policies.test.osp{,ml}. Lands first, red.in→do(51 files). Separate, mechanical, lands before the rest so every handler the conversion writes usesdofrom birth.Arithrequirement seeding, whole-effect arm re-entry rejection.wrapAdd…satMul), constant folding, file-scope rule.make benchre-run, spec status flip, drop the README exemption.Why the migration is tractable despite the number
The compiler classifies every site. After phase 2, an arithmetic
?:fails with the message already pinned byexamples/failscompilation/result_default_on_plain_value.ospo:A
?:whose scrutinee is a genuineResult— indexing, map lookup, HTTP, parsing — still compiles and stays. So the 6,408 sites are enumerated exactly, by the compiler, with no judgement call about which are which.Acceptance
[ARITH-TOTAL]has a rejection fixture or differential runtime test, exercised on native under all three memory backends and on wasm32.crates/run_test_corpus.shbyte-exact under default,--memory=arc(zero leaks),--memory=gc, andOSPREY_TARGET=wasm32.MathErrorremains in any.osp/.ospml/.ospo, spec snippet, or diagnostic golden.References
docs/plans/0027-arithmetic-effects.mddocs/specs/0037-ArithmeticEffects.md—[ARITH-TOTAL],[ARITH-EFFECT],[EFFECTS-HANDLE-DO]?: 0fabricates a value on arithmetic overflow — silent wrong answers, taught by the flagship showcase (3,405 sites) #230int; its fix relocated the silent failure rather than removing it)