# 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 <m.ail.json>` (the deterministic
projection) and `ail merge-prose <m.ail.json> <edited.prose.txt>`
(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.
