# JOURNAL Chronologische Notizen für mich. Nicht jede Änderung; nur Entscheidungen, Hindernisse, Beobachtungen, die zukünftige Iterationen brauchen. ## 2026-05-07 — Tag 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 - `crates/ail` — CLI - MVP-Ziel: `examples/sum.ail.json` → Binary, das 55 druckt. **Erreicht.** ## 2026-05-07 — Architektur-Review nach MVP Wieder am Pfad? Im Großen ja. Konkrete Beobachtungen: **Was hält:** - 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. **Schulden, die Zinsen tragen:** 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. **Plan Iteration 2 (jetzt):** 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. **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. ## 2026-05-07 — Iteration 2 fertig - 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. **Architektur-Check:** Keine strukturellen Abweichungen. Codegen liest noch direkt das Quell-AST (TIR-Stufe wird mit ADTs in Iteration 3 nötig). ## 2026-05-07 — Iteration 3 fertig: 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 erlaubt — Sub-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. **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. **Gewachsene Schulden:** 1. **Codegen liest immer noch direkt den Quell-AST.** Die Versuchung war stark, ohne TIR weiterzumachen — und 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. **Plan Iteration 4:** Die nächsten Schritte sind weniger eindeutig. Drei Kandidaten in Prioritätsreihenfolge: 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. Iteration 4 wird (1) + (2) — beides macht das LLM-Tooling stärker und hat moderates Risiko. ## 2026-05-07 — Workflow-Wechsel: 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. **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. Aufruf-Schema: System-Prompt-Body aus `agents/.md` als Präfix vor die konkrete Aufgabe + an `general-purpose`-Agent. Funktional identisch zum Subagent-Loading aus `.claude/agents/`, aber sichtbar im Repo. **Plan Iteration 4 (überarbeitet):** Modulsystem ist aufwändiger als erwartet (Cross-Module-Hashing, Import-Resolution). Erst die kleineren Tooling-Wins ziehen, dann das Modulsystem als Iteration 5: 1. **Strukturierte Fehlerausgabe** (`ail check --json` mit Diagnostic- Struct, stabile Codes wie `unbound-var`, `type-mismatch`). 2. **`ail diff `** — semantischer Modul-Diff via Hash-Vergleich pro Def. 3. **IR-Snapshot-Tests** — Regressions-Schutz für die Codegen-Pipeline. ## 2026-05-07 — Iteration 4 fertig: LLM-Tooling-Konsolidierung Drei Sub-Commits, jeder von einem `ailang-implementer`-Aufruf erzeugt und stichprobenartig vom Orchestrator verifiziert: - `93fe723` Iter 4a: `ail check --json` mit `Diagnostic`-Struct (`severity`, `code`, `message`, `def`, `ctx`). Stabile 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`. - `c652b12` Iter 4b: `ail diff [--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 `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. Test-Stand: 28 (vorher 19). 7 E2E + 4 IR-Snapshot + 9 ailang-check + 1 ailang-codegen + 7 ailang-core. **Erledigt aus dem Schulden-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. **Neue / präzisierte Schulden:** 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 `".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. **Plan Iteration 5:** Modulsystem mit Imports. Cross-Module-Hashing, Import-Auflösung, mehrere `.ail.json`-Files in einem Build. Multi- Diagnose-Refactor erst danach. Sub-Schritte: - **5a — Workspace-Loader.** `ailang_core::Workspace { modules: BTreeMap }` plus `load_workspace(entry: &Path)`, das `imports` rekursiv vom Eintrittsmodul folgt. Konvention: `import { module: "foo" }` löst auf `/foo.ail.json` neben dem Entry. Zyklus- Erkennung. CLI: bestehende Subkommandos arbeiten weiter auf einzelnem Modul; ein neues `ail workspace ` 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__`. 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 Rückwärtskompatibilität. Während Iter 5 gleich beim Bau **`source_filename` pfad-unabhängig halten** (nur Modulname, kein Verzeichnis-Prefix), sonst kippen die IR-Snapshots. ## 2026-05-07 — Iter 5b fertig: Cross-Module-Typcheck - `check_workspace(&Workspace) -> Vec` 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 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 ` 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`), `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 `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. ## 2026-05-07 — Iter 5c fertig: Cross-Module-Codegen - **Mangling-Bruch (mit Bedacht).** Alle AILang-Funktionen heißen jetzt `@ail__`, auch in Single-Modul-Programmen. Die alte Form `@ail_` ist weg. Strings/Const-Globals analog (`@.str___`, `@ail__`). Eintrittspunkt bleibt `main` als C-ABI: ein `define i32 @main()`-Trampoline ruft `@ail__main()`. Fehlt im Eintrittsmodul ein `main : () -> Unit !IO`, fällt der Build mit `MissingEntryMain`. - **Workspace-Lowering.** Neue Top-Level-API `ailang_codegen::lower_workspace(ws: &Workspace) -> Result` 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 (`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. **Schulden geschlossen:** - **#19 (`source_filename` härten).** In der Workspace-Welt ist `source_filename` jetzt einheitlich `.ail`, einmal pro Workspace. Der bisherige hartkodierte Pfad-Punkt entfällt damit. **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. ## 2026-05-07 — Iter 5d fertig: Tooling auf Workspace ausgedehnt - `ail manifest|describe|deps|diff --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`, `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. ## 2026-05-07 — Architektur-Review nach Iter 5 Architekt-Agent gerufen. Befunde: 1. **Mangling-Konsistenz hält.** `@ail__` 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__` statt `@.str___`). 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. **Plan Iteration 6 — Aufräumarbeiten:** 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` ü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". Reihenfolge: 1 zuerst (Doku-Trivialität), dann 2 vor 3 (deps ist eine Tooling-Wahrheit-Reparatur, Multi-Diag eine Strukturerweiterung).