Files
AILang/docs/plans/0081-docs-honesty-lint.1.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

38 KiB

docs-honesty-lint.1 — Implementation Plan

Parent spec: docs/specs/0039-docs-honesty-lint.md

For agentic workers: REQUIRED SUB-SKILL: use skills/implement to run this plan. Steps use - [ ] checkboxes for tracking.

Goal: Make docs/DESIGN.md + docs/PROSE_ROUNDTRIP.md mirror only the current state (present-tense), codify the honesty discriminator in-tree, and guard it with a wrap-robust enumerated pin + a wrap-robust advisory Sweep 5.

Architecture: Single iteration, six tasks. Task 1 lays the RED pin (the contract; wrap-robust via a whitespace-normalising helper). Task 2 ships wrap-robust Sweep 5 + all sweep-count lockstep wording + the architect bullet. Task 3 inserts the discriminator meta-subsection. Task 4 applies the 14 byte-exact DESIGN.md corrections + a procedural catch-all for any extra sweep hit. Task 5 fixes the PROSE satellite + relocates genuine forward intent to the roadmap. Task 6 turns the pin GREEN and runs full verification. No language/checker/codegen/runtime change; ail check/run/build byte-unchanged by construction.

Tech Stack: Rust integration test (crates/ailang-core/tests/), Bash (bench/architect_sweeps.sh), Markdown (docs/), agent-def Markdown (skills/audit/agents/).


Files this plan creates or modifies

  • Create: crates/ailang-core/tests/docs_honesty_pin.rs — wrap-robust enumerated absent/present regression pin, sibling to effect_doc_honesty_pin.rs.
  • Modify: bench/architect_sweeps.sh:50-56 — append wrap-robust Sweep 5; fix the "four sweeps clean" tail + header wording.
  • Modify: skills/audit/agents/ailang-architect.md:73-79 — add "DESIGN.md honesty drift" bullet after L79; fix "four sweeps" wording at L74,L77.
  • Modify: docs/DESIGN.md — insert discriminator meta-subsection into the ## Project ecosystem section (after L52); 14 byte-exact present-tense corrections (Task 4).
  • Modify: docs/PROSE_ROUNDTRIP.md:152-157 — strip the tool-use/MCP Wunschdenken paragraph; present-tense replacement.
  • Modify: docs/roadmap.md (P3 — Ideas) — add the relocated LLM tool-use / MCP / LSP forward-integration idea (the sole genuine forward intent stripped that is not already represented).

Procedural-enumeration note (spec L92-102, recon concern 3). The authoritative FIX scope is every hit of bash bench/architect_sweeps.sh after Sweep 5 is added, classified by the Task-4 discriminator table. The 14 corrections in Task 4 are every site knowable at plan time (Boss ran the corrected Sweep-5 regex against the current tree); Task 4 Step "catch-all" handles anything the sweep surfaces that is not in the table. The list is concrete (Iron Law) without being frozen-as-scope (the sweep stays authoritative at exec time).

