Files
AILang/design/contracts/authoring-surface.md
T
Brummel 19dc42f5ca design/ ledger: dense cross-linking via three topical splits + 88-link sweep
The first formal-links milestone shipped clause-5 + 8 links across the
existing file layout. Browsing surfaced that file-only granularity is
only as precise as the file boundaries — three files mixed two or
three navigation targets under one address, so the 8 links could not
multiply without ambiguity. This commit fixes the substrate, then
applies the sweep the original milestone deferred.

Splits (each extracts an already-self-contained section into its own
file so links land on the topic, not the parent doc's TOC):

  contracts/typeclasses.md
    → +contracts/prelude-classes.md  (Eq/Ord/Show ships, polymorphic `print`)
    → +contracts/method-dispatch.md  (5-step dispatch rule, candidate index)

  contracts/memory-model.md
    → +contracts/language-constraints.md  (the 4 binding constraints
                                           making RC sound without a
                                           cycle collector)

  models/authoring-surface.md
    → +models/prose-projection.md  (Form-B / `ail prose` / merge-prose)

Each new file enters design/INDEX.md as its own row (three contracts
share show_no_instance_e2e.rs / uniqueness.rs as ratifying tests;
prose-projection is a model). Two pre-existing links rebind to the
new topic-files (memory-model.md → method-dispatch.md;
float-semantics.md → prelude-classes.md).

Link sweep: 8 → 88 formal Markdown links over 23 files. Every file
in design/contracts/ + design/models/ now has at least one outgoing
link; the tree is fully connected. Links are file-relative
`[label](path)` per the established convention, fenced code blocks
are skipped (a `](` inside ```jsonc``` is literal text), the durable
tier (design/ + crates/ + runtime/) is enforced by clause-5.

Tests:
  - design_index_pin.rs (5/5 clauses): clean-cut, INDEX resolution,
    ratifying-test resolution, no decision-record prose in contracts/,
    body links durable + resolving.
  - docs_honesty_pin.rs (5/5): one assertion rebinds from typeclasses.md
    to prelude-classes.md (where the gated sentence now lives);
    design_corpus widens to include the 4 new files so the Wunschdenken
    / doc-archaeology sweeps continue to cover everything that used to
    live in the parents.

No spec/plan/journal for this batch — interactive collaboration after
the milestone closed; the user gated the splits explicitly before the
sweep.
2026-05-20 00:24:08 +02:00

4.2 KiB

Authoring surface

Decision 6: authoring surface

Form (A) is implemented as the ailang-surface crate (parser + printer). Form-A is gated against drift by ailang-surface/tests/round_trip.rs, which parses every .ail fixture, prints it back, re-parses, and demands canonical-byte equality. ail render and both branches of ail describe were rewired to use ailang_surface::print, making form (A) the sole text projection of a module — the legacy non-round-tripping pretty-printer code in pretty.rs was deleted at the same time, leaving only diagnostic helpers (type_to_string, pattern_to_string, manifest) public.

Architectural pin: data structure is the source of truth

The textual surface is not a replacement for the JSON-AST. It is one projection among potentially many. Concretely:

  • The JSON-AST keeps its role as the canonical, hashable, content- addressed representation of a module. All hashing, content- addressing, cross-module references, and typecheck/codegen input flow through the JSON-AST. No new hashable form is introduced.
  • The textual surface (form A, this Decision) is the AI authoring projection: optimised for me producing programs token-efficiently and for foreign LLMs producing programs from a spec. It is not optimised for human authors and does not need to be human-pleasant.
  • 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.
  • No human is expected to author AILang seriously. Authoring is AI work. Display and verification, by contrast, are concerns where human-facing alternatives may be useful — and which can therefore layer their own projections on top of the same AST without touching the surface or the core.

In code terms: ailang-core owns the AST. ailang-surface is one producer/consumer pair: text-form-A → AST → text-form-A. A hypothetical ailang-visual would be a different producer of the same AST. ailang-check and ailang-codegen consume only the AST and remain projection-agnostic.

Constraints (hard, in priority order)

  1. Formalizable for a foreign LLM. The grammar must fit in an EBNF/PEG spec of ≤ 30 productions. A model that has never seen AILang must be able to read the spec and produce conforming source zero-shot. Rules out: precedence between binary operators, semantic indentation, maximal-munch lexing, context-sensitive reductions.
  2. AST-isomorphic. Every surface form maps to exactly one AST shape. The full bijection between .ail.json and .ail (both directions, BLAKE3-stable hashing, Float-bits-hex encoding, workspace-CI enforcement points) is anchored as the top-level Roundtrip Invariant — this constraint records that Decision 6's surface-design choice must satisfy that invariant; the invariant itself lives at top level because the property is load-bearing on the language identity, not on this Decision's surface-design rationale.
  3. No external symbols. ASCII only. No Greek (), no arrows (), no subscripts. Reasoning: I substitute mojibake for non-ASCII characters under context pressure; foreign LLMs vary in how they tokenize Unicode.
  4. No precedence. Either everything is parenthesized, or there are no infix operators. Prefer the latter — add(x, 1) over x + 1. Removes a fail mode for both me and foreign LLMs.
  5. No semantic indentation. Block structure expressed by paired delimiters or terminator tokens. Indentation is informational only; the parser ignores it.
  6. One construct per token-list. Every AST node corresponds to exactly one parenthesized form (or atom). No "sometimes you can omit the parens" rules.
  7. AST surface stays frozen. The surface adapts to the AST, not the other way around. We do not change the JSON schema or invalidate hashes to make the surface prettier.

Ratified by: crates/ailang-surface/tests/round_trip.rs.