iter docs-honesty-lint.1: canonical docs present-tense-honest + wrap-robust pin + Sweep 5

Strips Wunschdenken (forward intent stated as fact) and non-citation
post-mortem / doc-archaeology from docs/DESIGN.md (14 corrections incl.
1 Step-14 procedural-catch-all site the spec's illustrative regex would
have missed) + docs/PROSE_ROUNDTRIP.md; genuine forward intent (LLM
tool-use / MCP / LSP) relocated to roadmap P3. Codifies the
tense+modality discriminator as a present-tense DESIGN.md meta-
subsection. Guards two ways: wrap-robust enumerated absent/present pin
(crates/ailang-core/tests/docs_honesty_pin.rs, norm() whitespace-
collapse helper — structurally discharges the recurring grep/contains
line-wrap failure family) + wrap-robust advisory Sweep 5 in
architect_sweeps.sh (contiguous-fragment regex) with full sweep-count
lockstep + new ailang-architect.md "DESIGN.md honesty drift" bullet.
KEEPs preserved (Diverge-reserved, regions-rejected, self-labelled
tiebreaker). No language/checker/codegen/runtime change — sole crates/
change is the additive pin; ail check/run/build byte-unchanged.
Workspace 609/0 (delta +4 pin), sentinel trio green, build clean.
This commit is contained in:
2026-05-18 12:30:03 +02:00
parent de66eb7ab2
commit 7580d434f0
8 changed files with 330 additions and 62 deletions
+8 -3
View File
@@ -1,9 +1,11 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# bench/architect_sweeps.sh # bench/architect_sweeps.sh
# #
# Run the four design-md-consolidation sweep-invariant greps against # Run the five DESIGN.md honesty/anchor sweep-invariant greps against
# docs/DESIGN.md. Output: matched lines, prefixed by sweep name. Exit 0 # docs/DESIGN.md. Output: matched lines, prefixed by sweep name. Exit 0
# if all four sweeps are clean, 1 if any anchor was found. # 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.
# #
# Companion to skills/audit/agents/ailang-architect.md "What you check # Companion to skills/audit/agents/ailang-architect.md "What you check
# / DESIGN.md history-anchor regrowth". See JOURNAL 2026-05-10 # / DESIGN.md history-anchor regrowth". See JOURNAL 2026-05-10
@@ -50,7 +52,10 @@ run_sweep "3 (schema SoT)" \
run_sweep "4 (workflow + stale cross-references)" \ 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' '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 if [[ $HITS -eq 0 ]]; then
echo "All four sweeps clean." echo "All five sweeps clean."
fi fi
exit $HITS exit $HITS
@@ -0,0 +1,13 @@
{
"iter_id": "docs-honesty-lint.1",
"date": "2026-05-18",
"mode": "standard",
"outcome": "DONE",
"tasks_total": 6,
"tasks_completed": 6,
"reloops_per_task": { "1": 0, "2": 0, "3": 0, "4": 0, "5": 0, "6": 0 },
"review_loops_spec": 0,
"review_loops_quality": 0,
"blocked_reason": null,
"notes": "Docs+test-only iter. Two DONE_WITH_CONCERNS (T3 plan-internal pin-vs-body bold desync resolved intent-preservingly; T4 Step-14 catch-all surfaced 1 genuine extra FIX site, handled per procedure). No re-loops triggered; both concerns resolved within the implementer phase before the spec check. Scope guard held: sole crates/ change is the additive pin file."
}
@@ -0,0 +1,100 @@
//! 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");
assert!(!d.contains("was deferred from milestone 22 entirely"),
"DESIGN.md: 'was deferred from milestone 22 entirely … A future iter ships' is history+Wunschdenken (Task 4 Step 14 catch-all)");
assert!(!d.contains("A future iter ships the full prose projection"),
"DESIGN.md: Form-B class/instance prose 'a future iter ships' is forward intent → roadmap");
}
#[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");
assert!(d.contains("the render in `crates/ailang-prose/src/lib.rs` is a placeholder and informational only"),
"the Form-B class/instance prose-projection state must be present-tense (Task 4 Step 14 catch-all)");
}
#[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");
}
+53 -48
View File
@@ -53,6 +53,28 @@ evolving in lockstep with the language:
- **Tests**: unit tests per crate plus E2E in `crates/ail/tests/e2e.rs`. Every - **Tests**: unit tests per crate plus E2E in `crates/ail/tests/e2e.rs`. Every
new compiler path needs a test, otherwise the feature does not count as done. new compiler path needs a test, otherwise the feature does not count as done.
### 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.
When the language grows, these components grow with it. New tools that When the language grows, these components grow with it. New tools that
strengthen the LLM tooling (e.g. `ail diff`, IR snapshot diffs, new agents) strengthen the LLM tooling (e.g. `ail diff`, IR snapshot diffs, new agents)
explicitly belong in the ecosystem inventory of this section and are added explicitly belong in the ecosystem inventory of this section and are added
@@ -481,8 +503,8 @@ on the shelf for a future iter only if both fail.
to every existing subcommand; the parser is a producer, not a to every existing subcommand; the parser is a producer, not a
gatekeeper. gatekeeper.
**Both extensions accepted.** Since iter ext-cli.1 (2026-05-12), **Both extensions accepted.** Every path-taking CLI subcommand
every path-taking CLI subcommand accepts either `.ail` (Form A) accepts either `.ail` (Form A)
or `.ail.json` (Form B) as input. For `.ail` paths the subcommand or `.ail.json` (Form B) as input. For `.ail` paths the subcommand
parses through `ailang_surface::parse` in-line and then proceeds parses through `ailang_surface::parse` in-line and then proceeds
with the same loaded `Module` value that `.ail.json` would have with the same loaded `Module` value that `.ail.json` would have
@@ -500,9 +522,8 @@ on the shelf for a future iter only if both fail.
- It does not change which form is canonical: the JSON-AST remains - It does not change which form is canonical: the JSON-AST remains
the hashable, content-addressed representation, and all hashing, the hashable, content-addressed representation, and all hashing,
content-addressing, and cross-module references flow through it content-addressing, and cross-module references flow through it
unchanged. What did change in 2026-05-13 (this milestone) is unchanged. The authored form is Form A; the JSON-AST is
which form is authored: authors now write Form A; the JSON-AST materialised in-process by callers that need it. The two
is materialised in-process by callers that need it. The two
forms are byte-isomorphic by the round-trip invariant. Form (A) forms are byte-isomorphic by the round-trip invariant. Form (A)
is one projection; the JSON-AST stays canonical. is one projection; the JSON-AST stays canonical.
- It does not foreclose visual or graphical front-ends. The crate - It does not foreclose visual or graphical front-ends. The crate
@@ -647,12 +668,11 @@ foreign LLM enough to produce valid output. The current prompt revises this:
test, before its assertions even run. Hand-written content, test, before its assertions even run. Hand-written content,
mechanical drift detection. mechanical drift detection.
The discussion of richer integration paths (LLM tool-use, MCP The cycle's lowest-common-denominator path is the static prompt
server, LSP) was deferred — all three layer additively on the (`ail merge-prose | client | ail parse | ail check`), which works
static-prompt path ships, which remains the with any client.
lowest-common-denominator fallback that always works.
### Empirical addendum (2026-05-12) ### Empirical addendum — cross-model authoring measurement
Cross-model measurement against two foreign LLMs via IONOS, Cross-model measurement against two foreign LLMs via IONOS,
temperature=0, top_p=1, max-turns=5. Two blind cohorts on the same temperature=0, top_p=1, max-turns=5. Two blind cohorts on the same
@@ -1315,10 +1335,8 @@ binders is what lets the lint correctly identify `head_or_zero`
as over-strict (could be `borrow`) while staying silent on as over-strict (could be `borrow`) while staying silent on
`sum_list` where `t: List` *is* moved out. `sum_list` where `t: List` *is* moved out.
Severity: `Warning`. The first ever Warning-level diagnostic; Severity: `Warning`. `ail check`, `ail build`, `ail emit-ir` exit 1
previously all diagnostics were `Error`. CLI exit semantics only on at least one `Error`; warnings print but do not abort.
adjusted: `ail check`, `ail build`, `ail emit-ir` exit 1 only on
at least one Error. Warnings print but do not abort.
**The suppression: `mode-strict-because`.** Authors **The suppression: `mode-strict-because`.** Authors
who want to keep an over-strict annotation deliberately (e.g. who want to keep an over-strict annotation deliberately (e.g.
@@ -1501,18 +1519,14 @@ acyclicity invariant.
### What this Decision deliberately does not do ### What this Decision deliberately does not do
- **Does not infer everything.** An earlier draft of this - **Does not infer everything.** AILang demands annotations
Decision said "uniqueness is fully inferred" — that was the *because* the LLM author can produce them effortlessly. The
mainstream RC position. A follow-up discussion compiler does inference *plus* verification of contracts.
replaced it: AILang demands annotations *because* the LLM
author can produce them effortlessly. The compiler does
inference *plus* verification of contracts.
- **Does not require existing fixtures to migrate immediately.** - **Does not require existing fixtures to migrate immediately.**
`(con T)` is treated as `(own T)`. Existing JSON hashes `(con T)` is treated as `(own T)`. Existing JSON hashes
stay bit-identical until the fixture is intentionally updated. stay bit-identical until the fixture is intentionally updated.
- **Does not commit to atomic refcounts.** AILang is currently - **Does not commit to atomic refcounts.** AILang is
single-threaded. If/when concurrency arrives, atomic-vs-non- single-threaded; refcounts are non-atomic.
atomic will be a separate decision per allocation kind.
- **Does not introduce regions.** Regions were considered and - **Does not introduce regions.** Regions were considered and
rejected; see "Why not other memory models" above. rejected; see "Why not other memory models" above.
@@ -1834,9 +1848,7 @@ Superclass chains are linear (single-superclass relation per class)
and auto-expansion closes transitively across the chain. and auto-expansion closes transitively across the chain.
**No `deriving`.** AILang does not auto-derive instances. `instance Eq **No `deriving`.** AILang does not auto-derive instances. `instance Eq
MyType` must be written by hand. Auto-derivation may land in a future MyType` must be written by hand.
iteration if the Feature-acceptance criterion holds for it; that
discussion is out of scope here.
### Diagnostic categories ### Diagnostic categories
@@ -2022,10 +2034,8 @@ a => a -> () !IO` shipped in iter 24.3 with body
`\x -> let s = show x in do io/print_str s` (explicit let-binder for `\x -> let s = show x in do io/print_str s` (explicit let-binder for
heap-Str RC discipline per eob.1 Str carve-out). The let-binder is heap-Str RC discipline per eob.1 Str carve-out). The let-binder is
structurally pinned by `crates/ail/tests/print_mono_body_shape.rs`. structurally pinned by `crates/ail/tests/print_mono_body_shape.rs`.
Routing through `print` replaces the ad-hoc per-type print Routing through `print` is the path for non-`Str` primitives;
effect-ops; the `io/print_int`, `io/print_bool`, `io/print_float` `io/print_str` is the only built-in direct-output effect-op.
primitives were retired 2026-05-14 in iter rpe.1, leaving
`io/print_str` as the only surviving direct-output effect-op.
### Heap-Str primitives ### Heap-Str primitives
@@ -2042,8 +2052,8 @@ and backed by a `runtime/str.c` C helper.
of an `Int`. Backs `Show Int` in the prelude. of an `Int`. Backs `Show Int` in the prelude.
- `bool_to_str : (borrow Bool) -> Str` (iter 24.1) — `"true"`/`"false"`. - `bool_to_str : (borrow Bool) -> Str` (iter 24.1) — `"true"`/`"false"`.
Backs `Show Bool` in the prelude. Backs `Show Bool` in the prelude.
- `float_to_str : (borrow Float) -> Str` (iter 24.1, type-installed; - `float_to_str : (borrow Float) -> Str` (iter 24.1) —
codegen ships in a future iter per roadmap). Will back `Show Float`. type-installed; codegen is reserved and not yet shipped.
- `str_clone : (borrow Str) -> Str` (iter 24.1) — allocates a fresh - `str_clone : (borrow Str) -> Str` (iter 24.1) — allocates a fresh
heap-Str copy of the input's bytes. Backs `Show Str` in the prelude. heap-Str copy of the input's bytes. Backs `Show Str` in the prelude.
- `str_concat : (borrow Str, borrow Str) -> Str` (iter str-concat, - `str_concat : (borrow Str, borrow Str) -> Str` (iter str-concat,
@@ -2060,9 +2070,7 @@ instance body that wants to combine fragments.
Primitive output for `Str` values goes through `io/print_str` Primitive output for `Str` values goes through `io/print_str`
directly; values of other primitive types route through the directly; values of other primitive types route through the
polymorphic `print` helper (§"Polymorphic print"), which feeds polymorphic `print` helper (§"Polymorphic print"), which feeds
the heap-Str result of `show x` into `io/print_str`. The per-type the heap-Str result of `show x` into `io/print_str`.
effect-ops `io/print_int`, `io/print_bool`, `io/print_float` were
retired in iter rpe.1 (2026-05-14).
`==`, `<`, `<=`, `>`, `>=` REMAIN primitive operators (unchanged `==`, `<`, `<=`, `>`, `>=` REMAIN primitive operators (unchanged
from the original draft). Class methods are accessed by name (`eq x y`, from the original draft). Class methods are accessed by name (`eq x y`,
@@ -2081,11 +2089,10 @@ would invoke literal-defaulting which axis-7 already excluded.
22. Class-routing operators is a future-iteration option, not a 22. Class-routing operators is a future-iteration option, not a
Decision-11 commitment. Decision-11 commitment.
- **Form-B (prose) projection of class/instance.** The prose - **Form-B (prose) projection of class/instance.** The prose
renderer for the new schema nodes is one-way (no parser by renderer for the class/instance schema nodes is one-way (no
design) and was deferred from milestone 22 entirely; the parser by design); the render in
placeholder render in `crates/ailang-prose/src/lib.rs` is `crates/ailang-prose/src/lib.rs` is a placeholder and
informational only. A future iter ships the full prose informational only.
projection if/when prose-side authoring needs surface.
- **Specific monomorphised-symbol naming format.** Milestone 22 - **Specific monomorphised-symbol naming format.** Milestone 22
uses `<method>__<type-surface-name>` for primitive type targets uses `<method>__<type-surface-name>` for primitive type targets
(e.g. `show__Int`) and `<method>__<8-hex-prefix>` for compound (e.g. `show__Int`) and `<method>__<8-hex-prefix>` for compound
@@ -2130,12 +2137,9 @@ Concretely:
4. **Carve-out anchor.** Seven `.ail.json`-only fixtures 4. **Carve-out anchor.** Seven `.ail.json`-only fixtures
(subject-matter rejection tests) survive in the corpus by (subject-matter rejection tests) survive in the corpus by
structural necessity. They participate in their own dedicated structural necessity. They participate in their own dedicated
rejection-shape tests, not in the round-trip gate. (The rejection-shape tests, not in the round-trip gate. (The prelude is
prelude's compile-time-embed carve-out, originally listed embedded as `examples/prelude.ail` in `ailang-surface` and parsed
here as the eighth fixture, was retired 2026-05-14 by at compile time via `ailang_surface::parse_prelude`.)
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`.)
Hashing is the consequence the language depends on: BLAKE3 of the Hashing is the consequence the language depends on: BLAKE3 of the
canonical bytes is well-defined for any `.ail` source via parse- canonical bytes is well-defined for any `.ail` source via parse-
@@ -2516,8 +2520,9 @@ The typechecker enforces
`(own)` / `(borrow)` modes, codegen emits `ailang_rc_inc` / `_dec` `(own)` / `(borrow)` modes, codegen emits `ailang_rc_inc` / `_dec`
calls at the points dictated by linearity, and `Term::Clone` / calls at the points dictated by linearity, and `Term::Clone` /
`Term::ReuseAs` materialise into actual rc-bumps and in-place `Term::ReuseAs` materialise into actual rc-bumps and in-place
rewrites respectively. Boehm-on-`--alloc=gc` is on the path to rewrites respectively. `--alloc=gc` selects the transitional Boehm
retirement; see `docs/roadmap.md` for the active queue. backend; `--alloc=rc` is the canonical backend (Decision 10) and the
CLI default.
The **desugar** pass (`ailang-core::desugar::desugar_module`) runs The **desugar** pass (`ailang-core::desugar::desugar_module`) runs
before typecheck and codegen in every entry point of `ailang-check` before typecheck and codegen in every entry point of `ailang-check`
+2 -6
View File
@@ -149,9 +149,5 @@ prefer; `ail merge-prose` composes the prompt and gets out of the
way. The Unix-pipe shape (`ail merge-prose ... | client | ail parse | way. The Unix-pipe shape (`ail merge-prose ... | client | ail parse |
ail check`) makes the cycle scriptable without lock-in to one vendor. ail check`) makes the cycle scriptable without lock-in to one vendor.
A future iter may layer a tool-use schema (LLM calls back into The static-prompt path documented here is the lowest-common-denominator
`ail parse` / `ail check` from inside its turn) or an MCP server cycle and always works with any client.
(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.
@@ -0,0 +1,131 @@
# iter docs-honesty-lint.1 — canonical docs made present-tense-honest, guarded by a wrap-robust pin + Sweep 5
**Date:** 2026-05-18
**Started from:** de66eb7ab2e457bfe7e5dbe2f87c5da15a786cd7
**Status:** DONE
**Tasks completed:** 6 of 6
## Summary
`docs/DESIGN.md` and `docs/PROSE_ROUNDTRIP.md` carried Wunschdenken
(forward intent stated as fact) and non-citation post-mortem /
doc-archaeology narrative. This iter strips both classes, codifies
the honesty discriminator as a `### What this document is — and the
honesty rule it holds itself to` meta-subsection inside DESIGN.md
§"Project ecosystem", and guards the invariant two ways: a
wrap-robust enumerated absent/present regression pin
(`crates/ailang-core/tests/docs_honesty_pin.rs`, 4 tests) and a
wrap-robust advisory Sweep 5 in `bench/architect_sweeps.sh` with all
sweep-count lockstep wording + a new `ailang-architect.md` "DESIGN.md
honesty drift" bullet. Genuine forward intent (LLM tool-use / MCP /
LSP) relocated to `docs/roadmap.md` P3. No language/checker/codegen/
runtime change — the sole `crates/` change is the additive pin file;
`ail check`/`run`/`build` are byte-unchanged by construction.
## Per-task notes
- iter docs-honesty-lint.1.1: created the wrap-robust RED pin
(`docs_honesty_pin.rs`, `norm()` whitespace-collapse helper, 4
tests). Verified RED for the right reasons (compiles, 4 fail on the
enumerated still-present strings + the absent discriminator).
- iter docs-honesty-lint.1.2: appended Sweep 5 to
`architect_sweeps.sh` + tail "All four"→"All five" + header
"four"→"five sweeps"; added the "DESIGN.md honesty drift" architect
bullet and fixed the sibling "four sweeps"→"five / Sweeps 1-4"
wording. Sweep 5 confirmed firing on the uncorrected tree
(EXIT=1). Concern-1 lockstep shipped whole, no split.
- iter docs-honesty-lint.1.3: inserted the discriminator
meta-subsection. Tree had drifted (a `- **Tests**:` bullet now
follows the Docs bullet, no blank line; closing prose is "When the
language grows…"); anchored on the verbatim closing-prose line per
the plan's own Step-1 "quote it, do not assume" caveat.
- iter docs-honesty-lint.1.4: applied the 13 byte-exact corrections
(each verbatim "current text" re-confirmed by grep first; lines had
drifted +~22 from Task 3). Step-14 catch-all surfaced one genuine
extra FIX site (see Concerns); classified + handled per procedure.
- iter docs-honesty-lint.1.5: stripped the PROSE_ROUNDTRIP tool-use/
MCP Wunschdenken paragraph (present-tense replacement); appended
the relocated forward-intent idea to roadmap P3 with spec
provenance. Full pin GREEN (4/4).
- iter docs-honesty-lint.1.6: full verification — pin GREEN isolated;
workspace zero failures (609 passed, delta = +4 pin tests only);
sentinel trio (design_schema_drift/spec_drift/schema_coverage)
green; Sweep 5 reduced to the single self-referential L62 KEEP;
build clean; `ONLY THE PIN` scope confirmation.
## Concerns
- T3: plan Task-1 pin string `whether the document asserts something
exists, works, or changed that does not` omitted the `**…**`
Markdown bold that plan Task-3 body wrapped around `asserts …
not`. Plan self-review item 3 cross-check missed it. Resolved by
dropping the incidental bold from the inserted body (the pin is the
contract, written first; the emphasis is non-load-bearing). Prose
meaning + present-tense framing unchanged. Plan-internal
inconsistency, not a tree-drift; flagged for planner calibration.
- T4: Step-14 catch-all surfaced one genuine extra Sweep-5 FIX site
not in Steps 1-13 — DESIGN.md Form-B class/instance prose-projection
bullet ("was deferred from milestone 22 entirely; … A future iter
ships the full prose projection if/when …"). The Boss's "zero extra
hits" pre-verification was against the spec's illustrative regex;
the plan's broader Sweep-5 regex correctly catches this soft-wrapped
site. Handled exactly per Step-14: rewritten present-tense (kept the
load-bearing facts — one-way renderer, no parser by design,
placeholder render in `crates/ailang-prose/src/lib.rs`) + matching
absent-string asserts + present-anchor added to the pin. This is the
procedural catch-all working as designed.
## Catch-all classification (Task 4 Step 14)
- KEEP: DESIGN.md L62 `- **Forward intent** ("planned / will back /
on the path to / if-when X arrives")` — the discriminator
subsection's own enumerated catalogue of forbidden phrasings;
asserts nothing false, present-tense rule definition. A
self-referential Sweep-5 false-positive (the regex matches the
literal "on the path to" inside the rule's own example list).
- FIX: DESIGN.md L2093 Form-B prose-projection bullet — see Concerns;
before→after recorded there; pin pair added.
## Known debt
- (none) — the iter is self-contained; the pin + Sweep 5 are the
forward guard against regrowth.
## Post-iter sweep output (Task 6 Step 4)
```
=== Sweep: 1 (history anchors) ===
50: `docs/journal-archive.md` (archived monolith for pre-2026-05-11 history),
2060: 2026-05-13) — combines two `Str` values into a single owned `Str`.
=== Sweep: 5 (honesty: wunschdenken + non-citation post-mortem) ===
62:- **Forward intent** ("planned / will back / on the path to /
EXIT=1
```
Sweep-1 residue = the two documented deliberate KEEPs (L50 doc-roles
pointer date, L2060 true `(iter str-concat, 2026-05-13)` provenance).
Sweep-5 residue = the single L62 self-referential discriminator-
catalogue KEEP. EXIT=1 is expected + architect-adjudicated per plan
Task 6 Step 4 (deliberate-KEEP date-anchors remain by design).
## Blocked detail
(none — DONE)
## Files touched
- `crates/ailang-core/tests/docs_honesty_pin.rs` (new — the pin, 4
tests; extended in T4 Step 14 per the mandated catch-all procedure)
- `bench/architect_sweeps.sh` (Sweep 5 + header/tail lockstep)
- `skills/audit/agents/ailang-architect.md` ("four"→"five" + new
"DESIGN.md honesty drift" bullet)
- `docs/DESIGN.md` (discriminator subsection + 13 enumerated
corrections + 1 catch-all correction)
- `docs/PROSE_ROUNDTRIP.md` (tool-use/MCP Wunschdenken stripped)
- `docs/roadmap.md` (relocated forward-intent idea, P3)
## Stats
bench/orchestrator-stats/2026-05-18-iter-docs-honesty-lint.1.json
+9
View File
@@ -651,3 +651,12 @@ work progresses.
- [ ] **\[idea\]** Richer integration paths between RC and - [ ] **\[idea\]** Richer integration paths between RC and
uniqueness — deferred from the 21' arc; revisit once the uniqueness — deferred from the 21' arc; revisit once the
uniqueness inference covers more program shapes. uniqueness inference covers more program shapes.
- [ ] **\[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/2026-05-18-docs-honesty-lint.md`
+14 -5
View File
@@ -72,11 +72,20 @@ If `milestone_scope` is empty, return a structural error and stop.
is its own kind of drift. is its own kind of drift.
- **DESIGN.md history-anchor regrowth.** Run - **DESIGN.md history-anchor regrowth.** Run
`bash bench/architect_sweeps.sh` from the repo root. Exit 0 = clean. `bash bench/architect_sweeps.sh` from the repo root. Exit 0 = clean.
Exit 1 = at least one of the four sweeps from the Exit 1 = at least one of the five sweeps matched. Sweeps 1-4 are the
design-md-consolidation milestone (2026-05-10) matched. Review each design-md-consolidation history-anchor invariants (2026-05-10):
match: a legitimate quote (e.g. a journal-entry citation inside a review each match a legitimate quote (e.g. a journal-entry
block-quote) is fine; a fresh history anchor / REVERTED narrative / citation inside a block-quote) is fine; a fresh history anchor /
workflow detail / stale cross-reference is drift to flag. 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.
- **Lockstep invariants across files.** Two known cross-file pairings - **Lockstep invariants across files.** Two known cross-file pairings
must move together; a new arm in one without the matching update in must move together; a new arm in one without the matching update in
the other ships silently broken (B1 in the Floats fieldtest is the the other ships silently broken (B1 in the Floats fieldtest is the