Out of FIX scope — deliberate KEEPs (do NOT edit these):

  • docs/DESIGN.md L1215-1221 — the (con T)/Type-unchanged parenthetical is self-labelled "a tiebreaker, not a rationale — the substantive reasons above are what justify the choice." It asserts nothing false; it honestly discloses it is not a reason. Effort-as-rationale-disguised is a different anti-pattern with its own CLAUDE.md home; it is outside this spec's two-pronged discriminator. KEEP.
  • docs/DESIGN.md L1516-1517 — "Does not introduce regions. Regions were considered and rejected; see …" is present-tense design rationale (a current exclusion + internal pointer). KEEP.
  • docs/DESIGN.md L2725 — "Diverge is a reserved effect name with no op and no codegen." Gold-form honest-reserved. KEEP (and pinned present in Task 1).
  • docs/DESIGN.md the bare (iter 24.1) / (iter str-concat) provenance tags on the heap-Str primitive list (L2041, L2043, L2045, L2047, L2049) — terse true provenance, asserts nothing false, not narrative. KEEP. (L2050's Sweep-1 hit is the bare date inside this true provenance tag — a Sweep-1 date false-positive, not drift.)
  • docs/DESIGN.md L48-52 — the ## Project ecosystem Docs bullet ("docs/journal-archive.md (archived monolith for pre-2026-05-11 history)") is the present-tense doc-roles pointer the discriminator subsection joins. The L50 Sweep-1 hit is its embedded date. KEEP.
  • crates/ailang-core/specs/form_a.md — not modified, not pinned. Its only forward-style line ("multi-superclass chains are not yet supported", soft-wrapped L130-131) is gold-form honest-reserved. (Recon concern 2: the spec prose collapsed the line-wrap; the decision — no change — is correct and needs no pin.)

Task 1: RED pin — docs_honesty_pin.rs (wrap-robust contract)

Files:

  • Create: crates/ailang-core/tests/docs_honesty_pin.rs

Rationale for the norm() deviation from the spec's illustrative pin shape (spec L164-201 used naive d.contains()): docs/DESIGN.md is hard-wrapped at ~70 columns, so almost every target phrase is split across two source lines. A naive single-line contains() would assert nothing on wrapped phrases (false green). norm() collapses every whitespace run (incl. newline+indent) to a single space before matching, making every assertion wrap-independent. This simultaneously discharges spec Testing-strategy item 0 AND planner self-review item 6 (pin ↔ replacement-body contiguity): with norm(), contiguity is automatic — Task 4 may wrap replacement prose however Markdown needs; the pin still matches on word content. This is strictly more robust than the effect-doc-honesty precedent's fragile "keep the phrase on one line" approach.

  • Step 1: Write the pin file
//! Regression pin: the canonical docs must stay present-tense-honest.
//!
//! `docs/DESIGN.md` (and `docs/PROSE_ROUNDTRIP.md`) must not assert
//! something exists/works/changed that does not. This pin fails if any
//! swept-and-corrected Wunschdenken / post-mortem string reappears, or
//! if any present-tense corrected anchor (incl. the protected
//! honest-reserved exceptions) is dropped. Matching is
//! whitespace-normalised so the assertions are independent of the
//! docs' hard line-wrap. Companion to `effect_doc_honesty_pin.rs` and
//! to Sweep 5 of `bench/architect_sweeps.sh`.

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}"))
}

/// Collapse every whitespace run (incl. newline + indent) to a single
/// space, so a phrase split across the docs' hard line-wrap still
/// matches a single-line assertion substring.
fn norm(s: &str) -> String {
    s.split_whitespace().collect::<Vec<_>>().join(" ")
}

#[test]
fn design_md_has_no_wunschdenken() {
    let d = norm(&read("docs/DESIGN.md"));
    assert!(!d.contains("is on the path to retirement"),
        "DESIGN.md: Boehm retirement is forward intent — it lives in docs/roadmap.md, not DESIGN.md");
    assert!(!d.contains("Will back `Show Float`"),
        "DESIGN.md: 'Will back Show Float' asserts a future capability as fact");
    assert!(!d.contains("codegen ships in a future iter"),
        "DESIGN.md: float_to_str codegen is reserved/not-yet-shipped, not a promised future ship");
    assert!(!d.contains("may land in a future iteration"),
        "DESIGN.md: auto-derivation 'may land in a future iteration' is Wunschdenken");
    assert!(!d.contains("If/when concurrency arrives"),
        "DESIGN.md: 'if/when concurrency arrives … will be' is forward conditional");
    assert!(!d.contains("was deferred — all three layer additively"),
        "DESIGN.md: deferred future integration paths are roadmap intent, not DESIGN.md");
}

#[test]
fn design_md_has_no_doc_archaeology() {
    let d = norm(&read("docs/DESIGN.md"));
    assert!(!d.contains("An earlier draft of this Decision said"),
        "DESIGN.md: doc-archaeology ('an earlier draft said … a follow-up replaced it') lives in docs/journals/");
    assert!(!d.contains("previously all diagnostics were `Error`"),
        "DESIGN.md: 'previously all diagnostics were Error' is project history → docs/journals/");
    assert!(!d.contains("What did change in 2026-05-13 (this milestone) is"),
        "DESIGN.md: 'what did change in <date> (this milestone)' is post-mortem narrative");
    assert!(!d.contains("primitives were retired 2026-05-14 in iter rpe.1"),
        "DESIGN.md: 'X were retired <date> in iter Y' is history → docs/journals/");
    assert!(!d.contains("were retired in iter rpe.1 (2026-05-14)"),
        "DESIGN.md: the second per-type-print-op retirement-narrative sentence is history");
    assert!(!d.contains("originally listed here as the eighth fixture, was retired"),
        "DESIGN.md: 'originally listed … was retired by milestone X' is doc-archaeology");
}

