Translate project content to English

Make English the project-wide language: all comments, string literals,
CLI help text, design docs, journal, agent prompts, and README. Only
CLAUDE.md (the user's own instruction file) stays German, and the live
conversation between user and Claude continues in German.

Adds a new "Project language: English" section to docs/DESIGN.md as
the durable convention. No logic changes — translation only. Four
internal error strings in the typechecker were retranslated; no
test asserts on their wording.

Verified: cargo test --workspace passes (44/44).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-07 12:17:48 +02:00
parent b1dbafc6f2
commit 7577ab8a90
21 changed files with 992 additions and 984 deletions
+138 -126
View File
@@ -1,136 +1,148 @@
# AILang — Designentscheidungen
# AILang — design decisions
Dieses Dokument hält die Kernentscheidungen für AILang fest. Es ist mein Vertrag mit
mir selbst über künftige Iterationen. Kürzungen statt Wachstum bevorzugen.
This document records the core decisions for AILang. It is my contract with
myself across future iterations. Prefer cuts over growth.
## Zielsetzung
## Goal
AILang ist eine Programmiersprache für LLM-Autoren. Sie wird zu LLVM IR kompiliert.
Performance: nativ, ohne GC für den MVP.
AILang is a programming language for LLM authors. It compiles to LLVM IR.
Performance: native, no GC for the MVP.
Optimiert für:
Optimised for:
- **Maschinenlesbarkeit** statt menschlicher Ergonomie. Quelle ist strukturiert.
- **Lokales Reasoning.** Jede Definition trägt ihren vollständigen Typ und ihre Effekte.
- **Beweisbarkeit.** Reine Kernsprache, explizite Effekte, optionale Refinements.
- **Robustheit gegen Halluzinationen.** Symbole sind hashbar; Tools können Existenz
verifizieren, ohne Kontextfenster zu verbrauchen.
- **Machine readability** over human ergonomics. The source is structured.
- **Local reasoning.** Every definition carries its full type and effects.
- **Provability.** Pure core language, explicit effects, optional refinements.
- **Robustness against hallucinations.** Symbols are hashable; tools can verify
existence without spending context window.
## Projekt-Ökosystem
## Project ecosystem
AILang ist nicht nur eine Sprache, sondern ein Ökosystem. Die Sprache an sich
ist nur dann wertvoll, wenn das Drumherum sie für ihren Zielnutzer (LLM-Autoren)
benutzbar, prüfbar und erweiterbar macht. Das Repo enthält daher mehrere
gleichwertige Komponentenkeine davon ist optional, alle entwickeln sich
parallel mit der Sprache:
AILang is not just a language but an ecosystem. The language on its own is
only valuable when its surroundings make it usable, checkable, and
extensible for its target user (LLM authors). The repo therefore contains
several equally important componentsnone of them optional, all of them
evolving in lockstep with the language:
- **Sprachkern** (`crates/ailang-core`, `crates/ailang-check`,
`crates/ailang-codegen`): AST, Typsystem, Codegen.
- **CLI** (`crates/ail`): Toolchain für Tooling-Konsumenten`manifest`,
`describe`, `deps`, `check`, `build`, etc., bevorzugt mit `--json` für
maschinelle Konsumption.
- **Beispiele** (`examples/`): kanonische `.ail.json`-Programme. Sie sind
Spezifikationsanker, nicht Demos — die E2E-Suite hängt an ihnen.
- **Agenten** (`agents/`): spezialisierte Sub-Prompts (Implementer,
Architect, Tester, Debugger), die das Projekt-eigene LLM-Tooling bilden.
Sie sind versionierter Bestandteil des Repos. Siehe `agents/README.md`.
- **Doku** (`docs/`): DESIGN.md (was und warum), JOURNAL.md (Verlauf).
- **Tests**: Unit pro Crate + E2E in `crates/ail/tests/e2e.rs`. Jeder neue
Compiler-Pfad braucht einen Test, sonst zählt das Feature nicht als fertig.
- **Language core** (`crates/ailang-core`, `crates/ailang-check`,
`crates/ailang-codegen`): AST, type system, codegen.
- **CLI** (`crates/ail`): toolchain for tooling consumers`manifest`,
`describe`, `deps`, `check`, `build`, etc., preferably with `--json` for
machine consumption.
- **Examples** (`examples/`): canonical `.ail.json` programs. They are
specification anchors, not demos — the E2E suite hangs off them.
- **Agents** (`agents/`): specialised sub-prompts (implementer,
architect, tester, debugger) that form the project's own LLM tooling.
They are a versioned part of the repo. See `agents/README.md`.
- **Docs** (`docs/`): DESIGN.md (what and why), JOURNAL.md (history).
- **Tests**: unit tests per crate plus E2E in `crates/ail/tests/e2e.rs`. Every
new compiler path needs a test, otherwise the feature does not count as done.
Wenn die Sprache wächst, wachsen diese Komponenten mit. Neue Tools, die das
LLM-Tooling stärken (z. B. `ail diff`, IR-Snapshot-Diffs, neue Agenten),
gehören explizit ins Ökosystem-Inventar dieses Abschnitts und werden hier
ergänzt, sobald sie etabliert sind.
When the language grows, these components grow with it. New tools that
strengthen the LLM tooling (e.g. `ail diff`, IR snapshot diffs, new agents)
explicitly belong in the ecosystem inventory of this section and are added
here as soon as they are established.
## Entscheidung 1: Quelle = Daten, nicht Text
## Project language: English
Ein Modul ist ein JSON-Objekt mit einem festen Schema. Es gibt keinen Parser für
Freitext. Tippfehler in Bezeichnern werden zu Hash-Lookup-Fehlern, die der Compiler
direkt vorschlägt zu fixen.
All in-tree content is written in English: source code (identifiers,
comments, string literals, CLI help), design documents, the journal, agent
prompts, READMEs, commit messages, and new examples. The only exception is
`CLAUDE.md`, which is the user's own instruction file. The live conversation
between user and me stays German for ergonomic reasons; everything that
lands in git is English. This keeps diffs and tooling output uniform and
matches the audience for AILang (LLM authors), for whom English is the
default.
Eine Textform existiert (`.ail`, S-Expression-artig), aber nur als bidirektionale
Projektion der JSON-Form. Sie ist für Menschen-Reviews und Diffs gedacht.
## Decision 1: source = data, not text
**Kanonisches Format:** `.ail.json` mit deterministischer Schlüsselreihenfolge.
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.
## Entscheidung 2: Content-addressed Definitionen
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.
Jede Top-Level-Definition hat einen `hash`-Wert (BLAKE3 über kanonisches JSON ohne
das `hash`-Feld selbst). Verweise zwischen Definitionen erfolgen primär per Name —
Namen sind für Lesbarkeit. Der Hash ist die kanonische Identität.
**Canonical format:** `.ail.json` with deterministic key order.
Vorteile:
## Decision 2: content-addressed definitions
- Refactoring durch Hinzufügen neuer Defs, nicht durch In-place-Änderung. Alte
Versionen bleiben aufrufbar, bis manuell entfernt.
- Caching von Typcheck-Ergebnissen und Codegen pro Hash.
- Diffs zeigen exakt, welche Def sich geändert hat.
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.
## Entscheidung 3: Reine Kernsprache + algebraische Effekte
Advantages:
Default sind totale, reine Funktionen. Effekte werden als Set im Funktionstyp
deklariert: `(Int) -> Int ![IO]`. Die Effektmenge ist row-polymorph
(`![IO | r]`). Im MVP sind nur die Effekte `IO` und `Diverge` (für Endlosschleifen)
verbaut.
- 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.
Dies ist die wichtigste LLM-Eigenschaft: Wenn ich eine Funktion lese, kann ich
ihrer Signatur trauen, ohne den Body zu lesen.
## Decision 3: pure core language + algebraic effects
## Entscheidung 4: Hindley-Milner + optionale Refinements
The default is total, pure functions. Effects are declared as a set in the
function type: `(Int) -> Int ![IO]`. The effect set is row-polymorphic
(`![IO | r]`). In the MVP only the effects `IO` and `Diverge` (for infinite
loops) are wired up.
MVP: HM mit Let-Polymorphismus. Alle Typen sind inferierbar, müssen aber im
Top-Level immer explizit annotiert sein (für lokales Reasoning).
This is the most important LLM property: when I read a function, I can trust
its signature without reading the body.
Später: Refinement-Annotationen, die zu SMT escalieren. `(i: Int | i >= 0)`. Vom
Anfang an im AST vorgesehen, aber im MVP einfach als opake Strings durchgereicht.
## Decision 4: Hindley-Milner + optional refinements
## Entscheidung 5: LLVM IR als Text emittieren
MVP: HM with let-polymorphism. All types are inferable, but at the top level
they must always be explicitly annotated (for local reasoning).
Statt `inkwell` oder `llvm-sys`: AILang erzeugt `.ll`-Dateien als Strings und
übergibt an `clang` zum Linken.
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.
Begründung:
## Decision 5: emit LLVM IR as text
- LLVM-IR-Textsyntax ist über Versionen weitgehend stabil.
- Keine Build-Abhängigkeit von einer bestimmten libllvm-Version.
- Generierter Code ist trivial inspizierbar, was Debugging massiv vereinfacht.
- LLM kann generierten IR direkt lesen, was bei opaken Bibliothekscalls schwerer ist.
Instead of `inkwell` or `llvm-sys`: AILang produces `.ll` files as strings
and hands them to `clang` for linking.
Trade-off: keine Inline-Optimierungen über die LLVM-API. Wir setzen auf
`clang -O2` als Standard-Pipeline.
Rationale:
## Mangling-Schema (Iter 5c)
- 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.
Alle AILang-Funktionen werden zu `@ail_<modul>_<def>` gemangelt — auch im
Single-Modul-Fall. Konstanten ebenfalls (`@ail_<modul>_<const>`). Globale
String-Literale tragen einen kurzen Hint zur Lesbarkeit:
`@.str_<modul>_<hint>_<idx>` (z. B. `@.str_sum_fmt_int_0`). Eintrittspunkt
ist eine `define i32 @main()`-Trampoline (C-/LLVM-ABI), die
`@ail_<entry-modul>_main()` aufruft. `source_filename` existiert genau
einmal pro Workspace und trägt den Eintrittsmodulnamen
(`<entry-modul>.ail`).
Trade-off: no inline optimisations through the LLVM API. We rely on
`clang -O2` as the standard pipeline.
## Konvention: Qualifizierte Cross-Module-Verweise (Iter 5b)
## Mangling scheme (Iter 5c)
Cross-Module-Aufrufe nutzen **keinen** neuen AST-Knoten. Stattdessen ist ein
`Term::Var { name }` mit genau einem Punkt im Namen ein qualifizierter
Verweis: `<prefix>.<def>`.
All AILang functions are mangled to `@ail_<module>_<def>` — even in the
single-module case. Constants likewise (`@ail_<module>_<const>`). Global
string literals carry a short hint for readability:
`@.str_<module>_<hint>_<idx>` (e.g. `@.str_sum_fmt_int_0`). The entry point
is a `define i32 @main()` trampoline (C / LLVM ABI) that calls
`@ail_<entry-module>_main()`. `source_filename` exists exactly once per
workspace and carries the entry-module name (`<entry-module>.ail`).
- `<prefix>` ist ein Import-Alias (`import { module: "X", as: "<prefix>" }`)
oder, falls ohne Alias importiert, der Modulname selbst.
- `<def>` ist der Name einer Top-Level-Definition im Zielmodul.
- Def-Namen DÜRFEN keinen Punkt enthalten — der Typchecker meldet
`invalid-def-name` mit `ctx: { "reason": "contains-dot" }`.
- Der Workspace-Loader (Iter 5a) findet alle erreichbaren Module; der
Typchecker (Iter 5b, `check_workspace`) löst Punkt-Namen über die
Import-Map auf. Diagnostic-Codes: `unknown-module` (Prefix nicht
importiert), `unknown-import` (Modul gefunden, Def nicht).
## Convention: qualified cross-module references (Iter 5b)
Hash-Stabilität: kein neuer AST-Knoten, keine umbenannten Felder — alle
bisherigen Modul-Hashes bleiben bitidentisch.
Cross-module calls use **no** new AST node. Instead, a `Term::Var { name }`
with exactly one dot in the name is a qualified reference: `<prefix>.<def>`.
## Datenmodell (MVP)
- `<prefix>` is an import alias (`import { module: "X", as: "<prefix>" }`)
or, when imported without an alias, the module name itself.
- `<def>` is the name of a top-level definition in the target module.
- Def names MUST NOT contain a dot — the typechecker reports
`invalid-def-name` with `ctx: { "reason": "contains-dot" }`.
- The workspace loader (Iter 5a) finds all reachable modules; the
typechecker (Iter 5b, `check_workspace`) resolves dotted names through the
import map. Diagnostic codes: `unknown-module` (prefix not imported),
`unknown-import` (module found, def not).
Hash stability: no new AST node, no renamed fields — all previous module
hashes stay bit-identical.
## Data model (MVP)
### Module
@@ -145,7 +157,7 @@ bisherigen Modul-Hashes bleiben bitidentisch.
### Def
`kind ∈ { "fn", "type", "effect", "const" }`. Im MVP nur `fn` und `const`.
`kind ∈ { "fn", "type", "effect", "const" }`. In the MVP only `fn` and `const`.
```jsonc
{
@@ -158,7 +170,7 @@ bisherigen Modul-Hashes bleiben bitidentisch.
}
```
### Term (Expression)
### Term (expression)
```jsonc
{ "t": "lit", "lit": { "kind": "int" | "bool" | "unit", "value": ... } }
@@ -169,7 +181,7 @@ bisherigen Modul-Hashes bleiben bitidentisch.
{ "t": "do", "op": "<eff>/<op>", "args": [Term...] }
```
`do` ist im MVP nur ein direkter Aufruf eines Built-in-Effekt-Ops (kein Handler).
In the MVP, `do` is only a direct call to a built-in effect op (no handler).
### Type
@@ -189,7 +201,7 @@ bisherigen Modul-Hashes bleiben bitidentisch.
├─ load + validate schema
├─ resolve names + assign hashes
├─ typecheck (HM, effect rows)
├─ lower to MIR (SSA-ähnlich, named SSA-Werte)
├─ lower to MIR (SSA-like, named SSA values)
├─ emit LLVM IR (.ll)
└─ clang -O2 *.ll -o binary
```
@@ -197,33 +209,33 @@ bisherigen Modul-Hashes bleiben bitidentisch.
## CLI
```
ail check <module.ail.json> — Lädt, validiert, typecheckt
ail manifest <module.ail.json> — Tabelle: name :: type !effects [hash]
ail describe <module> <name> — Detail einer Definition
ail render <module> — JSON → Pretty-Text
ail parse <module.ail> — Pretty-Text → JSON (für Bootstrapping)
ail emit-ir <module> — schreibt .ll
ail build <module> — komplette Pipeline → Binary
ail check <module.ail.json> — loads, validates, typechecks
ail manifest <module.ail.json> — table: name :: type !effects [hash]
ail describe <module> <name> — detail of a definition
ail render <module> — JSON → pretty-print
ail parse <module.ail> — pretty-print → JSON (for bootstrapping)
ail emit-ir <module> — writes .ll
ail build <module> — full pipeline → binary
```
## Verifikation und Korrektheit (über Zyklen)
## Verification and correctness (across cycles)
1. **Snapshot-Tests** für Pretty-Printer und IR-Emit. Diff macht Regressions sofort
sichtbar.
2. **Property-Tests** für Roundtrip JSON ↔ Pretty.
3. **End-to-End-Tests** für `examples/` mit erwartetem Programmoutput.
4. **Hash-Stabilität**: Test stellt sicher, dass dieselbe Def stets denselben Hash
produziert.
5. **CI-Pin** der Outputs in `tests/expected/`.
1. **Snapshot tests** for the pretty-printer and IR emit. The diff makes
regressions visible immediately.
2. **Property tests** for the JSON ↔ pretty-print roundtrip.
3. **End-to-end tests** for `examples/` with expected program output.
4. **Hash stability**: a test ensures the same def always produces the same
hash.
5. **CI pin** of the outputs in `tests/expected/`.
## Was MVP NICHT ist
## What the MVP is NOT
- Keine ADTs / Pattern Matching (Phase 2).
- Keine Closures / höhere Funktionen (Phase 2).
- Keine Effekt-Handler (Phase 3).
- Keine Refinements / SMT (Phase 4).
- Kein Modulsystem über Imports hinaus (Phase 2).
- Keine Strings als first-class. Nur ints + bools + unit.
- No ADTs / pattern matching (Phase 2).
- No closures / higher-order functions (Phase 2).
- No effect handlers (Phase 3).
- No refinements / SMT (Phase 4).
- No module system beyond imports (Phase 2).
- No first-class strings. Only ints + bools + unit.
Der MVP ist erfolgreich, wenn `examples/sum.ail.json` ein Binary erzeugt, das die
Summe 1..10 = 55 druckt.
The MVP is successful when `examples/sum.ail.json` produces a binary that
prints the sum 1..10 = 55.
+272 -276
View File
@@ -1,351 +1,347 @@
# JOURNAL
Chronologische Notizen für mich. Nicht jede Änderung; nur Entscheidungen,
Hindernisse, Beobachtungen, die zukünftige Iterationen brauchen.
Chronological notes for myself. Not every change; only decisions, obstacles,
and observations that future iterations will need.
## 2026-05-07 — Tag 0
## 2026-05-07 — Day 0
- Repo initialisiert. Auftrag in `CLAUDE.md`: LLM-native Sprache, LLVM-Backend.
- Designentscheidungen festgehalten in `docs/DESIGN.md`.
- Toolchain: `rustc 1.94`, `llvm-config 22.1.3`, `clang` vorhanden.
- Entschieden gegen `inkwell` zugunsten LLVM-IR-Text-Emit. Begründung im DESIGN.md.
- Workspace-Layout:
- `crates/ailang-core` — AST, Type, Hash, JSON-Schema
- `crates/ailang-check`Typchecker (kommt später)
- `crates/ailang-codegen`Lowering + LLVM IR Emit
- Repo initialised. Assignment in `CLAUDE.md`: LLM-native language, LLVM backend.
- Design decisions captured in `docs/DESIGN.md`.
- Toolchain: `rustc 1.94`, `llvm-config 22.1.3`, `clang` available.
- Decided against `inkwell` in favour of LLVM IR text emit. Rationale in DESIGN.md.
- Workspace layout:
- `crates/ailang-core` — AST, type, hash, JSON schema
- `crates/ailang-check`typechecker (comes later)
- `crates/ailang-codegen`lowering + LLVM IR emit
- `crates/ail` — CLI
- MVP-Ziel: `examples/sum.ail.json`Binary, das 55 druckt. **Erreicht.**
- MVP goal: `examples/sum.ail.json`binary that prints 55. **Achieved.**
## 2026-05-07 — Architektur-Review nach MVP
## 2026-05-07 — architecture review after the MVP
Wieder am Pfad? Im Großen ja. Konkrete Beobachtungen:
Still on track? Broadly yes. Concrete observations:
**Was hält:**
**What holds:**
- JSON-AST + canonical form + content-hash sind alle Lego-Steine, auf denen
spätere Tools ohne Refactor aufsetzen können (`ail deps`, `ail diff`).
- LLVM-IR-Text-Pipeline arbeitet wie geplant. Keine libllvm-Versionsschmerzen.
- Effekt-Set ist im Typsystem von Anfang an verbaut. Erweiterbar zu row-poly,
ohne den Kern zu ändern.
- JSON AST + canonical form + content hash are all lego bricks that later
tools can build on without a refactor (`ail deps`, `ail diff`).
- The LLVM IR text pipeline works as planned. No libllvm version pain.
- The effect set is wired into the type system from the start. Extensible to
row-poly without touching the core.
**Schulden, die Zinsen tragen:**
**Debt that accrues interest:**
1. **`current_block_label_for_phi` ist eine Heuristik** (siehe codegen). Bei
verschachtelten `if`-Termen wird sie das falsche Block-Label zurückgeben,
weil sie rückwärts den Body scannt. Tickend, weil keine Test-Fälle das
bisher triggern. Muss als nächstes fixiert werden, bevor neue Sprachfeatures
dazu kommen.
2. **Kein typed-AST.** Codegen liest direkt das Quell-AST und verlässt sich
darauf, dass der Typchecker vorher lief. Für MVP OK; sobald ADTs oder
Closures dazukommen, brauche ich eine getrennte typisierte IR-Stufe (TIR).
3. **`hash`-Feld ist nicht im AST.** Aktuell hashen wir das Def-Objekt direkt.
Wenn ich Hashes später als Felder mitserialisiere (Caching), muss der Hash
das Feld vor der Berechnung ausschließen.
1. **`current_block_label_for_phi` is a heuristic** (see codegen). On nested
`if` terms it will return the wrong block label, because it scans the body
backwards. Ticking, because no test cases trigger it yet. Must be fixed
next, before new language features arrive.
2. **No typed AST.** Codegen reads the source AST directly and relies on
the typechecker having run before. Fine for the MVP; once ADTs or
closures arrive, I will need a separate typed IR stage (TIR).
3. **The `hash` field is not in the AST.** Right now we hash the def object
directly. Once I serialise hashes as fields (caching), the hash will
need to exclude that field before computation.
**Plan Iteration 2 (jetzt):**
**Plan iteration 2 (now):**
1. Block-Label-Tracking sauber machen, verschachteltes-if-Test.
2. Strings als Literal + `io/print_str`.
3. Hello-world-Beispiel als zweiten E2E-Test.
4. CLI: `--json`-Output für maschinelle Konsumenten überall, wo es passt.
1. Clean up block-label tracking, with a nested-if test.
2. Strings as a literal + `io/print_str`.
3. Hello-world example as a second E2E test.
4. CLI: `--json` output for machine consumers wherever it fits.
**Plan Iteration 3:**
**Plan iteration 3:**
ADTs + Pattern Matching. Das ist der nächste große Sprung. Erfordert
typisierte IR-Stufe (TIR), weil Pattern Matching zu Decision-Trees lowered
wird, was eine andere Form ist als der AST.
ADTs + pattern matching. That is the next big jump. Requires a typed IR
stage (TIR), because pattern matching lowers into decision trees, which
have a different shape from the AST.
## 2026-05-07 — Iteration 2 fertig
## 2026-05-07 — iteration 2 done
- Block-Label-Tracking robust (verschachtelte `if`s funktionieren). Test
`max3_picks_largest` schützt das.
- Strings als `Lit::Str { value }`, Type `Str` -> LLVM `ptr`, mit
`io/print_str` Effekt-Op. `examples/hello.ail.json` druckt einen String.
- CLI: `manifest --json`, `builtins --json` für Tool-Konsumenten.
- `ail deps [--of NAME] [--json]` listet Aufruf-Edges. Effekt-Ops sind als
`effect:NAME` markiert, damit ein Konsument sie filtern kann.
- Block-label tracking is now robust (nested `if`s work). Test
`max3_picks_largest` protects it.
- Strings as `Lit::Str { value }`, type `Str` -> LLVM `ptr`, with
`io/print_str` effect op. `examples/hello.ail.json` prints a string.
- CLI: `manifest --json`, `builtins --json` for tool consumers.
- `ail deps [--of NAME] [--json]` lists call edges. Effect ops are tagged
`effect:NAME` so a consumer can filter them.
**Architektur-Check:** Keine strukturellen Abweichungen. Codegen liest noch
direkt das Quell-AST (TIR-Stufe wird mit ADTs in Iteration 3 nötig).
**Architecture check:** no structural deviations. Codegen still reads the
source AST directly (a TIR stage will become necessary with ADTs in
iteration 3).
## 2026-05-07 — Iteration 3 fertig: ADTs
## 2026-05-07 — iteration 3 done: ADTs
- TypeDef im AST mit Ctors. Ein Ctor hat `name` und `fields: [Type...]`.
- Term::Ctor (Konstruktion) und Term::Match (Pattern Matching).
- Patterns: `Wild`, `Var`, `Lit`, `Ctor { ctor, fields }`. Im MVP sind nested
Ctor-Patterns NICHT erlaubtSub-Patterns müssen `Var` oder `Wild` sein.
- Typchecker mit Type-Registry und `ctor_index` (ctor-name → ADT). Im Match
wird Exhaustiveness gegen die volle Konstruktormenge geprüft. Negativ-Test
schützt das.
- Codegen: Boxed-Heap-Layout. Pro Ctor-Anwendung `malloc(8 + 8*n)` Bytes;
Tag in offset 0, Felder ab offset 8 (8-Byte-Slots, native typed
load/store). Match: load tag + switch + arm-blocks + phi am Join.
- `examples/list.ail.json` (Cons/Nil-Liste, sum_list über match) liefert 42.
- TypeDef in the AST with ctors. A ctor has `name` and `fields: [Type...]`.
- Term::Ctor (construction) and Term::Match (pattern matching).
- Patterns: `Wild`, `Var`, `Lit`, `Ctor { ctor, fields }`. In the MVP,
nested ctor patterns are NOT allowedsub-patterns must be `Var` or
`Wild`.
- Typechecker with a type registry and `ctor_index` (ctor name → ADT). In
Match, exhaustiveness is checked against the full constructor set. A
negative test protects this.
- Codegen: boxed heap layout. Per ctor application, `malloc(8 + 8*n)`
bytes; tag at offset 0, fields from offset 8 (8-byte slots, native typed
load/store). Match: load tag + switch + arm blocks + phi at the join.
- `examples/list.ail.json` (Cons/Nil list, sum_list via match) returns 42.
**Erstaunlich problemlos.** Die Architekturentscheidungen aus Tag 0 haben
sich ausgezahlt: opaque ptr in LLVM 22 macht Boxed-Layout fast ohne
Glue-Code möglich; das Effekt-Tracking blieb von ADTs unberührt; der
JSON-AST nimmt neue Knoten-Typen sauber auf.
**Surprisingly painless.** The architecture decisions from day 0 paid off:
opaque ptr in LLVM 22 makes the boxed layout almost glue-free; effect
tracking was untouched by ADTs; the JSON AST takes new node types
cleanly.
**Gewachsene Schulden:**
**Debt accrued:**
1. **Codegen liest immer noch direkt den Quell-AST.** Die Versuchung war
stark, ohne TIR weiterzumachenund hat funktioniert, weil meine
Match-Restriktionen flach sind (keine nested Patterns). Sobald nested
Patterns kommen, braucht es ein Decision-Tree-Lowering, das ohne TIR
nicht sauber wird. Schulden anerkannt; nicht jetzt fällig.
2. **Kein GC.** Heap leakt. Akzeptabel für Demo-Programme; muss vor jedem
längerläufigen Programm angegangen werden. Optionen für Phase 4:
Refcount, Boehm-GC-Linkage, Region-Inference.
3. **Pretty-Printer für ADT-Werte zur Laufzeit fehlt.** `io/print_int`
reicht für Demos, aber ein generisches `show :: a -> Str` für ADTs
wäre wertvoll. Erfordert dispatch über tag — machbar, aber nicht jetzt.
1. **Codegen still reads the source AST directly.** The temptation to push
on without TIR was strongand worked, because my Match restrictions
are flat (no nested patterns). Once nested patterns arrive, decision-
tree lowering will not stay clean without TIR. Debt acknowledged; not
due now.
2. **No GC.** The heap leaks. Acceptable for demo programs; must be
addressed before any longer-running program. Options for Phase 4:
refcount, Boehm-GC linkage, region inference.
3. **No runtime pretty-printer for ADT values.** `io/print_int` is enough
for demos, but a generic `show :: a -> Str` for ADTs would be valuable.
Requires dispatch over the tag — feasible, but not now.
**Plan Iteration 4:**
**Plan iteration 4:**
Die nächsten Schritte sind weniger eindeutig. Drei Kandidaten in
Prioritätsreihenfolge:
The next steps are less obvious. Three candidates in priority order:
1. **Modulsystem (Imports).** Aktuell ist alles in einem Modul. Mit
mehreren Modulen + Cross-Module-Hashing wird die Sprache erst
praktikabel für mehrere Defs.
2. **Strukturierte Fehlerausgabe (`ail check --json`).** Damit Tools auf
Typfehler reagieren können, ohne Text zu parsen.
3. **Closures / höherwertige Funktionen.** Erfordert Closure-Konvertierung
und ist ein größerer Schritt.
1. **Module system (imports).** Right now everything is in a single module.
With multiple modules + cross-module hashing the language only becomes
practical for several defs.
2. **Structured error output (`ail check --json`).** So tools can react to
type errors without parsing text.
3. **Closures / higher-order functions.** Requires closure conversion and
is a bigger step.
Iteration 4 wird (1) + (2) — beides macht das LLM-Tooling stärker und
hat moderates Risiko.
Iteration 4 will be (1) + (2) — both strengthen the LLM tooling and have
moderate risk.
## 2026-05-07 — Workflow-Wechsel: Orchestrator + Agent-Repo
## 2026-05-07 — workflow change: orchestrator + agent repo
Auf Anregung des Auftraggebers Wechsel auf **Orchestrator-Modus**: ich
delegiere abgrenzte Implementierungs-Brocken an Sub-Agenten und behalte
nur Architektur-Entscheidungen, Reviews und Commit-Disziplin. Vier
spezialisierte Agenten formuliert: implementer, architect, tester,
debugger.
At the user's suggestion, switching to **orchestrator mode**: I delegate
clearly bounded implementation chunks to sub-agents and keep only
architecture decisions, reviews, and commit discipline. Four specialised
agents drafted: implementer, architect, tester, debugger.
**Wichtige Korrektur:** Auftraggeber hat verlangt, dass die Agenten nicht
in `.claude/agents/` versteckt liegen, sondern als sichtbarer Bestandteil
des Projekts unter `agents/` versioniert werden. DESIGN.md hat einen neuen
Abschnitt "Projekt-Ökosystem" bekommen, der das festhält: AILang ist nicht
nur eine Sprache, sondern Sprachkern + CLI + Examples + Agents + Doku +
Tests, alle gleichwertig.
**Important correction:** the user required the agents not to be hidden in
`.claude/agents/`, but versioned as a visible part of the project under
`agents/`. DESIGN.md gained a new section "Project ecosystem", which
records this: AILang is not just a language, but language core + CLI +
examples + agents + docs + tests, all of equal weight.
Aufruf-Schema: System-Prompt-Body aus `agents/<name>.md` als Präfix vor
die konkrete Aufgabe + an `general-purpose`-Agent. Funktional identisch
zum Subagent-Loading aus `.claude/agents/`, aber sichtbar im Repo.
Invocation scheme: the system-prompt body from `agents/<name>.md` as a
prefix before the concrete task + sent to the `general-purpose` agent.
Functionally identical to subagent loading from `.claude/agents/`, but
visible in the repo.
**Plan Iteration 4 (überarbeitet):**
**Plan iteration 4 (revised):**
Modulsystem ist aufwändiger als erwartet (Cross-Module-Hashing,
Import-Resolution). Erst die kleineren Tooling-Wins ziehen, dann das
Modulsystem als Iteration 5:
The module system is more involved than expected (cross-module hashing,
import resolution). First the smaller tooling wins, then the module system
as iteration 5:
1. **Strukturierte Fehlerausgabe** (`ail check --json` mit Diagnostic-
Struct, stabile Codes wie `unbound-var`, `type-mismatch`).
2. **`ail diff <a> <b>`** — semantischer Modul-Diff via Hash-Vergleich
pro Def.
3. **IR-Snapshot-Tests**Regressions-Schutz für die Codegen-Pipeline.
1. **Structured error output** (`ail check --json` with a Diagnostic
struct, stable codes like `unbound-var`, `type-mismatch`).
2. **`ail diff <a> <b>`** — semantic module diff via per-def hash
comparison.
3. **IR snapshot tests**regression protection for the codegen pipeline.
## 2026-05-07 — Iteration 4 fertig: LLM-Tooling-Konsolidierung
## 2026-05-07 — iteration 4 done: LLM tooling consolidation
Drei Sub-Commits, jeder von einem `ailang-implementer`-Aufruf erzeugt
und stichprobenartig vom Orchestrator verifiziert:
Three sub-commits, each produced by an `ailang-implementer` invocation
and spot-checked by the orchestrator:
- `93fe723` Iter 4a: `ail check --json` mit `Diagnostic`-Struct
(`severity`, `code`, `message`, `def`, `ctx`). Stabile Codes:
- `93fe723` Iter 4a: `ail check --json` with a `Diagnostic` struct
(`severity`, `code`, `message`, `def`, `ctx`). Stable codes:
`unbound-var`, `type-mismatch`, `arity-mismatch`,
`non-exhaustive-match`, `unknown-ctor`, `unknown-ctor-in-pattern`,
`nested-ctor-pattern-not-allowed`, `duplicate-def`,
`unknown-effect-op`, `unknown-type`, `schema-mismatch`. API:
`check_module(&Module) -> Vec<Diagnostic>`.
- `c652b12` Iter 4b: `ail diff <a> <b> [--json]` als struktureller Top-
Level-Def-Diff via BLAKE3-Hash. Vier Kategorien (added/removed/
changed/unchanged), alphabetisch sortiert, Exit-Code 1 bei Diff.
- `74a2005` Iter 4c: IR-Snapshot-Tests in
- `c652b12` Iter 4b: `ail diff <a> <b> [--json]` as a structural top-level
def diff via BLAKE3 hash. Four categories (added/removed/changed/
unchanged), sorted alphabetically, exit code 1 on diff.
- `74a2005` Iter 4c: IR snapshot tests in
`crates/ail/tests/snapshots/{sum,max3,hello,list}.ll`.
Normalisierung von `target triple`. Update via `UPDATE_SNAPSHOTS=1
cargo test ir_snapshot_`. Mismatch erzeugt `.actual`-Datei.
Normalisation of `target triple`. Update via `UPDATE_SNAPSHOTS=1
cargo test ir_snapshot_`. Mismatch produces an `.actual` file.
Test-Stand: 28 (vorher 19). 7 E2E + 4 IR-Snapshot + 9 ailang-check + 1
Test count: 28 (previously 19). 7 E2E + 4 IR snapshot + 9 ailang-check + 1
ailang-codegen + 7 ailang-core.
**Erledigt aus dem Schulden-Register:**
**Closed from the debt register:**
- Block-Tracking im Codegen ist seit Iter 2 kein Heuristik-Risiko mehr;
die explizite `current_block: String`-Spur wurde durch Iter 4c jetzt
zusätzlich von Snapshot-Tests gegen Regression geschützt. Schuld
geschlossen.
- Block tracking in codegen has not been a heuristic risk since Iter 2;
the explicit `current_block: String` track is now additionally
protected against regression by Iter 4c snapshot tests. Debt closed.
**Neue / präzisierte Schulden:**
**New / sharpened debt:**
1. `check_module` ist **single-shot**der erste Fehler bricht ab, kein
Multi-Diagnose-Sammeln. Spec war so, aber das Format suggeriert
Vec-Semantik. Echter Multi-Diagnose-Refactor wird billiger, sobald
TIR existiert (zentraler Fehler-Akku durch separate Stufe). Nicht
jetzt fällig.
2. `source_filename` im IR ist hartkodiert auf `"<module>.ail"`. Solange
es nur ein Top-Level-Modul gibt, plattform-stabil. Mit Iter 5
(Modulsystem + Imports) wird der Pfad relevant — dort gleich beim
Bau pfad-unabhängig halten, sonst kippen die Snapshots.
1. `check_module` is **single-shot**the first error aborts, no
multi-diagnostic gathering. The spec was that way, but the format
suggests Vec semantics. A real multi-diagnostic refactor will be
cheaper once TIR exists (a central error accumulator via a separate
stage). Not due now.
2. `source_filename` in the IR is hard-coded to `"<module>.ail"`. As long
as there is only one top-level module, that is platform-stable. With
Iter 5 (module system + imports) the path becomes relevant — keep it
path-independent at construction time, otherwise the snapshots will
tip over.
**Plan Iteration 5:** Modulsystem mit Imports. Cross-Module-Hashing,
Import-Auflösung, mehrere `.ail.json`-Files in einem Build. Multi-
Diagnose-Refactor erst danach.
**Plan iteration 5:** module system with imports. Cross-module hashing,
import resolution, multiple `.ail.json` files in one build. The multi-
diagnostic refactor only after that.
Sub-Schritte:
Sub-steps:
- **5a — Workspace-Loader.** `ailang_core::Workspace { modules:
BTreeMap<String, Module> }` plus `load_workspace(entry: &Path)`, das
`imports` rekursiv vom Eintrittsmodul folgt. Konvention: `import {
module: "foo" }` löst auf `<dir>/foo.ail.json` neben dem Entry. Zyklus-
Erkennung. CLI: bestehende Subkommandos arbeiten weiter auf einzelnem
Modul; ein neues `ail workspace <entry>` listet alle erreichbaren
Module mit Hash. Tests: zwei kleine Beispielmodule mit
Import-Beziehung; Zyklus-Test.
- **5b — Cross-Module-Typcheck.** Typchecker bekommt `&Workspace`
statt `&Module`. Imports werden im Env als Namespace eingehängt
(`alias.def` oder bei fehlendem Alias `module.def`). Neue Diagnostic-
Codes: `unknown-module`, `unknown-import`, `import-cycle`,
`ambiguous-name`. Tests pro Code.
- **5c — Cross-Module-Codegen.** Emitter erzeugt IR für alle Module im
Workspace, prefix-mangelt mit `@ail_<module>_<def>`. E2E-Test:
Programm, das eine Funktion aus Modul B in Modul A nutzt, liefert
korrektes Ergebnis im Binary.
- **5d — Tooling-Anpassungen.** `manifest`, `describe`, `deps`, `diff`
bekommen einen `--workspace`-Modus (rekursiv). Single-Modus bleibt
Default für ckwärtskompatibilität.
- **5a — workspace loader.** `ailang_core::Workspace { modules:
BTreeMap<String, Module> }` plus `load_workspace(entry: &Path)`, which
follows `imports` recursively from the entry module. Convention:
`import { module: "foo" }` resolves to `<dir>/foo.ail.json` next to the
entry. Cycle detection. CLI: existing subcommands keep working on a
single module; a new `ail workspace <entry>` lists all reachable
modules with hash. Tests: two small example modules with an import
relation; cycle test.
- **5b — cross-module typecheck.** The typechecker takes `&Workspace`
instead of `&Module`. Imports are mounted in the env as a namespace
(`alias.def` or, with no alias, `module.def`). New diagnostic codes:
`unknown-module`, `unknown-import`, `import-cycle`, `ambiguous-name`.
Tests per code.
- **5c — cross-module codegen.** The emitter produces IR for all modules
in the workspace, prefix-mangled with `@ail_<module>_<def>`. E2E test:
a program that uses a function from module B in module A returns the
correct result in the binary.
- **5d — tooling adjustments.** `manifest`, `describe`, `deps`, `diff`
gain a `--workspace` mode (recursive). The single mode stays the
default for backwards compatibility.
Während Iter 5 gleich beim Bau **`source_filename` pfad-unabhängig
halten** (nur Modulname, kein Verzeichnis-Prefix), sonst kippen die
IR-Snapshots.
During Iter 5, at construction time **keep `source_filename`
path-independent** (module name only, no directory prefix), otherwise
the IR snapshots will tip over.
## 2026-05-07 — Iter 5b fertig: Cross-Module-Typcheck
## 2026-05-07 — Iter 5b done: cross-module typecheck
- `check_workspace(&Workspace) -> Vec<Diagnostic>` als Top-Level-API.
`check_module` bleibt erhalten und hebt das Modul intern in einen
Trivial-Workspace.
- Konvention für qualifizierte Verweise (in DESIGN.md festgehalten):
`Term::Var { name }` mit genau einem Punkt = `<prefix>.<def>`. Prefix
ist Import-Alias oder Modulname. Kein neuer AST-Knoten, keine
umbenannten Felder ⇒ Hashes bleiben stabil; alle `ir_snapshot_*`
weiterhin grün.
- Drei neue Diagnostic-Codes: `unknown-module`,
`unknown-import`, `invalid-def-name` (mit `ctx.reason: "contains-dot"`).
- CLI: `ail check <entry>` lädt jetzt **immer** über `load_workspace`.
Workspace-Lade-Fehler werden im JSON-Modus zu strukturierten
Diagnostics mit Codes `module-not-found`, `module-cycle`,
`module-name-mismatch`, `module-hash-mismatch`, `schema-mismatch`.
`ail build` und `ail emit-ir` bleiben pro Einzelmodul (Codegen
cross-module ist 5c).
- Beispiele: `ws_main.ail.json` ruft jetzt `ws_lib.add` auf
(beobachtbar). Neu: `ws_broken.ail.json` (`unknown-import`),
- `check_workspace(&Workspace) -> Vec<Diagnostic>` as the top-level API.
`check_module` is preserved and internally lifts the module into a
trivial workspace.
- Convention for qualified references (recorded in DESIGN.md):
`Term::Var { name }` with exactly one dot = `<prefix>.<def>`. Prefix is
an import alias or module name. No new AST node, no renamed fields ⇒
hashes stay stable; all `ir_snapshot_*` still green.
- Three new diagnostic codes: `unknown-module`, `unknown-import`,
`invalid-def-name` (with `ctx.reason: "contains-dot"`).
- CLI: `ail check <entry>` now **always** loads via `load_workspace`.
Workspace load failures become structured diagnostics in JSON mode with
codes `module-not-found`, `module-cycle`, `module-name-mismatch`,
`module-hash-mismatch`, `schema-mismatch`. `ail build` and
`ail emit-ir` stay per single module (cross-module codegen is 5c).
- Examples: `ws_main.ail.json` now calls `ws_lib.add` (observable). New:
`ws_broken.ail.json` (`unknown-import`),
`ws_unknown_module.ail.json` (`unknown-module`).
- Tests: 37 grün (vorher 32). 4 neue Workspace-Integrationstests in
`crates/ailang-check/tests/workspace.rs`, ein neuer e2e-Test
- Tests: 37 green (previously 32). 4 new workspace integration tests in
`crates/ailang-check/tests/workspace.rs`, one new e2e test
`check_workspace_resolves_import`.
- Schulden: Single-Shot-Diagnostics weiterhin (Multi-Diagnose nach 5c).
Punkt-Konvention deckt nur einen Punkt ab — verschachtelte Modulpfade
(`a.b.c`) gibt es nicht; das wäre erst mit Hierarchie-Modulen ein
Thema und fällt aktuell als `unbound-var` durch.
- Debt: single-shot diagnostics still in place (multi-diagnostic after
5c). The dot convention covers exactly one dot — nested module paths
(`a.b.c`) do not exist; that would only be a topic with hierarchical
modules and currently falls through as `unbound-var`.
## 2026-05-07 — Iter 5c fertig: Cross-Module-Codegen
## 2026-05-07 — Iter 5c done: cross-module codegen
- **Mangling-Bruch (mit Bedacht).** Alle AILang-Funktionen heißen jetzt
`@ail_<modul>_<def>`, auch in Single-Modul-Programmen. Die alte Form
`@ail_<def>` ist weg. Strings/Const-Globals analog
(`@.str_<modul>_<hint>_<idx>`, `@ail_<modul>_<const>`). Eintrittspunkt
bleibt `main` als C-ABI: ein `define i32 @main()`-Trampoline ruft
`@ail_<entry-modul>_main()`. Fehlt im Eintrittsmodul ein
`main : () -> Unit !IO`, fällt der Build mit `MissingEntryMain`.
- **Workspace-Lowering.** Neue Top-Level-API
- **Mangling break (deliberate).** All AILang functions are now called
`@ail_<module>_<def>`, even in single-module programs. The old form
`@ail_<def>` is gone. Strings/const globals analogously
(`@.str_<module>_<hint>_<idx>`, `@ail_<module>_<const>`). The entry
point stays `main` as C ABI: a `define i32 @main()` trampoline calls
`@ail_<entry-module>_main()`. If the entry module has no
`main : () -> Unit !IO`, the build fails with `MissingEntryMain`.
- **Workspace lowering.** New top-level API
`ailang_codegen::lower_workspace(ws: &Workspace) -> Result<String>`
produziert eine einzelne `.ll` für den ganzen Workspace. Module
alphabetisch (BTreeMap-Order); Defs in AST-Reihenfolge. Cross-Module-
Calls werden im Codegen über die Import-Map des aufrufenden Moduls
aufgelöst — gleiche Logik wie im Typchecker, lokal dupliziert mit
Verweis (kein gemeinsames Helper-Modul, weil die Typ-Welten
unterschiedlich sind: Typchecker hantiert mit `Type`, Codegen mit
`FnSig` aus llvm-Typen).
- **CLI.** `ail build` und `ail emit-ir` laden jetzt immer den Workspace
und checken/lowern ihn vollständig. Single-Modul-Programme funktionieren
weiter (Trivial-Workspace mit einem Modul). `emit_ir(m)` bleibt im
Codegen-Crate als Bequemlichkeits-API erhalten und wrapt intern in
einen Trivial-Workspace.
- **Snapshots regeneriert.** `sum.ll`, `max3.ll`, `hello.ll`, `list.ll`
zeigen das neue Mangling. Neuer `ws_main.ll`-Snapshot dokumentiert den
Cross-Module-Build: `@ail_ws_main_main` ruft `@ail_ws_lib_add` auf.
- **Tests.** 40 grün (vorher 37). Neu: `workspace_build_runs_imported_fn`
(e2e: druckt 5), `ir_snapshot_ws_main`, `missing_entry_main_is_error`
(codegen-unit). Bestehende Verhaltens-Tests
produces a single `.ll` for the whole workspace. Modules in
alphabetical order (BTreeMap order); defs in AST order. Cross-module
calls are resolved in codegen via the import map of the calling module
— same logic as in the typechecker, locally duplicated with a
cross-reference (no shared helper module, because the type worlds
differ: the typechecker handles `Type`, codegen handles `FnSig` from
llvm types).
- **CLI.** `ail build` and `ail emit-ir` now always load the workspace
and check/lower it fully. Single-module programs keep working
(trivial workspace with one module). `emit_ir(m)` stays in the codegen
crate as a convenience API and internally wraps into a trivial
workspace.
- **Snapshots regenerated.** `sum.ll`, `max3.ll`, `hello.ll`, `list.ll`
show the new mangling. New `ws_main.ll` snapshot documents the
cross-module build: `@ail_ws_main_main` calls `@ail_ws_lib_add`.
- **Tests.** 40 green (previously 37). New: `workspace_build_runs_imported_fn`
(e2e: prints 5), `ir_snapshot_ws_main`, `missing_entry_main_is_error`
(codegen unit). Existing behaviour tests
(`sum_1_to_10_is_55`, `max3_picks_largest`, `hello_world_str_lit`,
`list_sum_via_match`) bleiben grün — Verhalten unverändert, nur das
Mangling ist neu.
`list_sum_via_match`) stay green — behaviour unchanged, only the
mangling is new.
**Schulden geschlossen:**
**Debt closed:**
- **#19 (`source_filename` härten).** In der Workspace-Welt ist
`source_filename` jetzt einheitlich `<entry-modul>.ail`, einmal pro
Workspace. Der bisherige hartkodierte Pfad-Punkt entfällt damit.
- **#19 (`source_filename` hardening).** In the workspace world,
`source_filename` is now uniformly `<entry-module>.ail`, once per
workspace. The previous hard-coded path dot is gone with it.
**Stand:** Modulsystem ist Ende-zu-Ende geschlossen — Loader + Typcheck
+ Codegen + Build sehen den Workspace als kohärente Einheit. Multi-
Diagnose-Refactor und ggf. ADT-Cross-Module bleiben für später.
**State:** the module system is closed end to endloader + typecheck +
codegen + build see the workspace as a coherent unit. The multi-
diagnostic refactor and possibly cross-module ADTs remain for later.
## 2026-05-07 — Iter 5d fertig: Tooling auf Workspace ausgedehnt
## 2026-05-07 — Iter 5d done: tooling extended to the workspace
- `ail manifest|describe|deps|diff <entry> --workspace` arbeiten jetzt
über alle Module des Workspaces. Default ohne Flag bleibt single-modul
für Rückwärtskompatibilität. Manifest sortiert nach `(modul, name)`,
describe akzeptiert Punktnotation `ws_lib.add`, deps emittiert
`{from_module, from_def, to_module, to_def}`-Edges, diff vergleicht
workspace-übergreifend mit added/removed/changed/unchanged_modules
und nested Sub-Diff pro changed_module.
- Refactor: `diff_def_lists` ist die einzige Quelle der 4-Kategorie-
Logik; Single- und Workspace-Diff teilen sie.
- Tests: 44 grün (vorher 40). Neu: `manifest_workspace_lists_all_defs`,
- `ail manifest|describe|deps|diff <entry> --workspace` now operate
across all modules of the workspace. The default without the flag stays
single-module for backwards compatibility. Manifest sorts by
`(module, name)`, describe accepts dotted notation `ws_lib.add`, deps
emits `{from_module, from_def, to_module, to_def}` edges, diff compares
workspace-wide with added/removed/changed/unchanged_modules and a
nested sub-diff per changed_module.
- Refactor: `diff_def_lists` is the single source of the four-category
logic; single and workspace diff share it.
- Tests: 44 green (previously 40). New: `manifest_workspace_lists_all_defs`,
`describe_workspace_resolves_qualified_name`,
`deps_workspace_includes_cross_module`, `diff_workspace_added_module`.
**Beobachtung (Schuld):** `deps` filtert Builtins/Locals/Funktions-
parameter nicht. Im Workspace-Modus wird das deutlicher als im Single-
Modus — `ws_lib.add` listet Edges auf `ws_lib.+` (Builtin) und
`ws_lib.a`/`ws_lib.b` (Funktionsparameter). Bekanntes Vorbestand-
Problem aus Iter 2; Task #22 im Backlog.
**Observation (debt):** `deps` does not filter builtins/locals/function
parameters. In workspace mode that becomes more visible than in single
mode — `ws_lib.add` lists edges to `ws_lib.+` (builtin) and
`ws_lib.a`/`ws_lib.b` (function parameters). A known pre-existing
issue from Iter 2; Task #22 in the backlog.
## 2026-05-07 — Architektur-Review nach Iter 5
## 2026-05-07 — architecture review after Iter 5
Architekt-Agent gerufen. Befunde:
Architect agent invoked. Findings:
1. **Mangling-Konsistenz hält.** `@ail_<modul>_<def>` durchgängig in
Funktionen, Konstanten, String-Globals, Cross-Module-Calls.
Trampoline korrekt. ADT-Konstruktoren sind absichtlich symbol-frei
(inline malloc).
2. **Modul-Hashes bitidentisch seit Iter 4.** Iter-5c-Snapshot-
Regenerierung war Codegen-Output-Änderung, nicht Hash-Bruch.
3. **Drift, sofort fällig:**
- DESIGN.md sagt `define i64 @main()`, der Codegen emittiert
`define i32 @main()` (siehe `sum.ll:35`).
- Stringschema-Notation in DESIGN.md verkürzt
(`@.str_<modul>_<idx>` statt `@.str_<modul>_<hint>_<idx>`).
4. **Schulden, die Zinsen tragen:** `deps`-Builtin-Leck (Task #22) ist
im Workspace-Modus zu einer Falschaussage geworden — vor dem
nächsten großen Sprung schließen.
1. **Mangling consistency holds.** `@ail_<module>_<def>` is consistent
across functions, constants, string globals, and cross-module calls.
The trampoline is correct. ADT constructors are deliberately
symbol-free (inline malloc).
2. **Module hashes bit-identical since Iter 4.** The Iter 5c snapshot
regeneration was a codegen-output change, not a hash break.
3. **Drift, due now:**
- DESIGN.md says `define i64 @main()`, codegen emits
`define i32 @main()` (see `sum.ll:35`).
- String-schema notation in DESIGN.md was shortened
(`@.str_<module>_<idx>` instead of `@.str_<module>_<hint>_<idx>`).
4. **Debt that accrues interest:** the `deps` builtin leak (Task #22) has
become a falsehood in workspace mode — close it before the next big
jump.
**Plan Iteration 6 — Aufräumarbeiten:**
**Plan iteration 6 — clean-up:**
1. **DESIGN.md-Drift fixen.** Mangling-Schema-Block aktualisieren,
`@main`-Signatur korrigieren, String-Globals präzise notieren.
2. **`deps`-Härtung (#22).** Pro Workspace eine Top-Level-Def-Tabelle
bauen; Edges, deren Ziel kein Top-Level-Symbol ist, filtern oder
als separate `builtin:`/`local:`-Kategorie ausgeben.
Funktionsparameter via Lexikalisches Scope-Tracking aus walk_term.
3. **Multi-Diagnose-Refactor (#20).** `check_workspace` akkumuliert
`Vec<Diagnostic>` über alle Defs, statt beim ersten Error zu
shortcuten. Intra-Def darf weiter shortcuten — der Wert ist
"alle defektiven Defs auf einmal sehen", nicht "alle defektiven
Subterme einer Def sehen".
1. **Fix DESIGN.md drift.** Update the mangling-scheme block, correct the
`@main` signature, and note the string globals precisely.
2. **`deps` hardening (#22).** Build a top-level def table per workspace;
filter edges whose target is not a top-level symbol, or emit them as
separate `builtin:`/`local:` categories. Function parameters via
lexical scope tracking from walk_term.
3. **Multi-diagnostic refactor (#20).** `check_workspace` accumulates
`Vec<Diagnostic>` across all defs instead of short-circuiting on the
first error. Intra-def may still short-circuit — the value is "see
all broken defs at once", not "see all broken sub-terms of one def".
Reihenfolge: 1 zuerst (Doku-Trivialität), dann 2 vor 3 (deps ist eine
Tooling-Wahrheit-Reparatur, Multi-Diag eine Strukturerweiterung).
Order: 1 first (doc triviality), then 2 before 3 (deps is a tooling-
truth fix, multi-diag is a structural extension).