## Context

Siehe proposal.md — Why. Technisch entscheidend ist der Ist-Zustand
dreier Stellen:

- `tb-ui::screen::TextScreen` (385 Zeilen) trägt die Bildschirmsemantik
  bereits vollständig: `cls`, `set_color`, `locate`, `csrlin`, `pos`,
  `view_print`, `cell`, `print`, `scroll_up`, `resize`. Ratatui kommt
  darin an genau zwei Stellen vor: `basic_color()` (4 Zeilen) und
  `impl Widget for &TextScreen` (~35 Zeilen).
- `tb-runtime::host::Host` ist ein Stream-Trait (`write(&str)`,
  `read_line()`). `tb-runtime` und `tb-ui` sind beide Blätter im
  Abhängigkeitsgraphen und treffen sich erst in `tb-cli`/`tb-ide`.
- Rund 50 dokumentierte Elemente besitzen im Frontend eine Signatur in
  `builtin_fn`/`builtin_stmt`, senken aber auf `HStmtKind::Unsupported`
  bzw. `HExpr::Unsupported` ab → Opcode `0x05` → Laufzeitfehler 73.

Zwei Entscheidungen vom 2026-09-03 rahmen die Arbeit: Das Windowing wird
— wie beim Vorbild — vom erzeugten Programm mitgebracht und nicht von der
IDE, sodass Forms und `PRINT` in denselben Zellenpuffer zeichnen. Die
Forms-Engine selbst bleibt dabei am von PLAN.md vorgesehenen Ort
`tb-ui::forms`.

## Goals / Non-Goals

**Goals:**

- Der Zellenpuffer liegt dort, wo sowohl die Laufzeitbibliothek als auch
  die spätere Forms-Engine in `tb-ui` ihn erreichen, ohne dass die
  Ausführungsschicht eine Terminal-Bibliothek zieht.
- Bildschirmverhalten ist ohne Terminal automatisiert prüfbar.
- Die Abdeckung misst sich selbst statt gepflegt zu werden.
- Die Umstellung der acht bestehenden Golden Files geschieht in einem
  nachvollziehbaren, einmaligen Schritt.

**Non-Goals:**

- Optimierung der Bildschirmausgabe (Diff-Minimierung, Teilaktualisierung)
  — erst wenn Messungen sie verlangen.
- Ein Rendering-Backend jenseits des Terminals.
- Die Forms-Engine selbst (Phase 4, `tb-ui::forms`); dieser Change
  liefert ihr nur die Zeichenfläche.

## Decisions

### D1: Zellenpuffer nach `tb-runtime::screen`, ratatui-frei

`Cell` und `TextScreen` ziehen unverändert nach `tb-runtime::screen`.
`basic_color()` und `impl Widget` bleiben in `tb-ui` und arbeiten künftig
auf dem Runtime-Typ.

*Warum:* `PRINT`, `LOCATE`, `CLS`, `COLOR`, `CSRLIN`, `POS` und die
Funktionsform von `SCREEN` sind Elemente der Laufzeitbibliothek. Sie
müssen den Puffer erreichen, ohne dass `tb-runtime` eine
Terminal-Bibliothek zieht — sonst braucht jeder VM- und Bibliothekstest
ein Terminal. Das trägt die Entscheidung allein.

*Verhältnis zur Forms-Engine:* Forms bleibt laut PLAN.md in
`tb-ui::forms` (Entscheidung 2026-09-03). Das passt zur Richtung: `tb-ui`
hängt künftig an `tb-runtime` und zeichnet von oben in denselben Puffer
wie `PRINT` — ein `MSGBOX` über einer Konsolenausgabe überdeckt deren
Zellen und gibt sie beim Schließen zurück. Läge der Puffer weiterhin in
`tb-ui`, wäre genau diese gemeinsame Fläche für die Bibliothek
unerreichbar. Umgekehrt entsteht kein Zyklus: die Ausführungsschicht
erreicht die Forms-Engine über die Host-Schnittstelle (D2), die sie
selbst deklariert und `tb-ui` implementiert.

*Alternativen:*
- `tb-runtime` hängt an `tb-ui`: null Doppelung, aber ratatui und
  crossterm landen in jedem VM-Test. Verworfen.
- Semantik bleibt in `tb-ui`, jeder Host führt einen eigenen Puffer:
  dieselbe Scroll-/Umbruch-/Doppelzellenlogik dreimal. Verworfen.

