OpenSpec: phase-3-runtime-bildschirm archiviert, Delta-Specs gesynct

Zwei neue Capabilities in den Hauptspecs:
- datei-eio: Dateiverwaltung, drei Zugriffsarten, UTF-32-Festtextfelder,
  Recordkonvertierung, Sperren, Dateisystem-Anweisungen
- sprachinventar: das Vollstaendigkeits-Inventar als pruefbares Artefakt

Fortgeschrieben: textbildschirm (Doppelzellen, Groessenunabhaengigkeit,
Bildschirmanweisungen), konsolen-basisbibliothek (Host-Neuschnitt, volle
String-/Format-/Datums-/Finanzbibliothek), kompat-testkorpus
(Snapshot-Vergleich, Nachweis der Groessenunabhaengigkeit),
sprach-frontend (Signaturpflicht, ON-ERROR-Scoping, DATA-Rohtext).

Bilanz: +32 Anforderungen, 6 geaendert, 1 ersetzt.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-04 06:55:02 +02:00
parent 52ccbb5848
commit d0461bea34
17 changed files with 700 additions and 40 deletions

View File

@@ -0,0 +1,287 @@
## 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 46 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.