iter effect-doc-honesty: make the effect-system documentation true

Standalone documentation-honesty tidy (no language/checker/codegen
change; `ail check`/`run` byte-unchanged by construction). Corrects
three false effect-system claims the effect-subsystem recon surfaced,
plus two satellite mentions, guarded by a new doc-presence pin:

- DESIGN.md Decision 3: removed the "row-polymorphic (`![IO | r]`)"
  claim (no EffectRow / row variable exists in any crate — effect
  sets are a flat, unordered, closed set unified by set-equality);
  reconciled "`IO` and `Diverge` are wired up" to IO-only +
  `Diverge` reserved/unimplemented (zero code in any crate),
  modelled on Decision 4's reserved-refinements precedent.
- DESIGN.md "What is not (yet) supported": "IO and Diverge ops"
  bullet -> IO-only + Diverge-reserved.
- ast.rs Term::Do doc-comment: "resolved against the effect-handler
  table at link time" -> the real mechanism (typecheck lookup in
  Env::effect_ops + literal lower_effect_op codegen match; no
  handler table, no link-time resolution).
- form_a.md:226 + rule 3, and main.rs merge-prose CONTRACT example:
  Diverge mentions reconciled in lockstep.
- new crates/ailang-core/tests/effect_doc_honesty_pin.rs: 4 tests,
  fiction-absent + corrected-anchor-present, single-line wrap-robust
  substrings.

