# Form (B) — prose projection whitepaper ## Form (B) — human prose projection AILang 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 the [authoring surface](../contracts/0001-authoring-surface.md)'s architectural pin 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 (see [roundtrip invariant](../contracts/0009-roundtrip-invariant.md)); form (B) deliberately is not. Re-integrating prose edits requires an external LLM mediator, not a compiler pass — the prompt template `ail merge-prose` composes the six-step cycle. Form (B) does not weaken any [authoring surface](../contracts/0001-authoring-surface.md) invariant: - The [JSON-AST](../contracts/0002-data-model.md) 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 under [pipeline](0003-pipeline.md). ## Form-A spec embedding An earlier `merge-prose` prompt instructed the LLM to emit JSON-AST and offered a 12-line schema-essentials reminder; that combination did not give a foreign LLM enough to produce valid output. The current prompt revises this: - The LLM emits **Form-A** (the canonical authoring surface), not JSON. JSON-AST stays the only hashable artefact, but it is not a writing surface. The user runs `ail parse foo.new.ail` before `ail check` to produce the canonical JSON. - `crates/ailang-core/specs/form_a.md` is the complete LLM-targeted Form-A specification — grammar, every term / pattern / type / def keyword, schema invariants, pitfall catalogue, four few-shot modules drawn from `examples/*.ail`. It is exported as `ailang_core::FORM_A_SPEC` and embedded verbatim in every `merge-prose` prompt. - `crates/ailang-core/tests/spec_drift.rs` walks every variant of `Term`, `Pattern`, `Type`, `Def`, `Literal` via exhaustive `match` and asserts an anchor for each appears in the spec. The exhaustive match is the load-bearing piece: adding a new variant without updating the match fails compilation in this test, before its assertions even run. Hand-written content, mechanical drift detection. The cycle's lowest-common-denominator path is the static prompt (`ail merge-prose | client | ail parse | ail check`), which works with any client.