*Aufwand:* Der Umzug ist mechanisch — betroffen sind der `use`-Block, die
zwei ratatui-Stellen und die Modulzuordnung. Die Semantik selbst wird
nicht angefasst; erweitert wird sie nur um die Doppelzellen (D4).

### D2: `Host` wird Bildschirm- statt Stream-Schnittstelle

`write`/`read_line` weichen einer Schnittstelle, die den Zellenpuffer zur
Anzeige übergibt und Ereignisse (Tastendruck, Größenänderung, Abbruch)
zurückliefert. Die Bibliothek schreibt nicht mehr in den Host, sondern in
den Puffer; der Host stellt dar.

*Warum:* `LOCATE 5,10`, `COLOR 14,1`, `CSRLIN` und besonders
`SCREEN(r,c)` — das ein Zeichen vom Bildschirm *zurückliest* — sind über
einen Zeichenstrom nicht ausdrückbar. Das ist ein Zwang, keine Präferenz.

*Wann dargestellt wird:* Der Host wird an denselben Tick-Grenzen
aufgerufen, an denen die VM heute schon die Abbruchprüfung macht, sowie
vor jeder blockierenden Eingabe. Kein zusätzlicher Mechanismus.

*Blockieren bleibt erlaubt* (Entscheidung D3 aus Phase 2 bleibt gültig):
`INPUT` und `INPUT$` blockieren im Host. Erst die Ereignisschleife der
Phase 4 bringt kooperative Zustellpunkte.

*Alternativen:*
- Stream-Host behalten und Bildschirmwirkungen als ANSI-Escapes
  hindurchreichen: `CSRLIN`/`POS`/`SCREEN()` bräuchten trotzdem einen
  Schattenpuffer, und der Capture-Host müsste ANSI parsen. Verworfen.

### D3: Korpusvergleich als getrimmter Snapshot

Die `.out`-Datei hält das Textbild des Puffers bis zur letzten belegten
Zeile und Spalte. Eine zweite Ebene mit Farbattributen kommt nur hinzu,
wenn das Programm `COLOR` verwendet.

*Warum getrimmt:* Ein voller 80×25-Snapshot für `hello.bas` wären 2000
Zeichen für zwei Zeichen Signal, und jeder Diff zeigte Rauschen. Getrimmt
bleiben die bestehenden acht Sollausgaben inhaltlich fast unverändert —
`PRINT`-only-Programme ergeben dasselbe Textbild wie bisher der Stream.

*Warum Attribute optional:* Programme ohne `COLOR` erzeugen sonst eine
zweite Ebene aus lauter identischen Werten.

*Umstellung:* Die acht Golden Files werden einmalig neu erzeugt und der
Diff wird geprüft — erwartet wird, dass sich nur Zeilen ändern, die
vorher auf `\r`/Umbruchverhalten beruhten. Ein unerwarteter Diff ist ein
Befund, kein Anpassungsgrund.

*Alternativen:*
- Zwei Harnesses (Stream für Alt-Tests, Snapshot für neue): dauerhaft
  doppelte Infrastruktur für einen einmaligen Umstellungsschmerz.
  Verworfen.
- Voller Snapshot: siehe oben. Verworfen.

### D4: Doppelzellen über Darstellungsbreite

Breite Zeichen belegen zwei Zellen: die erste trägt das Zeichen, die
zweite ist als Fortsetzung markiert. Neue Abhängigkeit `unicode-width`
in `tb-runtime`.

*Warum eine Abhängigkeit:* Die Breitentabelle folgt einem
Unicode-Annex und ändert sich mit jeder Unicode-Version; sie
nachzubauen hieße, sie dauerhaft nachzupflegen.

*Konsequenzen, die die Spec festhält:* Cursor rückt um zwei Spalten vor;
`POS` zählt die Anfangsspalte; ein breites Zeichen in der letzten Spalte
wird vollständig umgebrochen und die Spalte bleibt leer; `LOCATE` auf
eine Fortsetzungszelle wirkt auf den Zeichenanfang. Beim Überschreiben
einer Hälfte muss die andere geleert werden, sonst entstehen
Geisterzeichen.

### D4a: 80×25 ist nirgends eine Konstante

Anforderung des Projektinhabers (2026-09-03), Priorität für das ganze
Programm: 80×25 ist stets Synonym für „voller Bildschirm", nie eine feste
Grenze. Moderne Terminals haben keine feste Größe, und sie ändert sich
während der Ausführung.

