Reader-facing prose and rustdoc carried opaque shorthand like
"Decision 10", "clause-5", "mq.1", "ct.1", "eob.1", "rpe.1",
"post-mq.3", and "Iter 22b.1:" with no in-repo definition the reader
could follow. This commit replaces every such occurrence in the
durable tier the reader is most likely to land on (design/ ledger +
source //! module headers + the central /// public-item rustdoc) with
an inline content phrase plus, where applicable, a Markdown link to
the file that defines the referenced concept.
design/ ledger — 16 files:
Definition-site headings demoted from "Decision N: <title>" to
"<title>": authoring-surface, tail-calls, memory-model section in
rc-uniqueness.md, dual-allocator section, typeclass design,
effects "pure core + algebraic effects".
Cross-reference sites: "Decision 1" -> canonical-schema principle
(data-model); "Decision 3/4" -> effects + scope-boundaries; "Decision
6" -> authoring-surface; "Decision 8" -> tail-calls; "Decision 9" ->
rc-uniqueness (dual-allocator); "Decision 10" -> memory-model;
"Decision 11" -> typeclasses (model). "clause-5" -> body-link
durability gate. "clause-3" (in language-constraints) ->
bug-class-reintroduction discriminator. "mq.1/2/3", "ct.1/4",
"eob.1", "rpe.1" -> the canonical-form rule / the type-driven
dispatch / the Str carve-out / etc. "post-mq.3" -> "type-driven".
design/contracts/feature-acceptance.md: file-local "clauses 1/2/3"
-> "criteria 1/2/3" (sprachliche Kohärenz mit der File-Überschrift
"Feature-acceptance criterion"); "the clause-3 mechanism" -> "the
bug-class-reintroduction discriminator".
Source //! module headers — 24 files:
Stripped "Iter X.Y:" prefixes and "(Decision N)" / "(mq.X)" tags
from spec_drift, uniqueness, reuse_shape, migrate_canonical_types,
typeclass_22b{2,3,c}, suppress_filter, lift, mono, linearity,
diagnostic, method_dispatch_pin, method_collision_pin,
no_per_type_print_ops, mq3_multi_class_e2e, print_mono_body_shape,
print_no_leak_pin, cli_diag_human_workspace_load_error,
ct1_check_cli, prose snapshot, unbound_in_instance_method_pin,
mono_xmod_ctor_pattern, desugar.
Central /// public-item rustdoc:
ast.rs (full sweep — every "Iter X" + "Decision N" prefix
reformulated; mode/Type::Fn rustdoc now points at memory-model.md;
Constraint / SuperclassRef / InstanceDef / ClassDef rustdoc points
at typeclasses contract).
diagnostic.rs (all "(Iter X)" / "(mq.X)" tags on diagnostic codes
removed).
lib.rs (FORM_A_SPEC rustdoc points at authoring-surface.md
instead of "Decision 6").
canonical.rs (type_hash + Float-literal rustdoc).
Still outstanding (for a follow-up commit): ~500 inline `//`
code-body comments with `Iter X.Y` markers across the workspace, and
a handful of `///` rustdoc items in hash_pin / workspace_pin / lift /
mono / suppress_filter test-pin and internal-function bodies. Code
identifiers (test filenames like `mq3_multi_class_e2e.rs`, function
names like `iter18e_drop_iterative_default_preserves_hashes`) stay
verbatim per the user's "code identifiers stay verbatim" rule.
Tests: design_index_pin 5/5 + docs_honesty_pin 5/5; workspace builds
clean; full `cargo test --workspace` previously green (every
`test result: ok` line, no FAILED line).
4.1 KiB
Authoring surface
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 contract) 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::Modulevalues 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)
- 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.
- AST-isomorphic. Every surface form maps to exactly one AST
shape. The full bijection between
.ail.jsonand.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 the 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 file's surface-design rationale. - 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. - No precedence. Either everything is parenthesized, or there
are no infix operators. Prefer the latter —
add(x, 1)overx + 1. Removes a fail mode for both me and foreign LLMs. - No semantic indentation. Block structure expressed by paired delimiters or terminator tokens. Indentation is informational only; the parser ignores it.
- One construct per token-list. Every AST node corresponds to exactly one parenthesized form (or atom). No "sometimes you can omit the parens" rules.
- 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.