iter design-md-rolesplit.1 (DONE 9/9): DESIGN.md -> design/ ledger role-split

The 3020-line docs/DESIGN.md is replaced by the design/ ledger:
design/INDEX.md (sole addressable spine, typed Contracts+Models tables,
polymorphic links — prose file OR authoritative source //!), 14
design/contracts/*.md test-linked invariants + 3 source-link-only
contracts (mangling/env-construction/qualified-xref, no prose file —
code is SoT), 5 design/models/*.md whitepapers, and
docs/journals/2026-05-19-design-decision-records.md (the
relitigation-guard archive — every why/rejected/does-not-do/rollback/
empirical ### moved out at ###-granularity). Clean cut: git rm
docs/DESIGN.md, no stub.

RED-first crates/ailang-core/tests/design_index_pin.rs — the 4-clause
anti-regrowth spine (DESIGN.md-gone / every-INDEX-link-resolves /
every-contract-names-a-resolvable-ratifier /
contracts-carry-no-decision-record-prose) — demonstrably RED before,
GREEN after. Build-atomic by task ordering: design_schema_drift.rs's
include_str! (the only compile-time consumer) retargeted to
design/contracts/data-model.md BEFORE the deletion; its
## Data model/## Pipeline slicer dropped (a simplification the split
enables). 2 NoInstance diagnostics + 2 lockstep E2Es retargeted to
design/contracts/{float-semantics,typeclasses}.md. ~12 agent reading
lists + 5 SKILL bodies + CLAUDE.md + skills/README.md + ~25
code/C/.ail/spec comment xrefs retargeted; OQ7 dangling 'Iter 13b'
cite deleted (no forward target — a pointer would be fiction).
honesty-rule.md rewritten so the rule names the new home
(rationale->journals), resolving the recon-found internal
contradiction; the two docs_honesty_pin.rs:70,72 pinned phrases kept
verbatim+contiguous.

Boss-verified independently: cargo test --workspace 646 passed /
0 failed; design_index_pin 4/4; acceptance grep CLEAN of live
DESIGN.md refs (residuals = only the spec-mandated clause-4
deletion-enforcer). 2 DONE_WITH_CONCERNS routed to the mandatory
milestone-close audit: (a) str-abi.md:23 '(iter str-concat,
2026-05-13)' provenance stamp trips advisory architect_sweeps Sweep-1
— Boss-confirmed byte-identical to DESIGN.md@deeffb1:2062-2065, a
faithfully-migrated PRE-EXISTING anchor (regexes verbatim, only path
retargeted), NOT split-introduced — RATIFY-or-tidy at audit; (b) a
now stale-direction intra-prose 'see Str ABI below' cross-ref in
float-semantics.md — audit-adjudication candidate. Plan defect noted:
Task 9 Step 4's verbatim acceptance grep used a ^./ anchor not
matching the system's grep -rIn output; substance re-verified CLEAN.

Spec grounding-check PASS x2. Journals INDEX + decision-records
pointer appended (Boss-only).
This commit is contained in:
2026-05-19 13:04:22 +02:00
parent deeffb1872
commit 176821c2e7
76 changed files with 3707 additions and 3340 deletions
-3020
View File
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,431 @@
# Design decision-records — relitigation guard (migrated 2026-05-19)
Why-X-chosen / why-Y-rejected / deliberately-does-not-do / rollback
/ empirical-addendum prose, migrated out of the former
`docs/DESIGN.md` by the design-md-rolesplit milestone. A future
brainstorm reads this so it does not re-propose a settled-and-
rejected idea. Order preserved from the source. This file is
append-only history; it is NOT a contract surface.
## Decision 1: source = data, not text
A module is a JSON object with a fixed schema. There is no parser for
free-form text. Typos in identifiers turn into hash-lookup errors that the
compiler proposes a fix for directly.
A textual form exists (`.ail`, S-expression-like), but only as a
bidirectional projection of the JSON form. It is intended for human reviews
and diffs.
**Canonical format:** `.ail.json` with deterministic key order.
## Decision 2: content-addressed definitions
Every top-level definition has a `hash` value (BLAKE3 over canonical JSON
without the `hash` field itself). References between definitions go primarily
by name — names are for readability. The hash is the canonical identity.
Advantages:
- Refactoring by adding new defs, not by in-place change. Old versions stay
callable until manually removed.
- Caching of typecheck results and codegen per hash.
- Diffs show exactly which def has changed.
## Decision 4: Hindley-Milner + optional refinements
MVP: HM with let-polymorphism. All types are inferable, but at the top level
they must always be explicitly annotated (for local reasoning).
Later: refinement annotations that escalate to SMT. `(i: Int | i >= 0)`. They
are reserved in the AST from the start, but in the MVP they are simply passed
through as opaque strings.
## Decision 5: emit LLVM IR as text
Instead of `inkwell` or `llvm-sys`: AILang produces `.ll` files as strings
and hands them to `clang` for linking.
Rationale:
- The LLVM IR text syntax is largely stable across versions.
- No build dependency on a specific libllvm version.
- Generated code is trivially inspectable, which makes debugging much easier.
- An LLM can read the generated IR directly, which is harder with opaque
library calls.
Trade-off: no inline optimisations through the LLVM API. We rely on
`clang -O2` as the standard pipeline.
The rest of this section
records the *why* of Decision 6 for the audit trail; the constraints
listed below describe the surface as shipped.
## Why this is opening up
Early development authored everything as raw `*.ail.json`. That worked
for 17 fixture files (each ≤ 60 LOC of JSON) but does not scale. Two
breaking signals:
1. The token-economy cost of the JSON-AST is massive: a single integer
literal `1` is encoded as `{"t":"lit","lit":{"kind":"int","value":1}}`
— ~38 tokens of structural overhead per bit of semantics. For a stdlib
in the 200500 def range this displaces real attention budget.
2. JSON-AST authoring exposes a class of errors (wrong field names,
silently-accepted extra fields under `#[serde(default)]`,
inconsistent ctor casing) that surface only at load time. The
schema is correct-by-construction in storage but **error-prone in
authoring**.
Decision 1 anticipated this: it says a textual form exists "as a
bidirectional projection of the JSON form." The pretty-printer already
emits S-expression-style text (see `crates/ailang-core/src/pretty.rs`).
What is missing is the inverse direction — text → AST. Decision 6
adds that inverse as **one** authoring projection alongside the
existing pretty-printer; the JSON-AST remains the source of truth.
## First choice and rollback plan
**Try (A) first.** Reasoning: constraint 1 (formalizable) outweighs
constraint readability. (A) has a 3-rule core grammar with one
lexical disambiguation rule. (B) doubles the rule count and
re-introduces a soft form of precedence (`->` inside `forall`).
(C) is tempting because it is zero-design but the resulting spec is
visibly heterogeneous, which is exactly what constraint 1 was meant
to rule out.
If implementing (A) reveals that paren density actively hurts my
authoring (measurable: I make more wrong-paren errors than the
JSON-AST shape produced before), roll back and try (C). (B) stays
on the shelf for a future iter only if both fail.
## Implementation outline
- `crates/ailang-surface` — new crate. **Strictly additive.** PEG
parser produces existing `ailang-core::ast` types. No new AST
nodes, no schema changes, no new hashable form. Pretty-printer
for form (A) lives here too (the round-trip is the contract).
- `ailang-check`, `ailang-codegen` are **not modified**. They
continue to consume `ailang-core::ast::Module` values regardless
of which projection produced them.
- Round-trip test: for every `examples/*.ail.json`, parse the
corresponding hand-written `*.ail`, canonicalise, and assert
hash-equivalence to the original. Hash equivalence is the truth
check; the surface ships only if every fixture round-trips
identically.
- CLI: `ail parse <file.ail> -o <file.ail.json>`. Symmetric to
existing `ail render`. **`.ail.json` remains a first-class input**
to every existing subcommand; the parser is a producer, not a
gatekeeper.
**Both extensions accepted.** Every path-taking CLI subcommand
accepts either `.ail` (Form A)
or `.ail.json` (Form B) as input. For `.ail` paths the subcommand
parses through `ailang_surface::parse` in-line and then proceeds
with the same loaded `Module` value that `.ail.json` would have
produced. `ail parse` remains the explicit converter — it does
nothing the implicit dispatch in the other subcommands does not,
but it is the supported way to materialise a stable `.ail.json`
snapshot from a `.ail` source for diff / hash / cache purposes.
- Stdlib (`std_list`, `std_maybe`, ...) authored in
form (A) from day one **because that is the AI authoring
projection**, not because JSON authoring is forbidden. The
resulting `.ail.json` is what tests and downstream tools see.
## What this Decision deliberately does not do
- It does not change which form is canonical: the JSON-AST remains
the hashable, content-addressed representation, and all hashing,
content-addressing, and cross-module references flow through it
unchanged. The authored form is Form A; the JSON-AST is
materialised in-process by callers that need it. The two
forms are byte-isomorphic by the round-trip invariant. Form (A)
is one projection; the JSON-AST stays canonical.
- It does not foreclose visual or graphical front-ends. The crate
layout (`core` owns AST; `surface`/`visual`/... are siblings)
reserves that lane.
- It does not remove `.ail.json` as input. Every existing
CLI subcommand (`check`, `render`, `describe`, `emit-ir`,
`build`, `run`, `manifest`, `deps`, `diff`, `workspace`,
`builtins`) keeps its current `.ail.json` interface.
## Form refinements during implementation
Two productions in the original sketch had to be widened
during implementation to round-trip the existing AST faithfully.
Captured here for the spec record:
1. **`lam-term` carries types and effects.** The AST's `Term::Lam`
stores parallel `params`, `param_tys`, `ret_ty`, and `effects`
fields. The original sketch had only names. The implemented form is
```
lam-term ::= "(" "lam" "(" "params" typed-param* ")"
"(" "ret" type ")"
effects-clause? body-attr ")"
typed-param ::= "(" "typed" ident type ")"
```
This keeps the no-precedence / one-construct-per-token-list
invariants and adds no new lexical rules.
2. **`import-clause` admits an optional alias.** The AST's
`Import.alias` is `Option<String>`; the original sketch only
supported the `None` case. The implemented form is
```
import-clause ::= "(" "import" ident ("as" ident)? ")"
```
`as` is a bare ident token in this position; no special lexical
rule is needed.
Neither change extends the grammar's rule budget meaningfully:
the 30-production ceiling of constraint 1 is intact (the parser
implements ~28 named productions). All 17 `examples/*.ail.json`
fixtures round-trip identically through `print → parse → canonical
JSON`; the three hand-written `.ail` exhibits parse to canonical
JSON identical to their corresponding `.ail.json` files.
3. **Tail-call surface.** Decision 8 ships two new
productions, both positional analogues of their non-tail
counterparts. Their result terms set `Term::App.tail = true` /
`Term::Do.tail = true`; the typechecker's `verify_tail_positions`
pass enforces that the marker is only used in tail position.
```
tail-app-term ::= "(" "tail-app" term term+ ")"
tail-do-term ::= "(" "tail-do" ident term* ")"
```
Production count: ~30, still inside the 30-rule
constraint-1 budget. No new lexical rule (`tail-app` / `tail-do`
are bare ident tokens; no special casing).
## Empirical addendum — cross-model authoring measurement
Cross-model measurement against two foreign LLMs via IONOS,
temperature=0, top_p=1, max-turns=5. Two blind cohorts on the same
four MVP tasks (`t1_add_three`, `t2_length`, `t3_main_prints`,
`t4_count_zeros`); each cohort sees only its own form's mini-spec.
Subjects (both runs use the pipeline-error-formatting fix from ms.1
so JSON-cohort feedback carries the full anyhow cause chain):
- `Qwen/Qwen3-Coder-Next` —
`experiments/2026-05-12-cross-model-authoring/runs/2026-05-12-080864/`
- `meta-llama/CodeLlama-13b-Instruct-hf` —
`experiments/2026-05-12-cross-model-authoring/runs/2026-05-12-9197fd/`
| metric | Qwen / JSON | Qwen / AIL | CodeLlama / JSON | CodeLlama / AIL |
|---|---|---|---|---|
| reached green | 1/4 | 1/4 | 0/4 | 2/4 |
| first-attempt green | 1/4 | 1/4 | 0/4 | 2/4 |
| mean turns-to-green (green only) | 1.0 | 1.0 | — | 1.0 |
| total prompt tokens | 182,378 | 110,575 | 116,015 | 93,017 |
| total completion tokens | 14,972 | 3,474 | 2,711 | 2,234 |
| top error class | check (×3) | parse (×3) | check (×2) | parse (×2) |
Both subjects show the same direction on every metric the table
tracks. AIL is cheaper than JSON on prompt tokens (Qwen 61%,
CodeLlama 80% of the JSON cohort's spend) and cheaper on completion
tokens (Qwen 23%, CodeLlama 82%). AIL reached-green is greater than
or equal to JSON reached-green for each subject (Qwen ties at 1/4;
CodeLlama strictly dominates, 2/4 vs 0/4). The failure-class
symmetry observed on the original Qwen baseline holds for both
subjects: JSON cohorts fail at the typecheck stage, AIL cohorts at
the parse stage — each form's front-of-pipeline check. Three of the
four first-attempt-green cells observed across the two subjects are
AIL (Qwen-AIL t3, CodeLlama-AIL t1, CodeLlama-AIL t3); the
fourth is Qwen-JSON t3, the same task the original baseline already
flagged as the JSON-form's only first-attempt success. Two CodeLlama
JSON-cohort cells (`t2_length`, `t4_count_zeros`) terminated as
`api_failure` on turn 1 with zero tokens consumed — a transient
IONOS-side terminal error, not a model-output failure; CodeLlama
JSON-cohort numbers therefore average over 2 informative cells, not
4. The Qwen re-run also shifted by one cell against the cma.3
baseline (AIL 2/4 → 1/4, JSON 1/4 → 1/4) despite identical
temperature=0 inputs; this is IONOS-side state-of-day noise on a
single deterministic re-run, not an effect of the ms.1
feedback-formatting fix (which only changes JSON-cohort prompt
content).
**Scope of this addendum:** two subjects, n=1 each, deterministic.
This is a second data point pointing in the same direction as the
first, not a verdict. The universal claim of this Decision (".ail
is the AI authoring projection") would need ≥3 subjects with
statistical robustness to ratify; both current points point the
same way (AIL cohort cheaper and at-least-as-green) but neither
the sample size nor the cross-call noise observed in the Qwen
re-run is enough to close out the Decision.
## Why not other memory models
**Tracing GC.** Open-ended. Boehm's bench overhead is structurally on the allocate path (JOURNAL bench notes); tuning Boehm cannot move that
needle. A precise tracing GC would need read/write barriers,
root maps, generational machinery — months of work, with
irreducible pause-time variability at the end. The user's
framing was decisive: "Am GC kannst du ewig rumschrauben (und
bekommst trotzdem auch in 100 Jahren keine berechnbare
Performance)."
**Region inference (Tofte/Talpin / MLton-style).** Considered
seriously; rejected. Regions tie lifetimes to dynamic scope —
every value lives in some region, regions stack on entry/exit,
deallocation is bulk-by-region. The reduction: a region is an
explicit allocator with sugar plus a static check that nothing
escapes its lifetime. The deeper problem: regions assume
**stack-shaped lifetimes**. Real programs have non-stack-shaped
lifetimes — caches, memo tables, registries, lookup structures
whose lifetimes are not nested. Regions would either force these
into a `letregion` at the top of `main` (everyone's allocator
becomes the root region — useless), or require a region per
cache variant (combinatorial). RC has no lifetime-shape
assumption; it works for any DAG.
**Linear / ownership types as primary mechanism (Rust-style).**
Considered as a general-purpose alternative; rejected as primary.
Rust's borrow-checker is a great mechanism but requires the
author to thread lifetimes through every signature and accept
that some programs cannot be expressed without `unsafe`. For an
LLM-targeted language with recursion + closures everywhere, that
cost is too high — most of the source surface would be lifetime
annotations rather than logic. AILang uses a *subset* of these
ideas (mode annotations, linear consumption discipline)
selectively, layered on top of RC, where the annotations buy
concrete optimisations and never have to thread lifetimes
through callees.
## Why advisory + suppress instead of inference
Three reasons, all anchored in Decision 10's framing:
1. **Annotation states intent; inference picks
weakest-supporting.** These often coincide today but are
conceptually different. The annotation captures what the
author committed to (e.g. `(own T)` reserved for a planned
mutation that hasn't landed); inference would silently
relax it.
2. **Annotation is a drift-bremse.** Body change that flips
the inferred mode produces caller-side breakage at remote
sites. Annotation enforces the local-conflict-error pattern
instead.
3. **Forcing function for LLM authoring.** Without mandatory
annotation, the LLM never has to commit to ownership intent
before writing the body. The advisory lint plus suppress
lets us flag accidental over-strictness without weakening
the contract.
The suppress mechanism with mandatory-reason mirrors Rust's
`#[allow(...)]`-style escape hatch but sharpens it: the reason
is required (not optional), and it becomes part of the contract
the next reader sees. CLAUDE.md's "preserve correctness across
development cycles" is what this directly serves.
## Adjacent extensions for mutability (out of Decision 10's scope)
If future workloads need mutable arrays, hash tables, or other
inherently mutable primitives, the answer is **not** a tracing GC
backstop. The answer is a separate ownership/linear extension
that gates mutability behind static single-owner discipline. RC
+ uniqueness is the universal floor; ownership extends it for
specific high-performance primitives without rebreaking the
acyclicity invariant.
## What this Decision deliberately does not do
- **Does not infer everything.** AILang demands annotations
*because* the LLM author can produce them effortlessly. The
compiler does inference *plus* verification of contracts.
- **Does not require existing fixtures to migrate immediately.**
`(con T)` is treated as `(own T)`. Existing JSON hashes
stay bit-identical until the fixture is intentionally updated.
- **Does not commit to atomic refcounts.** AILang is
single-threaded; refcounts are non-atomic. The embedding ABI's
per-thread `ailang_ctx_t` keeps this correct under a host swarm:
each thread's allocations are private to its ctx, no RC cell
crosses a thread, so non-atomic refcounts stay sound.
- **Does not introduce regions.** Regions were considered and
rejected; see "Why not other memory models" above.
## What the typeclass design explicitly does NOT support
Each of the following is rejected by either schema (parser cannot
express the construct) or by an enumerated diagnostic. The
combination of axis-1 (single-param, no FunDeps, no assoc) and
axis-5 (kind `*` only) covers the space.
- **Multi-parameter classes** (`class Foo a b where ...`). Schema
rejects: `ClassDef.param` is a string, not a list.
- **Higher-kinded class params** (`class Functor f`). Rejected at
workspace-load time: `BareCrossModuleTypeRef` from canonical-form
validation fires before class-schema validation if any method
body uses the param as a `Type::Con` head — bare non-primitive
names must be declared as a `TypeDef` in the owning module, and
a class param is not.
- **Higher-rank polymorphism** (`forall a. (forall b. b -> b) -> a`).
Already rejected at parse time per the typeclass-conversation
rationale recorded in JOURNAL; constraint-bearing
signatures inherit that prohibition.
- **Existential / dyn dispatch** (`exists a. Show a => a`,
heterogeneous lists like `[Show]`). Schema does not express
existentials. The LLM-natural alternative is sum types.
- **Associated types** (`class Container c where type Element c`).
Schema does not express type-level methods.
- **Functional dependencies** (`class Convert a b | a -> b`). Required
only for multi-param classes; entails by axis 1.
- **`deriving` / auto-derivation** (`data Foo = ... deriving (Eq)`).
Future iteration, gated separately on Feature-acceptance.
- **Numeric literal defaulting.** `1` does not auto-resolve to `Int`
under an ambiguous `Num a` constraint. Bare polymorphic literals
with multiple satisfying instances fire `NoInstance` with a hint
to annotate. The LLM-author writes `1 : Int` or `1 : Float`.
- **Orphan-with-warning mode.** Coherence is hard. `OrphanInstance`
is always an error, never a warning. The corollary `--allow-orphans`
flag is not provided.
## What this decision does NOT commit to
- **Operator routing.** `==`, `<`, etc. stay primitive in milestone
22. Class-routing operators is a future-iteration option, not a
Decision-11 commitment.
- **Form-B (prose) projection of class/instance.** The prose
renderer for the class/instance schema nodes is one-way (no
parser by design); the render in
`crates/ailang-prose/src/lib.rs` is a placeholder and
informational only.
- **Specific monomorphised-symbol naming format.** Milestone 22
uses `<method>__<type-surface-name>` for primitive type targets
(e.g. `show__Int`) and `<method>__<8-hex-prefix>` for compound
types (where `<8-hex-prefix>` is the BLAKE3 hash of the canonical
type bytes). The `__` separator was chosen over `#` and `@` for
LLVM IR identifier legality.
- **Mode annotations on class methods.** Class method signatures
ARE full FnSigs and DO carry mode annotations per Decision 10;
the convention is `borrow` for read-only methods. User-defined
classes pick modes per method.
- **Number of Prelude classes.** Milestone 22 ships zero (no
Prelude). A future Prelude milestone gates class additions on
the Feature-acceptance criterion at the time of proposal.
## Env construction — why two parallel overlays (rationale of the code-SoT §"Env construction")
Behaviour of the two-overlay check environment is code-authoritative
(the `env-construction` ledger row is source-link only); only the
*why* moves here so a future brainstorm does not re-propose
collapsing the two maps:
The split is not a refactor wart. The two maps have distinct
semantic roles: an owning data index, and a derived lookup
accelerator over the same data. Collapsing them into one
variant-keyed map (`Map<Name, EntryKind>`) would force every
type-iterator and every ctor-lookup site to discriminate the variant
tag, reversing the optimisation the reverse index exists to
provide.
The mono-side overlay was narrowed to types-only at iter ct.3.2 because its
consumer is the runtime ctor lookup, which became type-driven
post-ct.2.2 — a different consumer story, hence a different
overlay shape. The asymmetry between the check side and the mono
side is by design and is pinned by
`crates/ailang-check/tests/duplicate_ctor_pin.rs`.
@@ -0,0 +1,91 @@
# iter design-md-rolesplit.1 — DESIGN.md → design/ ledger role-split (whole milestone)
**Date:** 2026-05-19
**Started from:** deeffb1872cd13a9fed2a0812d69e7273f49671b
**Status:** DONE
**Tasks completed:** 9 of 9
## Summary
The 3020-line `docs/DESIGN.md` is replaced by the `design/` ledger:
`design/INDEX.md` (sole addressable spine, a typed Contracts+Models
table), 14 `design/contracts/*.md` (test-linked invariants), 5
`design/models/*.md` (onboarding whitepapers), plus
`docs/journals/2026-05-19-design-decision-records.md` (the
relitigation-guard archive of every why/rejected/does-not-do/rollback
`###`). Content was sliced from DESIGN.md by the spec Appendix line
ranges with a deterministic Python slicer (`/tmp/ail-iter/
design-md-rolesplit.1/slice.py`, not committed) — byte-faithful
except (a) heading-level re-level, (b) the rewritten
`honesty-rule.md`, (c) the OQ7 cite deletion. The RED-first
`design_index_pin.rs` anti-regrowth spine (4 clauses) was
demonstrably RED before and is GREEN after. Every live `DESIGN.md`
reference in code, scripts, agents, SKILL bodies, CLAUDE.md, README,
runtime C, `.ail` fixtures, and `form_a.md` was retargeted to the
Appendix destination (or to a code-SoT note for the source-link
contracts). `docs/DESIGN.md` deleted via `git rm` (staged only).
Build-atomic by task ordering; whole `cargo test --workspace` GREEN,
zero failures.
## Per-task notes
- design-md-rolesplit.1.1: RED-first `crates/ailang-core/tests/design_index_pin.rs` — 4-clause structural pin written; verified RED (DESIGN.md present + design/ absent). Dropped one unused `Path` import (plan-verbatim code had an unused-import warning) — recorded as a concern.
- design-md-rolesplit.1.2: `design/INDEX.md` (spec-verbatim 2-table ledger + "## Project framing" preamble absorbing DESIGN.md:6-18/19-55/83-92) + 14 `design/contracts/*.md` sliced from the Appendix ranges + the rewritten `honesty-rule.md` (two `docs_honesty_pin.rs:70,72` phrases each contiguous on one physical line). INDEX link cells made workspace-root-relative (`design/contracts/...`) so the authoritative pin resolves them.
- design-md-rolesplit.1.3: 5 `design/models/*.md` sliced from the Appendix model-tagged ranges; `every_index_link_resolves` PASS for all 20 link rows.
- design-md-rolesplit.1.4: `docs/journals/2026-05-19-design-decision-records.md` — every Appendix `D` range concatenated in source order + the genuine Env-construction "why" rationale (source-link sections' behaviour dropped as code-SoT). Acceptance-criterion-2 completeness verified programmatically: every one of 66 `##`/`###` headings mapped exactly once, no overlap (only uncovered lines are DESIGN.md's own `#`-preamble :1-5 and one blank separator :244). Journals-INDEX pointer line (Step 2) **deferred to Boss** — see Concerns.
- design-md-rolesplit.1.5: retargeted `design_schema_drift.rs` (const `DESIGN_MD``DATA_MODEL` include_str! → data-model.md; deleted `data_model_section()` + `data_model_section_is_bounded()`; all anchor tests use `DATA_MODEL`), `docs_honesty_pin.rs` (per-test reads point at the design/ homes; three present-anchors found to live in the decision-records journal — read from there), `effect_doc_honesty_pin.rs:21` (normalised join of effects.md + scope-boundaries.md; added `norm()`). All three GREEN; build green with DESIGN.md still present.
- design-md-rolesplit.1.6: retargeted the Float-branch (`design/contracts/float-semantics.md`) and Show-branch (`design/contracts/typeclasses.md`, contiguous across the `\`-continuation) diagnostics in `ailang-check/src/lib.rs` + the 2 lockstep E2E assertions + module rustdocs; both E2Es GREEN.
- design-md-rolesplit.1.7: `bench/architect_sweeps.sh` repointed (`INDEX`/`DESIGN_GLOB`, recursive grep over design/contracts+models, regexes unchanged, exit-code contract preserved, exit-2 verified). Sweep-5 honesty-rule self-quote reworded; one pre-existing advisory Sweep-1 match remains (see Concerns).
- design-md-rolesplit.1.8: retargeted buckets (b) 12 agent files, (c) 5 SKILL bodies + skills/README.md, (d) CLAUDE.md (layout table + the "Roles of …" section, current-state-mirror discipline preserved & re-homed), (e) ~25 code/C/.ail/spec comment xrefs to the Appendix destinations (source-link contracts → code-SoT/INDEX note); OQ7 "Iter 13b" cite deleted (behaviour stated directly, no pointer). Build green.
- design-md-rolesplit.1.9: `git rm docs/DESIGN.md` (staged); workspace build green (build-atomicity proven — the only compile-time consumer was retargeted in Task 5); `cargo test --workspace` ALL GREEN incl. `design_index_pin`'s 4 clauses (RED→GREEN); acceptance grep CLEAN of live refs (residuals = the spec-mandated clause-4 pin only); architect_sweeps contract preserved.
## Concerns
- iter .1: dropped an unused `use std::path::Path` from the plan-verbatim `design_index_pin.rs` (it produced an unused-import warning). Minimal quality fix; the RED-first property is unchanged. Deviation from plan-verbatim text, recorded.
- iter .2: **Plan/spec internal contradiction (repaired).** Task 1's clause-3 marker list (spec-verbatim) includes `"an earlier draft"`; Task 2 Step 4's plan-verbatim `honesty-rule.md` text quoted `"an earlier draft said"` as a *forbidden-pattern example*, which `contracts_carry_no_decision_record_prose` flags forever. The spec's own sample honesty-rule.md (spec §"Concrete code shapes") does NOT contain that phrase. Repaired by rewording honesty-rule.md's History bullet to name the category without the tripwire substring, preserving the two `docs_honesty_pin.rs:70,72` pinned phrases verbatim+contiguous. Spec-faithful to §Architecture-4 ("rewritten to resolve the internal contradiction").
- iter .2: INDEX link cells changed from spec-verbatim `contracts/…` to workspace-root-relative `design/contracts/…` so the authoritative `design_index_pin.rs` (resolves `root().join(link)`) is GREEN. Ratifying-test column resolved to exact paths (`skills/brainstorm/SKILL.md`, `crates/ailang-surface/tests/round_trip.rs`) per spec lines 183-187 ("Boss-resolved to exact recon-verified paths"); the spec's short labels (`… Step 4`, `… round_trip.rs`) would not resolve under clause-2.
- iter .7 / .9 (DONE_WITH_CONCERNS): `architect_sweeps.sh` exits **1**, not the plan-expected **0**. Sole match: `design/contracts/str-abi.md:23` `(iter str-concat, 2026-05-13)` — an API-origin date faithfully migrated verbatim inside the `### Heap-Str primitives` block (spec Architecture-2: `###` moves whole, no sentence-surgery; the pre-split sweep against DESIGN.md flagged the same line identically — NOT a split-introduced regression). The sweep is explicitly advisory; this is a milestone-close `audit` adjudication item, not a relocation defect. Faithful-migration over content-dodging (CLAUDE.md memory).
## Known debt
- `design/contracts/float-semantics.md` retains the cross-prose `(see "Str ABI" below for the dual realisation)` inside the `float_to_str`/`int_to_str` paragraph (DESIGN.md:2795-2800) — the Str ABI section moved to `str-abi.md`, so this internal "below" cross-ref is now stale-direction. Not strippable at relocation time (no task prescribes intra-prose cross-ref rewriting; the Appendix is heading-level). `str-abi.md` contract exists; the phrase still names a real contract. Audit-adjudication candidate.
- `design/contracts/str-abi.md:23` iter-origin date stamp (the architect_sweeps Sweep-1 advisory match above) — audit adjudicates whether the iter/date origin annotations in the migrated `### Heap-Str primitives` API list are honest API-provenance or strippable history.
## Blocked detail
(none — DONE)
## Boss action required (NOT done by this agent — role + project rule)
- **Journals INDEX pointer (Task 4 Step 2):** the orchestrator role
Red-Flags any edit to `docs/journals/INDEX.md`, and
`skills/implement/SKILL.md` makes INDEX.md strictly Boss-only with
no exception. The decision-record *file* is created (the
substantive deliverable); the Boss appends, in the same commit,
newest-last to `docs/journals/INDEX.md`:
`- 2026-05-19 — design-decision-records (migration): relitigation-guard archive — every why/rejected/does-not-do/rollback/empirical ### moved out of the former docs/DESIGN.md by the design-md-rolesplit milestone. Companion to spec 2026-05-19-design-md-rolesplit.`
plus this iter's own pointer line per the standard Boss procedure.
- **Acceptance-grep plan-defect:** Task 9 Step 4's verbatim grep
filter `grep -vE '^\./docs/(...)'` uses a `^\./` anchor that does
NOT match this system's `grep -rIn` path output (`docs/...`, no
`./`), so the history-doc exclusion silently fails on the literal
command. The spec-criterion-8-faithful filter (anchor `^(\./)?`,
plus excluding `design_index_pin.rs` — the absence-enforcing
clause-4 pin the spec's own sketch mandates) returns
**CLEAN: zero live DESIGN.md references**. The 3 residual matches
are all `crates/ailang-core/tests/design_index_pin.rs` (the
spec-mandated `docs/DESIGN.md was resurrected` guard) — the
deletion *enforcer*, categorically not a live content xref.
## Files touched
- New: `design/INDEX.md`, `design/contracts/{14 files}.md`, `design/models/{5 files}.md`, `crates/ailang-core/tests/design_index_pin.rs`, `docs/journals/2026-05-19-design-decision-records.md`
- Deleted (staged): `docs/DESIGN.md`
- Modified — tests: `design_schema_drift.rs`, `docs_honesty_pin.rs`, `effect_doc_honesty_pin.rs`, `eq_float_noinstance.rs`, `show_no_instance_e2e.rs`, `duplicate_ctor_pin.rs`, `codegen_import_map_fallback_pin.rs`, `polyfn_dot_qualified_branch_pin.rs`, `embed_record_layout_pin.rs`, `embed_staticlib_lowering.rs`, `eq_ord_e2e.rs`, `e2e.rs`, `round_trip.rs`
- Modified — source: `ailang-check/src/lib.rs`, `ailang-codegen/src/{lib,drop,match_lower}.rs`, `ailang-core/src/ast.rs`, `ailang-surface/src/{lib,parse,print}.rs`, `ail/src/main.rs`, `ail-embed/src/lib.rs`, `ailang-core/specs/form_a.md`
- Modified — runtime/fixtures: `runtime/{rc,str}.c`, `crates/ail/tests/embed/{record,tick}_roundtrip.c`, `examples/fieldtest/floats_{3,4}*.ail`
- Modified — bench/skills/docs: `bench/architect_sweeps.sh`, `CLAUDE.md`, `skills/README.md`, 5 SKILL.md, 12 agent .md
## Stats
bench/orchestrator-stats/2026-05-19-iter-design-md-rolesplit.1.json
+2
View File
@@ -114,3 +114,5 @@
- 2026-05-19 — iter embedding-abi-m5.3 (DONE 3/3): final functional M5 iteration — the time-shard boundary-invisibility proof + friction harvest. One symbol (EURUSD), three disjoint contiguous single-month windows (2017-03/04/05), each folded on its own thread owning its own `Kernel` (`Ctx: !Send` ⇒ one-ctx-per-thread compile-time-enforced). Adds the purely-additive windowed adapter sibling `fold_window` (same `MidPriceStream`+`Kernel` path as `fold_symbol`, differs only by `stream_tick_windowed`), a new single-responsibility `[[bin]] timeshard_runner`, `chrono` as a **dev-dep** (the time-shard test derives `(y,m)→Unix-ms` bounds via data-server's own `NaiveDate…timestamp_millis()` precedent and passes raw ms to the bin, keeping the bin date-math-free and Invariant 1 untouched), and `tests/timeshard.rs` carrying the spec §3 strength-ordered assertions: (a) each shard's `(acc,n)` **bit-exact** vs a single-thread host fold of THAT EXACT window in data-server stream order — the first actual proof of the chunk/window-boundary-invisibility claim the entire M4 retirement rests on; (b) host-summed partials bit-exact by construction; (c) the whole-window [2017-03..2017-05] single-thread total within `REL_TOL=1e-6` only (cross-shard f64 re-association is host arithmetic, NOT a kernel property — asserting it bit-exact would be the bug; tolerance derived from the recursive-summation error bound with ~4 orders of safety, shown in the test). Plus the now-deterministic global leak-Σ (post-`dbd76e5`) and a journal-only friction-timing capture (no bench gate, no timing assertion — flake-free). RED was a genuine deterministic compile failure (declared-but-absent `[[bin]] timeshard_runner` source; surfaced as Cargo's missing-source error rather than the plan's predicted `env!` error — same root cause, same trigger, no false-green; recorded as a benign plan-text imprecision in Concerns). The shipped m5.2 symbol-fan path (`swarm_runner.rs`, `swarm.rs`) and m5.1 core stay byte-untouched and green. Boss-verified independently: **timeshard determinism 5/5** (consistent ~2.7s, no jitter); full ail-embed suite **0 failed AND 0 ignored** across all binaries (lib 2, swarm 2 — the m5.2 leak-proof stays un-ignored & green, the whole saga's resolution intact —, smoke 1, rt_archive 1, timeshard 1); isolated `embed_rc_global_stats_race` still green; AILang workspace `cargo metadata` data-server count 0; compiler-surface diff empty (zero diff to crates/ailang-*, crates/ail/, runtime/, examples/*.ail, root Cargo.toml, DESIGN.md). **Friction-harvest deliverable (P2 flat-array-decision input): host-per-tick-FFI ≈ 658 ms / 3 192 562 ticks ≈ ~206 ns/tick at real EURUSD volume.** M5's functional work is complete; the milestone closes next via the mandatory `audit` (which also handles the pre-existing `docs/DESIGN.md:2358-2360` "additive M4 concern" drift, explicitly out of m5.3 scope). → 2026-05-19-iter-embedding-abi-m5.3.md
- 2026-05-19 — audit embedding-abi-m5 (milestone close): DRIFT (one doc-honesty tidy) + RATIFY (gc_rss trio, evidenced) + causally-exonerated pre-existing P2 bench noise. Architect: **Invariant 1 PASS** (zero data-server in any compiler crate/runtime; ail-embed its own workspace root; only "finance" hits are the M3 ABI export *symbol name* — ABI not data-server knowledge), M3 frozen-layout SSOT unmoved (the lone rc.c change `7bfa11e` touched only the global stats counters), rc.c header-comment honesty accurate; drift = `[medium]` DESIGN.md:2358-2360 stale "additive M4 concern" (PRE-EXISTING, M4 retired, correctly scoped out of M5) + `[medium]` DESIGN.md §"Embedding ABI" silent-drift (it asserts "no shared mutable runtime state … data-race-free" but the now-atomic global RC-stats fallback IS shared mutable state under concurrency — M5 is AILang's first concurrent consumer) + `[low]` rc.c:88 "two unconditional ++" imprecise — one tidy, same honesty axis, not split. Bencher (my pre-audit "gc_rss is allocator-orthogonal → environmental" hypothesis was **refuted by evidence** — the verification discipline working): the 3× `*.gc_rss_kb +32%` are GENUINE, DETERMINISTIC, M5-attributable — `7bfa11e`'s atomic split a branch in `ailang_rc_alloc` (reached by GC programs via `str.c` int→str at `print`), spilling `%r14/%rbx`; a stale interior list pointer in the new spill slot is conservatively retained by Boehm, pinning a ~32 MB dead spine (Boehm collection #12 freed PRE 32 MB vs HEAD 32 B); bisects exactly on the commit, IR byte-identical, `ail` sha256-identical; RC/bump RSS dead-stable. `bump_s`/`max_us` = pre-existing tracked-P2 (bump RC-orthogonal; max_us 1-sample tail-jitter, PRE often worse, +65→+104% swing between runs minutes apart — dispositively noise), NOT M5. Adjudication: **RATIFY** the gc_rss trio — language reason (Decision 9: Boehm is explicitly transitional; the cost is purely a Boehm conservative-scan false-retention artifact on the transitional allocator of a *correct, necessary, user-approved swarm-safety fix*, codegen-layout-fragile, RC path the committed model and unaffected; chasing it with an out-of-scope codegen change invests in the path being removed) — selective baseline re-anchor `bench/baseline.json` lines 14/44/64 ONLY (137448/137768/137636, bencher-confirmed steady-state; every other metric byte-pristine — a wholesale --update-baseline would have absorbed the P2 noise and broken the established-envelope discipline); re-run confirms the 3 now `ok` at ±0.3%. **NO-ratify / causally exonerated** for bump_s×2 + max_us×2 — baseline pristine, identical disposition to the M2/M3/prelude-decouple closes. Friction-harvest recorded (P2 input): host-per-tick-FFI ≈ ~206 ns/tick at real EURUSD volume. M5 closes after the doc-honesty tidy lands + Boss-verified. Ratify discipline honored: updated baseline JSON + this paired JOURNAL ratify entry committed together. → 2026-05-19-audit-embedding-abi-m5.md
- 2026-05-19 — iter embedding-abi-m5.tidy (DONE 3/3): resolves the M5 milestone-close audit DRIFT (28ab56a). DOC/COMMENT-ONLY, recon-pin-verified, single cohesive commit (M2.tidy a80d495 / M3.tidy 63d7d60 precedent — pins are the coverage, no RED, no audit/fieldtest gate). `docs/DESIGN.md` edit-1 §"Free (host side)": dropped the retired-M4 forward-reference ("an additive M4 concern, not a contradiction of this freeze" — M4 retired 2026-05-18) → present-tense current fact (a boxed-field record is **not** an M3 embedding type, export-gate-rejected; the freeze covers exactly the all-scalar single-ctor record). edit-2 §"Embedding ABI": reconciled the now-inaccurate "(no shared mutable runtime state — … data-race-free, sanitiser-verified)" blanket with the real post-7bfa11e state — the per-allocation hot path (per-ctx counters + per-object refcount header) is non-atomic by design and never shared (`Ctx: !Send`, single-thread-per-ctx); the one datum a multi-threaded host shares is the global RC-stats fallback counter, atomic-relaxed so the swarm's leak accounting is exact; "data-race-free, sanitiser-verified" retained (still true). `runtime/rc.c:88` 18g.0-block comment-only: stale "two unconditional `++` operations" → accurate "one counter bump per alloc/free … relaxed atomic add on the global null-ctx fallback, a plain `++` on the per-ctx path" (consistent with the adjacent already-correct :93-106 atomic block + :45-55 Threading header). Boss-verified independently: all pins green (design_schema_drift 8/0, docs_honesty_pin 5/0, effect_doc_honesty_pin 4/0, embed_record_layout_pin 1/0) — the mechanical proof the edits moved no pinned/hashed byte; the adjacent separately-pinned bare-scalar sentence is byte-identical (shifted 2299→2305 by net-added lines; `docs_honesty_pin.rs:135` is a substring pin, passes); rc.c diff strictly comment-only (filter empty); `cargo build --workspace` Finished; git scope = only docs/DESIGN.md + runtime/rc.c + journal/stats. Clears the M5-audit doc-honesty debt; no new debt. This is the FINAL M5 iteration — M5 (the M1M5 Embedding ABI arc) is now functionally complete, audited, ratified, and doc-honest. → 2026-05-19-iter-embedding-abi-m5.tidy.md
- 2026-05-19 — iter design-md-rolesplit.1 (DONE 9/9, whole milestone): the 3020-line `docs/DESIGN.md` replaced by the `design/` ledger — `design/INDEX.md` (sole addressable spine, typed Contracts+Models tables, polymorphic links: prose file OR authoritative source `//!`), 14 `design/contracts/*.md` test-linked invariants + 3 source-link-only contracts (mangling/env-construction/qualified-xref, no prose file — code is SoT), 5 `design/models/*.md` onboarding whitepapers, and `docs/journals/2026-05-19-design-decision-records.md` (the relitigation-guard archive: every why/rejected/does-not-do/rollback/empirical `###` moved out at `###` granularity). Clean cut (`git rm docs/DESIGN.md`, no stub). RED-first `crates/ailang-core/tests/design_index_pin.rs` 4-clause anti-regrowth spine (DESIGN.md-gone / every-INDEX-link-resolves / every-contract-names-a-resolvable-ratifier / contracts-carry-no-decision-record-prose) demonstrably RED→GREEN. Build-atomic by task ordering (the only compile-time consumer, `design_schema_drift.rs` `include_str!`, retargeted to `design/contracts/data-model.md` BEFORE the deletion; the `## Data model`/`## Pipeline` slicer dropped — a simplification the split enables). 2 NoInstance diagnostics + their 2 lockstep E2Es retargeted to `design/contracts/{float-semantics,typeclasses}.md` (the contiguity-across-`\`-continuation hazard scrubbed). ~12 agent reading lists + 5 SKILL bodies + CLAUDE.md + skills/README.md + ~25 code/C/.ail/spec comment xrefs retargeted to the Appendix destinations; OQ7 dangling "Iter 13b" cite deleted (no forward target — a pointer would be fiction). honesty-rule.md rewritten so the rule names the new home (rationale→journals), resolving the recon-found internal contradiction; the two `docs_honesty_pin.rs:70,72` pinned phrases preserved verbatim+contiguous. Boss-verified independently: whole `cargo test --workspace` **646 passed / 0 failed**, `design_index_pin` 4/4, acceptance grep **CLEAN of live DESIGN.md refs** (residuals = only the spec-mandated clause-4 deletion-enforcer), honesty-rule repair carries no clause-3 marker. Spec grounding-check PASS ×2 (one re-dispatch after a corrected commitment-4 pin-status claim; one after the Boss-adjudicated relocation-appendix amendment resolving plan-recon's 7 open questions — ledger completed to 17 contract rows incl. the qualified-xref/str-abi/scope-boundaries additions the 12-list under-counted). 2 DONE_WITH_CONCERNS routed to the mandatory milestone-close `audit`: (a) `design/contracts/str-abi.md:23` `(iter str-concat, 2026-05-13)` API-provenance stamp trips advisory `architect_sweeps.sh` Sweep-1 — Boss-confirmed **byte-identical to DESIGN.md@deeffb1:2062-2065**, a faithfully-migrated PRE-EXISTING anchor (sweep regexes verbatim, only the path retargeted), NOT a split-introduced regression — RATIFY-or-tidy at audit; (b) the now stale-direction `(see "Str ABI" below)` intra-prose cross-ref in `float-semantics.md` (the Appendix is heading-level; no task prescribes intra-prose cross-ref rewrite) — audit-adjudication candidate. One plan defect recorded (Task 9 Step 4's verbatim acceptance grep used a `^\./` anchor not matching the system's `grep -rIn` output; substance independently re-verified CLEAN). → 2026-05-19-iter-design-md-rolesplit.1.md
- 2026-05-19 — design-decision-records (migration): relitigation-guard archive — every why/rejected/does-not-do/rollback/empirical ### moved out of the former docs/DESIGN.md by the design-md-rolesplit milestone. Companion to spec 2026-05-19-design-md-rolesplit. → 2026-05-19-design-decision-records.md