*Konsequenz für die Umsetzung:* Kein Zweig darf gegen 80 oder 25
vergleichen. Die Abmessungen kommen ausschließlich aus dem Puffer
(`cols()`/`rows()`), abgeleitet aus der Darstellungsfläche. Betroffen
sind `CLS` (voller aktueller Bildschirm), der voreingestellte
Scrollbereich, die Umbruchspalte, die Scrollzeile sowie die
Grenzprüfungen von `LOCATE` und der Funktionsform von `SCREEN`.

*Größenänderung zur Laufzeit* ist damit kein Sonderfall, sondern der
Normalfall: Das Größenänderungsereignis kommt über den Host (D2), der
Puffer wird angepasst, Inhalt bleibt oben links, Cursor und
`VIEW PRINT`-Bereich werden geklemmt. Ein Bereich, der durch die
Verkleinerung leer würde, fällt auf den vollen Bildschirm zurück, statt
ungültig zu bleiben.

*Das Mindestmaß bleibt eine Darstellungsschranke:* Ist die Fläche kleiner
als 80×25, zeigt die Darstellungsschicht den Hinweis „Terminal zu klein"
(bestehende Entscheidung 2026-09-02). Das ist eine untere Schranke der
Anzeige, keine Bildschirmgröße für das Programm.

*Prüfbarkeit:* Der Test-Host bekommt seine Größe je Korpusprogramm
explizit gesetzt, ohne stillschweigenden Vorgabewert. Mindestens ein
Bildschirmprogramm läuft bei zwei Größen mit je eigener Sollausgabe —
eine hartkodierte 80 oder 25 fällt damit sofort auf. Ein weiteres
durchläuft eine Größenänderung mitten in der Ausführung.

### D4b: Das Inventar geht der Umfangsschätzung vor

Die Erhebung in Aufgabe 1.1 (2026-09-03) hat gezeigt, dass die
ursprüngliche Umfangsliste dieses Changes drei ganze Gruppen nicht
enthielt: die 13 finanzmathematischen Funktionen, die 14
Record-Konvertierungsfunktionen und einen Rest an Dateisystem- und
Systemelementen. Sie hat außerdem zwei Elemente widerlegt, die
`phase-3-isam` fälschlich führte (`CHECKPOINT`, `TEXTCOMP`).

*Konsequenz für die Arbeitsweise:* Maßgeblich ist das Inventar, nicht die
Aufzählung im Proposal. Wo beide auseinandergehen, wird das Proposal
nachgezogen. Aufzählungen in Proposal und Specs sind Mindestumfang.

*Konsequenz für die Quelle:* `bas7advr.hlp/TOPIC_LIST.html` ist
nachweislich unvollständig — `SGN`, `SQR`, `SPACE$`, `STRING$`, `CSRLIN`,
`BEEP` und `DATE$` als Funktion fehlen dort. Das Inventar bildet die
Vereinigungsmenge aus dieser Liste, `qb45advr.hlp/TOPIC_LIST.html` und
`docs/sprachreferenz.md`; jeder Eintrag nennt seine Fundstelle. Die
Zählung einer einzelnen Themenliste ist kein Vollständigkeitsnachweis.

### D5: Inventar als Markdown-Tabelle mit Abgleichstest

`docs/inventar.md` trägt eine Pipe-Tabelle mit festen Spalten (Name, Art,
Gruppe, Status, Fundstelle). Ein Test parst sie und hält sie gegen
`builtin_fn`/`builtin_stmt`, die `Unsupported`-Marker und
`banned_feature`.

*Warum Markdown und nicht TOML/YAML:* Der Plan nennt „maschinenlesbar",
aber keinen Konsumenten außer der Messbarkeit selbst; Zielpfad ist
ausdrücklich `docs/inventar.md`, und das IDE-Hilfesystem (Phase 5)
rendert `docs/`-Markdown. Eine Pipe-Tabelle ist mit einem Split
maschinenlesbar. Ein zweites Format bräuchte einen Generator, um wieder
bei Markdown zu landen.

*Der Test ist der eigentliche Punkt:* Ohne ihn verrottet der Status. Mit
ihm kann das Inventar nicht falsch werden, ohne dass die Suite rot wird.

### D6: `Unsupported`-Marker bleiben, aber nur noch für Phase 4

Die Marker verschwinden nicht — Ereignissteuerung, `SETUEVENT`, `MSGBOX`
und `INPUTBOX$` behalten sie bis Phase 4. Der Abgleichstest aus D5 stellt
sicher, dass genau die im Inventar als `offen` geführten Elemente noch
Marker tragen.