#[test]
fn design_md_present_tense_anchors_present() {
    let d = norm(&read("docs/DESIGN.md"));
    // the discriminator meta-subsection (Task 3)
    assert!(d.contains("the honesty rule it holds itself to"),
        "DESIGN.md must carry the discriminator meta-subsection heading");
    assert!(d.contains("whether the document asserts something exists, works, or changed that does not"),
        "DESIGN.md discriminator must state the operational test");
    // protected honest-reserved exceptions — over-correction guard
    assert!(d.contains("`Diverge` is a reserved effect name with no op and no codegen"),
        "the gold-form honest-reserved anchor must remain (do not over-strip)");
    assert!(d.contains("Regions were considered and rejected"),
        "the present-tense design-rationale exclusion must remain (do not over-strip)");
    assert!(d.contains("a tiebreaker, not a rationale"),
        "the self-labelled tiebreaker is honest and stays (do not over-strip)");
    // corrected present-tense anchors (Task 4)
    assert!(d.contains("`--alloc=gc` selects the transitional Boehm backend"),
        "DESIGN.md must describe Boehm present-tense, not as 'on the path to retirement'");
    assert!(d.contains("type-installed; codegen is reserved and not yet shipped"),
        "float_to_str must be present-tense honest-reserved");
    assert!(d.contains("AILang demands annotations *because* the LLM author can produce them effortlessly"),
        "the 'does not infer everything' bullet must be present-tense (no doc-archaeology)");
    assert!(d.contains("`io/print_str` is the only built-in direct-output effect-op"),
        "the print-op set must be stated present-tense, not as a retirement narrative");
}

#[test]
fn prose_roundtrip_md_has_no_wunschdenken() {
    let p = norm(&read("docs/PROSE_ROUNDTRIP.md"));
    assert!(!p.contains("A future iter may layer a tool-use schema"),
        "PROSE_ROUNDTRIP.md: tool-use/MCP forward intent lives in docs/roadmap.md");
    assert!(p.contains("the lowest-common-denominator cycle and always works with any client"),
        "PROSE_ROUNDTRIP.md must close present-tense on the static-prompt path");
}
  • Step 2: Run the pin — verify it fails RED for the right reasons

Run: cargo test --workspace -p ailang-core --test docs_honesty_pin Expected: COMPILES, 4 tests run, all 4 FAIL. Specifically: design_md_has_no_wunschdenken fails on the first assert! ("is on the path to retirement" still present); design_md_has_no_doc_archaeology fails ("An earlier draft of this Decision said" still present); design_md_present_tense_anchors_present fails ("the honesty rule it holds itself to" absent — Task 3 not yet done); prose_roundtrip_md_has_no_wunschdenken fails ("A future iter may layer a tool-use schema" still present). This RED state is the contract Tasks 3-5 discharge.


Task 2: Wrap-robust Sweep 5 + sweep-count lockstep + architect bullet

Files:

  • Modify: bench/architect_sweeps.sh:50-56
  • Modify: skills/audit/agents/ailang-architect.md:73-79 (bullet) and :74,77 (wording)

Concern-1 lockstep (recon): adding a fifth sweep makes "All four sweeps clean." / the header "four design-md-consolidation sweeps" / ailang-architect.md "the four sweeps" stale. Per planner self-review item 7, every coordinated edit ships in THIS task — no deferral.

The Sweep-5 regex uses contiguous source-line fragments, not full phrases: architect_sweeps.sh's run_sweep is line-based (grep -nE … "$DESIGN"), and the target phrases are soft-wrapped, so a full-phrase pattern would miss them (Boss verified: the spec's illustrative full-phrase regex caught only 4 of the ~12 sites). Each fragment below was verified contiguous on a single current DESIGN.md line.

  • Step 1: Append Sweep 5 after Sweep 4

In bench/architect_sweeps.sh, the current lines 50-56 are:

run_sweep "4 (workflow + stale cross-references)" \
    'agents/README\.md|ailang-docwriter|Recently lifted|see the JOURNAL queue|later the same day|Sharpened later|closure conversion in JOURNAL'

if [[ $HITS -eq 0 ]]; then
    echo "All four sweeps clean."
fi
exit $HITS

Replace those exact lines with:

run_sweep "4 (workflow + stale cross-references)" \
    'agents/README\.md|ailang-docwriter|Recently lifted|see the JOURNAL queue|later the same day|Sharpened later|closure conversion in JOURNAL'

run_sweep "5 (honesty: wunschdenken + non-citation post-mortem)" \
    'on the path to|may land in a future|ships in a future iter|[Ww]ill back `Show|[Ww]ill eventually (be|support|back)|[Ii]f/?when [a-z]+ (arrives|lands)|[Aa]n earlier draft of this|previously all diagnostics were|[Ww]hat did change in 20|were retired 20[0-9]{2}-|retired in iter [a-z]|originally listed|\bwas deferred\b|foundation for [a-z]'

if [[ $HITS -eq 0 ]]; then
    echo "All five sweeps clean."
fi
exit $HITS
  • Step 2: Fix the script header "four sweeps" wording

