Files
AILang/docs/plans/0075-effect-doc-honesty.md
Brummel 832375f2ac convention: counter-prefix file naming across docs/specs/, docs/plans/, design/contracts/, design/models/
All 176 files in the four accumulating directories now use a
zero-padded 4-digit counter prefix that reflects creation order
(`NNNN-slug.md`). The counter is assigned per directory in strict
git-log creation order; ties broken alphabetically by original name.
The old `YYYY-MM-DD-` prefix on docs/specs/ and docs/plans/ files is
dropped — the date is recoverable from git log and the counter
carries the ordering.

A file's counter is stable for the life of the file: never reassigned,
never reused, never compacted. Deleted files retire their counter;
subsequent files do not fill the gap. This is the property that lets
cross-references stay literal — refs use the full filename including
the counter (`design/contracts/0007-honesty-rule.md`) so they grep
cleanly and resolve directly without a glob step.

313 cross-references updated across .md/.rs/.toml/.c/.json files
(test pins, include_str! paths, design-INDEX entries, baseline notes,
runtime C comments, inter-contract markdown links incl. bare basename
and `../models/foo.md` forms).

CLAUDE.md gets a new "File-naming convention" section spelling out
the rule and rationale. skills/brainstorm/SKILL.md and
skills/planner/SKILL.md updated so new spec/plan creation produces
counter-prefixed names from the start.

The full test suite (cargo test --workspace) passes.
2026-05-28 13:31:31 +02:00