### D7: Reihenfolge der Umsetzung

```
  1. Inventar (Sollseite)         -> misst alles Folgende
  2. Puffer-Umzug + Host + Snapshot -> BREAKING, zusammen in einem Schritt
  3. Bildschirmanweisungen        -> braucht 2
  4. Strings/Format/Datum/Mathematik -> unabhaengig, jederzeit
  5. Datei-E/A                    -> unabhaengig von 2/3
  6. Altlasten Phase 2            -> unabhaengig
```

Schritt 2 ist der einzige Bruch und wird bewusst als eine Einheit
umgesetzt: Puffer-Umzug, `Host`-Neuschnitt und Snapshot-Umstellung hängen
so eng zusammen, dass ein Zwischenzustand die Suite ohnehin rot ließe.
Schritte 4–6 können vorgezogen werden, falls Schritt 2 sich zieht.

## Risks / Trade-offs

- **Der `Host`-Bruch legt kurzzeitig die gesamte Suite lahm** → Schritt 2
  als eine Einheit umsetzen, nicht in Teilschritten; erst wenn Puffer,
  Host und Harness zusammen stehen, wird gemessen.
- **Neu erzeugte Golden Files zementieren einen Fehler** → Der Diff der
  acht Dateien wird einzeln geprüft. Erwartung: `PRINT`-only-Programme
  ändern sich nicht. Jede Abweichung wird begründet oder ist ein Bug.
- **Doppelzellen brechen bestehende Annahmen** (`POS` = Zeichenzahl,
  Zelle = Zeichen) → Korpustest mit CJK/Emoji inklusive Umbruch am
  rechten Rand, bevor die übrige Bildschirmarbeit darauf aufsetzt.
- **`RND`-Kompatibilität ist Rekonstruktionsarbeit** — die Zahlenfolge
  des Vorbilds ist nur über die Dokumentation und Beobachtung greifbar →
  Algorithmus dokumentieren; gelingt die exakte Folge nicht, wird die
  Abweichung in der Sprachreferenz festgehalten statt still
  abzuweichen.
- **UTF-32-Records machen Dateien binär inkompatibel zum Vorbild** →
  bewusst entschieden (2026-09-02); Preis ist, dass Originaldateien nicht
  gelesen werden können. Muss in der Sprachreferenz stehen.
- **`SHELL`, `FILES`, Satzsperren und `LPRINT` sind plattformabhängig** →
  jede Abweichung wird in der Sprachreferenz unter „Abweichungen"
  benannt; stilles Abweichen ist laut Guiding Principle ein Bug.
- **Das Inventar aus der Original-Hilfe zu erheben ist der stillste
  Aufwandstreiber** → als eigener erster Schritt geführt, damit sein
  Umfang sichtbar wird, statt sich in die Implementierungsarbeit zu
  mischen.

## Migration Plan

1. Inventar anlegen (rein additiv, kein Bruch).
2. Bruchschritt in einem Zug: `TextScreen` nach `tb-runtime::screen`,
   `tb-ui` auf Adapterrolle, `Host` neu, Harness auf Snapshot, acht
   Golden Files neu erzeugen und Diff prüfen.
3. Alles Weitere additiv — jedes neue Element wechselt im Inventar von
   `offen` auf `implementiert`, der Abgleichstest hält beides zusammen.

Rücknahme: Schritt 2 ist ein zusammenhängender Commit und als Ganzes
rücknehmbar. Danach gibt es keinen weiteren Bruch.

## Open Questions

- Umfang der `KEY n`-Funktionstasten-Makros (`KEY LIST`/`ON`/`OFF`): Die
  Anweisung steht im Scope; welche Makro-Semantik das Vorbild genau
  zeigt, ist bei der Umsetzung aus der Original-Hilfe zu klären und in
  docs/sprachreferenz.md festzuhalten. Berührt weder Puffer noch Host
  noch Aufgabenschnitt.
- Ersatzwert für `FRE` auf einer Plattform ohne DOS-Speichermodell: bei
  der Umsetzung zu wählen und zu dokumentieren.
- Konkrete Gestalt der Schnittstelle, über die die Forms-Engine in
  `tb-ui` später eigene Zeichenbereiche im Puffer belegt und wieder
  freigibt: erst in Phase 4 zu klären, wenn die Steuerelemente stehen.
  Ändert weder Puffer noch Host noch Aufgabenschnitt dieses Changes.