In bench/architect_sweeps.sh, the current header lines 4-6 are:

# Run the four design-md-consolidation sweep-invariant greps against
# docs/DESIGN.md. Output: matched lines, prefixed by sweep name. Exit 0
# if all four sweeps are clean, 1 if any anchor was found.

Replace with:

# Run the five DESIGN.md honesty/anchor sweep-invariant greps against
# docs/DESIGN.md. Output: matched lines, prefixed by sweep name. Exit 0
# if all five sweeps are clean, 1 if any anchor was found. Sweeps 1-4
# are the design-md-consolidation history-anchor invariants; Sweep 5
# is the docs-honesty-lint Wunschdenken / post-mortem invariant.

And the current header line 16:

# A non-zero exit is advisory, not authoritative: the architect agent

is unchanged (the advisory contract still holds for all five). No edit to line 16; it is quoted only to bound the replacement at line 6.

  • Step 3: Run the sweep — confirm Sweep 5 fires on the still-uncorrected tree

Run: bash bench/architect_sweeps.sh; echo "EXIT=$?" Expected: EXIT=1. Output now includes a === Sweep: 5 (honesty: …) === block listing the uncorrected honesty sites (at minimum DESIGN.md lines containing "on the path to", "may land in a future", "ships in a future iter", "Will back `Show", "If/when concurrency", "An earlier draft of this", "previously all diagnostics were", "What did change in 20", "were retired 20…", "retired in iter", "originally listed", "was deferred"). Sweeps 1-4 unchanged from the recon baseline. This proves Sweep 5 detects the wrapped sites the spec's illustrative regex missed.

  • Step 4: Add the "DESIGN.md honesty drift" architect bullet

In skills/audit/agents/ailang-architect.md, the current lines 73-79 are the "DESIGN.md history-anchor regrowth" bullet:

- **DESIGN.md history-anchor regrowth.** Run
  `bash bench/architect_sweeps.sh` from the repo root. Exit 0 = clean.
  Exit 1 = at least one of the four sweeps from the
  design-md-consolidation milestone (2026-05-10) matched. Review each
  match: a legitimate quote (e.g. a journal-entry citation inside a
  block-quote) is fine; a fresh history anchor / REVERTED narrative /
  workflow detail / stale cross-reference is drift to flag.

Replace those exact lines with (the wording fix "four"→"five sweeps … 1-4 / Sweep 5" PLUS the new sibling bullet appended):

- **DESIGN.md history-anchor regrowth.** Run
  `bash bench/architect_sweeps.sh` from the repo root. Exit 0 = clean.
  Exit 1 = at least one of the five sweeps matched. Sweeps 1-4 are the
  design-md-consolidation history-anchor invariants (2026-05-10):
  review each match — a legitimate quote (e.g. a journal-entry
  citation inside a block-quote) is fine; a fresh history anchor /
  REVERTED narrative / workflow detail / stale cross-reference is
  drift to flag.
- **DESIGN.md honesty drift.** Sweep 5 of `bench/architect_sweeps.sh`
  (the docs-honesty-lint invariant) 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.
  • Step 5: Confirm no other "four sweeps" string survives in the architect file

Run: grep -n "four sweeps\|the four sweeps\|four design-md" skills/audit/agents/ailang-architect.md Expected: no output (exit 1 from grep = no match). If any line prints, it is a missed lockstep site — fix it to the "five / Sweeps 1-4 + Sweep 5" wording before proceeding.


Task 3: Insert the discriminator meta-subsection into DESIGN.md

Files:

  • Modify: docs/DESIGN.md (insert after L52, inside ## Project ecosystem)

The ## Project ecosystem section spans L19-L59. Its Docs bullet is L48-L52; the section's closing prose begins L56. Insert the new ### subsection between the Docs bullet and the closing prose.

  • Step 1: Read the exact insertion anchor

Run: sed -n '52,57p' docs/DESIGN.md Expected (verbatim, current tree):

  specs), `plans/` (per-iteration implementation plans).

The set of skills + agents is the development toolchain; new entries
are added here as soon as they are established. The skill system is
documented in `skills/README.md`; the agent roster lives there too.

(Line 52 ends the Docs bullet; line 53 is blank; lines 54-55 begin the closing prose. The exact closing-prose wording on your tree is what Step 2 anchors against — quote it from this Step's output, do not assume.)

  • Step 2: Insert the meta-subsection after the blank line that follows L52

Insert, immediately after the specs), \plans/` …line and its following blank line (i.e. as a new block beforeThe set of skills + agents is the development toolchain`):

### 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.

(Trailing blank line included so the following The set of skills + agents … paragraph keeps its blank-line separator.)

  • Step 3: Verify the subsection landed and is present-tense