cargo test --workspace 600 -> 604 (4 new pin tests, 0 regressions);
design_schema_drift / spec_drift / schema_coverage stay green,
empirically confirming none scans the effect-prose region.
This commit is contained in:
2026-05-16 13:25:49 +02:00
parent c41e0e5a9e
commit a29700cc9e
8 changed files with 179 additions and 11 deletions
+10 -4
View File
@@ -168,9 +168,14 @@ Advantages:
## Decision 3: pure core language + algebraic effects
The default is total, pure functions. Effects are declared as a set in the
function type: `(Int) -> Int ![IO]`. The effect set is row-polymorphic
(`![IO | r]`). In the MVP only the effects `IO` and `Diverge` (for infinite
loops) are wired up.
function type: `(Int) -> Int ![IO]`. The effect set is a
flat, unordered, closed set of effect names, unified by set-equality —
there is no effect row variable; a signature lists exactly the effects
its body may perform.
In the MVP only the effect `IO` is wired up (its sole op is `io/print_str`).
`Diverge` (for non-termination) is reserved as an effect name but is
unimplemented — no op, no codegen, no checker injection — in the same
sense Decision 4 reserves refinements.
This is the most important LLM property: when I read a function, I can trust
its signature without reading the body.
@@ -2719,7 +2724,8 @@ proof is the protection.
Snapshot of the current boundary. Items move out of this list
as iterations land; the JOURNAL records when.
- No effect handlers — only the built-in IO and Diverge ops.
- No effect handlers — only the built-in `IO` op (`io/print_str`).
`Diverge` is a reserved effect name with no op and no codegen.
- No refinements / SMT escalation.
- No HM inference inside bodies. Top-level def types are explicit;
polymorphism is opt-in via `Type::Forall { vars, body }`. Inside
@@ -0,0 +1,81 @@
# iter effect-doc-honesty — effect-system documentation-honesty tidy
**Date:** 2026-05-16
**Started from:** c41e0e5a9e48fd0099eda8c286f10ec7a30e8e8a
**Status:** DONE
**Tasks completed:** 6 of 6
## Summary
Documentation-honesty tidy: corrected three false effect-system
claims plus their two satellite mentions across the project docs,
guarded by one new doc-presence regression pin. No language,
checker, or codegen code changed — only a doc-comment, two markdown
spec files, an internal CLI guidance string, and a new integration
test. `ail check`/`ail run` behaviour is byte-unchanged by
construction; `cargo test --workspace` 600 → 604 (the four new pin
tests, zero regressions), `cargo build --workspace` clean. The
three sentinel drift/coverage tests (`design_schema_drift`,
`spec_drift`, `schema_coverage`) stay green, empirically confirming
none scans the effect-prose region.
## Per-task notes
- iter effect-doc-honesty.1: new pin `crates/ailang-core/tests/effect_doc_honesty_pin.rs`
(4 tests asserting fiction strings absent + corrected anchors
present across the 4 edited files); authored first, ran RED 0/4
for the right reason (TDD: RED before corrections).
- iter effect-doc-honesty.2: `docs/DESIGN.md` Decision 3 row-polymorphic
block → flat/unordered/closed set-equality + Diverge-reserved
wording; line-2722 "IO and Diverge ops" bullet → IO-only +
Diverge-reserved bullet. `design_md_effect_prose_is_true` GREEN.
- iter effect-doc-honesty.3: `crates/ailang-core/src/ast.rs` `Term::Do`
doc-comment effect-handler-table-at-link-time fiction → real
mechanism (typecheck against `Env::effect_ops`, `lower_effect_op`
literal-match codegen). `term_do_doc_comment_is_true` GREEN.
- iter effect-doc-honesty.4: `crates/ailang-core/specs/form_a.md`
line-226 "currently `IO` and `Diverge`" → IO-built-in-op /
Diverge-reserved; rule-3 "`Diverge` for `diverge/*`" →
io/print_str-is-the-only-built-in-op. `form_a_spec_effect_names_are_true`
GREEN.
- iter effect-doc-honesty.5: `crates/ail/src/main.rs` merge-prose
CONTRACT effect-annotation bullet dropped the non-existent
`(effects Diverge)` example. Whole pin 4/4 GREEN.
- iter effect-doc-honesty.6: full verification — whole pin 4/4,
`cargo test --workspace` 604/0 (600 prior + 4), drift/coverage
trio green, `cargo build --workspace` clean.
## Concerns
- Task 2: the plan's verbatim Task-2 replacement body soft-wrapped
the phrase "flat, unordered,\nclosed set of effect names"
mid-phrase, which contradicts Task-1's single-line pin substring
`"flat, unordered, closed set of effect names"` AND the plan's
own self-review item 6 ("every pin substring is single-line — the
documented iter-revert grep-wrap failure mode is avoided"). The
two authoritative artefacts (exact replacement text vs. exact
pin) could not both be applied verbatim. Resolved by preserving
the exact corrected *wording* (byte-identical to the plan's
intent) and the exact pin (Task 1, unchanged), moving only the
soft-wrap column so the asserted phrase stays contiguous on one
line. No wording added/dropped/changed. This is a planner
self-review-item-3 cross-check gap (the asserted-substring vs.
replacement-body check did not account for the replacement body's
own line wrap) — orchestrator may want to tighten the planner's
pin/replacement-body wrap cross-check.
## Known debt
- (none) — the tidy is self-contained; no deferred follow-up.
## Files touched
- `crates/ailang-core/tests/effect_doc_honesty_pin.rs` (new — the pin)
- `docs/DESIGN.md` (Decision 3 + "What is not supported" bullet)
- `crates/ailang-core/src/ast.rs` (`Term::Do` doc-comment)
- `crates/ailang-core/specs/form_a.md` (line 226 + rule 3)
- `crates/ail/src/main.rs` (merge-prose CONTRACT string)
## Stats
bench/orchestrator-stats/2026-05-16-iter-effect-doc-honesty.json
+1
View File
@@ -80,3 +80,4 @@
- 2026-05-15 — iter it.3 (BLOCKED, bounce-back): destructive terminal iteration of iteration-discipline. Task 1 (non-destructive: pre-migration oracle + the spec-delegated class-(b) live sweep) ran complete — 40 recursive corpus fixtures classified + oracled under `bench/it3-oracle/`; zero production code changed; HEAD clean at c992eb9. The mandated Task-1.3 sweep surfaced a **fundamental milestone-design flaw**, not a migration nuisance: 6 fixtures contain `build(d: Int) = if d==0 then Leaf else Node(1, build(d-1), build(d-1))` — a terminating, maximally-LLM-natural, **non-structural (Int param, no ADT-candidate) non-tail BRANCHING (double) recursion** that the milestone's totality dichotomy ("structural-over-ADT OR loop/recur, else error") makes **inexpressible**: not ADT-structural, and not `recur`-able (branching ≠ single tail back-edge). Post-it.3 it is a hard `NonStructuralRecursion` with no expressible alternative — the milestone as-specified would fail its own feature-acceptance criterion (clause 1: the LLM reaches for `build(d-1),build(d-1)`; clause 2: the milestone would *remove* expressivity). The orchestrator correctly refused a 4th plan-patch and bounced (`feedback_spec_over_plan_patches`). Boss verdict: the totality story overlooked a second canonical total-by-construction scheme — well-founded recursion on a strictly-decreasing non-negative Int measure. Resolution requires a new additive iteration **it.2b** (widen the it.2 guardedness checker to accept Int-bounded recursion incl. branching) BEFORE it.3 re-dispatches; the purity sub-fork (Int≠Nat: total only for non-negative entry) touches the user-co-designed purity pillar → user design decision, recommended option A1 (accept with a documented non-negative-entry obligation, analogous to array-bounds). Notify sent; loop stopped. The RC-RSS/18g.1 load-bearing risk (it.3 Task 5.5/5b) was NOT reached and must NOT be treated as resolved by the milestone-close audit → 2026-05-15-iter-it.3.md
- 2026-05-16 — iter revert: Iteration-discipline milestone (it.1 `96db54d` + it.2 `a4be1e5`) fully backed out by one **forward** iteration (`main` sacrosanct — never rewound; `1ff7e81`, the pre-`9973546` commit, is the per-region byte oracle). Root cause: the milestone was an over-escalation of fieldtest finding F1 (a `[friction]` item whose own minimal recommendation was a DESIGN.md note); its totality dichotomy made the maximally-LLM-natural `build(d:Int)=Node(1,build(d-1),build(d-1))` inexpressible (it.3 BLOCKED), and the only in-thesis escape (A1/it.2b) conceded the language's first documented-unenforced totality precondition — a purity-pillar dilution the user rejected. `Term::Loop`/`Term::Recur`/`LoopBinder`, the `verify_structural_recursion` guardedness pass + `term_contains_loop` + `Diverge`-injection + the transitively-it.2 `module_fns` plumbing, the five `Recur*`/`NonStructuralRecursion` `CheckError` variants (+ `code()` + 3 dedicated `ctx()` arms), the it.1 codegen loop-header/phi/back-edge + parallel `block_terminated` setter, and all walker arms are removed — `crates/ailang-check/src/lib.rs` and every reverted source/test file byte-identical to `1ff7e81`. Surgical keeps: feature-acceptance **clause 3** (DESIGN.md + brainstorm SKILL.md, worked example de-claimed "shipped"→hypothetical) and the F3 P2 todo. Sole net addition: an honest F1/F4 documented-idiom note (tail-recursive accumulator fallback; `examples/mut_counter.ail`) guarded by a doc-presence test. 16 it.1/it.2 fixtures + 2 pin files + `bench/it3-oracle/` deleted; 2 RC fixtures restored to `1ff7e81`; `bench/orchestrator-stats/2026-05-15-iter-it.{1,2,3}.json` kept (historical record). Roadmap: Iteration-discipline block + blocking-fork section removed; the genuine total-Int-recursion ambition preserved as a deferred P2 milestone sequenced behind a future `Nat`/refinement-types milestone (not abandoned — correctly sequenced). Correctness gate PRISTINE: 164 surviving `1ff7e81`-era fixtures `ail check`/`ail run` byte-identical to pre-milestone behaviour (1ff7e81 worktree reference compiler, zero drift); `cargo test --workspace` 600/0; zero residual it.1/it.2 production surface. The old it.* journals/plans + the superseded-headered `2026-05-15-iteration-discipline.md` stay as historical record → 2026-05-16-iter-revert.md
- 2026-05-16 — audit iteration-discipline-revert (milestone close, clean): architect confirmed the revert byte-pristine (18 src + 5 test files byte-identical to `1ff7e81`, zero residual it.1/it.2 production surface, DESIGN.md coherent, roadmap/spec hygiene correct); one `[medium]` drift — a dangling `loop`/`recur` cross-reference into the reverted milestone inside the *live* Stateful-islands roadmap scope bullet — fixed inline (Boss doc-hygiene; reworded to keep the real "no `while`; repetition stays recursion" scope guard, drop the reverted-milestone reference). Bench: `compile_check.py`/`cross_lang.py` exit 0; `check.py` exit 1 on the single metric `bench_list_sum.bump_s` +12.71%. Bencher localisation: HEAD and `1ff7e81` bump binaries `cmp`-identical, interleaved 3×60-run measurement shows headoracle delta ~0 (±1.5% stdev) with *both* ~+11% over the 2026-05-09 baseline → environmental/hardware drift, NOT a revert regression (third independent proof codegen == `1ff7e81`). Resolution: carry-on, baseline NOT ratified (Iron Law — nothing intentionally moved the metric; byte-identical codegen must not move the baseline; the spec's PRISTINE mandate is satisfied because pristine = "no regression introduced", proven). Pre-existing `*.bump_s` baseline-vs-hardware staleness (the `1ff7e81` oracle trips the same +11%) filed forward as a separate P2 `[todo]` (bench-harness recalibration, no language change), not mis-attributed to the revert. Fieldtest skipped (revert restores the already-field-tested pre-`9973546` surface; Task-11 164-fixture byte-equivalence is the stronger proof). Milestone closed clean → 2026-05-16-audit-iteration-discipline-revert.md
- 2026-05-16 — iter effect-doc-honesty: standalone documentation-honesty tidy (split out from the retired effect-op-arg-modes bundle — user rejected bundling a real DESIGN.md fix with speculative build-ahead infra). Corrected three false effect-system claims the recon surfaced: DESIGN.md Decision 3 "row-polymorphic (`![IO | r]`)" (no `EffectRow`/row var exists — effect sets are flat closed sets unified by set-equality) + "`IO` and `Diverge` … are wired up" (`Diverge` is 100% vapour: zero code in any crate) → reconciled to IO-only/Diverge-reserved-unimplemented (modelled on Decision 4's reserved-refinements precedent); DESIGN.md §"What is not supported" "IO and Diverge ops" bullet; `ast.rs` `Term::Do` doc-comment "resolved against the effect-handler table at link time" → the real mechanism (typecheck `Env::effect_ops` lookup + `lower_effect_op` literal codegen match, no handler table). Satellite lockstep: `form_a.md:226` + rule-3, `main.rs:289` merge-prose CONTRACT example. Guarded by a new 4-test doc-presence pin `crates/ailang-core/tests/effect_doc_honesty_pin.rs` (fiction-absent + corrected-anchor-present, single-line substrings — wrap-robust). No language/checker/codegen change; `ail check`/`run` byte-unchanged by construction. `cargo test --workspace` 600 → 604; drift/coverage trio (`design_schema_drift`/`spec_drift`/`schema_coverage`) green, empirically confirming none scans the effect-prose region. One concern: the plan's Task-2 replacement body soft-wrapped a phrase the single-line pin asserts; orchestrator resolved correctly (exact wording + exact pin preserved, wrap column moved) — recurring-class planner gap, fixed forward by a Step-5 self-review tightening → 2026-05-16-iter-effect-doc-honesty.md