Skip to content

[SHOWSTOPPER] Land plan 0027 (arithmetic as an effect) before the ?: tax compounds further — 6,401 of 6,408 sites appeared in the last five weeks #231

Description

@MelbourneDeveloper

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-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

  1. 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.
  2. 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.
  3. 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.
  4. 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.
  5. 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 0 — red tests pinning [SHOWSTOPPER] ?: 0 fabricates a value on arithmetic overflow — silent wrong answers, taught by the flagship showcase (3,405 sites) #230's three silent wrong answers, in tests/core/arithmetic/effect_policies.test.osp{,ml}. Lands first, red.
  • Phase 1 — Default handle binder indo (51 files). Separate, mechanical, lands before the rest so every handler the conversion writes uses do from birth.
  • Phase 2 — checker: plain arithmetic types, Arith requirement seeding, whole-effect arm re-entry rejection.
  • 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 (wrapAddsatMul), 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.
  • Coverage thresholds did not go down.

References

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    criticalshowstopperCritical: blocks usage, must fix before release

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions