From 2852326dd35fa06994edca0997e2cc669a52773a Mon Sep 17 00:00:00 2001 From: Brummel Date: Fri, 8 May 2026 18:27:34 +0200 Subject: [PATCH] design: ratify form (B) prose projection (iter 20e tidy) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Family-20 tidy-iter: ailang-architect drift review surfaced DESIGN.md silence on the new prose surface as the highest-leverage gap. Decision 6 now has a 'Form (B) — human prose projection' subsection naming the lossy-vs-lossless contract, the no-parser- by-design choice, and the LLM-mediated round-trip. CLI block gained 'ail prose' and 'ail merge-prose'. No code changes. Form (B) does not weaken any Decision 6 invariant: JSON-AST remains the only hashable artefact, form (A) the canonical authoring surface, 30-production grammar unchanged. Two architect findings deferred (prompt-template lockstep test; snapshot-fixture cross-family coupling). Family 20 closes. JOURNAL queue: empty. --- docs/DESIGN.md | 62 +++++++++++++++++++++++++++++++++++++++ docs/JOURNAL.md | 77 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 139 insertions(+) diff --git a/docs/DESIGN.md b/docs/DESIGN.md index 3c18df8..d3e5def 100644 --- a/docs/DESIGN.md +++ b/docs/DESIGN.md @@ -454,6 +454,64 @@ JSON identical to their corresponding `.ail.json` files. constraint-1 budget. No new lexical rule (`tail-app` / `tail-do` are bare ident tokens; no special casing). +### Form (B) — human prose projection (Family 20) + +**Status: shipped.** Family 20 (Iter 20a–20d, 2026-05-08) ships a +second textual projection of the AST: `ailang-prose`, a one-way +projection from `Module → human-readable text`. It is **not** an +authoring surface; it is the "display" projection that Decision 6's +architectural pin (line 167–176) explicitly anticipated: + +> *"Future projections are explicitly anticipated: a visual / +> graphical front-end is a plausible second projection for human +> review and inspection (display being the one case where non-AI +> eyes matter). The architecture leaves room: any producer of +> well-formed `ailang-core::ast::Module` values is a valid +> front-end."* + +Form (B) targets the specific failure mode where a human reviewer +needs to read an AILang module quickly. Form (A) was designed to +fit a 30-production EBNF spec and to be parsed zero-shot by foreign +LLMs; that prioritisation makes it dense and visually noisy for +human readers. Form (B) inverts the trade-offs: + +- **Rust-flavoured surface.** Braces and `=>` for match arms, + Rust-aligned 4-level operator precedence, infix arithmetic + (`a + b`, not `+(a, b)`), unary `!` for `not`. +- **Lossy by design.** Projection elides machinery the LLM can + re-derive: `(con T)` wrappers (`(con Int)` → `Int`), the + `(fn-type (params ...) (ret ...))` wrap, `(term-ctor T C ...)` + collapses to `C(...)`, redundant parens. Only the AST machinery + whose information is recoverable from typecheck context. +- **Lossless on load-bearing detail.** Mode annotations + (`own T`, `borrow T`), effects (`with IO`), explicit `clone`, + `reuse-as`, doc strings, type annotations on signatures and + lambdas, the `tail` flag — all preserved verbatim. + +Critically, **form (B) has no parser**. Form (A) is round-trippable +by construction (Decision 6 constraint 2); form (B) deliberately is +not. Re-integrating prose edits requires an external LLM mediator, +not a compiler pass — see `docs/PROSE_ROUNDTRIP.md` for the +six-step cycle and the prompt template `ail merge-prose` composes. + +Form (B) does not weaken any Decision 6 invariant: + +- The JSON-AST remains the only hashable artefact. Prose is not + hashed, not content-addressed, not load-bearing for any + cross-module reference. +- Form (A) remains the canonical authoring surface. Foreign LLMs + still author against form (A); humans review and edit through + form (B). +- The 30-production grammar of form (A) is unchanged. +- `ailang-check` and `ailang-codegen` remain projection-agnostic; + `ailang-prose` is a downstream consumer of `ailang-core::ast`, + parallel to `ailang-surface` but in the rendering direction only. + +The CLI gains `ail prose ` (the deterministic +projection) and `ail merge-prose ` +(the mediator-prompt composer); both are listed in the CLI section +below. + ## Decision 7: redundancy removal — `Term::If` is not a primitive **Status: REVERTED in Iter 14g.** This decision was made on shaky grounds — @@ -1311,6 +1369,10 @@ ail manifest — table: name :: type !effects [hash] ail describe — detail of a definition (form-A body) ail render — JSON-AST → form-A text (exact inverse of `parse`) ail parse — form-A text → canonical JSON-AST +ail prose — JSON-AST → form-B (lossy human prose, no parser; Family 20) +ail merge-prose + — compose the LLM-mediator prompt for the prose round-trip + (see docs/PROSE_ROUNDTRIP.md) ail deps — list cross-module references ail diff — content-addressed def-level diff ail workspace — list all modules transitively reachable from entry diff --git a/docs/JOURNAL.md b/docs/JOURNAL.md index 30bb674..4734cff 100644 --- a/docs/JOURNAL.md +++ b/docs/JOURNAL.md @@ -9167,3 +9167,80 @@ practice the family stays as-shipped. validates the merge product; `merge-prose` only composes the prompt. + +## 2026-05-08 — Family-20 tidy-iter (Iter 20e) + +Per CLAUDE.md "Tidy-iter at family boundaries" — every named iter +family closes with a tidy-iter. `ailang-architect` ran a drift +review of family 20 and reported three actionable items, plus two +non-actionable observations. + +### Architect findings + + 1. **DESIGN.md silent on form (B).** Decision 6 documents form (A) + extensively but never names the new prose surface. The CLI + section (`docs/DESIGN.md` "## CLI") lists `render`/`parse` but + not `prose`/`merge-prose`. JOURNAL has the design pinning, but + DESIGN.md is the canonical spec — this is the highest-leverage + fix. + 2. **Prompt-template drift hazard.** `compose_merge_prose_prompt` + in `crates/ail/src/main.rs` and the prompt block in + `docs/PROSE_ROUNDTRIP.md` are byte-equivalent twins maintained + by hand. No test enforces equality. Bounded — the comment in + `main.rs` line 240–242 already names the lockstep — but real. + 3. **Snapshot-fixture coupling.** `ailang-prose/tests/snapshot.rs` + binds prose tests to four 18-family RC fixtures. Any future + iter touching those examples must re-render the `.prose.txt`. + Not actionable until a real refactor forces it. + +### Resolved this iter + +**Item 1 — ratified.** Two edits to `docs/DESIGN.md`: + + - New subsection at the end of Decision 6 (after Iter 14e + refinements, before Decision 7): `### Form (B) — human prose + projection (Family 20)`. ~50 lines documenting form (B) as the + "display" projection Decision 6's architectural pin already + anticipated, the lossy-vs-lossless contract, and the fact that + form (B) has no parser by design (round-trip is LLM-mediated, + not compiler-passed). + - CLI block extended with `ail prose` and `ail merge-prose` lines. + +The new subsection explicitly does NOT weaken any Decision 6 +invariant: JSON-AST remains the only hashable artefact, form (A) +remains the canonical authoring surface, the 30-production grammar +is unchanged, and `check`/`codegen` stay projection-agnostic. + +### Deferred + +**Item 2 (prompt-template lockstep)** — the existing +`// Keep this template in lockstep with docs/PROSE_ROUNDTRIP.md` +comment at `main.rs:240–242` is the audit trail. Adding an +equality test would require parsing the Markdown block in the +doc; brittle. Stays queued; will close if the next iter touching +the prompt actually surfaces the drift. + +**Item 3 (snapshot-fixture coupling)** — not actionable; documented +as known consequence of cross-family snapshot binding. + +### Family 20 final state + +- 20a: prose renderer skeleton + `ail prose` CLI +- 20b: infix + paren elision + doc-wrap + nested-match +- 20c: rolled into 20a +- 20d: `ail merge-prose` + `docs/PROSE_ROUNDTRIP.md` +- 20e: this tidy-iter — DESIGN.md ratification + +Three deferred polishes from 20b (let-inlining, `do io/print_int` +→ `print` sugar, doc-wrap widow control) remain queued; all three +need real-corpus signal to know whether the heuristic is right. + +JOURNAL queue: empty again. + +### Files touched this iter + +- `docs/DESIGN.md` — Form (B) subsection in Decision 6, CLI block + extension. ~60 lines added. +- `docs/JOURNAL.md` — this entry. + +No code changes. Build/test status unchanged from 20d (green).