Run: grep -n "the honesty rule it holds itself to\|asserts something exists, works, or changed that does not" docs/DESIGN.md Expected: two lines print (the heading ~L54 area and the discriminator sentence). Confirm by eye that the inserted block contains no first-person future ("we will", "planned") — it describes a rule the document currently obeys.


Task 4: Apply the 14 byte-exact DESIGN.md corrections + procedural catch-all

Files:

  • Modify: docs/DESIGN.md (14 enumerated sites + any extra sweep hit)

Every correction below gives the exact current text (verbatim, with its line numbers as of plan time — re-confirm with the cited grep since Task 3 shifted lines below L54 by +~22) and the exact replacement. Apply each with an exact-string edit. The discriminator classification is stated for each so the implementer can verify the judgement, not just transcribe.

Line-number caveat: Task 3 inserted ~22 lines at ~L54, so every site below L54 is now ~22 lines lower than its plan-time number. Anchor every edit by the verbatim string, not the line number. The grep in each step relocates the site reliably.

  • Step 1: L484 — strip the iter history-anchor (history-anchor)

Run: grep -n "Both extensions accepted" docs/DESIGN.md Current text (verbatim, two lines):

  **Both extensions accepted.** Since iter ext-cli.1 (2026-05-12),
  every path-taking CLI subcommand accepts either `.ail` (Form A)

Replace with:

  **Both extensions accepted.** Every path-taking CLI subcommand
  accepts either `.ail` (Form A)

(The bold lead is a present-tense fact and stays; only the Since iter ext-cli.1 (2026-05-12), provenance clause is removed. The trailing or \.ail.json` (Form B) as input.` on the next line is unchanged.)

  • Step 2: L503 — present-tense the authored-form statement (post-mortem)

Run: grep -n "What did change in 2026-05-13" docs/DESIGN.md Current text (verbatim):

  unchanged. What did change in 2026-05-13 (this milestone) is
  which form is authored: authors now write Form A; the JSON-AST
  is materialised in-process by callers that need it. The two

Replace with:

  unchanged. The authored form is Form A; the JSON-AST is
  materialised in-process by callers that need it. The two
  • Step 3: L650-653 — present-tense the cycle's fallback (Wunschdenken + malformed)

Run: grep -n "richer integration paths" docs/DESIGN.md Current text (verbatim, four lines — note the current text is also grammatically broken: "layer additively on the static-prompt path ships, which remains"):

The discussion of richer integration paths (LLM tool-use, MCP
server, LSP) was deferred — all three layer additively on the
static-prompt path ships, which remains the
lowest-common-denominator fallback that always works.

Replace with:

The cycle's lowest-common-denominator path is the static prompt
(`ail merge-prose | client | ail parse | ail check`), which works
with any client.

(LLM tool-use / MCP / LSP forward integration intent is relocated to docs/roadmap.md P3 in Task 5 Step 2.)

  • Step 4: L655 — de-date the empirical-addendum heading (mild archaeology)

Run: grep -n "### Empirical addendum" docs/DESIGN.md Current text (verbatim):

### Empirical addendum (2026-05-12)

Replace with:

### Empirical addendum — cross-model authoring measurement

(The section content — the measurement results — is present-tense evidence for Decision 6 and is unchanged.)

  • Step 5: L1318-1321 — present-tense the Warning severity (post-mortem)

Run: grep -n "The first ever Warning-level diagnostic" docs/DESIGN.md Current text (verbatim, four lines):

Severity: `Warning`. The first ever Warning-level diagnostic;
previously all diagnostics were `Error`. CLI exit semantics
adjusted: `ail check`, `ail build`, `ail emit-ir` exit 1 only on
at least one Error. Warnings print but do not abort.

Replace with:

Severity: `Warning`. `ail check`, `ail build`, `ail emit-ir` exit 1
only on at least one `Error`; warnings print but do not abort.
  • Step 6: L1504-1509 — present-tense the "does not infer everything" bullet (doc-archaeology)

Run: grep -n "An earlier draft of this" docs/DESIGN.md Current text (verbatim, six lines):

- **Does not infer everything.** An earlier draft of this
  Decision said "uniqueness is fully inferred" — that was the
  mainstream RC position. A follow-up discussion
  replaced it: AILang demands annotations *because* the LLM
  author can produce them effortlessly. The compiler does
  inference *plus* verification of contracts.

Replace with:

- **Does not infer everything.** AILang demands annotations
  *because* the LLM author can produce them effortlessly. The
  compiler does inference *plus* verification of contracts.
  • Step 7: L1513-1515 — present-tense the atomic-refcount bullet (Wunschdenken)

