|
Important
|
This document is the single source of truth for the status of AffineScript’s
known soundness holes. If any other document — |
This ledger is test-anchored and content-bound: every row names the fixture
or test that proves its claim, and tools/check-soundness-ledger.sh (wired into
just check + CI) fails the build if any named anchor goes missing, if a status
surface stops pointing here, if a named anchor’s content hash drifts from the
digest manifest (tools/soundness-anchors.sha256), if a soundness path moved
since the freshness stamp without the stamp being advanced, or if a
residual (pinned) / open (tracked) row lacks a live, currently-failing pin.
Those bindings are what stop the ledger going stale silently — including in the
one direction the name-only design could not catch: a fixture that survives but
has its assertion quietly weakened underneath it.
Ground-truthed at 65c2ae3 on 2026-06-21 by source
inspection at the cited file:line anchors, with dune build and
dune runtest green, and the anchor-digest manifest regenerated
(tools/check-soundness-ledger.sh --reseal) in the same change. The status
words below are deliberate; read them precisely.
Soundness status used to live in ~6 places that drifted independently — the
capability matrix, a survey block in .claude/CLAUDE.md, dated STATE-
snapshots, session handoffs, the wiki. A reader (human or agent) who happened to
open the stale one got a stale answer, in the *dangerous direction: "this hole
is still open" long after it was fixed, or — worse in principle — the reverse.
This file is the structural fix, resting on four mechanisms, each enforced by
the gate rather than asserted in prose:
| One surface |
Soundness-hole status is stated here and nowhere else. Other docs may summarise and link here; they may not maintain a competing per-issue ledger. The gate enforces the back-link. |
| Anchored to tests |
Each claim cites the executable artefact that proves it. The gate verifies the artefact exists. |
| Content-bound |
The gate also verifies the artefact still says what it said:
each anchor — fixture, pinned-test body, and suite file — is pinned to a content
digest in |
| Stamped, and the stamp is enforced |
The freshness attribute records the
main-ancestor commit this was last reconciled against. The gate fails if |
| Term | Meaning |
|---|---|
|
The previously-unsound behaviour is gone. The program that used to be wrongly accepted is now rejected, or the wrong result is now the right one. |
|
The construct is not implementable on this path, but it errors at compile-time or run-time — it never silently emits a plausible-wrong value. "Fail loud, never silent" is the governing rule. |
|
The feature whose surface could not be honestly enforced was withdrawn from the language, so there is no unenforced surface left to mislead anyone. |
|
A known-incorrect behaviour still exists, but an xfail test pins it: the test asserts the desired behaviour, currently fails (the bug is present), and is caught by the xfail harness as a tolerated expected-failure. It flips to an unexpected pass — failing the build with a distinct "is the hole fixed?" message — the day the hole is fixed. |
|
A known gap with no fix yet, tracked by an issue. Stated here so it cannot be
mistaken for a guarantee. Pinned the same way as |
|
Implemented and load-bearing for common cases; the named residual is the gap. |
Every row in this table is in scope for the gate. There are no soundness-relevant holes outside this table; the "still open" prose below is a reader’s digest of these rows, not a separately-maintained list.
| Issue | Soundness claim (current behaviour) | Status | Proven in code |
|---|---|---|---|
#554 / #177 |
Use-after-move through a callee-returned borrow is rejected.
|
|
|
#555 (codegen) |
|
|
|
#555 (interp, multi-shot) |
Calling |
|
|
#555 (interp, non-tail single-shot) / #623 |
|
|
|
#556 |
Async CPS table-miss fails loud instead of silently lowering synchronously. |
|
|
#558 |
Refinement / dependent types were removed in v1 (2026-04-10). There is no
|
|
|
#559 (concrete) |
Overlapping trait impls whose self types unify are rejected as a coherence violation, before the check pass, so an ambiguous instance base is reported up front. |
|
|
#559 (generic subsumption) |
Generic-subsumption overlap — |
|
|
#553 (Polonius) |
The Polonius extractor (ADR-022) is implemented through M3 but is
TEST-ONLY / unwired — it gates no production verdict. The lexical |
|
|
#555-stub / #624 |
The Lean and Why3 experimental backends now FAIL LOUD on an early |
|
|
The implementation holes marked fixed / loud-fail / removed above are
closed. Honesty requires naming what is not a guarantee. Each item here is a
row in the table above, restated for the hurried reader — not a separate list:
-
Interpreter non-tail resume (#555 / #623,
residual (pinned)) — the5-not-105shape. Silent on the interpreter path; pinned bytest_resume_nontail_xfail. (Stub backends droppingreturn(#555-stub / #624) is nowloud-fail— Lean and Why3 reject an earlyreturninstead of silently dropping it — so it is no longer a "still open" residual. Treat all non-reference backends as experimental anyway, seedocs/CAPABILITY-MATRIX.adoc.)
If you are deciding whether AffineScript is "sound enough" for a use, the interpreter non-tail resume row above is the one remaining residual, and the metatheory caveat below applies to it.
A residual (pinned) / open (tracked) row carries an xfail test that asserts
the desired behaviour and currently fails because the hole is present. The
xfail harness (test/xfail/test_xfail_pins.ml) tolerates that expected failure
and reports XFAIL-OK; if the assertion ever passes, the harness reports
XPASS, exits non-zero, and the gate surfaces a distinct "pin for #N is passing
— is the hole fixed? update the row" message.
That second job is a social guarantee wearing a mechanical costume. The harness
mechanically catches the flip; but it relies on the engineer reading XPASS as
"good — now advance the ledger row to fixed`" rather than "annoying — silence
it". The gate cannot tell those apart. So: when an `*_xfail pin reports XPASS,
the correct first move is to open this ledger, not to silence the test. This is
the one place the anti-drift machinery hands off to a human, and it is named here
so the hand-off is deliberate.
Closing an implementation hole is not the same as having metatheory. The
soundness arguments for the holes above (docs/academic/proofs/.adoc, the
comments in lib/borrow.ml) remain *prose, and the Solo core fragment’s
Soundness.idr progress / preservation are still ?todo. Mechanisation has
grown substantially — the axiom-free Coq/Rocq formal/ track now covers the
Wave-0 codegen-preservation seed (K-1 K1_CodegenPreservation.v, K-1-with-let
K1Let_CodegenPreservation.v), the F-1 transformer-preservation composition, and
the four Wave-0 siblings P-2 (progress), P-3 (borrow-graph, with loan edges
move-locality), F-3 and F-4 (#620–#627). These target core/fragment metatheory;
none yet covers a hole in this ledger — they are not the full AffineScript
borrow checker / interpreter / backends. The obligations, their rigour tiers, and
their exact status are catalogued in docs/PROOF-NEEDS.adoc (umbrella #513).
The one distinction to carry away, stated so the polish of this ledger is not mistaken for the rigour of the metatheory:
|
Note
|
The compiler now fails loud on these shapes and the residuals are pinned by tests — so the bookkeeping about soundness is mechanically enforced and current. The soundness arguments for the holes in this ledger are still paper proofs or fragment proofs, not a machine-checked guarantee about the whole language. A green gate here means "the status table is honest and the fixtures still say what they said," not "AffineScript is proven sound". |
The v1 release-readiness ledger is issue #563.
-
Ground-truth by running the tool. Reproduce the behaviour against the built compiler (
dune build && dune runtest,dune exec affinescript — …). Never edit a status here from another doc — that is how drift starts. If the compiler disagrees with a row, the compiler wins and the row is corrected (that is how the #559-generic row above was fixed). -
Change the row, change the anchor, change the digest. If you fix or break a hole, update the status word, its anchoring fixture/test, and regenerate
tools/soundness-anchors.sha256(tools/check-soundness-ledger.sh --reseal) in the same change. The gate fails if a named anchor no longer exists or if its content digest drifts from the manifest without a reseal. -
Bump the stamp, in the same change. Update
:ground-truth-sha:(to a real main-ancestor commit you verified against) and:ground-truth-date:. The gate fails if a soundness path moved since the stamp without the stamp advancing. -
Do not fork the ledger. If another doc needs to mention soundness status, it links here; it does not copy the table. The gate checks the back-links from
README.adoc,docs/CAPABILITY-MATRIX.adoc,docs/PROOF-NEEDS.adoc,docs/NAVIGATION.adoc, and.claude/CLAUDE.md.
-
CAPABILITY-MATRIX.adoc — authoritative feature readiness (this ledger owns soundness-hole status; the matrix links here).
-
PROOF-NEEDS.adoc — what must be proven, at what rigour, with what status (now including the mechanized Wave-0
formal/track). -
reports/HANDOFF-2026-06-17-soundness.adoc, reports/TIDY-UP-LEDGER-2026-06-16.adoc — the dated session reports this ledger consolidates (historical inputs).