docs(ledger): fix two coherence gaps surfaced by the project skim

A read-only coherence skim across the project (the contract edits
themselves verified code-true, INDEX bijection exact, all pins green)
surfaced two pre-existing drifts — both predating this audit, neither
from the contract pass. Fixed in the same conservative style.

1. Model 0008 (ownership-totality) §1+§2 narrated the `Implicit`
   leak in the PRESENT tense, contradicting the file's own STATUS
   header (Implicit deleted via #55, 76b21c0), contract 0008 ("There
   is no `Implicit`"), and the live fixture. §1 claimed "This is
   documented intentional behaviour today ... the fixture asserts
   `live = 1`"; the `rc_let_implicit_returning_app.ail` fixture now
   asserts `live = 0` (its own comment marks the `live = 1` lane as
   "Pre-0062"). §2 claimed "the typechecker already treats
   `Implicit ≡ Own` (`ParamMode::mode_eq`)"; that variant and fn no
   longer exist. Rewrote both to past tense (the leak the cutover
   fixed). The STATUS header had been updated at cutover; these two
   bodies had not. The design argument (§2-§8) is untouched — the
   header frames it as the whitepaper's reasoning and points readers
   to the contract for current state.

2. Contract 0010 (scope-boundaries) referenced 18 example fixtures as
   `examples/*.ail.json` — files that exist only as `.ail` since the
   form-A-default migration (JSON is derived in-process; only the
   twelve carve-outs remain `.ail.json` on disk). All 18 → `.ail`
   (every target verified present). Same single stale file-path ref in
   model 0001 §3 (`list_map_poly`) corrected; model 0001's other two
   `.ail.json` mentions are intentional references to the canonical
   JSON *form* (the whitepaper's subject) and were left.

Honesty sweep clean; design_index_pin / docs_honesty_pin /
effect_doc_honesty_pin green; no dangling example path remains.
This commit is contained in:
2026-06-02 11:57:51 +02:00
parent 4ec8f90b19
commit c747cdf932
3 changed files with 36 additions and 37 deletions
+1 -1
View File
@@ -2,7 +2,7 @@
## Candidate notations (same `map` encoded in each)
The reference target — the polymorphic `map` from `examples/list_map_poly.ail.json`:
The reference target — the polymorphic `map` from `examples/list_map_poly.ail`:
```
data List a where Nil | Cons a (List a)
+17 -18
View File
@@ -33,21 +33,21 @@ the sharpened §7 questions, and §8 (relation to Rust).*
---
## 1. The leak that motivates it
## 1. The leak that motivated it
A user function whose return **type** is an RC-heap value (`Str`, or a
boxed ADT) but whose signature omits the ownership mode carries
A user function whose return **type** was an RC-heap value (`Str`, or a
boxed ADT) but whose signature omitted the ownership mode carried
`ret_mode == ParamMode::Implicit` — the parser's silent default, which
nothing infers post-parse. Codegen reads `Implicit` faithfully: the
caller does **not** `dec` the returned slab, so the owned allocation
leaks (`live = 1` under `AILANG_RC_STATS`). This is documented
intentional behaviour today: `../contracts/0008-memory-model.md` states
`ret_mode` is author-required and never inferred, and the
`examples/rc_let_implicit_returning_app.ail` fixture asserts `live = 1`
as correct under the Implicit back-compat lane.
nothing inferred post-parse. Codegen read `Implicit` faithfully: the
caller did **not** `dec` the returned slab, so the owned allocation
leaked (`live = 1` under `AILANG_RC_STATS`). The #55 cutover removed
this lane: `../contracts/0008-memory-model.md` now requires an explicit
author-supplied `ret_mode`, and the
`examples/rc_let_implicit_returning_app.ail` fixture asserts `live = 0`
(the returned cell frees at the call site).
The leak is the symptom. The cause is that an ownership default exists
at all.
The leak was the symptom. The cause was that an ownership default
existed at all.
## 2. The principle: ownership is authored, never defaulted
@@ -60,12 +60,11 @@ annotations are kept*; *machine readability over human readability*.
the owner of a value crossing a signature boundary — and must free it
— is **not recoverable from the type alone**: a `Str`-returning `own`
and a `Str`-returning `borrow` are indistinguishable by type; only the
mode says who frees. The code makes this concrete: the typechecker
already treats `Implicit ≡ Own` (`ParamMode::mode_eq`,
`crates/ailang-core/src/ast.rs`), yet codegen reads `Implicit`
separately and omits the caller `dec`. The *same* fn-type is read two
ways depending on whether one knows codegen's special-casing — exactly
the trust break local reasoning exists to forbid.
mode says who frees. The code made this concrete: the typechecker
treated `Implicit ≡ Own`, yet codegen read `Implicit` separately and omitted the
caller `dec`. The *same* fn-type was read two ways depending on
whether one knew codegen's special-casing — exactly the trust break
local reasoning exists to forbid.
So the principle is: **ownership is authored, never defaulted.**