Run: grep -n "Does not commit to atomic refcounts" docs/DESIGN.md Current text (verbatim, three lines):

- **Does not commit to atomic refcounts.** AILang is currently
  single-threaded. If/when concurrency arrives, atomic-vs-non-
  atomic will be a separate decision per allocation kind.

Replace with:

- **Does not commit to atomic refcounts.** AILang is
  single-threaded; refcounts are non-atomic.
  • Step 8: L1837-1839 — drop the auto-derivation future tail (Wunschdenken)

Run: grep -n "AILang does not auto-derive instances" docs/DESIGN.md Current text (verbatim, four lines):

**No `deriving`.** AILang does not auto-derive instances. `instance Eq
MyType` must be written by hand. Auto-derivation may land in a future
iteration if the Feature-acceptance criterion holds for it; that
discussion is out of scope here.

Replace with:

**No `deriving`.** AILang does not auto-derive instances. `instance Eq
MyType` must be written by hand.

(The "may land in a future iteration if the criterion holds" tail is speculative — not a committed forward intent — so it is deleted, not relocated; a roadmap "maybe someday" entry would itself be vapour.)

  • Step 9: L2045-2046 — present-tense honest-reserved float_to_str (Wunschdenken)

Run: grep -n "float_to_str : (borrow Float)" docs/DESIGN.md Current text (verbatim, two lines):

- `float_to_str : (borrow Float) -> Str` (iter 24.1, type-installed;
  codegen ships in a future iter per roadmap). Will back `Show Float`.

Replace with:

- `float_to_str : (borrow Float) -> Str` (iter 24.1) —
  type-installed; codegen is reserved and not yet shipped.

(type-installed is honest present-reserved and stays in present- tense form; "codegen ships in a future iter per roadmap" and "Will back Show Float" are the Wunschdenken removed. The roadmap already carries the heap-Str/float_to_str queue entry — no relocation.)

  • Step 10: L2025-2028 — present-tense the print-op set (post-mortem)

Run: grep -n "primitives were retired 2026-05-14 in iter rpe.1" docs/DESIGN.md Current text (verbatim, four lines):

Routing through `print` replaces the ad-hoc per-type print
effect-ops; the `io/print_int`, `io/print_bool`, `io/print_float`
primitives were retired 2026-05-14 in iter rpe.1, leaving
`io/print_str` as the only surviving direct-output effect-op.

Replace with:

Routing through `print` is the path for non-`Str` primitives;
`io/print_str` is the only built-in direct-output effect-op.
  • Step 11: L2063-2065 — delete the duplicate retirement-narrative sentence (post-mortem)

Run: grep -n "were retired in iter rpe.1 (2026-05-14)" docs/DESIGN.md Current text (verbatim, three lines):

the heap-Str result of `show x` into `io/print_str`. The per-type
effect-ops `io/print_int`, `io/print_bool`, `io/print_float` were
retired in iter rpe.1 (2026-05-14).

Replace with:

the heap-Str result of `show x` into `io/print_str`.

(The preceding clause already states the present behaviour; the "were retired in iter rpe.1" sentence is pure history and is deleted.)

  • Step 12: L2133-2138 — present-tense the prelude-embed parenthetical (doc-archaeology)

Run: grep -n "originally listed" docs/DESIGN.md Current text (verbatim, six lines):

rejection-shape tests, not in the round-trip gate. (The
prelude's compile-time-embed carve-out, originally listed
here as the eighth fixture, was retired 2026-05-14 by
milestone prelude-decouple; the prelude is now embedded as
`examples/prelude.ail` in `ailang-surface` and parsed at
compile time via `ailang_surface::parse_prelude`.)

Replace with:

rejection-shape tests, not in the round-trip gate. (The prelude is
embedded as `examples/prelude.ail` in `ailang-surface` and parsed
at compile time via `ailang_surface::parse_prelude`.)
  • Step 13: L2519-2520 — present-tense the Boehm backend (Wunschdenken)

Run: grep -n "is on the path to" docs/DESIGN.md Current text (verbatim, two lines):

rewrites respectively. Boehm-on-`--alloc=gc` is on the path to
retirement; see `docs/roadmap.md` for the active queue.

Replace with:

rewrites respectively. `--alloc=gc` selects the transitional Boehm
backend; `--alloc=rc` is the canonical backend (Decision 10) and the
CLI default.

(Boehm-retirement forward intent is already a P2 todo in docs/roadmap.md — no relocation needed.)

  • Step 14: Procedural catch-all — classify any remaining Sweep-5 hit

