From 8c664c18da1b7a8e78c71abf01bd84254e4dcfee Mon Sep 17 00:00:00 2001 From: Brummel Date: Sun, 17 May 2026 22:15:47 +0200 Subject: [PATCH] =?UTF-8?q?spec:=20LLM-surface=20discipline=20=E2=80=94=20?= =?UTF-8?q?meta-principle=20+=20open=20forks?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Distillation of the effects→input→state→closures→recursion design session. Ratifies one meta-principle (disciplined surface, mechanism stays in the compiler) as standing orchestrator guidance, separates the three bundled DESIGN.md commitments (content-address-definitions is cheap and kept; total value-purity + RC-no-collector are the expensive ones), and parks four open forks with epistemic status for later brainstorm pickup. No feature ratified here. --- .../2026-05-17-llm-surface-discipline.md | 222 ++++++++++++++++++ 1 file changed, 222 insertions(+) create mode 100644 docs/specs/2026-05-17-llm-surface-discipline.md diff --git a/docs/specs/2026-05-17-llm-surface-discipline.md b/docs/specs/2026-05-17-llm-surface-discipline.md new file mode 100644 index 0000000..8dad306 --- /dev/null +++ b/docs/specs/2026-05-17-llm-surface-discipline.md @@ -0,0 +1,222 @@ +# LLM-Surface Discipline — Design-Principles Spec + +**Date:** 2026-05-17 +**Status:** Principles distillation — the meta-principle is ratified +orchestrator guidance; the candidate decisions in §6 are NOT +ratified and each still requires the normal `brainstorm` + +Feature-acceptance gate before it touches `DESIGN.md` or is +implemented. +**Authors:** Brummel (orchestrator) + Claude + +## What this spec is + +A distillation of one design session that walked from "what are +effects" down through input, world-passing, statefulness, +closures, recursion, and content-addressing. It is **not** a +milestone spec: no feature is being proposed for implementation +here. It records (a) one meta-principle that recurred five times +and is now treated as standing orchestrator guidance, (b) a +separability analysis that re-aims the project's cost/benefit +scrutiny, and (c) a list of open forks with their epistemic +status so a later `brainstorm` can pick them up without +re-deriving the reasoning. + +## What this spec is NOT + +It does not ratify `loop`/`recur`, closure-private state, the +removal of `mut`, `argv`-into-`main`, or any change to the cycle +/ content-addressing commitments. Those are §6 candidates. It +does not relitigate Decision 10. It carries no implementation +plan. + +## 1. The meta-principle (ratified guidance) + +> **The LLM author writes intent in a disciplined surface form. +> The raw, mechanical, invariant-threatening transformation stays +> in the compiler / IR, where the author never hand-manages it. +> Exposing the raw mechanism to the author is the consistently +> wrong move for this audience.** + +This is the operational reading of DESIGN.md's +Feature-acceptance criterion and the "machine readability over +human ergonomics" goal, specialised to control/state/effect +constructs. It is not "hide complexity"; it is "the author +asserts *what*, the compiler bears the *mechanical invariant*, +and a violated invariant surfaces as a compile error rather than +as a silent deep-input failure." + +## 2. Evidence — the five instances + +Each row: the disciplined surface AILang keeps (or should keep) +vs. the raw mechanism it must NOT push onto the author. + +1. **Effects.** Surface: an effect label on the signature, the + checker reconciles it. Raw: the author hand-threads a `World` + token through every call (world-passing). — World-passing is + sound under Decision 10's uniqueness machinery, but it + relocates the bookkeeping onto the author. Effect label kept. +2. **Stateful closures.** Surface: a closure that simply *is* a + stateful value, its cell born in its own env. Raw: the author + manages capture + escape + the `mut`→move transport. Closure- + private state needs no cycles, no weak refs, no linearity-pass + extension; it is the simplest of the three options examined. +3. **Move-capture.** Surface: state declared where it is used. + Raw: a `move`-list, a linearity-pass scope expansion, + use-after-move diagnostics, per-var-vs-per-block residence. + Move-capture exists only to *transport* state from an + enclosing owner to a closure; if the state has no reason to + exist anywhere but the closure, the transport problem + dissolves and so does the machinery. +4. **Iteration / recursion.** Surface: recursion (LLM-natural for + structural descent) and a single iteration construct. Raw: + the author hand-compiles a naturally-recursive algorithm into + an explicit-stack worklist loop. Stack-safety of non-tail + recursion is the *compiler's* job (cf. the existing + `(drop-iterative)` escape hatch for the drop cascade), not an + author obligation discharged by banning recursion. +5. **Jump.** Surface: `loop`/`recur` — re-iteration is the only + thing `recur` can mean, the jump is structural. Raw: either + the author-asserted `tail` flag on `tail-app` (a forgotten + flag silently degrades to a stack-growing call: correct on + small inputs, overflow on deep ones — a hallucination-shaped + failure), or a raw labeled `goto` (kills local reasoning, + forces mutable loop state, breaks the expression model and + the structured-AST hash). + +Five-for-five, the right answer was "disciplined surface, +mechanism stays in the compiler." This is the through-line. + +## 3. The robustness corollary + +For this audience an invariant guaranteed **by construction / +structurally** strictly dominates one **asserted by the author +via a flag or annotation**. Rationale: the failure mode of a +forgotten assertion is silent, input-size-dependent, and +indistinguishable from correct code in a small test — exactly +the hallucination-shaped failure the language exists to prevent. +Canonical anti-example: the `tail-app` `tail: true` flag. +Canonical positive: `recur`, which has no non-tail spelling. + +## 4. The separability analysis (re-aims cost scrutiny) + +`DESIGN.md` bundles three logically distinct commitments as if +they were one. They are separable: + +1. **Content-address definitions.** Needs only a canonical + serialisation + a hash of a finite definition AST. Cheap. + Delivers the audience-specific wins (verify a symbol without + spending context; semantic def-diff; trust a signature + without transitive reading). **Worth it. Keep.** +2. **Total equational purity of every runtime value.** The + commitment that forbids the LLM-natural stateful closure. + Expensive. +3. **RC without a cycle collector.** Requires acyclic runtime + values. Expensive. + +The cost this session repeatedly hit belongs to (2)+(3), not to +(1). AILang already content-addresses only structure / +definitions, never live runtime values, so (1) does not require +the no-cycle memory model — (2) and (3) do. The productive +question for any future brainstorm is therefore **not** "is +content-addressing worth it" (it is, and it is cheap) but +"must value-purity be *total*, or does the weaker line +**acyclic + unaliased** suffice?" Closure-private state sits +exactly on that weaker line: not equationally pure, but acyclic +and unaliased — it keeps (1) and (3) intact while unblocking the +construct the LLM reaches for. + +## 5. Concrete surface anchors + +These pin the *shape* the principles imply. They are +illustrative, not ratified syntax. + +Single iteration construct (`loop`/`recur` as pure tail-recursion +sugar; loop state is rebound parameters, not mutation): + +``` +(fn sum_to + (type (fn-type (params (con Int)) (ret (con Int)))) + (params n) + (body + (loop (acc Int 0) (i Int 1) + (if (app > i n) + acc + (recur (app + acc i) (app + i 1)))))) +``` + +Closure-private state (state born in the closure; no enclosing +`mut`, no `move`, no transport): + +``` +(fn make_counter + (type (fn-type (params) (ret (fn-type (params) (ret (con Int)))))) + (params) + (body + (lam (state (var counter Int 0)) (params) (ret (con Int)) + (body (seq (assign counter (app + counter 1)) counter))))) +``` + +Cheapest usefulness move (`argv` into `main`; `main(args)` stays +pure, the impurity stays at the trampoline C-ABI seam — no +effect, no Decision touched): + +``` +(fn main + (type (fn-type (params (con List (con Str))) (ret (con Int)))) + (params args) + (body ...)) +``` + +## 6. Open forks — candidate decisions, NOT ratified + +Each needs `brainstorm` + the Feature-acceptance gate before it +becomes DESIGN.md or a plan. Priority order reflects usefulness +per cost. + +1. **`argv` into `main` (highest usefulness per cost).** Moves + the language out of the closed-program vacuum. Touches no + Decision; `main(args)` is a pure function, impurity confined + to the trampoline. One real implementation cost: the + trampoline must build an RC-correct `List` (Str slabs + + cons cells with valid refcount headers) — the in-the-small + form of the marshalling-into-RC-heap problem. Bounded, local, + no language-semantics change. +2. **`loop`/`recur` as the single iteration surface.** Re-attempt + of the reverted Iteration-discipline centrepiece, but + **standalone** — de-bundled from the structural-guardedness + checker and the `Diverge` effect that entangled and sank the + original milestone. Honest cost to spec explicitly: retiring + `tail-app` wholesale loses general proper-tail-calls (mutual + recursion, tail-call into a different fn) with a stack + guarantee. State the cost in the spec; do not retire it + silently. +3. **Closure-private mutable state, possibly replacing `mut`.** + Sealed scalar `mut` without loop and without capture is + largely redundant with `let`/`if`/`match`; its only on-record + justification is the narrow ergonomic mut-local fieldtest + finding. Removing a shipped, fieldtested feature requires the + project's own empirical gate: re-author the mut-local + fieldtest tasks with closure-private state + plain `let`; if + the reported hallucination friction does not return, `mut` is + scaffolding and can fall, and the move-capture milestone is + then unnecessary (it solved a transport problem that only + existed because `mut` births state in the wrong place). +4. **Cycles / content-addressing (deepest; deliberately not + opened).** Allowing cycles to get "real" closures negates + Decision 10 and transitively Decision 1/2. Per §4 it is the + wrong target anyway — the cheap, high-value commitment + (content-address definitions) is not the expensive one. Named + here as the deepest fork so it is not stumbled into; a + near-term milestone must not open it. + +## Cross-references + +- `docs/DESIGN.md` — Decision 1/2 (source = data, content- + addressed), Decision 3 (pure core + effects), Decision 8 + (verified tail calls), Decision 9/10 (RC + uniqueness, no + collector), Feature-acceptance criterion. +- `docs/specs/2026-05-15-mut-local.md` — the sealed-mut milestone + whose redundancy §6.3 questions. +- `docs/specs/2026-05-16-iteration-discipline-revert.md` — why + the bundled loop/recur milestone was backed out; §6.2 is the + de-bundled re-attempt.