Files
AILang/docs/specs/0039-docs-honesty-lint.md
T
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

324 lines
18 KiB
Markdown
Raw 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.
# DESIGN.md / docs honesty lint — Design Spec
**Date:** 2026-05-18
**Status:** Draft — awaiting user spec review
**Authors:** Brummel (orchestrator) + Claude
## Goal
Make `docs/DESIGN.md` (and the in-tree satellites that repeat its
claims) mirror **only** the actual current state of the language —
correct, present-tense — and make that property *load-bearing* so it
cannot silently regrow.
`docs/DESIGN.md` is the artefact the `ailang-architect` agent
drift-checks the codebase against. A DESIGN.md that asserts something
which is not true, or that narrates future intent / past mishaps as if
they were part of the language description, makes the drift review
unsound at its root: the architect cannot tell "code drifted from the
spec" from "the spec was never true". This milestone closes that.
Two failure modes are banned from the canonical docs:
- **Wunschdenken** — aspirational "foundation for / we'd like /
planned / will back / on the path to / if-when X arrives we'll …"
forward intent. Genuine forward intent belongs in `docs/roadmap.md`,
never in DESIGN.md.
- **Post-mortem / doc-archaeology narrative** — "an earlier draft
said X; a follow-up replaced it / previously all diagnostics were
Error / what changed in <date> is / X was retired <date> in iter Y
/ preserved for the audit trail". Project and document history
belongs in `docs/journals/`, never in DESIGN.md.
One exception is legitimate and must be **preserved**: a
**present-tense reserved / unimplemented / deliberately-excluded**
claim that is explicitly AND correctly labelled as such. The
effect-doc-honesty precedent (2026-05-16) established the gold form:
*"`Diverge` is a reserved effect name with no op and no codegen."*
The discriminator is whether the doc asserts something
exists/works/changed that does not — not whether the sentence
mentions the future or the past.
## Architecture
Three layers, one iteration (the structure ratified with the user;
mirrors the effect-doc-honesty precedent, which shipped as a single
6-task iter):
1. **The discriminator, codified present-tense inside DESIGN.md.**
A short meta-subsection added to DESIGN.md's existing
document-roles area states the honesty rule the document holds
itself to. This makes the boundary discoverable by any future LLM
reading DESIGN.md and gives the architect a citable in-tree
anchor. It is itself written present-tense (it describes a rule
the document *currently* obeys, not one it *will* adopt).
2. **The corrections.** Every passage that the discriminator
classifies as Wunschdenken or non-citation post-mortem is
rewritten present-tense (or, for genuine forward intent, relocated
to `docs/roadmap.md`; the roadmap already carries the Boehm-
retirement and auto-derivation queue entries, so most relocations
are deletions, not moves). Scope: `docs/DESIGN.md` plus the
in-tree satellites that repeat the same claim — `docs/PROSE_ROUNDTRIP.md`
and any load-bearing `///` / `//!` doc-comment that echoes a
corrected DESIGN.md sentence. (`crates/ailang-core/specs/form_a.md`
was swept: its sole hit, *"multi-superclass chains are not yet
supported"*, is gold-form honest-reserved — no change.)
3. **The anti-regrowth mechanism — pin (hard) + sweep+architect
(standing).** Two complementary guards, exactly the
design-md-consolidation pattern already in the tree:
- **Enumerated pin (authoritative, cargo-test gate).** A new
`crates/ailang-core/tests/docs_honesty_pin.rs`, sibling to the
existing `effect_doc_honesty_pin.rs`: for every corrected site,
one assertion that the fiction/Wunschdenken/post-mortem string
is **absent** and one that the present-tense corrected anchor is
**present**. This is a hard `cargo test --workspace` gate.
- **Sweep 5 + architect bullet (standing, advisory, forward).**
`bench/architect_sweeps.sh` already runs four
design-md-consolidation greps as an advisory companion to the
architect's "DESIGN.md history-anchor regrowth" check. Sweep 2
*already* partially catches Wunschdenken (`A future iter may`,
`REVERTED`) — but its wording is too narrow (it misses "may land
in a future iteration", "is on the path to retirement", "an
earlier draft of this Decision said", "what changed in <date>
is", "were retired <date> in iter"). This milestone adds
**Sweep 5 (honesty)** closing those wording gaps, plus a new
**"DESIGN.md honesty drift"** bullet in the architect's "What
you check" list that cites the DESIGN.md discriminator
meta-subsection. The pin protects the corrected set; Sweep 5 +
the bullet catch *new* regrowth the enumerated pin cannot see.
**Why the enumeration is procedural, not a frozen list.** The
authoritative scope is *"the discriminator applied to the full
`architect_sweeps.sh` (incl. Sweep 5) output, every hit classified"*.
Running the existing sweep already surfaces post-mortem sites a
narrow hand-grep misses (e.g. DESIGN.md:2027/2065 *"primitives were
retired 2026-05-14 in iter rpe.1"*, :503 *"what did change in
2026-05-13 (this milestone) is"*, :2135 *"originally listed here …
was retired"* — all currently waved through as Sweep-1 "legitimate
quotes"). The spec owns the discriminator and the procedure; the
planner owns the byte-exact classified enumeration (planner Iron
Law). A frozen orchestrator list would itself be a honesty risk.
## Concrete code shapes
This is a **documentation-integrity milestone: it has no `.ail`
authoring surface by nature.** Per the brainstorm skill's no-surface
clause, the concrete deliverables are (a) the executable pin (Rust),
and (b) the prose before → after — exactly the effect-doc-honesty
precedent's shape. Feature-acceptance clause 1 ("an LLM reaches for
it") is N/A (no surface); the measurable correctness improvement
(clause 2) is that the architect's drift review stops being unsound;
clause 3 is trivially met (no language construct, no bug class).
### Primary deliverable A — the discriminator, as it will read in DESIGN.md
A new present-tense meta-subsection (placed in DESIGN.md's
document-roles area, near the existing `roadmap.md` / `journals/`
pointer at ~line 50):
```markdown
### What this document is — and the honesty rule it holds itself to
DESIGN.md describes what AILang **is now**: schema, semantics,
invariants, runtime contracts, and the rationale for each. It is
present-tense by construction. Two things never belong here:
- **Forward intent** ("planned / will back / on the path to /
if-when X arrives") — that lives in `docs/roadmap.md`.
- **History** ("an earlier draft said / previously / what changed
in <date> / retired in iter Y") — that lives in `docs/journals/`.
The single legitimate exception is a present-tense reserved or
deliberately-excluded claim that is explicitly and correctly
labelled (e.g. "`Diverge` is a reserved effect name with no op and
no codegen"; "No `deriving` — instances are written by hand";
"Regions were considered and rejected; see X"). The discriminator
is not whether a sentence mentions past or future, but whether the
document **asserts something exists, works, or changed that does
not**. Design rationale — *why* the language excludes a thing — is
in scope and present-tense; the *narrative of how the document
reached that wording* is not.
```
### Primary deliverable B — worked discriminator classifications (the clause-2 evidence)
The discriminator is the design judgement. These are the boundary
cases, each shown before → after, that *prove* it is operational
(not a hand-wave):
| DESIGN.md | Before | Classification | After |
|---|---|---|---|
| ~L1504 | "An earlier draft of this Decision said 'uniqueness is fully inferred' … A follow-up discussion replaced it: AILang demands annotations *because* …" | **Post-mortem** (doc-archaeology) → journals | "AILang demands annotations because the LLM author can produce them effortlessly; the compiler does inference plus verification of contracts." |
| ~L2519 | "Boehm-on-`--alloc=gc` is on the path to retirement; see `docs/roadmap.md` for the active queue." | **Wunschdenken** (a plan, not a present fact) → roadmap (already carries the Boehm-retirement entry) | "`--alloc=gc` selects the transitional Boehm backend; `--alloc=rc` is the canonical backend (Decision 10) and the CLI default." |
| ~L2046 | "`float_to_str` (iter 24.1, type-installed; codegen ships in a future iter per roadmap). Will back `Show Float`." | "type-installed" honest-reserved (**keep**); "Will back `Show Float`" Wunschdenken (**strip**) | "`float_to_str : (borrow Float) -> Str` — type-installed; codegen reserved, not yet shipped." |
| ~L1318 | "The first ever Warning-level diagnostic; previously all diagnostics were `Error`." | **Post-mortem** → journals | "Severity: `Warning`. `ail check`/`build`/`emit-ir` exit 1 only on at least one `Error`; warnings print but do not abort." |
| ~L484 | "**Both extensions accepted.** Since iter ext-cli.1 (2026-05-12), every path-taking CLI subcommand accepts either …" | **History-anchor** (inline narrative, not a block-quote citation) → strip anchor | "Every path-taking CLI subcommand accepts either `.ail` (Form A) or `.ail.json` (Form B) as input." |
| ~L1516 | "Does not introduce regions. Regions were considered and rejected; see 'Why not other memory models' above." | **Design rationale** (present-tense exclusion + internal pointer) | **KEEP unchanged** — this is the protected exception, not drift. |
| ~L655 | "### Empirical addendum (2026-05-12)" + cross-model measurement results | Section *content* is present-tense evidence for Decision 6 (**keep**); the dated heading is a mild archaeology marker | Heading de-dated to "### Empirical addendum (cross-model authoring measurement)"; data unchanged. |
| ~L2725 | "`Diverge` is a reserved effect name with no op and no codegen." | Gold-form honest-reserved | **KEEP unchanged** — the discriminator's positive anchor. |
### Secondary supporting — the pin shape (mirrors `effect_doc_honesty_pin.rs`)
```rust
//! Regression pin: the canonical docs must stay present-tense-honest.
//! Fails if any swept-and-corrected Wunschdenken / post-mortem string
//! reappears, or any present-tense corrected anchor is dropped.
use std::fs;
fn read(rel: &str) -> String { /* identical helper to effect_doc_honesty_pin.rs */ }
#[test]
fn design_md_has_no_wunschdenken() {
let d = read("docs/DESIGN.md");
assert!(!d.contains("is on the path to retirement"), "..."); // L2519
assert!(!d.contains("Will back `Show Float`"), "..."); // L2046
assert!(!d.contains("may land in a future iteration"), "..."); // L1837
// … one per corrected Wunschdenken site
}
#[test]
fn design_md_has_no_doc_archaeology() {
let d = read("docs/DESIGN.md");
assert!(!d.contains("An earlier draft of this Decision said"), "..."); // L1504
assert!(!d.contains("previously all diagnostics were"), "..."); // L1318
assert!(!d.contains("were retired 2026-05-14 in iter rpe.1"), "..."); // L2027/2065
// … one per corrected post-mortem site
}
#[test]
fn design_md_present_tense_anchors_present() {
let d = read("docs/DESIGN.md");
assert!(d.contains("the honesty rule it holds itself to"), "discriminator meta-subsection present");
assert!(d.contains("`Diverge` is a reserved effect name with no op and no codegen"),
"the gold-form honest-reserved anchor must remain (regression: do not over-strip)");
// … the discriminator's protected-exception anchors, pinned so the
// sweep-and-correct does not over-correct a legitimate reserved claim
}
#[test]
fn prose_roundtrip_md_has_no_wunschdenken() {
let p = read("docs/PROSE_ROUNDTRIP.md");
assert!(!p.contains("A future iter may layer a tool-use schema"), "..."); // L152
}
```
### Secondary supporting — Sweep 5 + architect bullet
`bench/architect_sweeps.sh`, appended after Sweep 4:
```bash
run_sweep "5 (honesty: wunschdenken + non-citation post-mortem)" \
'on the path to retirement|may land in a future iteration|[Ww]ill back `Show|[Ww]ill (eventually|later) (be|support|back)|[Ii]f/?when .* (arrives|lands).*(will|would)|an earlier draft of this Decision said|previously all diagnostics were|what did change in 2026-|were retired 20[0-9]{2}-[0-9]{2}-[0-9]{2} in iter|originally listed here'
```
`skills/audit/agents/ailang-architect.md`, new bullet in "What you
check" (sibling to "DESIGN.md history-anchor regrowth"):
```markdown
- **DESIGN.md honesty drift.** Sweep 5 of `bench/architect_sweeps.sh`
flags Wunschdenken / non-citation post-mortem. Apply the
discriminator from DESIGN.md §"What this document is — and the
honesty rule it holds itself to": a hit is drift unless it is a
present-tense correctly-labelled reserved/excluded claim, present-
tense design rationale, or a block-quote journal citation. Forward
intent is roadmap drift; document/project history is journal drift.
```
## Components
| Artefact | Change |
|---|---|
| `docs/DESIGN.md` | + discriminator meta-subsection (~line 50 area); ~1218 passages rewritten present-tense / stripped (planner enumerates byte-exact from the classified sweep output) |
| `docs/PROSE_ROUNDTRIP.md` | L152 "A future iter may layer …" tool-use/MCP Wunschdenken stripped (the present-tense doc describes the cycle that exists; roadmap already owns forward integration intent) |
| load-bearing `///`/`//!` doc-comments | any that echo a corrected DESIGN.md sentence, corrected in lockstep (effect-doc-honesty satellite precedent) |
| `crates/ailang-core/tests/docs_honesty_pin.rs` | **new** — enumerated absent/present pin, sibling to `effect_doc_honesty_pin.rs` |
| `bench/architect_sweeps.sh` | + Sweep 5 (honesty) |
| `skills/audit/agents/ailang-architect.md` | + "DESIGN.md honesty drift" bullet citing the meta-subsection |
| `docs/roadmap.md` | the P1 honesty-lint entry checked off + this spec referenced; relocations land here only if a stripped forward-intent line is *not* already represented (audit-time check — most are already present) |
No language, checker, codegen, or runtime code changes. `ail
check`/`run`/`build` byte-unchanged by construction.
## Data flow
Build/test path is unchanged (docs + a new test file + a shell
sweep + an agent-definition file). The only new runtime artefact is
the pin test, which reads the doc files from `CARGO_MANIFEST_DIR/../../`
exactly as `effect_doc_honesty_pin.rs` does. The architect's review
flow gains one bullet and one sweep; both are advisory exactly like
the existing four sweeps (the script header already states non-zero
exit is advisory, architect-adjudicated).
## Error handling
- **Over-correction risk** (stripping a legitimate honest-reserved
claim): countered by the pin's *present*-anchor assertions —
`design_md_present_tense_anchors_present` fails if the gold-form
reserved claims (`Diverge` reserved, `No deriving`, regions
rejected, the known carve-outs) are dropped. The discriminator's
protected-exception list is itself pinned.
- **Sweep-5 false positives**: by design advisory, architect-
adjudicated against the codified discriminator — identical to the
existing four sweeps. Not a hard gate.
- **Pin brittleness** (a future legitimate edit trips an absent-
assertion): acceptable and intended — the pin is a *presence*
contract; a future edit that reintroduces a banned string must
consciously update the pin, which is the point (same contract as
`effect_doc_honesty_pin.rs`).
## Testing strategy
0. **Single-line pin substrings (carried from the effect-doc-honesty
Concern).** Every asserted absent/present substring MUST be a
contiguous single line in the doc file. The known failure mode:
the plan's verbatim corrected-replacement body soft-wraps a phrase
mid-string, contradicting a single-line pin substring, so the two
authoritative artefacts cannot both be applied verbatim. The
planner's pin-substring ↔ replacement-body-wrap cross-check must
account for the replacement body's own line wrap.
1. **RED first** (TDD, per implementer discipline): author
`docs_honesty_pin.rs` *before* the corrections; it runs RED
(absent-assertions fail because the fiction strings are still
present; present-anchor assertions fail because the discriminator
subsection is not yet written).
2. Apply the discriminator to the full `architect_sweeps.sh` (incl.
the new Sweep 5) output; classify every hit; correct every
FIX-classified passage; add the meta-subsection.
3. Pin goes GREEN. `cargo test --workspace` = prior + N new pin
tests, zero regressions.
4. The three sentinel drift/coverage tests (`design_schema_drift`,
`spec_drift`, `schema_coverage`) stay green (empirically confirms
none scans the corrected prose regions — same check the
effect-doc-honesty iter ran).
5. `bash bench/architect_sweeps.sh` re-run: Sweep 5 clean (0 honesty
hits); Sweeps 14 unchanged except where a corrected line removed
a previously-waved Sweep-1 date-anchor.
6. `cargo build --workspace` clean; `bench/check.py`,
`bench/compile_check.py`, `bench/cross_lang.py` unaffected (no
codegen change) — the milestone-close audit confirms.
## Acceptance criteria
- [ ] DESIGN.md carries the present-tense discriminator
meta-subsection; it is itself present-tense (describes a rule
the document currently obeys).
- [ ] Every `architect_sweeps.sh`+Sweep-5 hit is classified by the
discriminator; every FIX-classified passage in DESIGN.md +
satellites is rewritten present-tense or relocated to roadmap;
every KEEP passage (honest-reserved, design-rationale,
block-quote citation) is unchanged.
- [ ] `crates/ailang-core/tests/docs_honesty_pin.rs` exists, was
authored RED-first, and is GREEN: every corrected fiction
string absent, every present-tense + protected-exception
anchor present.
- [ ] `bench/architect_sweeps.sh` has Sweep 5; running it reports
zero honesty hits.
- [ ] `skills/audit/agents/ailang-architect.md` has the "DESIGN.md
honesty drift" bullet citing the meta-subsection.
- [ ] `cargo test --workspace` green (prior count + new pin tests, 0
regressions); `cargo build --workspace` clean; the three
sentinel drift/coverage tests still green.
- [ ] No language/checker/codegen/runtime change; `ail
check`/`run`/`build` byte-unchanged.
- [ ] `docs/roadmap.md` P1 honesty-lint entry checked off; any
stripped genuine forward intent not already in the roadmap is
added there.