Run: bash bench/architect_sweeps.sh For every line in the === Sweep: 5 (honesty: …) === block that is NOT one of the 13 sites corrected in Steps 1-13 above, apply the discriminator (DESIGN.md §"What this document is — and the honesty rule it holds itself to"):

  • present-tense correctly-labelled reserved/excluded claim → KEEP, record "KEEP: — reason" in the per-iter journal.

  • present-tense design rationale → KEEP, record likewise.

  • block-quote journal citation → KEEP, record likewise.

  • Wunschdenken (forward intent) → rewrite present-tense or, if genuine committed forward intent not already in docs/roadmap.md, relocate there; record "FIX: — before→after" in the journal AND add a matching assert!(!d.contains("…")) + present-anchor pair to docs_honesty_pin.rs.

  • post-mortem / doc-archaeology → rewrite present-tense; record + pin as above. This step makes the enumeration the sweep-output-classified set (spec L92-102), not a frozen list. Expected on the current tree: zero additional hits beyond Steps 1-13 (Boss pre-verified), so this step typically records only the KEEP set; it exists so a missed/new site cannot slip the gate.

  • Step 15: Re-run the pin — DESIGN.md assertions now GREEN

Run: cargo test --workspace -p ailang-core --test docs_honesty_pin design_md Expected: design_md_has_no_wunschdenken, design_md_has_no_doc_archaeology, and design_md_present_tense_anchors_present all PASS (Task 3 + Task 4 discharged them). prose_roundtrip_md_has_no_wunschdenken is NOT in this filter (Task 5 discharges it).


Task 5: PROSE_ROUNDTRIP satellite + roadmap relocation

Files:

  • Modify: docs/PROSE_ROUNDTRIP.md:152-157

  • Modify: docs/roadmap.md (P3 — Ideas)

  • Step 1: Strip the tool-use/MCP Wunschdenken paragraph

Run: grep -n "A future iter may layer a tool-use schema" docs/PROSE_ROUNDTRIP.md Current text (verbatim, L152-157, the final paragraph of the file):

A future iter may layer a tool-use schema (LLM calls back into
`ail parse` / `ail check` from inside its turn) or an MCP server
(any MCP-compatible client discovers AILang's resources, tools, and
prompts) on top of this cycle. Both are additive — the static-prompt
fallback documented here remains the lowest-common-denominator path
that always works.

Replace with:

The static-prompt path documented here is the lowest-common-denominator
cycle and always works with any client.
  • Step 2: Add the relocated forward intent to roadmap P3

In docs/roadmap.md, the ## P3 — Ideas section ends with the Richer integration paths between RC and uniqueness idea. Read the last P3 entry:

Run: grep -n "Richer integration paths between RC and" docs/roadmap.md Current tail (verbatim):

- [ ] **\[idea\]** Richer integration paths between RC and
  uniqueness — deferred from the 21' arc; revisit once the
  uniqueness inference covers more program shapes.

Append immediately after that entry's last line:

