# Prose round-trip cycle ## Why The prose surface (`ail prose`, Iter 20a / 20b) is a deliberately **lossy** projection of an `.ail.json` module: it strips `(con T)` wrappings, drops redundant parentheses, infixes arithmetic, suppresses schema rigor that the LLM can re-derive. That makes prose pleasant to read and edit — but it also means there is no syntactic parser that turns edited prose back into a `.ail.json`. Re-integration of free-text edits therefore requires an **LLM mediator**: a model that understands both the prose intent and the load-bearing detail in the original module, and emits a new module that respects both. This document describes the workflow, the prompt template `ail merge-prose` composes for that mediator, and the failure modes to watch for. ## What the LLM produces Iter 20f revised a load-bearing piece of this cycle: the LLM emits **Form-A** (an `.ail` document — the canonical authoring surface fixed by Decision 6), not JSON-AST. JSON-AST is the canonical hashable artefact, but it is not a writing surface — every type reference wraps in `{"k": "con", ...}`, every term in `{"t": "...", ...}`, and a single missing `param_modes` entry is a schema error rather than a parse error with a position. Form-A is the form `examples/*.ail` are written in, the form `ail render` produces, and the form `ail parse` consumes. The full LLM-targeted specification is shipped inside the binary as `ailang_core::FORM_A_SPEC` (sourced from `crates/ailang-core/specs/form_a.md`) and embedded verbatim in every `merge-prose` prompt; the LLM does not need prior AILang exposure. ## The cycle ``` 1. ail prose foo.ail.json > foo.prose.txt 2. $EDITOR foo.prose.txt # human edits freely 3. ail merge-prose foo.ail.json foo.prose.txt > prompt.txt 4. cat prompt.txt | > foo.new.ail 5. ail parse foo.new.ail > foo.new.ail.json 6. ail check foo.new.ail.json 7. mv foo.new.ail.json foo.ail.json # if check is clean ``` Step 1 is the deterministic projection (Iter 20a / 20b). Step 3 is the prompt composer this iter ships. The prompt embeds the Form-A spec, the original module rendered as Form-A, and the edited prose. The original module is loaded via `ailang_core::load_module` (which validates the schema) and rendered via `ailang_surface::print`. Step 4 is the user's external LLM client — Claude Code, the Anthropic API, OpenAI's CLI, anything that takes a prompt on stdin and emits text on stdout. AILang ships no client of its own; see *Why no built-in API client* below. Step 5 parses the LLM's Form-A back into a JSON-AST. Parser errors are positional and can be fed back to the LLM in a follow-up prompt. Step 6 typechecks. The full diagnostic catalogue applies — mode violations, exhaustiveness, effect closure, etc. Step 7 accepts the new module if check is clean. ## The prompt template `ail merge-prose ` reads the original (parsing it, then re-rendering as Form-A), reads the prose file byte-for-byte, and prints the following structure to stdout — no fences, no JSON envelope. The shape is exactly what a human would compose by hand if they wanted to drive the same cycle without the CLI helper. ``` You are integrating prose edits back into an AILang module. ROLE Your job is to produce an updated AILang module in Form-A (an .ail file) that reflects the human's prose edits while preserving the load-bearing semantic detail from the original module. CONTRACT The prose is the source of intent ... [list of preservation rules] OUTPUT Output ONLY the new Form-A bytes. ... [no fences, no commentary] FORM-A SPECIFICATION <<