357 lines
12 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Effect-prose documentation-honesty tidy — Implementation Plan
> **Parent:** no spec — this is a **tidy iteration** (documentation
> drift correction). Scope authority is the `docs/roadmap.md` P2
> `[~] [todo]` "DESIGN.md effect-prose is fiction" (split out
> 2026-05-16 from the retired effect-op-arg-modes bundle). Facts
> triple-verified by three prior `ailang-grounding-check` PASSes;
> exact byte sites mapped by `ailang-plan-recon` 2026-05-16.
>
> **For agentic workers:** REQUIRED SUB-SKILL: use `skills/implement`
> to run this plan. Steps use `- [ ]` checkboxes.
**Goal:** Make three false effect-system claims in the project's
documentation true, in lockstep with their two satellite mentions,
guarded by one new doc-presence pin. No language, checker, or
codegen change.
**Architecture:** Pure documentation edits across four files
(`docs/DESIGN.md`, `crates/ailang-core/src/ast.rs` doc-comment,
`crates/ailang-core/specs/form_a.md`, `crates/ail/src/main.rs`
internal CONTRACT string) plus one new regression pin
(`crates/ailang-core/tests/effect_doc_honesty_pin.rs`). Recon
confirmed no existing drift/coverage test scans the effect-prose
region, so the only RED risk is the new pin, by construction; it is
authored first (TDD: RED before the corrections, GREEN after).
**Tech Stack:** `ailang-core` (doc-comment + form_a.md + the new
integration test), `ail` (main.rs CONTRACT string), `docs/DESIGN.md`.
---
## Files this plan creates or modifies
- Create: `crates/ailang-core/tests/effect_doc_honesty_pin.rs` — one
test asserting corrected strings present + false strings absent
across the four edited files.
- Modify: `docs/DESIGN.md:171-173` — Decision 3 effect-row + Diverge
claims.
- Modify: `docs/DESIGN.md:2722` — "What is not (yet) supported"
Diverge fragment.
- Modify: `crates/ailang-core/src/ast.rs:438-439``Term::Do`
doc-comment.
- Modify: `crates/ailang-core/specs/form_a.md:226` and `:356-358`
the two Diverge mentions.
- Modify: `crates/ail/src/main.rs:288-290` — merge-prose CONTRACT
effect-annotation example.
---
## Task 1: The doc-presence pin (RED first)
**Files:**
- Create: `crates/ailang-core/tests/effect_doc_honesty_pin.rs`
- [ ] **Step 1: Write the pin**
Robust-substring strategy (single-line fragments only — no
cross-line-wrap matches; the iter-revert journal documented that a
wrap-spanning grep fragment silently mismatches). Each of the four
files is read at runtime via `CARGO_MANIFEST_DIR`-relative paths
(the pattern the F1/F4 sibling `design_schema_drift.rs:434-449`
uses).
```rust
//! Regression pin: the effect-system documentation must stay true.
//!
//! Three load-bearing effect claims were fiction until the
//! 2026-05-16 effect-doc-honesty tidy: a non-existent
//! row-polymorphic effect row, `Diverge` advertised as a wired-up
//! built-in op (zero code in any crate), and a `Term::Do`
//! doc-comment naming a non-existent link-time effect-handler
//! table. This pin fails if any of the fiction strings reappears
//! or any corrected anchor is dropped.
use std::fs;
fn read(rel: &str) -> String {
let p = concat!(env!("CARGO_MANIFEST_DIR"), "/../../");
fs::read_to_string(format!("{p}{rel}"))
.unwrap_or_else(|e| panic!("read {rel}: {e}"))
}
#[test]
fn design_md_effect_prose_is_true() {
let d = read("docs/DESIGN.md");
// fiction absent
assert!(!d.contains("row-polymorphic"),
"DESIGN.md: the effect row is NOT row-polymorphic (no EffectRow / row var exists)");
assert!(!d.contains("`IO` and `Diverge` (for infinite"),
"DESIGN.md: `Diverge` is not a wired-up MVP effect");
assert!(!d.contains("the built-in IO and Diverge ops"),
"DESIGN.md §What-is-not-supported: there is no built-in Diverge op");
// corrected anchors present
assert!(d.contains("flat, unordered, closed set of effect names"),
"DESIGN.md Decision 3 must describe the real (flat set-equality) effect model");
assert!(d.contains("`Diverge` (for non-termination) is reserved"),
"DESIGN.md Decision 3 must state Diverge is reserved/unimplemented");
}
#[test]
fn term_do_doc_comment_is_true() {
let a = read("crates/ailang-core/src/ast.rs");
assert!(!a.contains("resolved against the effect-handler table at link time"),
"ast.rs Term::Do: there is no effect-handler table and no link-time resolution");
assert!(a.contains("resolved at typecheck against `Env::effect_ops`"),
"ast.rs Term::Do doc must describe the real resolution mechanism");
}
#[test]
fn form_a_spec_effect_names_are_true() {
let f = read("crates/ailang-core/specs/form_a.md");
assert!(!f.contains("`Diverge` for `diverge/*`"),
"form_a.md: there is no `diverge/*` op namespace");
assert!(!f.contains("currently `IO` and `Diverge`"),
"form_a.md: `Diverge` is reserved, not a currently-usable effect");
assert!(f.contains("The only built-in effect op is `io/print_str`"),
"form_a.md rule 3 must name the real single built-in effect op");
}
#[test]
fn merge_prose_contract_example_is_true() {
let m = read("crates/ail/src/main.rs");
assert!(!m.contains("`(effects Diverge)`"),
"main.rs CONTRACT: do not use a non-existent effect as the example");
}
```
- [ ] **Step 2: Run — expect RED**
Run: `cargo test --workspace -p ailang-core --test effect_doc_honesty_pin`
Expected: FAIL — `design_md_effect_prose_is_true` (and the others)
panic on the still-present fiction strings / still-absent corrected
anchors.
---
## Task 2: `docs/DESIGN.md` corrections
**Files:**
- Modify: `docs/DESIGN.md:170-173`, `docs/DESIGN.md:2722`
- [ ] **Step 1: Decision 3 (lines 170-173)**
Replace exactly:
```
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.
```
with:
```
The default is total, pure functions. Effects are declared as a set in the
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.
```
(Lines 168-169 header/blank and 175-176 stay untouched.)
- [ ] **Step 2: "What is not (yet) supported" (line 2722)**
Replace exactly:
```
- No effect handlers — only the built-in IO and Diverge ops.
```
with:
```
- No effect handlers — only the built-in `IO` op (`io/print_str`).
`Diverge` is a reserved effect name with no op and no codegen.
```
- [ ] **Step 3: Run the pin — `design_md_effect_prose_is_true` GREEN**
Run: `cargo test --workspace -p ailang-core --test effect_doc_honesty_pin design_md_effect_prose_is_true`
Expected: PASS.
---
## Task 3: `crates/ailang-core/src/ast.rs` `Term::Do` doc-comment
**Files:**
- Modify: `crates/ailang-core/src/ast.rs:438-439`
- [ ] **Step 1: Replace the two doc lines**
Replace exactly:
```
/// Effect operation invocation (e.g. `do print "hi"`). The `op` is
/// resolved against the effect-handler table at link time.
```
with:
```
/// Effect operation invocation (e.g. `do io/print_str "hi"`). The
/// `op` is resolved at typecheck against `Env::effect_ops` (an
/// `IndexMap<String, EffectOpSig>`) and lowered by a literal
/// `match` in codegen (`lower_effect_op`); there is no
/// effect-handler table and no link-time resolution.
```
(Lines 440-441 — blank `///` and the Iter 14e cross-ref — and the
`Do { ... }` variant body 442-447 stay untouched.)
- [ ] **Step 2: Run the pin — `term_do_doc_comment_is_true` GREEN**
Run: `cargo test --workspace -p ailang-core --test effect_doc_honesty_pin term_do_doc_comment_is_true`
Expected: PASS.
---
## Task 4: `crates/ailang-core/specs/form_a.md` Diverge mentions
**Files:**
- Modify: `crates/ailang-core/specs/form_a.md:226`, `:356-358`
- [ ] **Step 1: Line 226 (valid-effect-name enumeration)**
Replace exactly:
```
`EFFECT-NAME` is a bare identifier — currently `IO` and `Diverge`.
```
with:
```
`EFFECT-NAME` is a bare identifier. The only effect with a built-in
op today is `IO` (op `io/print_str`); `Diverge` is a reserved name
(no op, unimplemented).
```
(Line 227 "Effects are a set; order is irrelevant." stays — and is
now corroborated by the DESIGN.md set-equality wording.)
- [ ] **Step 2: Lines 356-358 (rule 3 op→effect mapping)**
Replace exactly:
```
3. **Effects on side-effecting fns.** A function whose body uses `(do ...)`
MUST list every effect operation's effect in its `(effects ...)`
clause. `IO` for `io/print_*`; `Diverge` for `diverge/*`.
```
with:
```
3. **Effects on side-effecting fns.** A function whose body uses `(do ...)`
MUST list every effect operation's effect in its `(effects ...)`
clause. The only built-in effect op is `io/print_str`, whose
effect is `IO`.
```
- [ ] **Step 3: Run the pin — `form_a_spec_effect_names_are_true` GREEN**
Run: `cargo test --workspace -p ailang-core --test effect_doc_honesty_pin form_a_spec_effect_names_are_true`
Expected: PASS.
---
## Task 5: `crates/ail/src/main.rs` merge-prose CONTRACT example
**Files:**
- Modify: `crates/ail/src/main.rs:288-290`
- [ ] **Step 1: Replace the effect-annotation bullet**
Replace exactly:
```
- Effect annotations on return types (`(effects IO)`,
`(effects Diverge)`). Prose shows these as `with IO` etc.; if
uncertain, keep what the original had.
```
with:
```
- Effect annotations on return types (e.g. `(effects IO)`).
Prose shows these as `with IO` etc.; if uncertain, keep what
the original had.
```
- [ ] **Step 2: Run the pin — `merge_prose_contract_example_is_true` GREEN**
Run: `cargo test --workspace -p ail --test effect_doc_honesty_pin merge_prose_contract_example_is_true`
(Note: the pin is an `ailang-core` integration test; run the whole
pin here.)
Run: `cargo test --workspace -p ailang-core --test effect_doc_honesty_pin`
Expected: PASS (all four tests).
---
## Task 6: Full verification
- [ ] **Step 1: Whole pin GREEN**
Run: `cargo test --workspace -p ailang-core --test effect_doc_honesty_pin`
Expected: PASS — 4/4.
- [ ] **Step 2: No collateral drift**
Run: `cargo test --workspace`
Expected: PASS — the prior green count + 4 new pin tests; in
particular `design_schema_drift.rs`, `spec_drift.rs`,
`schema_coverage.rs` stay green (recon confirmed none scans the
effect-prose region; this step proves it empirically).
- [ ] **Step 3: Build sanity (no behavioural change)**
Run: `cargo build --workspace`
Expected: clean. No codegen / checker code was touched — `ail check`
/ `ail run` behaviour is byte-unchanged by construction (doc-comment
+ markdown + an internal guidance string only).
---
## Self-review (planner Step 5, inline)
1. **Scope coverage:** every roadmap-`[todo]` clause (a)/(b)/(c) +
form_a.md ×2 + main.rs + guard test has a task. ✓
2. **Placeholder scan:** no "TBD"/"TODO"/"similar to"/"appropriate";
every code-changing step carries exact before→after text. ✓
3. **Type/string consistency:** the pin's asserted corrected
substrings (`"flat, unordered, closed set of effect names"`,
`` "`Diverge` (for non-termination) is reserved" ``,
`"resolved at typecheck against `Env::effect_ops`"`,
`"The only built-in effect op is `io/print_str`"`) appear
verbatim in the Task 2/3/4 replacement bodies; the asserted
absent substrings (`"row-polymorphic"`,
`"the built-in IO and Diverge ops"`,
`"resolved against the effect-handler table at link time"`,
`` "`Diverge` for `diverge/*`" ``, `"currently `IO` and `Diverge`"`,
`` "`(effects Diverge)`" ``) are exactly the strings removed.
Cross-checked task-by-task. ✓
4. **Step granularity:** every step is one edit or one command,
2-5 min. ✓
5. **No commit steps.** ✓ (Boss commits the whole tidy.)
6. **Line-wrap robustness:** every pin substring is single-line
(no `\n`-spanning fragment) — the documented iter-revert
grep-wrap failure mode is avoided. ✓