- [ ] **\[idea\]** LLM tool-use schema / MCP server / LSP front-end
  over the prose-roundtrip cycle — additive layers on top of the
  static-prompt `ail merge-prose | client | ail parse | ail check`
  path (LLM calls back into `ail parse`/`ail check` mid-turn; an
  MCP-compatible client discovers AILang's resources/tools/prompts).
  Relocated here from DESIGN.md §"prose roundtrip" + PROSE_ROUNDTRIP.md
  by the docs-honesty-lint milestone (forward intent does not live in
  the canonical docs).
  - context: `docs/specs/0039-docs-honesty-lint.md`
  • Step 3: Re-run the full pin — all four tests GREEN

Run: cargo test --workspace -p ailang-core --test docs_honesty_pin Expected: 4 tests, all PASS.


Task 6: Full verification

Files: none modified — verification only.

  • Step 1: Pin GREEN in isolation

Run: cargo test --workspace -p ailang-core --test docs_honesty_pin Expected: test result: ok. 4 passed; 0 failed.

  • Step 2: Full workspace test — no regression

Run: cargo test --workspace 2>&1 | tail -5 Expected: all suites pass; total = prior baseline + 4 (the new pin tests). Zero failures. (Baseline before this iter is whatever cargo test --workspace reported at 928e1c0; the only delta is +4.)

  • Step 3: Sentinel drift/coverage trio still green

Run: cargo test --workspace -p ailang-core --test design_schema_drift --test spec_drift --test schema_coverage Expected: all green — empirically confirms none of the three scans the corrected prose regions (same invariant the effect-doc-honesty iter relied on).

  • Step 4: Sweep 5 clean; Sweeps 1-4 reduced where corrected

Run: bash bench/architect_sweeps.sh; echo "EXIT=$?" Expected: the === Sweep: 5 …=== block is ABSENT (zero honesty hits — every FIX site corrected). Sweeps 1-4: Sweep 1's hit count is reduced versus the recon baseline (the corrected L484/L503/L655/L2027/L2065/L2135 date-anchors are gone); any residual Sweep-1 hits are the deliberate KEEPs (L50 doc-roles pointer date, L2050 true (iter str-concat, 2026-05-13) provenance). EXIT may still be 1 iff a deliberate-KEEP Sweep-1 date-anchor remains — that is expected and architect- adjudicated, not a failure of this iter. Record the post-iter sweep output in the per-iter journal.

  • Step 5: Workspace builds; no language/codegen change

Run: cargo build --workspace 2>&1 | tail -2 Expected: Finished clean, zero warnings introduced by this iter. (No crates/**/src/** file other than the new test was touched — confirm with git diff --stat -- 'crates/**/src/**' = empty.)

  • Step 6: ail behaviour byte-unchanged (spec invariant)

Run: git diff --stat -- 'crates/' 'runtime/' | grep -v 'tests/docs_honesty_pin.rs' || echo "ONLY THE PIN" Expected: ONLY THE PIN (the sole crates/ change is the new test file; no checker/codegen/runtime source touched, so ail check/run/build are byte-unchanged by construction — no behavioural re-test needed).


Self-review (planner Step 5) — completed inline

  1. Spec coverage: spec Architecture layer 1 (discriminator subsection) → Task 3; layer 2 (corrections, DESIGN.md + satellites) → Tasks 4-5; layer 3 (pin + Sweep 5 + architect bullet) → Tasks 1-2. Spec Components table rows all mapped (DESIGN.md→T3/T4, PROSE→T5, doc-comments→T4 catch-all if any echo surfaces, pin→T1, architect_sweeps.sh→T2, ailang-architect.md→T2, roadmap→T5). Acceptance criteria 1-8 each have a verifying step (T3S3, T4S14, T1+T5S3, T6S4, T2S4-5, T6S2-3, T6S5-6, T5S2). Covered.
  2. Placeholder scan: no "TBD/TODO/implement later/similar to Task/add appropriate". Every edit shows verbatim before + exact after. The Task-4 catch-all is a fully-specified decision procedure with a concrete rubric, not a placeholder (spec-mandated procedural enumeration, L92-102).
  3. Type/name/path consistency: docs_honesty_pin.rs, norm(), read(), sweep name "5 (honesty: …)", the bullet title "DESIGN.md honesty drift", the subsection heading "What this document is — and the honesty rule it holds itself to" are identical across Tasks 1/2/3/6. Pin present-anchors quote the exact Task-3/Task-4 replacement strings (cross-checked: "--alloc=gc selects the transitional Boehm backend" = T4S13 after; "type-installed; codegen is reserved and not yet shipped" = T4S9 after; "AILang demands annotations because the LLM author can produce them effortlessly" = T4S6 after; "io/print_str is the only built-in direct-output effect-op" = T4S10 after; "the lowest-common-denominator cycle and always works with any client" = T5S1 after; discriminator anchors = T3 body).
  4. Step granularity: every step is one edit or one command, 2-5 min. Task 4 is 15 steps but each is one site.
  5. No commit steps: none present.
  6. Pin/replacement substring contiguity: discharged structurally by norm() (Task 1) — all pin assertions match on whitespace-normalised content, so no replacement-body line-wrap can desync from a pin substring. Each pin present-anchor was word-for-word cross-checked against its Task-3/4/5 replacement (item 3 above). This is the recurring grep/contains line-wrap family (iter-revert Concern 2; effect-doc-honesty Task 2) — scrubbed here by the normalising mechanism, not by fragile single-line discipline.
  7. Compile-gate vs deferred-caller: N/A — no signature change; the only crates/ change is an additive test file. No crate-wide compile gate depends on a deferred edit. Task 2's Sweep-5-fires check (T2S3) precedes the corrections by design (makes the candidate list runnable — recon concern 3 chicken/egg resolved).
  8. Verification-command filter strings resolve: --test docs_honesty_pin targets the file created in T1 (exists by the time any Run referencing it executes — T1 is first). --test design_schema_drift --test spec_drift --test schema_coverage — recon ratified all three files exist. cargo test … design_md filter (T4S15) matches the three design_md_* test fns named in T1. grep -n "four sweeps…" (T2S5) is an expect-empty negative check with an explicit "no output" expected result, so "nothing matched" is the asserted success, not a masked no-op. git diff --stat filters (T6S5-6) are existence/emptiness checks with explicit expected output.