design: ratify form (B) prose projection (iter 20e tidy)
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.
This commit is contained in:
@@ -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 <m.ail.json>` (the deterministic
|
||||
projection) and `ail merge-prose <m.ail.json> <edited.prose.txt>`
|
||||
(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 <module.ail.json> — table: name :: type !effects [hash]
|
||||
ail describe <module> <name> — detail of a definition (form-A body)
|
||||
ail render <module.ail.json> — JSON-AST → form-A text (exact inverse of `parse`)
|
||||
ail parse <module.ailx> — form-A text → canonical JSON-AST
|
||||
ail prose <module.ail.json> — JSON-AST → form-B (lossy human prose, no parser; Family 20)
|
||||
ail merge-prose <m.ail.json> <m.prose.txt>
|
||||
— compose the LLM-mediator prompt for the prose round-trip
|
||||
(see docs/PROSE_ROUNDTRIP.md)
|
||||
ail deps <module.ail.json> — list cross-module references
|
||||
ail diff <a.ail.json> <b.ail.json> — content-addressed def-level diff
|
||||
ail workspace <entry.ail.json> — list all modules transitively reachable from entry
|
||||
|
||||
Reference in New Issue
Block a user