OpenSpec-Review dokumentieren und ISAM-Vertragsabweichungen beheben

This commit is contained in:
2026-09-05 17:55:09 +02:00
parent 19804e0e2d
commit 57d6386a5a
61 changed files with 3087 additions and 86 deletions

View File

@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-09-05

View File

@@ -0,0 +1,33 @@
## Context
oeffnen ruft direkt Database::begin_write auf, während schreiben/lesen laufende Transaktionen wiederverwenden. Undo enthält nur Eingefuegt/Vorher für Sätze. FREEFILE fragt ausschließlich Dateien ab. Siehe proposal.md und die Delta-Specs.
## Goals / Non-Goals
**Goals:** Die bestehende redb-Schicht und die vorhandenen Bindungen behalten; Transaktions- und Nummernlogik an ihren gemeinsamen Einstiegspunkten berichtigen.
**Non-Goals:** Kein neues Speichersystem, keine verteilte Atomarität über mehrere Datenbankdateien, keine Performance-Umschreibung der Indizes.
## Decisions
### D1 — Gemeinsame Transaktion verwenden
OPEN und Layoutprüfung benutzen bei aktiver Transaktion deren vorhandenen Schreibkontext; außerhalb bleiben sie einzelne Transaktionen. Das vermeidet die Selbstblockade. OPEN und DELETETABLE verwenden kanonische absolute Pfade als Schlüssel für Datenbanken, Transaktionen und Bindungen; bei neuen Dateien wird zunächst der vorhandene Elternpfad kanonisiert. Nach erfolgreicher Dateianlage wird der vollständige Pfad erneut kanonisiert und als endgültige Identität an alle Aufrufer zurückgegeben, damit neu auflösbare Symlinks keinen zweiten Cache-Schlüssel erzeugen. CHDIR ändert damit weder bestehende Bindungen noch die Bedeutung der Cache-Schlüssel. Eine zweite Verbindung oder ein impliziter Commit würde Isolation und ROLLBACK brechen. CLOSE ohne Nummer und RESET lösen wie CLOSE #n nur Bindungen; die Freigabe der Datenbankdateien bleibt vom Transaktionsende getrennt.
### D2 — Sicherungspunkte umfassen Struktur und Inhalt
Das vorhandene Undo-Protokoll um die tatsächlich unterstützten Index-/Tabellenstrukturänderungen und deren Metadaten erweitern; in umgekehrter Operationsreihenfolge zurücknehmen. Layoutinformationen für Rücknahme auch nach CLOSE behalten. ROLLBACK ALL bleibt der Abbruch der redb-Transaktion. Keine Kopie der gesamten Datenbank je Savepoint. Struktur-Undo speichert alte Metadaten sowie beim Löschen die betroffenen rohen Satz-/Indexeinträge; diese Nutzdaten zählen zum SETMEM-Puffer und werden vor der Mutation gegen die Grenze geprüft. Satz-Undo trägt sein Layout selbst. Nach Rücknahme werden ungültige aktive Indizes gelöscht und Bindungen an verschwundene Tabellen oder zurückgenommene Layouts gelöst; gültige Bindungen behalten einen unpositionierten Cursor. Erfolgreiches DELETETABLE löst alle Bindungen dieser Tabelle; die Rücknahme stellt den Bestand, aber keine geschlossenen Bindungen wieder her. DELETE prüft die alten Satzbytes vor der Mutation gegen den verfügbaren Puffer, auch außerhalb einer expliziten Transaktion.
### D3 — Dateinummern gemeinsam prüfen
FREEFILE fragt beide bestehenden Belegungsprüfungen ab. Kein dritter Nummernspeicher, der von den Bindungen abweichen kann. Beide OPEN-Einstiege prüfen auch die andere Verwaltung und weisen Doppelbelegung mit Fehler 55 zurück. Ist keine als INTEGER darstellbare freie Nummer übrig, meldet FREEFILE Fehler 67.
## Risks / Trade-offs
- Versehentlicher Commit durch OPEN → nach Öffnen/Schließen weiterer Bindungen ROLLBACK ALL und erneutes Öffnen zur Persistenzprüfung.
- Fehlendes Undo für Strukturänderungen → CREATEINDEX, DELETEINDEX, DELETETABLE und Tabellenneuanlage vor/nach mehreren Savepoints prüfen.
- Hängender Regressionstest → Der CLI-Unterprozess hat fünf Sekunden Frist. Jeder Runtime-Datenbanktest und jede Korpusausführung werden zusätzlich über einen unabhängigen Watchdog nach 30 Sekunden mit Prozessstatus 124 beendet. Der ganze Testprozess darf beim Timeout scheitern, damit blockierende Bibliotheksaufrufe keine unendlichen Testläufe hinterlassen. Ein absichtlich blockierter Kind-Test beweist den Abbruch; der Elternprozess hat eine separate Kill-Frist.
## Migration Plan
Eigenständig. Gemeinsame Änderungen in builtins.rs mit laufzeit-eingabe-und-systemstatus sequenziell integrieren. Bestehende Spec-/Codeverträge erst nach erfolgreicher Umsetzung synchronisieren und archivieren. Die Tasks werden nach ihrer jeweiligen Abnahme abgeschlossen; Spec-Synchronisation und Archivierung folgen getrennt. Änderungen als zusammenhängenden Commit je Change integrieren; bei Fehlschlag auf den vorherigen Code zurückgehen und neue Datenformatversionen nicht mit alten Lesern öffnen.

View File

@@ -0,0 +1,28 @@
## Why
ISAM kann beim Öffnen einer weiteren Tabelle innerhalb einer Transaktion blockieren, Sicherungspunkte lassen Indexänderungen stehen und FREEFILE liefert bereits belegte Nummern. F01F03 des Reviews zeigen diese Fälle mit reproduzierbaren CLI-Programmen. Siehe [Review vom 05.09.2026](../../../../docs/reviews/2026-09-05-openspec-code-review.md).
## What Changes
- Zusätzliche ISAM-Bindungen innerhalb einer Transaktion ohne Selbstblockade und ohne impliziten Commit erlauben.
- Sicherungspunkte auf Satz- und Indexänderungen einschließlich Metadaten anwenden.
- FREEFILE über gewöhnliche und ISAM-Dateinummern gemeinsam bestimmen.
- Datenbankpfade über Verzeichniswechsel und Aliase korrekt zuordnen, Bindungen bei DELETETABLE lösen und die Puffergrenze vor DELETE prüfen.
- Blockierende Regressionen durch Prozessfristen für Runtime-Datenbanktests und Korpusausführungen absichern.
## Capabilities
### New Capabilities
Keine neuen Capability-Pfade.
### Modified Capabilities
- `isam-datenbank`: Transaktionales Öffnen, Pfadidentität, vollständige Rücknahme über Sicherungspunkte, Bindungen nach Tabellenlöschung und Puffergrenzen präzisieren.
- `datei-eio`: Freie Dateinummern ausdrücklich über alle Zugriffsarten bestimmen.
## Impact
crates/tb-runtime/src/isam.rs, builtins.rs und fileio.rs; ISAM-Unit-Tests, CLI-Korpus und docs/dateiformate.md. Keine neue Bibliothek und kein neues Datenbankformat.
Eigenständig. Gemeinsame Änderungen in builtins.rs mit laufzeit-eingabe-und-systemstatus sequenziell integrieren.

View File

@@ -0,0 +1,30 @@
## MODIFIED Requirements
### Requirement: Dateinummern und Öffnen in drei Zugriffsarten
`OPEN` SHALL in beiden Syntaxen des Vorbilds (Klausel- und Kurzform) die
Zugriffsarten sequenziell (`INPUT`, `OUTPUT`, `APPEND`), wahlfrei
(`RANDOM`) und binär (`BINARY`) unterstützen und die Datei an eine
Dateinummer binden. `FREEFILE` SHALL die kleinste freie Dateinummer
liefern. `CLOSE` ohne Argument SHALL alle offenen Dateien schließen,
`RESET` ebenso. Eine bereits belegte Dateinummer MUST Laufzeitfehler 55
„File already open" auslösen, eine nicht geöffnete Nummer Fehler 52
„Bad file name or number", eine fehlende Datei bei `INPUT` Fehler 53
„File not found".
FREEFILE SHALL die gemeinsame Belegung durch sequenzielle, wahlfreie, binäre und ISAM-Bindungen berücksichtigen.
#### Scenario: Sequenzielles Schreiben und Lesen
- **WHEN** eine Datei mit `OPEN "d.txt" FOR OUTPUT AS #1` beschrieben, geschlossen und mit `FOR INPUT` wieder geöffnet wird
- **THEN** liefert das Lesen genau die geschriebenen Zeilen
#### Scenario: Doppeltes Öffnen derselben Nummer
- **WHEN** `OPEN` eine bereits offene Dateinummer verwendet
- **THEN** tritt Laufzeitfehler 55 auf
#### Scenario: Freie Dateinummer
- **WHEN** `#1` offen ist und `FREEFILE` aufgerufen wird
- **THEN** liefert `FREEFILE` eine Nummer, die noch nicht belegt ist
#### Scenario: ISAM belegt eine Dateinummer
- **WHEN** eine ISAM-Tabelle auf #1 und sonst keine Datei offen ist
- **THEN** liefert FREEFILE den Wert 2

View File

@@ -0,0 +1,111 @@
## MODIFIED Requirements
### Requirement: Datenbank- und Tabellenbindung an eine Dateinummer
`OPEN datenbank$ FOR ISAM typname tabellenname AS #n` SHALL eine Tabelle
einer Datenbankdatei an eine Dateinummer binden. Das Satzlayout SHALL
sich aus dem benannten benutzerdefinierten Typ ergeben; dessen Felder
sind die Spalten der Tabelle. Existiert die Tabelle noch nicht, SHALL sie
mit diesem Layout angelegt werden; existiert sie mit abweichendem Layout,
MUST Laufzeitfehler 88 „ISAM - Database inconsistent" auftreten. Ein
ungültiger Tabellen- oder Datenbankname MUST Fehler 81 „ISAM - Invalid
name" auslösen. `CLOSE` SHALL die Bindung lösen; ausstehende Änderungen
sind dabei bereits festgeschrieben, weil jede Operation außerhalb einer
Transaktion für sich eine Transaktion ist. Eine laufende Transaktion MUST
`CLOSE` unberührt lassen — über ihr Ende entscheiden allein `COMMITTRANS`
und `ROLLBACK ALL`. `DELETETABLE` SHALL eine Tabelle samt ihren
Indizes entfernen; eine nicht vorhandene Tabelle MUST Fehler 82 „ISAM -
Table not found" auslösen.
Eine weitere Bindung derselben Datenbank innerhalb einer laufenden Transaktion SHALL deren bisherigen Zustand sehen und MUST ohne implizites Festschreiben oder Selbstblockade entstehen.
OPEN und DELETETABLE SHALL relative Datenbankpfade im aktuellen Arbeitsverzeichnis auflösen und kanonisch auflösbare Aliase derselben Datei gemeinsam verwalten. Bereits offene Bindungen MUST nach CHDIR weiterhin auf ihre ursprünglich geöffnete Datenbank zeigen. DELETETABLE SHALL bei Erfolg alle Bindungen der entfernten Tabelle lösen; weitere Zugriffe über diese Nummern MUST Fehler 52 melden. Eine Rücknahme SHALL die gelöschte Tabelle wiederherstellen, ohne geschlossene Bindungen erneut zu öffnen.
#### Scenario: Gleicher relativer Name nach Verzeichniswechsel
- **WHEN** nach OPEN in Verzeichnis A alle Bindungen geschlossen werden und derselbe relative Name in Verzeichnis B geöffnet wird
- **THEN** betreffen Lesen und Schreiben die Datei in B; die Datei in A bleibt unverändert
#### Scenario: Pfadalias in einer Transaktion
- **WHEN** dieselbe vorhandene Datenbank nach BEGINTRANS und INSERT über einen relativen oder absoluten Pfadalias erneut geöffnet wird
- **THEN** sieht die neue Bindung die bisherigen Änderungen ohne zweiten Writer; ROLLBACK ALL nimmt die Änderungen zurück
#### Scenario: Neuanlage über einen noch nicht auflösbaren Symlink
- **WHEN** OPEN eine Datenbank über einen Symlink mit bislang fehlender Zieldatei anlegt und die Datenbank danach innerhalb einer Transaktion erneut über Alias oder Zielpfad geöffnet wird
- **THEN** teilen alle Bindungen denselben Transaktionszustand; Sicherungspunkt-Rücknahme und ROLLBACK ALL funktionieren über beide Pfade
#### Scenario: Bindungen nach Tabellenlöschung und Rücknahme
- **WHEN** eine mehrfach gebundene Tabelle nach SAVEPOINT mit DELETETABLE gelöscht und anschließend zurückgenommen wird
- **THEN** sind die alten Bindungen geschlossen; ein erneutes OPEN sieht wieder die ursprünglichen Sätze und Indizes
#### Scenario: Tabelle wird beim ersten Öffnen angelegt
- **WHEN** eine Datenbank ohne die Tabelle `Kunden` mit `OPEN "db" FOR ISAM KundeTyp "Kunden" AS #1` geöffnet wird
- **THEN** entsteht die Tabelle mit den Feldern des Typs und die Dateinummer ist nutzbar
#### Scenario: Abweichendes Satzlayout
- **WHEN** eine bestehende Tabelle mit einem Typ geöffnet wird, dessen Felder nicht zu ihren Spalten passen
- **THEN** tritt Laufzeitfehler 88 auf
#### Scenario: CLOSE lässt eine laufende Transaktion stehen
- **WHEN** innerhalb einer Transaktion eine andere Dateinummer mit `CLOSE` geschlossen wird
- **THEN** läuft die Transaktion weiter und ein folgendes `ROLLBACK ALL` nimmt ihre Änderungen zurück
#### Scenario: Entfernte Tabelle
- **WHEN** `DELETETABLE` für eine nicht vorhandene Tabelle aufgerufen wird
- **THEN** tritt Laufzeitfehler 82 auf
#### Scenario: Weitere Tabelle während einer Transaktion
- **WHEN** nach BEGINTRANS und INSERT eine zweite Tabelle derselben Datenbank geöffnet und danach ROLLBACK ALL ausgeführt wird
- **THEN** kehrt OPEN zurück; die eingefügten Daten sind nach Wiederöffnung nicht festgeschrieben
### Requirement: Transaktionen mit Rücknahme
`BEGINTRANS` SHALL eine Transaktion eröffnen, `COMMITTRANS` alle
Änderungen seit ihrem Beginn festschreiben und `ROLLBACK` sie
zurücknehmen. `SAVEPOINT` SHALL als **Funktion** einen Sicherungspunkt
setzen und dessen Kennung liefern; `ROLLBACK` mit einer solchen Kennung
SHALL auf diesen Punkt zurücksetzen, ohne die Transaktion aufzugeben.
`ROLLBACK ALL` SHALL alle Änderungen seit `BEGINTRANS` zurücknehmen und
die Transaktion beenden. Nach jeder Rücknahme MUST der Satzbestand
einschließlich aller Indizes dem Zustand zum Bezugspunkt entsprechen und
der Cursor als unpositioniert gelten. Eine beim Programmende nicht
festgeschriebene Transaktion MUST wirkungslos bleiben.
Die Rücknahme auf einen Sicherungspunkt SHALL auch seitdem angelegte oder gelöschte Indizes und Tabellenstrukturen einschließlich ihrer Metadaten zurücknehmen. CLOSE einer Bindung MUST die spätere Rücknahme ihrer Änderungen weiterhin erlauben.
#### Scenario: Rücknahme stellt den Bestand wieder her
- **WHEN** nach `BEGINTRANS` Sätze eingefügt und gelöscht werden und dann `ROLLBACK` folgt
- **THEN** entspricht der Bestand beim erneuten Durchlauf dem Zustand vor `BEGINTRANS`
#### Scenario: Rücknahme auf einen Sicherungspunkt
- **WHEN** innerhalb einer Transaktion `s = SAVEPOINT` ausgewertet, danach geändert und mit `ROLLBACK s` zurückgesetzt wird
- **THEN** bleiben die Änderungen vor dem Sicherungspunkt erhalten und die danach sind zurückgenommen
#### Scenario: ROLLBACK ALL beendet die Transaktion
- **WHEN** nach mehreren Sicherungspunkten `ROLLBACK ALL` ausgeführt wird
- **THEN** entspricht der Bestand dem Zustand vor `BEGINTRANS` und die Transaktion ist beendet
#### Scenario: Indexanlage nach Sicherungspunkt
- **WHEN** nach SAVEPOINT ein Index angelegt und auf den Sicherungspunkt zurückgesetzt wird
- **THEN** ist der neue Index nicht vorhanden; SETINDEX mit seinem Namen löst Fehler 83 aus
#### Scenario: Geschlossene Tabelle rücknehmen
- **WHEN** eine geänderte Tabellenbindung vor ROLLBACK auf einen Sicherungspunkt geschlossen wird
- **THEN** gelingt die Rücknahme und der bei Wiederöffnung sichtbare Satz-/Indexbestand entspricht dem Sicherungspunkt
### Requirement: Pufferverwaltung mit benanntem Fehler
`SETMEM` SHALL als **Funktion** die für ISAM verfügbare Puffergröße um
den übergebenen Betrag ändern und die danach verfügbare Größe liefern.
Reicht
der verfügbare Puffer für eine Operation nicht aus, MUST Laufzeitfehler 89
„ISAM - Insufficient ISAM buffers" auftreten statt eines generischen
Fehlers oder eines stillen Abbruchs. Weicht die Bedeutung von `SETMEM`
mangels DOS-Speichermodell vom Vorbild ab, MUST die Abweichung in
docs/sprachreferenz.md dokumentiert sein.
DELETE SHALL den benötigten Satz-/Undo-Puffer vor der Änderung prüfen. Bei Fehler 89 MUST Satzbestand, Indexbestand und Cursor unverändert bleiben.
#### Scenario: Pufferfehler wird benannt
- **WHEN** eine Operation den verfügbaren ISAM-Puffer überschreitet
- **THEN** tritt Laufzeitfehler 89 auf
#### Scenario: DELETE bei ausgeschöpftem Puffer
- **WHEN** DELETE auf einem gültigen aktuellen Satz bei unzureichendem ISAM-Puffer ausgeführt wird
- **THEN** meldet es Fehler 89 ohne den Satz zu löschen, den Cursor zu verschieben oder Undo hinzuzufügen; eine spätere Rücknahme bleibt möglich

View File

@@ -0,0 +1,46 @@
## 1. Transaktionspfade
- [x] 1.1 OPEN/Layoutanlage auf den bestehenden Transaktionskontext führen; verifizieren mit OPEN → BEGINTRANS → INSERT → zweites OPEN derselben Datenbank unter harter Prozessfrist und anschließendem ROLLBACK ALL.
- [x] 1.2 Strukturänderungen am bestehenden Undo-Protokoll rücknehmbar machen und benötigte Layouts über CLOSE erhalten; verifizieren mit verschachtelten Savepoints, CREATEINDEX/DELETEINDEX/DELETETABLE, geschlossenen Bindungen und unverändertem Bestand nach Wiederöffnung.
## 2. Dateinummern und Abnahme
- [x] 2.1 FREEFILE gegen beide Dateiverwaltungen prüfen; verifizieren mit gemischten OPEN-Modi, ISAM #1, Lücken nach CLOSE und wiederverwendeten Nummern.
- [x] 2.2 ISAM-Korpus um die regressiven Ablaufkombinationen erweitern; verifizieren mit cargo test -p tb-runtime und cargo test -p tb-cli --test compat.
- [x] 2.3 Transaktions-/Sicherungspunktgrenzen dokumentieren und Delta-Specs prüfen; verifizieren mit git diff --check und openspec validate isam-transaktionen-und-dateinummern --strict.
## Abnahme am 05.09.2026
- `cargo test -p tb-runtime --quiet`: 130 bestanden.
- `cargo test -p tb-cli --test compat --quiet`: 15 bestanden; der Golden-Generator bleibt ignoriert. Drei neue Korpusprogramme mit unabhängig festgelegten Sollausgaben.
- `cargo test --workspace --quiet`: 385 bestanden, 0 fehlgeschlagen, 1 bewusst ignoriert.
- Der CLI-Test für OPEN innerhalb einer Transaktion läuft mit einer harten Fünf-Sekunden-Frist und prüft Sichtbarkeit sowie ROLLBACK ALL nach CLOSE.
- Formatprüfung der vier geänderten Rust-Dateien, `git diff --check` und `openspec validate isam-transaktionen-und-dateinummern --strict` bestanden.
## 3. Befunde der Verifikation
- [x] 3.1 V01: Datenbankpfade eindeutig auflösen; Verzeichniswechsel, relative/absolute Aliase und DELETETABLE über Aliase prüfen.
- [x] 3.2 V02: Alle gefährdeten Runtime-/Korpusläufe mit einer wirksamen Prozessfrist schützen und einen absichtlich blockierten Testprozess nachweislich beenden.
- [x] 3.3 V03: Bindungen gelöschter Tabellen lösen; Zugriffe danach, Wiederanlage und Rücknahme der Löschung prüfen.
- [x] 3.4 V04: DELETE vor Mutation gegen die Puffergrenze prüfen; Bestand, Indizes und Cursor bei Fehler 89 unverändert erhalten.
- [x] 3.5 Alle Befunde erneut verifizieren, Dokumentation und Delta-Specs abgleichen sowie Workspace-Tests und OpenSpec-Validierung abschließen.
## Nachprüfung der Befunde am 05.09.2026
- Runtime: 134 Tests bestanden; CLI-Kompatibilität: 16 bestanden, Golden-Generator ignoriert.
- Workspace: 390 bestanden, 0 fehlgeschlagen, 1 bewusst ignoriert.
- V01: Neuer Korpusfall `isamidentitaet.bas`, Unit-Test für Pfad-/Symlinkaliase und separate CLI-Prozesse bestätigen getrennte Daten in a/db und b/db.
- V02: Jeder Runtime-Datenbanktest und Korpuslauf hat eine harte Prozessfrist; der Selbsttest beendet einen absichtlich blockierten Kindprozess mit Status 124.
- V03: Zwei alte Bindungen sind nach DELETETABLE unbenutzbar; Rücknahme und Wiederöffnung stellen Sätze und Eindeutigkeit wieder her.
- V04: DELETE meldet bei vollem Puffer Fehler 89; Satzbestand, Indizes, Cursor und Undo bleiben unverändert. Genau passende Puffergröße und anschließende Rücknahme sind geprüft.
## 4. Letzter Aliasbefund
- [x] 4.1 V05: Nach Neuanlage die tatsächliche kanonische Dateiidentität an Cache und Bindungen weitergeben; Symlink-Neuanlage mit erneutem OPEN über Alias und Ziel, DELETETABLE, Sicherungspunkt und ROLLBACK ALL prüfen.
## Schlussabnahme V05
- `symlink_neuanlage_behaelt_die_dateiidentitaet`: bestanden für relative und absolute Symlinkziele, inklusive Alias-/Zielbindung, DELETETABLE, Sicherungspunkt, ROLLBACK ALL und erneutem Öffnen.
- Ursprüngliche CLI-Reproduktion: Alias und Ziel liefern innerhalb der Transaktion je einen Satz; nach ROLLBACK ALL ist der Bestand leer.
- `cargo test --workspace --quiet`: 391 bestanden, 0 fehlgeschlagen, 1 bewusst ignoriert.
- Format-/Diffprüfung und strikte OpenSpec-Validierung bestanden; vier Anforderungen in zwei Hauptspecs synchronisiert und alle 15 Hauptspecs validiert.

View File

@@ -0,0 +1,162 @@
# Verifikation: isam-transaktionen-und-dateinummern
Nachprüfung am 05.09.2026, aktueller uncommitteter Arbeitsbaum auf `19804e0`.
Geprüft: Proposal, Design, Tasks, Delta-Specs, betroffene Hauptspec-Verträge,
Implementierung, Regressionstests und ursprüngliche Fehlerreproduktionen.
**Schlussabnahme: V01V05 behoben und nachgeprüft. Keine offenen Befunde.
Die Hauptspecs sind synchronisiert; alle 11 Tasks sind abgeschlossen.**
| Dimension | Ergebnis |
|---|---|
| Vollständigkeit | 11/11 Tasks abgeschlossen |
| Korrektheit | 4 Anforderungen, 20 Szenarien zugeordnet; Neuanlage über Symlink zusätzlich abgesichert |
| Kohärenz | D1D3 eingehalten; Dokumentation und Delta-Specs um die Korrekturen ergänzt |
| Befunde | 0 offen; V01V05 behoben |
## V05 — Neuanlage über einen Symlink: behoben
**Vorher:** Bei `alias.db → target.db` mit noch fehlender Zieldatei gelang
das erste OPEN; das zweite OPEN derselben Datei meldete Fehler 88. Die erste
Anlage speicherte den Alias als Cache-Schlüssel, die nächste Auflösung ergab
den Zielpfad.
**Korrektur:** `db_oeffnen` bestimmt nach erfolgreicher Dateianlage die
endgültige kanonische Identität, speichert den Datenbankhandle darunter und
gibt diesen Pfad an OPEN beziehungsweise DELETETABLE zurück. Bindungen,
Transaktionen und Undo verwenden damit von Anfang an den Zielpfad.
**Nachweise:**
- `symlink_neuanlage_behaelt_die_dateiidentitaet` in
[isam/tests.rs](../../../../crates/tb-runtime/src/isam/tests.rs): relative und
absolute Symlinkziele, erneutes OPEN über Alias und Ziel, gemeinsamer
Transaktionsbestand, DELETETABLE über Alias, Sicherungspunkt-Rücknahme,
ROLLBACK ALL und persistierter Ausgangsbestand nach erneutem Öffnen.
- Ursprüngliche CLI-Reproduktion in einem frischen temporären Verzeichnis,
mit fünf Sekunden Frist: beide zusätzlichen Bindungen sehen einen Satz;
nach ROLLBACK ALL liefert LOF den Wert 0. Prozessstatus 0, keine Fehler 88/52.
- Vollständige Workspace-Suite: **391 bestanden, 0 fehlgeschlagen,
1 bewusst ignoriert**. Davon 135 Runtime-Tests und 16 aktive CLI-Korpus-/Harness-Tests.
## V01 — Datenbankidentität: behoben
**Vorher:** Nach CLOSE und CHDIR konnte OPEN mit demselben relativen Namen
in die alte Datei schreiben. OPEN über `./db` erkannte eine bereits laufende
Transaktion derselben Datei nicht und meldete Fehler 88.
**Korrektur:** [datenbank_pfad](../../../../crates/tb-runtime/src/isam.rs#L1560)
löst vorhandene Dateien kanonisch auf. Bei neuen Dateien wird ihr vorhandener
Elternpfad kanonisiert. OPEN und DELETETABLE verwenden diesen Schlüssel
durchgängig für Datenbanken, Transaktionen, Bindungen und Rücknahme.
**Nachweise:**
- [isamidentitaet.bas](../../../../tests/compat/isamidentitaet.bas): Nach Wechsel
von a nach b ist die zweite Datenbank leer; der Alias `./db` sieht innerhalb
einer Transaktion den gleichen Bestand. DELETETABLE über `../a/db` und
Rücknahme funktionieren ebenfalls.
- [pfadaliase_teilen_transaktion_und_ruecknahme](../../../../crates/tb-runtime/src/isam/tests.rs#L696):
absolute Pfade mit `..` und auf Unix auch ein Symlink teilen denselben Writer.
- Separate CLI-Prozesse lesen anschließend genau einen Satz mit Nr=1 aus
`a/db` und einen Satz mit Nr=2 aus `b/db`. Beide Dateien existieren.
## V02 — Prozessfristen: behoben
**Vorher:** Nur der besondere CLI-Unterprozess war geschützt; die direkten
ISAM-Aufrufe im Korpustest und in Runtime-Tests konnten weiterhin hängen.
**Korrektur:** [Prozessfrist](../../../../tests/support/prozessfrist.rs) überwacht
jeden Runtime-Datenbanktest über dessen TempDb sowie jeden Aufruf von
[run_corpus_file](../../../../crates/tb-cli/tests/compat.rs#L359), einschließlich
beider Korpusdurchläufe und des bewusst ignorierten Golden-Generators.
Nach 30 Sekunden beendet ein unabhängiger Watchdog den gesamten betroffenen
Testprozess mit Status 124. Beim regulären Abschluss wird die Frist aufgehoben.
Der spezielle CLI-Unterprozess behält seine eigene Fünf-Sekunden-Frist.
**Nachweis:** `frist_beendet_blockierten_testprozess` startet einen Kind-Test,
der absichtlich unbegrenzt blockiert. Dessen Watchdog beendet ihn mit Status 124
und benannter Fehlermeldung; der Elternprozess prüft beides und besitzt zusätzlich
eine unabhängige Fünf-Sekunden-Kill-Frist. Dieser Test besteht sowohl im Runtime-
als auch im CLI-Testbinary. Keine Änderung am Produktcode zur Simulation nötig.
## V03 — Bindungen nach DELETETABLE: behoben
**Vorher:** Ein alter Handle konnte nach DELETETABLE unbemerkt eine interne
Satztabelle ohne Layoutmetadaten wieder anlegen.
**Korrektur:** [tabelle_loeschen](../../../../crates/tb-runtime/src/isam.rs#L988)
löst nach erfolgreicher Löschung alle Bindungen genau dieser Tabelle und
Datenbank. Eine Rücknahme stellt den Bestand wieder her, öffnet aber keine
zuvor geschlossenen Bindungen. Ein neues OPEN bindet den restaurierten Bestand.
**Nachweise:**
- [deletetable_loest_alle_betroffenen_bindungen](../../../../crates/tb-runtime/src/isam/tests.rs#L725):
Beide alten Handles melden Fehler 52, eine andere Tabelle bleibt gebunden.
Rücknahme und Wiederöffnung stellen Sätze und eindeutigen Index wieder her.
Erneute Löschung und Neuanlage ergeben eine leere Tabelle ohne alten Index.
- Die ursprüngliche CLI-Reproduktion meldet jetzt Fehler 52 beim INSERT über
die alte Nummer und LOF=0 nach Neuanlage. RETRIEVE auf der leeren Tabelle
meldet erwartungsgemäß Fehler 85; es gibt keinen Geistersatz.
## V04 — DELETE bei ausgeschöpftem Puffer: behoben
**Vorher:** DELETE speicherte Undo und entfernte den Satz trotz Puffergröße 0.
**Korrektur:** [satz_loeschen](../../../../crates/tb-runtime/src/isam.rs#L1123)
prüft die alten Satzbytes gegen den noch verfügbaren Puffer, bevor Satz oder
Indizes verändert beziehungsweise Undo hinzugefügt werden.
**Nachweise:**
- [delete_pufferfehler_erhaelt_saetze_indizes_und_cursor](../../../../crates/tb-runtime/src/isam/tests.rs#L760):
Fehler 89 außerhalb und innerhalb einer Transaktion lässt Bestand, aktiven
Index, Cursor und Undo unverändert. Bei exakt passender Größe gelingt ein
DELETE; der nächste scheitert ohne Änderung. Rücknahme restauriert beide
Sätze und gibt den Puffer wieder frei.
- Ursprüngliche CLI-Reproduktion: `free=0`, `ERR=89`, `LOF=1`, `restored=1`.
## Szenarionachweise
| Delta-Szenario | Nachweis |
|---|---|
| Sequenzielles Schreiben und Lesen | `tests/compat/dateien.bas` |
| Doppeltes Öffnen derselben Nummer | `isamdateinummern.bas`: Fehler 55 in beiden Richtungen |
| Freie Dateinummer | `isamdateinummern.bas`: gemischte Modi und wiederbelegte Lücken |
| ISAM belegt eine Dateinummer | `isamdateinummern.bas`: FREEFILE=2 |
| Gleicher relativer Name nach Verzeichniswechsel | `isamidentitaet.bas` und separate Persistenzprüfung |
| Neuanlage über einen noch nicht auflösbaren Symlink | `symlink_neuanlage_behaelt_die_dateiidentitaet` und CLI-Reproduktion |
| Pfadalias in einer Transaktion | `isamidentitaet.bas`, Runtime-Pfadaliastest |
| Bindungen nach Tabellenlöschung und Rücknahme | Runtime-DELETETABLE-Bindungstest |
| Tabelle wird beim ersten Öffnen angelegt | `isamtabellen.bas` |
| Abweichendes Satzlayout | `isamtabellen.bas`: Fehler 88 |
| CLOSE lässt eine laufende Transaktion stehen | `close_beendet_keine_transaktion`, `isamopen.bas` |
| Entfernte Tabelle | `isamtabellen.bas`, `isamopen.bas`: Fehler 82 |
| Weitere Tabelle während einer Transaktion | `isamopen.bas` und CLI-Test mit Prozessfrist |
| Rücknahme stellt den Bestand wieder her | CLI-Probe der Erstprüfung: INSERT/DELETE und bare ROLLBACK ergeben LOF=1 und LOC=0; Rücknahmepfad unverändert |
| Rücknahme auf einen Sicherungspunkt | `isamtrans.bas`, `isamstruktur.bas`, Runtime-Strukturtests |
| ROLLBACK ALL beendet die Transaktion | `isamtrans.bas`; CLI-Probe der Erstprüfung mit zwei Savepoints und anschließendem Fehler 5 |
| Indexanlage nach Sicherungspunkt | `isamstruktur.bas`: Fehler 83; Runtime-Strukturtests |
| Geschlossene Tabelle rücknehmen | `isamstruktur.bas`, Runtime-Tests für CLOSE und Struktur-Rücknahme |
| Pufferfehler wird benannt | `isampuffer.bas`, `struktur_undo_prueft_puffer_vor_dem_loeschen` |
| DELETE bei ausgeschöpftem Puffer | Neuer Runtime-DELETE-Puffertest und ursprüngliche CLI-Reproduktion |
## Prüfungen einschließlich erneuter Verifikation
- `cargo test -p tb-runtime --quiet`: 135 bestanden.
- `cargo test -p tb-cli --test compat --quiet`: 16 bestanden, Golden-Generator ignoriert.
- Erneut `cargo test --workspace --quiet`: **391 bestanden, 0 fehlgeschlagen,
1 bewusst ignoriert**; einschließlich beider Watchdog-Selbsttests.
- Formatprüfung aller geänderten Rust-Dateien und des neuen Testhelfers bestanden.
- `git diff --check` und Whitespace-Prüfung der neuen Dateien bestanden.
- `openspec validate isam-transaktionen-und-dateinummern --strict`: bestanden.
- Ursprüngliche Reproduktionen und positive Zusatzproben erneut ausgeführt:
Index-Neuanlage mit geändertem Aufbau und Rücknahme, RESET/COMMIT,
Rücknahme über zwei Datenbankdateien, Programmende ohne COMMIT sowie
Persistenz über neue CLI-Prozesse. Alle externen Proben mit fünf Sekunden Frist.
Keine GUI-, Windows-/Linux-Abnahme, Speichererschöpfung oder simulierten
redb-I/O-Ausfälle ausgeführt. Keine neue Abhängigkeit, kein neues Datenbankformat.
Die vier geänderten Anforderungen wurden in die Hauptspecs datei-eio und
isam-datenbank synchronisiert; alle 15 Hauptspecs bestehen die strikte Validierung.

View File

@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-09-05

View File

@@ -0,0 +1,41 @@
## Context
RtState hält getrennte Tasten-/Mauspuffer; forms_zustellen setzt weitere Handlerframes auf, auch vor der ersten Anweisung eines gerade begonnenen Handlers. Der Stmt-Pfad dupliziert die Trap-Zustellung. SLEEP benutzt eine zweite reale Uhr. tb-vm zieht tb-ui samt unbedingten Terminaldependencies ein. Siehe proposal.md und die Delta-Specs.
## Goals / Non-Goals
**Goals:** Eine gemeinsame Zustellentscheidung nutzen und Ereignisordnung vom Host bis zum sichtbaren Handlerablauf bewahren.
**Non-Goals:** Keine präemptiven Threads, kein neuer Scheduler und keine Änderung der festgelegten Priorität gleichzeitig fälliger klassischer Trap-Quellen.
## Decisions
### D1 — Reihenfolge einmal festhalten
Geordnete Hostereignisse bis zur Annahme durch Trap, Control oder Konsoleneingabe erhalten. KEY STOP merkt höchstens ein Ereignis, lässt aber die Taste normal lesbar. Keine spätere Rekonstruktion der Reihenfolge aus zwei bereits getrennten Queues.
### D2 — Zustellung und Handlerbeginn koordinieren
Die vorhandenen Zustellpunkte auf eine gemeinsame Entscheidung führen. Der erste Statement-Eintritt eines zugestellten Handlers darf nicht durch das nächste bereits wartende Ereignis überholt werden; rekursive Zustellung derselben Forms-Ereignisprozedur verhindern und Rückkehr geordnet abarbeiten. Explizites DOEVENTS/Modalität bleiben kooperative Zustellpunkte. Menüsperre gilt überall und verändert keine expliziten ON/OFF/STOP-Zustände.
### D3 — Host besitzt Zeit und Warten
Instant-Fallback aus der VM entfernen. Für blockierende Wartephasen eine Host-Wartemöglichkeit mit Deadline vorsehen, die Terminal/Pipe in Realzeit und Testhosts durch virtuelle Zeit umsetzen; SLEEP darf weder mit stehender virtueller Zeit scheinbar erfolgreich enden noch busy-spinnen. Ohne Trap, Timer oder zeitabhängige Mausauswertung keine Zeitabfrage.
### D4 — Backend als vorhandene Paketgrenze
Terminalmodule und deren Dependencies in tb-ui durch ein terminal-Feature abtrennen; tb-vm nutzt tb-ui ohne dieses Feature, der CLI-Runner aktiviert es ausdrücklich. Modelle/FRM/Dialogs im terminalfreien Teil verwenden nur Host und Zellenpuffer. Eine zusätzliche Forms-Crate ist dafür nicht erforderlich.
### D5 — BackTab normalisieren
KeyCode::BackTab auf denselben Tab-Eingabepfad mit gesetztem Shiftbit abbilden. Kein eigener Fokusmechanismus.
## Risks / Trade-offs
- Neue Zustellung kann Referenzen/Fehlerzustand eines wartenden Frames beschädigen → bestehende Trap-, RESUME-, BYREF- und Modalitätstests mit ausführen.
- Cargo-Feature-Unifikation kann Terminalabhängigkeiten verdecken → terminalfreien VM-Build über einen isolierten Einbetter und dessen aufgelösten Dependency-Graph prüfen.
- Virtuelle Hosts können bisher SLEEP ohne Zeitfortschritt verwenden → diese Hosts ausdrücklich auf die neue Wartevereinbarung umstellen, keine reale Rückfallebene beibehalten.
## Migration Plan
Vor forms-zustand-und-bedienung umsetzen. F06 bleibt Eigentum dieses Changes; Forms-State-Change verändert diese Requirement-Blöcke nicht. Bestehende Spec-/Codeverträge erst nach erfolgreicher Umsetzung synchronisieren und archivieren. Bis dahin bleiben alle Tasks offen. Änderungen als zusammenhängenden Commit je Change integrieren; bei Fehlschlag auf den vorherigen Code zurückgehen und neue Datenformatversionen nicht mit alten Lesern öffnen.

View File

@@ -0,0 +1,30 @@
## Why
Ereignisse gehen verloren, laufen in falscher Reihenfolge oder umgehen Menüsperren. F04F08, F18 und F20 zeigen außerdem Zeitabfragen ohne Bedarf und eine unerwünschte Terminalabhängigkeit der VM. Siehe [Review vom 05.09.2026](../../../docs/reviews/2026-09-05-openspec-code-review.md).
## What Changes
- Signal 1/2, KEY STOP und die FIFO-Reihenfolge von Tasten, Maus und Forms-Handlern durchgängig erhalten.
- Menüsperre und Zustellpunkte für Statements, DOEVENTS, SLEEP und blockierende Eingaben vereinheitlichen.
- Zeit ausschließlich bedarfsgerecht über den Host verwenden und Forms-Ereignisse während SLEEP zustellen.
- BackTab in den vorhandenen Rückwärtstab-Pfad übersetzen.
- VM und Forms ohne Terminalbackend separat baubar machen.
## Capabilities
### New Capabilities
Keine neuen Capability-Pfade.
### Modified Capabilities
- `ereignissteuerung`: Reihenfolge, KEY-STOP-Verhalten, Signalzustellung und Hostzeit durch ergänzende Grenzfallszenarien absichern.
- `vm-ausfuehrung`: Den Pfad ohne Zeitbedarf und die Fortsetzung nach Ereignissen präzisieren.
- `forms-steuerelemente`: Fokusreihenfolge, BackTab und Menüsperre an allen Zustellpunkten festhalten.
- `textbildschirm`: Terminalfreie Einbettung der vollständigen VM einschließlich Forms präzisieren.
## Impact
RtState/Traps, Vm::exec und Zustell-/Wartepfade, tb-ui TerminalHost und Cargo-Features; Unit-, VM- und CLI-Tests. Keine Änderung des BASIC-Sprachumfangs.
Vor forms-zustand-und-bedienung umsetzen. F06 bleibt Eigentum dieses Changes; Forms-State-Change verändert diese Requirement-Blöcke nicht.

View File

@@ -0,0 +1,139 @@
## MODIFIED Requirements
### Requirement: Ereignisquellen mit Zeit vom Host
Die Laufzeit SHALL Ereignisse aus vier Quellen führen: Tastatur, Maus,
Zeit und benutzerdefinierte Ereignisse. Die Zeit SHALL vom Host bezogen
werden;
die Laufzeit MUST NOT für die Ereigniszustellung selbst auf die
Systemuhr zugreifen. Ein Host ohne Terminal MUST die Zeit frei stellen
können, sodass zeitgesteuerte Programme ohne Wartezeit und mit
reproduzierbarem Ergebnis prüfbar sind.
Auch Wartefristen der VM SHALL ausschließlich von der Hostzeit abhängen. Ein Host mit stehender virtueller Zeit MUST NOT durch reale verstrichene Zeit eine Frist scheinbar erreichen.
#### Scenario: Zeitgesteuerter Trap ohne echte Wartezeit
- **WHEN** ein Testhost die Zeit um 5 Sekunden vorstellt und ein Programm `ON TIMER(5) GOSUB Marke` mit `TIMER ON` aktiv hat
- **THEN** wird der Trap zugestellt, ohne dass der Test tatsächlich wartet
#### Scenario: Wiederholter Lauf liefert dasselbe Ergebnis
- **WHEN** dasselbe zeitgesteuerte Korpusprogramm zweimal mit demselben Zeitverlauf ausgeführt wird
- **THEN** ist die Ausgabe beide Male identisch
#### Scenario: Stehende virtuelle Zeit
- **WHEN** ein Host während SLEEP seine Zeit unverändert hält
- **THEN** endet SLEEP nicht wegen realer Zeit; eine vom Host gemeldete Frist oder ein zugestelltes Ereignis beendet das Warten
### Requirement: Mausereignisse in Zellenkoordinaten
Die Darstellungsschicht SHALL Mausereignisse der Ausführungsumgebung an
die Laufzeit weitergeben: Drücken, Loslassen und Bewegung, jeweils mit
gedrückter Taste, Umschaltzustand und Position. Die Position SHALL in
Zellen des Textbildschirms angegeben werden, gezählt wie bei `LOCATE`,
also 1-basiert. Ereignisse außerhalb der aktuellen Bildschirmfläche MUST
verworfen werden. Die Reihenfolge zwischen Maus- und Tastenereignissen
SHALL der Reihenfolge ihres Eintreffens entsprechen. Solange kein
Verbraucher ein Mausereignis annimmt, MUST es am Zustellpunkt verworfen
werden, damit die Warteschlange nicht unbegrenzt wächst.
Die Reihenfolge SHALL bis zur beobachtbaren Ausführung der zugehörigen Handler erhalten bleiben; getrennte Quellarten MUST NOT nachträglich nach ihrer Art umgeordnet werden.
#### Scenario: Position als Zelle
- **WHEN** in der linken oberen Ecke der Darstellungsfläche die linke Maustaste gedrückt wird
- **THEN** trägt das Ereignis Zeile 1 und Spalte 1
#### Scenario: Reihenfolge bleibt erhalten
- **WHEN** eine Taste gedrückt und danach die Maus geklickt wird
- **THEN** liefert die Warteschlange erst das Tasten-, dann das Mausereignis
#### Scenario: Ereignis außerhalb der Fläche
- **WHEN** ein Mausereignis eine Position außerhalb der aktuellen Bildschirmgröße nennt
- **THEN** wird es verworfen und erreicht die Warteschlange nicht
#### Scenario: Kein Verbraucher, keine Anhäufung
- **WHEN** ein Programm ohne Verbraucher für Mausereignisse läuft und die Maus dauerhaft bewegt wird
- **THEN** wächst die Warteschlange nicht über ihre Grenze und das Programm läuft unverändert weiter
#### Scenario: Taste vor Maus bis zum Handler
- **WHEN** eine Taste vor einem Mausklick eintrifft und beide Handler eine Kennung anhängen
- **THEN** steht die Tastenkennung vor der Mauskennung
#### Scenario: Maus vor Taste bis zum Handler
- **WHEN** ein Mausklick vor einer Taste eintrifft
- **THEN** wird zuerst der Maushandler wirksam
### Requirement: Abschließende Liste der Zustellpunkte
Ereignisse SHALL ausschließlich an folgenden Punkten zugestellt werden
— das Vorbild prüft die Ereignismarke „before executing the next
statement":
an einer Anweisungsgrenze, bei `DOEVENTS`, während `SLEEP` und vor einer
blockierenden Eingabe. Zwischen zwei Zustellpunkten MUST NOT ein
Trap-Handler beginnen; insbesondere MUST NOT eine Anweisung in ihrer
Mitte unterbrochen werden. Ein zugestelltes Ereignis SHALL ein laufendes
`SLEEP` beenden.
Forms-Ereignisse SHALL auch während SLEEP an dessen bestehenden Zustellpunkten verarbeitet werden. Eine für das Menü geltende Zustellsperre SHALL an jedem Zustellpunkt gleichermaßen gelten.
#### Scenario: Keine Zustellung innerhalb einer Anweisung
- **WHEN** ein Ereignis fällig wird, während eine mehrteilige Anweisung ausgewertet wird
- **THEN** läuft die Anweisung zu Ende und der Handler beginnt erst an der folgenden Anweisungsgrenze
#### Scenario: SLEEP endet durch Ereignis
- **WHEN** ein Programm `SLEEP 60` ausführt und nach 2 Sekunden ein aktiver Trap fällig wird
- **THEN** wird der Handler ausgeführt und `SLEEP` kehrt danach zurück, ohne die vollen 60 Sekunden abzuwarten
#### Scenario: DOEVENTS stellt zu
- **WHEN** ein anstehendes Ereignis vorliegt und `DOEVENTS` ausgewertet wird
- **THEN** läuft der zugehörige Handler, bevor `DOEVENTS` einen Wert liefert
#### Scenario: Forms-Timer beendet SLEEP
- **WHEN** während SLEEP ein aktiver Forms-Timer fällig wird
- **THEN** läuft dessen Handler und SLEEP kehrt danach zurück
### Requirement: Getrappte Tasten verlassen den Eingabestrom
Eine Taste, für die ein aktiver `ON KEY`-Trap besteht, SHALL dem Trap
zugestellt werden und MUST NOT zusätzlich über `INKEY$`, `INPUT`,
`LINE INPUT` oder `INPUT$` sichtbar werden. Bei `OFF` oder `STOP` der
Quelle SHALL die Taste dem normalen Eingabestrom erhalten bleiben.
Unter STOP SHALL die Taste normal lesbar bleiben und zugleich höchstens ein Ereignis für das spätere ON gemerkt werden.
#### Scenario: Getrappte Taste erscheint nicht bei INKEY$
- **WHEN** `ON KEY(1) GOSUB Marke` mit `KEY(1) ON` gilt und F1 gedrückt wird
- **THEN** läuft der Handler und ein anschließendes `INKEY$` liefert den leeren String
#### Scenario: Nicht getrappte Taste bleibt im Strom
- **WHEN** derselbe Trap mit `KEY(1) OFF` gilt und F1 gedrückt wird
- **THEN** liefert `INKEY$` die Sondertastenfolge für F1
#### Scenario: STOP merkt und erhält Eingabe
- **WHEN** unter KEY(1) STOP F1 gedrückt, per INKEY$ gelesen und danach KEY(1) ON ausgeführt wird
- **THEN** liefert INKEY$ die Sondertastenfolge und der Handler läuft nach ON genau einmal
### Requirement: Signal-Trap auf Betriebssystemsignalen
`ON SIGNAL(n%) GOSUB` und die Steueranweisung `SIGNAL(n%)` SHALL
denselben Regeln für Maskierung, Zustellung und Wiedereintritt
unterliegen wie die übrigen Quellen. `n%` SHALL auf
Betriebssystemsignale abgebildet werden, und zwar ausschließlich auf die
Menge, die auf allen Zielplattformen besteht: 1 auf den
Unterbrechungswunsch (`SIGINT`), 2 auf den Beendigungswunsch
(`SIGTERM`). Ein anderer Wert MUST namentlich abgewiesen werden — zur
Übersetzungszeit, wenn er konstant ist, sonst mit Laufzeitfehler 5. Ein
Signal MUST NOT im Signalkontext verarbeitet werden; es SHALL über den
Host als Ereignis in die Warteschlange gelangen. Die Abweichung zur
OS/2-gebundenen Quelle des Vorbilds SHALL in der Sprachreferenz
ausgewiesen sein.
#### Scenario: Signal-Trap folgt der Maskierung
- **WHEN** `ON SIGNAL(1) GOSUB Marke` mit `SIGNAL(1) STOP` gilt und ein `SIGINT` eintrifft
- **THEN** läuft der Handler erst nach `SIGNAL(1) ON`, und zwar genau einmal
#### Scenario: Unzulässige Signalnummer
- **WHEN** ein Modul `ON SIGNAL(7) GOSUB Marke` enthält
- **THEN** wird es namentlich abgewiesen und die Meldung nennt den zulässigen Bereich
#### Scenario: Signal ohne Trap bleibt Abbruch
- **WHEN** ein `SIGINT` eintrifft, ohne dass ein `SIGNAL(1)`-Trap aktiv ist
- **THEN** bleibt es beim bisherigen Abbruchverhalten
#### Scenario: SIGTERM wird zugestellt
- **WHEN** SIGNAL(2) ON gilt und ein SIGTERM über den Host eintrifft
- **THEN** wird das Ziel von ON SIGNAL(2) GOSUB am nächsten erlaubten Zustellpunkt ausgeführt

View File

@@ -0,0 +1,53 @@
## MODIFIED Requirements
### Requirement: Fokus, Tabreihenfolge und Access-Keys
Der Fokus SHALL mit Tab in aufsteigender `TabIndex`-Folge und mit
Umschalt-Tab rückwärts wechseln; Elemente mit `TabStop = 0` oder
`Enabled = 0` MUST übersprungen werden. Ein `&` im Text SHALL den
folgenden Buchstaben zum Access-Key machen, der mit Alt das Element
auslöst oder ihm den Fokus gibt. Enter SHALL die `Default`-Schaltfläche
auslösen, Esc die `Cancel`-Schaltfläche. Fokuswechsel MUST `LostFocus`
am alten und `GotFocus` am neuen Element auslösen, in dieser Reihenfolge.
Die über das Terminal gelieferte Rückwärtstab-Taste SHALL denselben Fokuswechsel wie Tab mit Umschalt auslösen. Die Reihenfolge LostFocus vor GotFocus SHALL auch an den Wirkungen der BASIC-Handler sichtbar bleiben.
#### Scenario: Tab überspringt
- **WHEN** das mittlere von drei Elementen `TabStop = 0` trägt und Tab gedrückt wird
- **THEN** erhält das dritte Element den Fokus
#### Scenario: Access-Key
- **WHEN** eine Schaltfläche `Caption = "&OK"` trägt und Alt+O gedrückt wird
- **THEN** wird ihr `Click`-Ereignis ausgelöst
#### Scenario: Reihenfolge der Fokusereignisse
- **WHEN** der Fokus von `Text1` auf `Text2` wechselt
- **THEN** läuft erst `Text1_LostFocus`, danach `Text2_GotFocus`
#### Scenario: Handlerwirkungen in Fokusreihenfolge
- **WHEN** LostFocus den Text L und GotFocus den Text G an dieselbe Variable anhängen
- **THEN** lautet das Ergebnis LG
#### Scenario: Rückwärtstab vom Terminal
- **WHEN** das Terminal eine Rückwärtstab-Taste liefert
- **THEN** wechselt der Fokus zum vorherigen zulässigen TabIndex
### Requirement: Menüsystem
Ein Formular SHALL eine Menüleiste mit bis zu sechs Ebenen tragen.
Menüeinträge SHALL Access-Keys (`&`), Shortcuts, `Checked`, `Enabled`,
`Visible` und Separatoren (`-`) unterstützen; ein Separator MUST NOT
`Checked`, deaktiviert oder mit Shortcut versehen sein, ein Menütitel
MUST NOT einen Shortcut tragen. Solange ein Menü geöffnet ist, MUST die
Zustellung von Zeitereignissen und klassischen Traps ruhen und danach
fortgesetzt werden.
#### Scenario: Menüauswahl löst Click aus
- **WHEN** ein Menüeintrag über seinen Access-Key gewählt wird
- **THEN** läuft seine `Click`-Prozedur
#### Scenario: Traps ruhen im geöffneten Menü
- **WHEN** ein Menü geöffnet ist und ein Zeit-Trap fällig wird
- **THEN** läuft sein Handler erst, nachdem das Menü geschlossen wurde
#### Scenario: UEVENT an gewöhnlicher Anweisungsgrenze
- **WHEN** ein Menü offen ist und UEVENT vor einer Zuweisung ansteht
- **THEN** läuft dessen Handler erst nach dem Schließen des Menüs

View File

@@ -0,0 +1,20 @@
## MODIFIED Requirements
### Requirement: Zellenpuffer ohne Terminalabhängigkeit
Der Zellenpuffer mit der vollständigen Bildschirmsemantik (Cursor,
Farbattribute, Umbruch, Scrollen, Scrollbereich, Größenänderung) SHALL
ohne Terminal instanziierbar, veränderbar und auslesbar sein. Die
Anbindung an ein konkretes Terminal SHALL ausschließlich in der
Darstellungsschicht liegen. Damit MUST jedes Bildschirmverhalten in
automatischen Tests ohne Terminal prüfbar sein, und die Ausführungsschicht
MUST NOT von einer Terminal-Bibliothek abhängen.
Ein separater Einbetter SHALL die VM einschließlich Forms ohne Terminalbackend bauen und ausführen können. Der CLI-Runner SHALL das Backend ausdrücklich zuschalten.
#### Scenario: Bildschirmverhalten im Test ohne Terminal
- **WHEN** ein Testprogramm in einer Umgebung ohne Terminal `LOCATE 5, 10 : PRINT "x"` ausführt
- **THEN** trägt die Zelle (5, 10) das Zeichen `x` und der Test benötigt kein Terminal
#### Scenario: Terminalfreier Einbetter
- **WHEN** ein separates Programm ausschließlich die VM mit einem Capture-Host einbindet
- **THEN** enthält sein aufgelöster Abhängigkeitsbaum keine Terminalbibliothek und die Forms-Tests können darin laufen

View File

@@ -0,0 +1,24 @@
## MODIFIED Requirements
### Requirement: Ereigniszustellung an Anweisungsgrenzen
Die VM SHALL an Anweisungsgrenzen prüfen, ob ein Ereignis zuzustellen
ist, und in diesem Fall das `GOSUB`-Ziel des zugehörigen Traps
ausführen. Der unterbrochene Zustand — Aufrufkette, Schleifen, Locals,
`GOSUB`-Stapel und Fehlerzustand — MUST dabei erhalten bleiben; mit
`RETURN` SHALL die Ausführung an der unterbrochenen Stelle fortfahren.
Die Prüfung MUST NOT pro Opcode erfolgen; solange kein Trap definiert ist,
MUST sie auf einen einzelnen Test hinauslaufen und MUST NOT die Uhr lesen.
Ohne klassische Traps, aktive Forms-Timer oder zeitabhängige Mausauswertung SHALL auch die Forms-Integration keine Hostzeit abfragen.
#### Scenario: Trap aus einer Prozedur heraus
- **WHEN** ein Ereignis zugestellt wird, während eine `SUB` läuft, und das Trap-Ziel im Modulrumpf liegt
- **THEN** läuft der Handler und nach `RETURN` setzt die `SUB` an der unterbrochenen Anweisung fort
#### Scenario: Ohne aktiven Trap wird die Uhr nicht gelesen
- **WHEN** ein Programm ohne jede `ON …`-Trap-Definition ausgeführt wird
- **THEN** fragt die Ausführung an keiner Anweisungsgrenze die Zeit ab, und der Durchsatz bleibt im Rahmen der Messgenauigkeit unverändert
#### Scenario: Einfaches Programm liest keine Ereigniszeit
- **WHEN** ein Programm nur Zuweisungen und END ausführt und ein Testhost Zeitabfragen zählt
- **THEN** ist der Zähler am Ende 0

View File

@@ -0,0 +1,15 @@
## 1. Hosteingabe und Maskierung
- [ ] 1.1 SIGNAL(2), KEY STOP und BackTab am gemeinsamen Eingang berichtigen; verifizieren durch Tests für beide Signale, alle drei KEY-Zustände mit INKEY$/INPUT und den Terminal-Key-Mapper.
- [ ] 1.2 Geordnete Tasten-/Mausannahme implementieren; verifizieren mit K→M, M→K und gemischten Folgen einschließlich ungetrappter und getrappter Tasten.
## 2. Dispatch und Warten
- [ ] 2.1 Handlerbeginn/Rückkehr und Menüsperre an allen Zustellpunkten vereinheitlichen; verifizieren mit LostFocus→GotFocus, gleichzeitig wartenden Handlern, UEVENT bei offenem Menü, DOEVENTS und den bestehenden Fehler-/Modalitätstests.
- [ ] 2.2 Hostgesteuertes Warten und bedarfsgerechte Zeitabfrage implementieren; verifizieren mit einem Host, der unerwartete Uhrabfragen zählt/abweist, eingefrorener und vorrückender Zeit sowie einem Forms-Timer, der SLEEP beendet.
- [ ] 2.3 Terminal-Feature in tb-ui abgrenzen und CLI ausdrücklich anschließen; verifizieren durch isolierten terminalfreien VM-Einbetter, Dependency-Graph ohne crossterm/ratatui/signal-hook und normalen CLI-Build.
## 3. Abnahme
- [ ] 3.1 Gezielte geordnete Ereignisprogramme zum Korpus ergänzen; verifizieren mit cargo test --workspace und Vergleich der bestehenden Snapshots ohne automatisches Neuaufzeichnen.
- [ ] 3.2 Zeit-/Zustellverträge und Backend-Feature dokumentieren; verifizieren mit Compile-/VM-Benchmarks und openspec validate ereigniszustellung-und-hostgrenzen --strict.

View File

@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-09-05

View File

@@ -0,0 +1,32 @@
## Context
Die LISTINDEX-Prüfung konvertiert -1 vor dem oberen Grenzvergleich nach usize. Änderungen an INTERVAL/ENABLED löschen timer_last, dessen nächster Default 0 ist. Timerereignisse werden bereits über die bestehende Queue zugestellt. Siehe proposal.md und die Delta-Specs.
## Goals / Non-Goals
**Goals:** Gültige Übergänge an den bestehenden Property-/Listenmethoden korrigieren und Timerzustand an die Hostzeit koppeln.
**Non-Goals:** Keine neue Control-Bibliothek, keine Änderung der festgelegten Optik, keine zweite Ereignisschleife.
## Decisions
### D1 — Auswahl ist ein eigener gültiger Zustand
-1 vor dem oberen Grenzvergleich behandeln. Einfügungen vor dem ausgewählten Eintrag verschieben dessen Index; Entfernen des gewählten Eintrags hebt die Auswahl auf, Entfernen davor verschiebt sie zurück. ListBox.Text liefert ohne Auswahl leer, eine editierbare ComboBox behält ihren separaten Eingabetext.
### D2 — Aktivierungszeit statt Nullzeit
Beim Übergang auf einen aktiven Timer den aktuellen Hostzeitpunkt als Ausgangsfrist speichern. Ändernde Property-Zugriffe erhalten diesen Zeitpunkt über den bereits vorhandenen VM/Host-Pfad; kein Systemuhrzugriff und kein globaler Zeitcache. Interval=0/Enabled=0 entfernt anstehende Zustellungen dieses Timers. Explizites Umschalten des Intervalls beginnt eine neue Frist.
### D3 — Laufende Timer behalten Nachholregel
Ein kontinuierlich aktiver 100-ms-Timer bekommt bei 250 ms zwei Ereignisse; das gilt nur für Zeit innerhalb seiner aktiven Phase. Reihenfolge gleichzeitig fälliger Timer bleibt dokumentiert nach Namen und bei gleichem Arraynamen nach Index.
## Risks / Trade-offs
- Änderung an Auswahl kann ComboBox-Eingabetext löschen → Stilarten und editierbaren Text getrennt testen.
- Intervalländerung im eigenen Handler kann alte Queueeinträge übriglassen → Reset/Deaktivierung mit bereits wartenden Ereignissen prüfen.
## Migration Plan
Nach ereigniszustellung-und-hostgrenzen. Ändert nur die Requirements zu Listen und Timer, nicht Fokus/Menü desselben Capability-Pfads. Bestehende Spec-/Codeverträge erst nach erfolgreicher Umsetzung synchronisieren und archivieren. Bis dahin bleiben alle Tasks offen. Änderungen als zusammenhängenden Commit je Change integrieren; bei Fehlschlag auf den vorherigen Code zurückgehen und neue Datenformatversionen nicht mit alten Lesern öffnen.

View File

@@ -0,0 +1,24 @@
## Why
Listen und Timer erfüllen einfache gültige Zustandsübergänge nicht: ListIndex=-1 liefert Fehler 5; ein spät aktivierter Timer holt Zeit seit Programmstart nach. F12/F13 betreffen das Zustandsmodell unabhängig von dessen späterem Ereignisdispatch. Siehe [Review vom 05.09.2026](../../../docs/reviews/2026-09-05-openspec-code-review.md).
## What Changes
- Explizites Aufheben einer Listenauswahl unterstützen und List/Text/Count bei Einfügen/Entfernen konsistent halten.
- Timerfristen ab Aktivierung bzw. neu gesetztem Intervall starten und deaktivierte Zeiten nicht nachholen.
## Capabilities
### New Capabilities
Keine neuen Capability-Pfade.
### Modified Capabilities
- `forms-steuerelemente`: Listen-Auswahl und Zeitbezug des Timer-Steuerelements durch präzise Zustandsübergänge ergänzen.
## Impact
crates/tb-ui/src/forms.rs, Forms-Unit-/VM-Tests, Formular-Korpus und docs/forms-referenz.md. Baut auf der geordneten Ereigniszustellung auf.
Nach ereigniszustellung-und-hostgrenzen. Ändert nur die Requirements zu Listen und Timer, nicht Fokus/Menü desselben Capability-Pfads.

View File

@@ -0,0 +1,51 @@
## MODIFIED Requirements
### Requirement: Steuerelemente mit Listeninhalt
ListBox und ComboBox SHALL `ADDITEM` und `REMOVEITEM` unterstützen und
`List`, `ListCount`, `ListIndex` und `Text` konsistent führen; bei
`Sorted = -1` SHALL die Einfügereihenfolge der Sortierung folgen.
`ListIndex = -1` SHALL „keine Auswahl" bedeuten. Die ComboBox SHALL die
drei Stilarten (Dropdown, Simple, Dropdown List) darstellen.
Eine Zuweisung von ListIndex = -1 SHALL in leerer wie gefüllter Liste zulässig sein. Einfügen vor der Auswahl SHALL ihren Index verschieben, Entfernen der Auswahl SHALL sie aufheben. Eine ListBox ohne Auswahl SHALL Text als leeren String liefern; editierbare ComboBox-Stile SHALL ihren unabhängigen Eingabetext erhalten.
#### Scenario: Element hinzufügen
- **WHEN** `List1.ADDITEM "b"` und `List1.ADDITEM "a"` bei `Sorted = -1` ausgeführt werden
- **THEN** liefert `List1.List(0)` den Wert `a` und `List1.ListCount` den Wert 2
#### Scenario: Keine Auswahl
- **WHEN** eine ListBox ohne Auswahl gelesen wird
- **THEN** liefert `ListIndex` den Wert 1
#### Scenario: Auswahl ausdrücklich aufheben
- **WHEN** nach ADDITEM die Eigenschaft ListIndex auf -1 gesetzt wird
- **THEN** tritt kein Fehler auf und ListIndex ist -1
#### Scenario: Eintrag vor Auswahl einfügen
- **WHEN** vor einem ausgewählten Eintrag ein Element eingefügt wird
- **THEN** bleibt derselbe Eintrag ausgewählt und sein Index steigt um 1
### Requirement: Timer-Steuerelement
Ein Timer SHALL bei `Enabled = -1` und `Interval > 0` sein
`Timer`-Ereignis im eingestellten Abstand auslösen, gestützt auf die
Zeitquelle der Ereignissteuerung. `Interval = 0` SHALL ihn abschalten.
Sind mehrere Timer gleichzeitig fällig, SHALL die Reihenfolge festgelegt
und dokumentiert sein.
Die erste Frist SHALL ab dem Einschalten beziehungsweise neu gesetzten Intervall zählen. Zeit vor der Aktivierung MUST NOT nachgeholt werden. Deaktivierung oder Interval = 0 SHALL noch anstehende Timerereignisse verwerfen.
#### Scenario: Timer feuert im Abstand
- **WHEN** ein Timer mit `Interval = 100` läuft und die Zeit um 250 ms vorrückt
- **THEN** ist sein Ereignis zweimal gelaufen
#### Scenario: Interval 0 schaltet ab
- **WHEN** `Timer1.Interval = 0` gesetzt wird
- **THEN** läuft kein weiteres Ereignis
#### Scenario: Späte Aktivierung
- **WHEN** bei Hostzeit 1000 ms ein zuvor inaktiver Timer mit Interval 100 eingeschaltet wird
- **THEN** läuft bis 1099 ms kein Timerereignis und bei 1100 ms genau eines
#### Scenario: Abschalten verwirft anstehende Ereignisse
- **WHEN** Timerereignisse anstehen und vor ihrer Zustellung Interval auf 0 gesetzt wird
- **THEN** werden sie nicht mehr zugestellt

View File

@@ -0,0 +1,13 @@
## 1. Listen
- [ ] 1.1 ListIndex=-1 korrekt zulassen und Text/List/Count bei ADDITEM/REMOVEITEM konsistent halten; verifizieren mit leeren/sortierten Listen, Einfügen vor Auswahl, Entfernen der Auswahl und allen ComboBox-Stilen.
## 2. Timer
- [ ] 2.1 Aktivierungs-/Änderungszeit über den Host erfassen und deaktivierte Queueeinträge entfernen; verifizieren mit Aktivierung bei 1000 ms, erster Fälligkeit bei 1100 ms, Änderung im Handler und Interval=0.
- [ ] 2.2 Fortlaufende Timer und gleichzeitige Fälligkeit erhalten; verifizieren mit exakt zwei Ereignissen bei 250 ms sowie mehreren Timer-/Arraynamen und Wiederholbarkeit.
## 3. Abnahme
- [ ] 3.1 Formular-Korpus um Listenauswahl und späte Timeraktivierung ergänzen und Referenz dokumentieren; verifizieren mit cargo test -p tb-ui, cargo test -p tb-vm und cargo test -p tb-cli --test compat.
- [ ] 3.2 Delta-Specs und Diffs prüfen; verifizieren mit openspec validate forms-zustand-und-bedienung --strict und git diff --check.

View File

@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-09-05

View File

@@ -0,0 +1,37 @@
## Context
INPUT # liest stets eine ganze Zeile und ersetzt fehlende Felder durch leer. INPUT$ zählt Tastendrücke bzw. Dateibytes statt Zeichen. bi_cv prüft nur Mindestlänge. Zeitzone::ermitteln erfasst den Startzustand, versatz kann später still auf UTC fallen. Siehe proposal.md und die Delta-Specs.
## Goals / Non-Goals
**Goals:** Die vorhandenen Lese-/Konvertierungspfade mit erhaltener Position, klaren Fehlern und abfragbarem Zeitzustand korrigieren.
**Non-Goals:** Kein allgemeines CSV-Paket, keine Änderung der UTF-32-Recorddarstellung, kein Stellen der Betriebssystemuhr.
## Decisions
### D1 — Ein Lesefortschritt je Datei
Sequenzieller Dateiinput hält Feld-/Zeichenrest und Datei-/Decoderposition zusammen. INPUT # kann Felder derselben Zeile in mehreren Aufrufen lesen; fehlende geforderte Felder führen zu Fehler 62. LINE INPUT und INPUT$ müssen mit demselben logischen Fortschritt zusammenspielen, statt über getrennte Handles dieselben Bytes erneut zu lesen.
### D2 — Codepoints und Bytedarstellung trennen
INPUT$ zählt Unicode-Codepoints, behält den Rest einer erweiterten Tastenfolge und decodiert UTF-8 vor der Zeichenzählung. GET/PUT und MK/CV bleiben Byte-/UTF-32-Recordpfade; CV verlangt genau seine Zielbreite. Keine stillen Ersatzwerte für beschädigte Teilsequenzen.
### D3 — Fehler am Dateieinstieg zuordnen
Bei NotFound zwischen fehlendem Elternpfad und fehlender Datei unterscheiden; vorhandene allgemeine Fehlerabbildung wiederverwenden und die zusätzliche Pfadprüfung an den Dateisystemoperationen bündeln.
### D4 — Zonenzustand als kleiner lesbarer Vertrag
TIMEZONEKNOWN ohne Argument liefert INTEGER -1/0. Zeitwert und Status beruhen auf derselben jeweils gültigen Zonenermittlung; späterer Rückfall darf nicht beim Startzustand stehenbleiben. Rust-Einbetter können den Zustand weiter prüfen, CLI-Warnung bleibt ergänzend. Der Name ist die vorgeschlagene additive API dieses Changes und wird als TerminalBasic-Erweiterung dokumentiert.
## Risks / Trade-offs
- Gemischte INPUT-/INPUT$-/LINE INPUT-Aufrufe können Positionen verschieben → alle Übergänge auf derselben Datei und an CRLF/EOF testen.
- Strengere CV-Länge ändert bisher permissive Eingaben → ausdrücklich dokumentieren, Fehler 5 entspricht dem vorhandenen Spec-Vertrag.
- Zonenermittlung hängt vom System ab → Tests müssen Ermittlungsausfall und Wiederherstellung injizieren können, ohne die Rechnerzeitzone zu ändern.
## Migration Plan
Eigenständig; FREEFILE bleibt vollständig im ISAM-Change. TIMEZONEKNOWN ersetzt keine monotone Ereigniszeit und teilt deren Zustellpfad nicht. Bestehende Spec-/Codeverträge erst nach erfolgreicher Umsetzung synchronisieren und archivieren. Bis dahin bleiben alle Tasks offen. Änderungen als zusammenhängenden Commit je Change integrieren; bei Fehlschlag auf den vorherigen Code zurückgehen und neue Datenformatversionen nicht mit alten Lesern öffnen.

View File

@@ -0,0 +1,29 @@
## Why
Die Laufzeit verwirft INPUT-Felder, liest INPUT$ mit falscher Zeichenzählung und weicht bei CV-Längen sowie Dateifehlern ab. Zusätzlich fehlt die spezifizierte BASIC-Abfrage des Zeitzonenrückfalls (F14F17/F19). Siehe [Review vom 05.09.2026](../../../docs/reviews/2026-09-05-openspec-code-review.md).
## What Changes
- INPUT # als fortlaufenden Feldleser mit korrektem EOF-Verhalten führen.
- INPUT$ für Unicode-Codepoints und Teilfolgen von Sondertasten auf exakt n Zeichen begrenzen.
- Exakte CV-Argumentbreiten und Datei-/Pfadfehler 53/76 sicherstellen.
- Die verlangte Zonenzustandsabfrage als parameterlose INTEGER-Funktion TIMEZONEKNOWN konkretisieren: -1 bei ermittelter Zone, 0 beim UTC-Rückfall.
## Capabilities
### New Capabilities
Keine neuen Capability-Pfade.
### Modified Capabilities
- `datei-eio`: Fortlaufende Eingabefelder, exakte Record-Konvertierungsbreite und präzise Pfadfehler.
- `textbildschirm`: INPUT$ zählt genau n Codepoints in Terminal- und Dateieingabe.
- `konsolen-basisbibliothek`: Programmseitige Zonenzustandsabfrage und sichtbarer Rückfall auch nach dem Start.
- `sprach-frontend`: Signatur der konkretisierten Zonenzustandsfunktion.
## Impact
tb-runtime Datei-/Builtin-/Zeitmodule, tb-vm INPUT-Pfad, Frontend-Builtin/HIR-Zuordnung, Inventar und Eingabe-/Zeitzonen-Korpus. TIMEZONEKNOWN ist eine additive Konkretisierung des bestehenden Solls, keine Behauptung historischer BASIC-Kompatibilität.
Eigenständig; FREEFILE bleibt vollständig im ISAM-Change. TIMEZONEKNOWN ersetzt keine monotone Ereigniszeit und teilt deren Zustellpfad nicht.

View File

@@ -0,0 +1,84 @@
## MODIFIED Requirements
### Requirement: Sequenzielle Datenübertragung
`PRINT #` SHALL wie `PRINT` formatieren (Druckzonen, Zahlendarstellung mit
Vorzeichenspalte), `WRITE #` SHALL Elemente komma-getrennt mit Strings in
Anführungszeichen schreiben. `INPUT #` SHALL komma-getrennte Felder lesen
und dabei Anführungszeichen auswerten, `LINE INPUT #` die ganze Zeile ohne
Zeilenende. `EOF` SHALL das Erreichen des Dateiendes melden; Lesen über das
Dateiende hinaus MUST Laufzeitfehler 62 „Input past end of file" auslösen.
INPUT # SHALL nicht konsumierte Felder einer Zeile für spätere Aufrufe bewahren und bei Bedarf über Zeilen hinweg weiterlesen. Fehlende geforderte Felder am Dateiende MUST Fehler 62 auslösen statt als leere Ersatzwerte zu gelten.
#### Scenario: WRITE # und INPUT # sind gegenläufig
- **WHEN** `WRITE #1, "a,b", 5` geschrieben und die Zeile mit `INPUT #1, s$, n` gelesen wird
- **THEN** enthält `s$` den Text `a,b` und `n` den Wert 5
#### Scenario: Lesen über das Dateiende
- **WHEN** `INPUT #1, x` nach dem letzten Datensatz ausgeführt wird
- **THEN** tritt Laufzeitfehler 62 auf
#### Scenario: Felder über zwei Aufrufe
- **WHEN** WRITE #1, "a", "b" geschrieben und nach Wiederöffnung zweimal INPUT #1 mit je einer Stringvariablen ausgeführt wird
- **THEN** liefern die Aufrufe a und b
#### Scenario: Zu wenige Felder
- **WHEN** nur ein Feld vorhanden ist und INPUT #1 zwei Felder verlangt
- **THEN** tritt Fehler 62 auf
### Requirement: Record-Konvertierungsfunktionen
Die Bibliothek SHALL die Konvertierungsfunktionen für Recordpuffer
bereitstellen: `MKI$`, `MKL$`, `MKS$`, `MKD$` und `MKC$` wandeln einen
numerischen Wert in seine Byte-Darstellung als String, `CVI`, `CVL`,
`CVS`, `CVD` und `CVC` wandeln zurück. Jedes Paar MUST gegenläufig sein.
Ein Argument, dessen Länge nicht zur Zielbreite passt, MUST
Laufzeitfehler 5 „Illegal function call" auslösen. `MKSMBF$`, `MKDMBF$`,
`CVSMBF` und `CVDMBF` SHALL dieselbe Rolle für das
Microsoft-Binärformat älterer BASIC-Versionen erfüllen; ist dieses
Format ein deklariertes Non-Feature, MUST die Ablehnung namentlich
erfolgen und in der Sprachreferenz unter „Abweichungen" stehen.
Die Argumentlänge der CV-Funktionen SHALL exakt ihrer Zielbreite entsprechen, nicht nur einer Mindestbreite.
#### Scenario: MK und CV sind gegenläufig
- **WHEN** `CVL(MKL$(123456))` ausgewertet wird
- **THEN** ist das Ergebnis 123456
#### Scenario: Falsche Argumentlänge
- **WHEN** `CVI("x")` mit einem ein Zeichen langen String ausgewertet wird
- **THEN** tritt Laufzeitfehler 5 auf
#### Scenario: Zu langes CVI-Argument
- **WHEN** CVI("abc") ausgewertet wird
- **THEN** tritt Laufzeitfehler 5 auf
### Requirement: Dateisystem-Anweisungen mit plattformübergreifender Pfadsemantik
`KILL`, `NAME … AS`, `FILES`, `CHDIR`, `CHDRIVE`, `MKDIR`, `RMDIR`,
`CURDIR$`, `DIR$` sowie `SHELL` als Anweisung **und** als Funktion SHALL
verfügbar sein. `CURDIR$` SHALL das aktuelle Verzeichnis liefern, `DIR$`
den ersten bzw. — bei leerem Argument — den nächsten Treffer eines
Suchmusters und den leeren String, wenn keiner mehr folgt. Die
`SHELL`-Funktion SHALL den Beendigungsstatus des aufgerufenen Prozesses
liefern. `CHDRIVE` SHALL auf Plattformen ohne Laufwerksbuchstaben
namentlich abgewiesen oder als Non-Feature dokumentiert werden. Pfade SHALL sowohl `\` als auch `/` als Trennzeichen
akzeptieren und auf die Konvention der laufenden Plattform abgebildet
werden; Laufwerksbuchstaben SHALL auf Nicht-Windows-Plattformen als
gewöhnlicher Namensbestandteil behandelt oder mit benannter Ablehnung
abgewiesen werden. Fehlende Dateien MUST Fehler 53 auslösen, fehlende
Pfade Fehler 76 „Path not found".
#### Scenario: Rückwärts-Schrägstrich auf Unix
- **WHEN** `OPEN "unter\d.txt" FOR OUTPUT AS #1` auf einer Unix-Plattform ausgeführt wird
- **THEN** entsteht die Datei `d.txt` im Verzeichnis `unter`
#### Scenario: Verzeichnis durchlaufen
- **WHEN** `DIR$("*.txt")` und danach `DIR$("")` wiederholt ausgewertet werden
- **THEN** liefern sie nacheinander alle passenden Namen und zuletzt den leeren String
#### Scenario: Fehlender Pfad
- **WHEN** `MKDIR` in einem nicht existierenden Elternverzeichnis aufgerufen wird
- **THEN** tritt Laufzeitfehler 76 auf
#### Scenario: OPEN mit fehlendem Elternpfad
- **WHEN** OPEN FOR INPUT einen Dateinamen in einem nicht vorhandenen Elternverzeichnis verwendet
- **THEN** tritt Fehler 76 auf; fehlt nur die Datei in einem vorhandenen Verzeichnis, tritt Fehler 53 auf

View File

@@ -0,0 +1,23 @@
## MODIFIED Requirements
### Requirement: Zonenermittlung mit Rückfallebene
Die Laufzeit SHALL die Zeitzone des Rechners über die
Betriebssystemeinstellung bestimmen. Lässt sie sich nicht ermitteln,
SHALL UTC gelten und das Programm ohne Fehler weiterlaufen. Ob die
Rückfallebene greift, MUST für ein Programm erkennbar sein und in
docs/sprachreferenz.md beschrieben stehen; ein stiller Wechsel auf UTC
MUST NOT vorkommen.
Die parameterlose INTEGER-Funktion TIMEZONEKNOWN SHALL -1 für eine ermittelte Systemzone und 0 für den UTC-Rückfall liefern. Auch eine nach Programmstart scheiternde Zonenermittlung SHALL diesen Zustand sichtbar ändern; die Abfrage SHALL keine Zeitzone oder Systemuhr verstellen.
#### Scenario: Zone nicht ermittelbar
- **WHEN** die Zeitzone des Rechners nicht bestimmt werden kann
- **THEN** rechnen die Zeitfunktionen in UTC und das Programm läuft ohne Fehler weiter
#### Scenario: Rückfallebene ist erkennbar
- **WHEN** ein Programm den Zonenzustand abfragt
- **THEN** unterscheidet die Antwort erkennbar zwischen ermittelter Zone und Rückfall auf UTC
#### Scenario: Rückfall nach Programmstart
- **WHEN** zunächst eine Zone ermittelt wurde, eine spätere Ermittlung aber scheitert
- **THEN** verwenden die Zeitfunktionen UTC und TIMEZONEKNOWN liefert 0

View File

@@ -0,0 +1,27 @@
## MODIFIED Requirements
### Requirement: Jedes dokumentierte Element besitzt eine Signatur
Die Semantik SHALL für jedes im Sprachinventar geführte Element eine
Signatur besitzen (Argumentanzahl und -typen) — auch für Elemente, deren
Laufzeitverhalten noch aussteht. Ein im Inventar geführtes Element MUST
NOT als unbekannter Bezeichner oder mit einem generischen Syntaxfehler
abgewiesen werden; die Diagnose bzw. der Laufzeitfehler MUST es
namentlich nennen (Guiding Principle). Das gilt insbesondere für die
Gruppen, die bisher gar keine Signatur haben: die finanzmathematischen
Funktionen, die Record-Konvertierungsfunktionen (`MK…$`/`CV…`), die
Dateisystem-Funktionen `CURDIR$`, `DIR$`, `CHDRIVE`, sowie `LPOS`,
`STACK`, `SYSTEM`, `ERDEV`/`ERDEV$` und `SetFormatCC`.
Die TerminalBasic-Erweiterung TIMEZONEKNOWN SHALL als parameterlose INTEGER-Funktion eine Signatur besitzen und im Inventar mit ihrer tatsächlichen Umsetzung geführt sein.
#### Scenario: Element ohne Laufzeitverhalten wird namentlich abgewiesen
- **WHEN** ein Modul eine im Inventar als `offen` geführte Funktion verwendet
- **THEN** nennt die Ablehnung deren Namen und nicht „unbekannter Bezeichner"
#### Scenario: Argumentprüfung ohne Laufzeitverhalten
- **WHEN** ein Modul `MKL$()` ohne Argument enthält
- **THEN** meldet die Semantik eine Argumentanzahl-Diagnose, die `MKL$` nennt
#### Scenario: Signatur der Zonenzustandsabfrage
- **WHEN** PRINT TIMEZONEKNOWN übersetzt wird
- **THEN** entsteht eine INTEGER-Funktionsabfrage; TIMEZONEKNOWN(1) wird mit benannter Argumentanzahl-Diagnose abgewiesen

View File

@@ -0,0 +1,26 @@
## MODIFIED Requirements
### Requirement: Tastatureingabe ohne Zeilenmodell
`INKEY$` SHALL ohne zu blockieren die nächste anstehende Taste liefern:
den leeren String bei leerem Puffer, ein Zeichen bei einer
Zeichentaste, eine zwei Zeichen lange Folge mit führendem Nullzeichen bei
einer Sondertaste. `INPUT$(n [, #dateinummer])` SHALL genau `n` Zeichen
lesen und dabei blockieren, ohne sie am Bildschirm zu wiederholen.
INPUT$ SHALL n Unicode-Codepoints liefern. Nicht verbrauchte Zeichen einer Sondertastenfolge und noch nicht gelesene Dateizeichen SHALL für Folgeaufrufe erhalten bleiben. Dateieingabe SHALL UTF-8 vor der Zeichenzählung decodieren.
#### Scenario: INKEY$ bei leerem Tastaturpuffer
- **WHEN** `INKEY$` ohne anstehende Taste ausgewertet wird
- **THEN** liefert es den leeren String und blockiert nicht
#### Scenario: Sondertaste als zwei Zeichen
- **WHEN** F1 gedrückt wurde und `INKEY$` ausgewertet wird
- **THEN** hat das Ergebnis die Länge 2 und beginnt mit dem Nullzeichen
#### Scenario: Sondertastenfolge in zwei Reads
- **WHEN** F1 ansteht und zweimal INPUT$(1) ausgeführt wird
- **THEN** liefert der erste Aufruf das Nullzeichen und der zweite den Scancode, jeweils genau ein Zeichen
#### Scenario: Unicode aus Datei
- **WHEN** eine UTF-8-Datei mit ä beginnt und INPUT$(1,#1) aufgerufen wird
- **THEN** liefert der Aufruf ä ohne Ersatzzeichen

View File

@@ -0,0 +1,14 @@
## 1. Eingabe und Recordfunktionen
- [ ] 1.1 Fortlaufenden INPUT-#-Lesefortschritt implementieren; verifizieren mit zwei Einzelreads nach WRITE zweier Felder, mehreren Zeilen, Quotes/Kommas und Fehler 62 bei zu wenigen Feldern.
- [ ] 1.2 INPUT$ auf exakt n Codepoints führen und Reste erhalten; verifizieren mit F1 und zwei INPUT$(1)-Aufrufen, UTF-8 ä/Emoji aus Datei, gemischten Lesearten und EOF.
- [ ] 1.3 CV-Zielbreiten exakt prüfen; verifizieren mit allen fünf MK/CV-Paaren und jeweils zu kurzen/passenden/zu langen Argumenten.
- [ ] 1.4 Datei-/Elternpfadfehler unterscheiden; verifizieren mit fehlender Datei in vorhandenem Verzeichnis, fehlendem Elternpfad und bestehenden erfolgreichen Dateisystemfällen.
## 2. Zonenzustand
- [ ] 2.1 TIMEZONEKNOWN durch Frontend, HIR, Dispatch und Inventar ergänzen und dynamischen Rückfall sichtbar halten; verifizieren mit ermittelter Zone, UTC-Rückfall, späterem Ausfall/Wiederherstellung und unverändertem DATE$/TIME$/NOW/TIMER-Versatz.
## 3. Abnahme
- [ ] 3.1 Sprach-/Bibliotheksreferenz und Eingabe-/Zonenkorpus ergänzen; verifizieren mit cargo test --workspace, unveränderten vorhandenen Snapshots und openspec validate laufzeit-eingabe-und-systemstatus --strict.

View File

@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-09-05

View File

@@ -0,0 +1,37 @@
## Context
input_sources/append_project_source erzeugen einen einzigen String, doppelte Definitionen werden teilweise entfernt. FormCatalog.find benutzt den ersten globalen Namen. PROC enthält Code/Zeilen inline, OBJS nur Objektstammdaten. Das CLI-build-Ergebnis verwirft die separaten FormFile-Werte. Siehe proposal.md und die Delta-Specs.
## Goals / Non-Goals
**Goals:** Den vorhandenen Compilerpipeline-Vertrag auf ein vollständiges Projekt erweitern und daraus ein allein ausführbares Artefakt liefern.
**Non-Goals:** Keine IDE-Oberfläche, keine native Executable-Verpackung, keine Binärkompatibilität zu alten unvollständigen TBC-Versionen.
## Decisions
### D1 — Quellidentität vor Expansion sichern
Projektmitglieder und Include-Segmente als Quellen mit stabiler ID führen; physische Datei/Zeile/Spalte getrennt von BASICs numerischem ERL-Label bewahren. Kein nachträgliches Zurückrechnen aus der verketteten Zeilenzahl. Einmodul-APIs können den Projektpfad mit einem Mitglied aufrufen.
### D2 — Namen innerhalb ihres Containers auflösen
Form- und Control-IDs bleiben numerisch; Lookup sucht zuerst im expliziten Formular/aktuellen Modul. Modulprozeduren erhalten qualifizierte IDs, Form_Load pro Formular eine eigene Bindung. Doppelte lokale Namen dürfen nicht durch globales Deduplizieren verschwinden; echte Duplikate im selben Scope melden.
### D3 — Selbstständiges Kompilat
Vor dem Serialisieren das vollständige Forms-Anfangsmodell mit Werten, Indizes und Eltern-IDs aufnehmen. Lesen rekonstruiert dieselben Anfangswerte; CLI run akzeptiert das neue TBC direkt. Normale Projektdateien müssen nach build zum Ausführen nicht mehr verfügbar sein.
### D4 — Formatrevision statt zusätzlicher Scheinabschnitte
Inline-Code und Inline-Quellortinstruktionen beibehalten, aber vollständige Quell-/Modultabelle und Prozedursignaturen hinzufügen. Version 4 reservieren, vorhandene Kennungen dokumentieren und OBJS um vollständige Anfangsdaten erweitern. Kein separater CODE/LINES-Abschnitt allein zur Erfüllung einer veralteten Strukturvorgabe. Unbekannte/ältere inkompatible Versionen klar ablehnen.
## Risks / Trade-offs
- Scope-Änderungen können bisher zufällig verbundene globale Namen verändern → Mehrmodulprogramme mit SHARED/COMMON, wiederholten Includes und gleichnamigen privaten Prozeduren prüfen.
- Verlust von ERL-Semantik durch neue Quellorte → numerische Labels und physische Dateizeilen ausdrücklich getrennt testen.
- Containeränderung kann fremde Dateien fehlinterpretieren → Längen/IDs/Versionen beim Laden prüfen und Roundtrip-Ausführung mit entfernten Quellen testen.
## Migration Plan
Eigenständiger Compiler-/Container-Change. Nach Ereigniszustellung integrieren, falls beide interp.rs ändern. F24 und alle Bytecode-Requirement-Blöcke gehören ausschließlich hierher. Bestehende Spec-/Codeverträge erst nach erfolgreicher Umsetzung synchronisieren und archivieren. Bis dahin bleiben alle Tasks offen. Änderungen als zusammenhängenden Commit je Change integrieren; bei Fehlschlag auf den vorherigen Code zurückgehen und neue Datenformatversionen nicht mit alten Lesern öffnen.

View File

@@ -0,0 +1,30 @@
## Why
Mehrere Module/Formulare werden zu einem Quelltext und globalen Objektraum zusammengelegt. Dadurch scheitern formularlokale Namen, Fehlerorte stimmen nicht und FRM-Anfangswerte fehlen nach dem Kompilat-Rundlauf (F09F11); Containerdokumentation und Spec sind außerdem überholt (F24). Siehe [Review vom 05.09.2026](../../../docs/reviews/2026-09-05-openspec-code-review.md).
## What Changes
- Projekt- und Include-Quellen mit ursprünglichem Modul/Datei/Zeile erhalten und Prozeduren/Controls im jeweiligen Namensraum auflösen.
- Quellorte und Breakpoints mit Modulidentität durch Frontend, Kompilat und VM führen.
- Formularbeschreibungen und Anfangswerte in das eigenständig ladbare Kompilat aufnehmen.
- **BREAKING**: Kompilatversion für das vollständige Projektmodell erhöhen; ältere Versionen weiterhin klar mit Versionsnennung ablehnen.
- Container-/Opcode-Spezifikation und Dokumentation an das tatsächlich vereinbarte Format angleichen.
## Capabilities
### New Capabilities
Keine neuen Capability-Pfade.
### Modified Capabilities
- `bytecode-kompilat`: Eigenständiges Projektkompilat, vollständige Quellorte und dokumentiertes Containerformat.
- `sprach-frontend`: Projektübergreifende Diagnostik und formularlokale Objektauflösung.
- `forms-objektmodell`: Gleichnamige Controls unterschiedlicher Formulare separat adressieren.
- `vm-ausfuehrung`: Breakpoints und Inspektion über Modulidentität ermöglichen.
## Impact
CLI input_sources/expand_includes, Frontend SourcePos/HIR/FormCatalog, Codegen/bytecode/interp, FRM-Anwendung, Tests und docs/tbvm-design.md/dateiformate.md. Kein neuer Linker als separater Prozess.
Eigenständiger Compiler-/Container-Change. Nach Ereigniszustellung integrieren, falls beide interp.rs ändern. F24 und alle Bytecode-Requirement-Blöcke gehören ausschließlich hierher.

View File

@@ -0,0 +1,59 @@
## MODIFIED Requirements
### Requirement: Monomorpher Opcode-Satz
Der Codegenerator SHALL jedes HIR-Konstrukt in typisierte, monomorphe
Opcodes übersetzen: Arithmetik, Vergleiche und Konvertierungen tragen den
Operandentyp im Opcode (kein Tag-Dispatch über Werte zur Laufzeit);
implizite Konvertierungen erscheinen als explizite Konvertierungs-Opcodes
an den vom HIR bestimmten Stellen. Der Opcode-Satz SHALL in
docs/tbvm-design.md vollständig dokumentiert sein.
Die Dokumentation SHALL sämtliche tatsächlich serialisierbaren Instruktionen einschließlich Forms, Ereignissteuerung und Dateioperationen mit Operanden und Encoding führen.
#### Scenario: Gemischter Ausdruck wird monomorph
- **WHEN** `d# = i% + 1.5#` übersetzt wird (INTEGER-Variable, DOUBLE-Ziel)
- **THEN** enthält der Bytecode einen Konvertierungs-Opcode INTEGER→DOUBLE und eine DOUBLE-Addition, keinen generischen Additions-Opcode
#### Scenario: Namen sind zur Laufzeit aufgelöst
- **WHEN** eine Variable oder Prozedur im Bytecode referenziert wird
- **THEN** geschieht das über Slot- bzw. Tabellenindizes, nicht über Namens-Lookups
### Requirement: `.tbc`-Containerformat
Das Kompilat SHALL als `.tbc`-Datei serialisierbar und wieder ladbar sein:
Magic `TBC\0`, Formatversion und dokumentierte Abschnittstabelle.
Der Container SHALL Quellen und Module, Konstantenpool, TYPE-Layouts,
modulqualifizierte Prozeduren mit vollständiger Signatur, Locals und Code,
DATA sowie die vollständigen Forms-Anfangsbeschreibungen führen.
Code und Quellortinformationen SHALL inline in den Prozeduren liegen
dürfen; eigenständige CODE-/LINES-Abschnitte sind nicht erforderlich. Laden und erneutes Serialisieren MUST verlustfrei sein;
eine unbekannte Formatversion MUST mit einer klaren Fehlermeldung
abgewiesen werden.
Ein aus FRM- oder Projektquellen gebautes Kompilat SHALL ohne erneuten Zugriff auf diese Quellen ausführbar sein und ihre Anfangseigenschaften und Designzeit-Control-Arrays bewahren.
#### Scenario: Roundtrip
- **WHEN** ein kompiliertes Modul als `.tbc` geschrieben und wieder geladen wird
- **THEN** ist das geladene Kompilat funktional identisch (gleiche Ausführung, gleiche Zeilenzuordnung)
#### Scenario: Unbekannte Version
- **WHEN** eine `.tbc`-Datei mit höherer Formatversion geladen wird
- **THEN** wird das Laden mit einer Meldung abgelehnt, die die Version nennt
#### Scenario: Formularwerte nach Kompilat-Rundlauf
- **WHEN** ein Formular mit Text1.Text = "hello" kompiliert, gespeichert und ohne Quelldatei geladen wird
- **THEN** liefert der erste Zugriff auf Text1.Text weiterhin hello
### Requirement: Zeilentabelle für Fehlerortung
Der Bytecode SHALL jede Anweisung ihrem Ursprung (Moduldatei, Zeile)
zuordnen, sodass Laufzeitfehlermeldungen, `ERL`, Breakpoints und
Einzelschritt die Quellzeile exakt benennen können.
Die Zuordnung SHALL auch nach Include-Expansion und bei mehreren Projektmitgliedern die ursprüngliche Datei und physische Zeile bewahren. Numerische BASIC-Zeilenlabels für ERL SHALL davon getrennt bleiben.
#### Scenario: Fehler nennt Zeile
- **WHEN** in Zeile 42 eines Programms ein Laufzeitfehler ohne Handler auftritt
- **THEN** nennt die Fehlermeldung Zeile 42
#### Scenario: Fehler aus Projektbibliothek
- **WHEN** lib.bas in physischer Zeile 2 einen Fehler auslöst und über app.mak aufgerufen wird
- **THEN** nennt die Meldung lib.bas und Zeile 2 statt einer verketteten Projektzeile

View File

@@ -0,0 +1,27 @@
## MODIFIED Requirements
### Requirement: Formulare und Steuerelemente als benannte Objekte
Ein Formular SHALL unter seinem Namen ansprechbar sein, ein
Steuerelement unter seinem Namen innerhalb seines Formulars. Eigenschaften
SHALL über `objekt.eigenschaft` gelesen und geschrieben werden, mit dem
in der Forms-Referenz festgelegten Typ und Wertebereich. Es SHALL keine
Standardeigenschaft geben: `Text1 = "x"` MUST abgewiesen werden. Das
Schreiben eines Wertes außerhalb des Wertebereichs MUST Laufzeitfehler 5
auslösen; eine zur Laufzeit schreibgeschützte Eigenschaft MUST bei
Zuweisung einen Fehler auslösen.
#### Scenario: Eigenschaft lesen und schreiben
- **WHEN** `Text1.Text = "hallo"` ausgeführt und danach `Text1.Text` gelesen wird
- **THEN** liefert der Zugriff `hallo`
#### Scenario: Keine Standardeigenschaft
- **WHEN** ein Programm `Text1 = "x"` enthält
- **THEN** wird es abgewiesen und die Meldung nennt `Text1`
#### Scenario: Wert außerhalb des Bereichs
- **WHEN** `Check1.Value = 3` ausgeführt wird
- **THEN** tritt Laufzeitfehler 5 auf
#### Scenario: Getrennte Werte gleichnamiger Controls
- **WHEN** Form1!Text1.Text auf a und Form2!Text1.Text auf b gesetzt werden
- **THEN** liefern die beiden Zugriffe unabhängig a und b

View File

@@ -0,0 +1,50 @@
## MODIFIED Requirements
### Requirement: Diagnostik mit Vorbild-Meldungen
Diagnosen SHALL exakte 1-basierte Quelltextpositionen (Zeile, Spalte)
tragen; die Meldungstexte SHALL dem Katalog des Vorbilds folgen, wo eine
Entsprechung existiert (z. B. „Type mismatch", „Duplicate definition",
„Label not defined", „Subprogram not defined", „Overflow").
Bei Projekten und Includes SHALL jede Diagnose den ursprünglichen Dateinamen mit dessen physischer Zeile und Spalte tragen.
#### Scenario: Position in der Diagnose
- **WHEN** in Zeile 3 ein Typkonflikt auftritt
- **THEN** nennt die Diagnose Zeile 3 und die Spalte des Ausdrucks
#### Scenario: Include-Diagnose
- **WHEN** ein eingebundenes Include einen Typfehler enthält
- **THEN** nennt die Diagnose die Include-Datei und ihre eigene Quellposition
### Requirement: Objektzugriffe in Grammatik und Semantik
Der Parser SHALL Punktpfade auf Formulare und Steuerelemente
(`objekt.eigenschaft`), den Container-Operator
`form!steuerelement[.eigenschaft]`, `IF TYPEOF ausdruck IS klasse`, den
Metabefehl `$FORM` und die Datentypen `FORM` und `CONTROL` als
Parametertypen erkennen. Die Semantik SHALL Eigenschaften gegen die
Klasse des Objekts prüfen und ihren Typ bestimmen. Ein Punktpfad, dessen
Basis weder ein benutzerdefinierter Typ noch ein bekanntes Objekt ist,
MUST eine Diagnose erzeugen, die Basis und Eigenschaft namentlich nennt;
er MUST NOT zu einer impliziten Variablen mit Punkt im Namen werden.
Gleichnamige Steuerelemente unterschiedlicher Formulare SHALL im jeweiligen Formularnamensraum aufgelöst werden; die explizite Containerangabe SHALL vor der lokalen Namenssuche wirken.
#### Scenario: Unbekanntes Objekt wird benannt
- **WHEN** ein Modul `Text9.Text = "a"` ohne Steuerelement `Text9` enthält
- **THEN** nennt die Diagnose `Text9` als unbekanntes Objekt statt „Type mismatch" zu melden
#### Scenario: Unbekannte Eigenschaft wird benannt
- **WHEN** ein Modul `Text1.Farbe = 3` enthält
- **THEN** nennt die Diagnose `Farbe` als unbekannte Eigenschaft von `TextBox`
#### Scenario: Eigenschaftstyp wird geprüft
- **WHEN** ein Modul `Text1.Text = 5` enthält
- **THEN** meldet die Semantik einen Typkonflikt mit Nennung der Eigenschaft
#### Scenario: TYPEOF
- **WHEN** ein Modul `IF TYPEOF Ziel IS CommandButton THEN` enthält
- **THEN** wird es übersetzt und die Klasse gegen die bekannten Steuerelementklassen geprüft
#### Scenario: Gleichnamige Controls verschiedener Formulare
- **WHEN** Form1 und Form2 jeweils Text1 enthalten und Form2!Text1.Text gelesen wird
- **THEN** wird Text1 in Form2 aufgelöst und das Programm übersetzt diagnosefrei

View File

@@ -0,0 +1,24 @@
## MODIFIED Requirements
### Requirement: Unterbrechbare step()-Ausführung
Die VM SHALL als zustandsbehaftete, vom Einbetter getriebene Struktur
ausführbar sein: An Anweisungsgrenzen prüft sie ein Flag-Wort und kann
anhalten (Breakpoint auf (Modul, Zeile), Einzelschritt, Abbruchwunsch).
Im angehaltenen Zustand MUST der Einbetter Variableninhalte (Skalare,
Arrayelemente, UDT-Felder) inspizieren und die Ausführung fortsetzen
können. Die Tick-Prüfung erfolgt nur an Anweisungsgrenzen, nicht pro
Opcode.
Ein Breakpoint SHALL durch Modulidentität und physische Quellzeile bestimmt sein. Gleiche Zeilennummern anderer Module MUST NOT denselben Breakpoint auslösen.
#### Scenario: Breakpoint hält an
- **WHEN** ein Breakpoint auf Zeile 10 gesetzt ist und die Ausführung Zeile 10 erreicht
- **THEN** hält die VM vor der Anweisung an und meldet Modul und Zeile
#### Scenario: Inspektion im Halt
- **WHEN** die VM an einem Breakpoint steht
- **THEN** liefert die Inspektions-API den aktuellen Wert einer benannten Variablen
#### Scenario: Gleiche Zeile in zwei Modulen
- **WHEN** ein Breakpoint auf Modul A, Zeile 10 gesetzt ist und Modul B Zeile 10 ausführt
- **THEN** läuft B weiter; erst A Zeile 10 hält an

View File

@@ -0,0 +1,15 @@
## 1. Quellen und Scopes
- [ ] 1.1 Quell-/Modulidentitäten durch Include-Expansion und Frontend erhalten; verifizieren mit Compile- und Runtimefehlern aus lib.bas:2 und verschachtelten Includes.
- [ ] 1.2 Form-/Prozedurscopes korrekt auflösen; verifizieren mit zwei Form_Load-Prozeduren, zwei Text1-Controls und unterschiedlichen Form1!Text1/Form2!Text1-Werten sowie echten Duplikaten im selben Scope.
## 2. Kompilat und Debugger
- [ ] 2.1 Vollständige Forms-Anfangsdaten, Quelltabelle und Prozedursignaturen serialisieren; verifizieren durch bytegleichen Kompilat-Rundlauf und gleiche Ausführung inklusive Designzeit-Control-Arrays.
- [ ] 2.2 Modulbezogene Breakpoints/Quellortmeldungen ergänzen; verifizieren mit identischen physischen Zeilennummern in zwei Modulen, Include-Fehlern, Einzelschritt und unverändertem ERR/ERL.
- [ ] 2.3 CLI-run für TBC anschließen und Versionsgrenze anheben; verifizieren mit build, Entfernen aller Quellen, run des Kompilats und klarer Ablehnung inkompatibler Versionen.
## 3. Abnahme
- [ ] 3.1 Container und vollständige Opcode-/Operandentabelle dokumentieren; verifizieren mit Format-/Opcode-Abgleich, cargo test --workspace und dem Compile-Budget.
- [ ] 3.2 Delta-Specs vollständig gegen die erhaltenen Szenarien prüfen; verifizieren mit openspec validate projektmodule-und-kompilat --strict und git diff --check.

View File

@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-09-05

View File

@@ -0,0 +1,37 @@
## Context
pruefe_forms erwartet jede Klasseneigenschaft und jedes Ereignis pauschal als implementiert. Der Formularfallback in handle_key erzeugt nur KEYDOWN, während FORM_KEYPRESS/FORM_KEYUP implementiert heißen. traps.bas und formular.frm fehlen die genannten Kombinationen. write_text besitzt bereits einen Erhaltungsmodus. Siehe proposal.md und die Delta-Specs.
## Goals / Non-Goals
**Goals:** Grüne Prüfungen an beobachtbares Sollverhalten binden und veraltete Normtexte gezielt berichtigen.
**Non-Goals:** Kein erneutes allgemeines Refactoring, kein ungezieltes Testframework, kein Aufweichen der übrigen Spezifikationen, keine Änderung des Bytecode-Vertrags dieses Changes (Eigentum projektmodule-und-kompilat).
## Decisions
### D1 — Vorhandenen Inventartest erweitern
Inventareinträge weiterhin aus der Markdowntabelle lesen, aber für implementiert mindestens erzeugbare Frontend-/HIR-Pfade ohne Unsupported und auflösbare Runtime-Ziele prüfen. Syntaxformen mit expliziten kleinen Programmvorlagen abdecken; Forms-Probequellen aus Klassen-/Signaturmetadaten erzeugen. Klassentabellen allein gelten nicht als Laufzeitnachweis. Für Ereignisse reale Auslöser bis zum BASIC-Handler prüfen; die konkret fehlenden Form-KeyPress/KeyUp ergänzen, nicht den Status auf offen setzen.
### D2 — Korpusfälle nach fehlender Bedingung hinzufügen
Bestehenden CaptureHost/Harness wiederverwenden; jede Maskierungsquelle in ON/OFF/STOP, EVENT OFF, Control-Alt-Key, Shift-Tab und Fokusreihenfolge mit deklarierter Ereignis-/Zeitfolge prüfen. Sollbilder werden aus erwarteter Semantik festgelegt und bei Änderung fachlich geprüft.
### D3 — Bestehende Produktentscheidungen synchronisieren
Nur LINE/PAINT/VIEW/SCREEN 013 aus der pauschalen Grafikablehnung ausnehmen; verbleibende Non-Features behalten. FRM: unverändertes Lesen/Schreiben erhält die Originalbytes; neues/geändertes kanonisches Schreiben lässt Defaults aus. Keine Änderung des funktionierenden Erhaltungsmodus nötig.
### D4 — Abschließender Review als Befundnachweis
Nach den fünf Korrektur-Changes jede F-ID mit konkretem Regressionstest/Prüfergebnis versehen. Den Bericht von 05.09. als historischen Ausgangsstand erhalten und eine eigene Ergebnisdatei anlegen. Den vorhandenen öffentlichen Fremdprogrammbestand mit seiner dokumentierten Revision verwenden; bei fehlendem Bestand Nachweis ausdrücklich offenlassen statt eine erfolgreiche Bedienung zu behaupten.
## Risks / Trade-offs
- Generierte Tests spiegeln wieder nur Implementierungsmetadaten → erwartete Ergebnisse für kritische Ereignisse und negative Markerfälle unabhängig in kleinen Quellen festhalten.
- Überlappung mit funktionalen Korrekturen → diese zuerst integrieren und nur die hier benannten verbleibenden Eventquellen ändern.
- Fremdprogramme können Hardware-Non-Features außerhalb des akzeptierten Bestands enthalten → dokumentierten Prüfbestand/Revision festhalten, keine globale Kompatibilitätszusage daraus ableiten.
## Migration Plan
Abschließende Gesamtabnahme nach allen fünf anderen Changes. Die Grafik-/FRM-Textpräzisierungen können vorher überprüft werden; Archivierung und Gesamtabschluss erst nach deren Regressionen. Bestehende Spec-/Codeverträge erst nach erfolgreicher Umsetzung synchronisieren und archivieren. Bis dahin bleiben alle Tasks offen. Änderungen als zusammenhängenden Commit je Change integrieren; bei Fehlschlag auf den vorherigen Code zurückgehen und neue Datenformatversionen nicht mit alten Lesern öffnen.

View File

@@ -0,0 +1,30 @@
## Why
Der aktuelle Inventarabgleich bestätigt überwiegend Tabellen gegen dieselben Tabellen, während konkrete Formularereignisse fehlen. Korpusabdeckung und Hauptspecs sind nach den jüngsten Features nicht vollständig nachgezogen (F21F23/F25). Siehe [Review vom 05.09.2026](../../../docs/reviews/2026-09-05-openspec-code-review.md).
## What Changes
- Inventarabgleich auf tatsächlich erreichbare Frontend-/Laufzeitpfade und benannte negative Nachweise stützen; fehlende deklarierte Form_KeyPress/Form_KeyUp-Pfade ergänzen.
- Explizite Korpusfälle für KEY/UEVENT-Maskierung, Control-Access-Key, Rückwärtstab und geordnete Formularereignisse ergänzen.
- Grafik-Non-Features mit der bereits dokumentierten implementierten Untermenge abgleichen.
- FRM-Schreibvertrag in Erhaltung unveränderter Quelldateien und kanonische Ausgabe präzisieren.
- Den Review nach Umsetzung der fünf Korrektur-Changes erneut gegen jeden Befund abgleichen.
## Capabilities
### New Capabilities
Keine neuen Capability-Pfade.
### Modified Capabilities
- `sprachinventar`: Inventarstatus mit unabhängigem Frontend-/Laufzeitnachweis absichern.
- `kompat-testkorpus`: Verlangte Ereigniskombinationen und Fremdprogramm-Nachweise systematisch erreichbar machen.
- `sprach-frontend`: Grafik-Ausnahmen zur Non-Feature-Abweisung korrekt beschreiben.
- `forms-dateiformat`: Byteerhalt und kanonisches Weglassen von Defaultwerten klar unterscheiden.
## Impact
Inventar-/Korpustests, konkrete Formular-Eventquellen, docs/inventar.md, Sprach-/Forms-/Dateiformatreferenzen. Keine pauschale Statusherabsetzung, keine automatische Erzeugung neuer Golden Files.
Abschließende Gesamtabnahme nach allen fünf anderen Changes. Die Grafik-/FRM-Textpräzisierungen können vorher überprüft werden; Archivierung und Gesamtabschluss erst nach deren Regressionen.

View File

@@ -0,0 +1,22 @@
## MODIFIED Requirements
### Requirement: Schreiben ist die Umkehrung des Lesens
Das unveränderte Schreiben einer gelesenen Formularbeschreibung SHALL
die Originaldatei bytegleich erhalten, einschließlich explizit
geschriebener Vorgabewerte. Beim kanonischen Schreiben einer neuen oder
geänderten Beschreibung SHALL nur geschrieben werden, was vom
Vorgabewert abweicht. Für diese kanonische Ausgabe SHALL Reihenfolge
und Einrückung festgelegt und dokumentiert sein, damit zwei Läufe
dieselbe Datei erzeugen.
#### Scenario: Rundlauf
- **WHEN** eine Formulardatei gelesen und unverändert wieder geschrieben wird
- **THEN** ist die geschriebene Datei byte-gleich zur gelesenen
#### Scenario: Vorgabewerte werden nicht geschrieben
- **WHEN** eine neue oder geänderte Beschreibung kanonisch geschrieben wird und ein Steuerelement nur Vorgabewerte trägt
- **THEN** enthält sein Block außer `Begin`/`End` keine Eigenschaftszeile
#### Scenario: Expliziter Default im unveränderten Original
- **WHEN** eine gelesene Datei einen Vorgabewert ausdrücklich enthält und unverändert geschrieben wird
- **THEN** bleibt auch diese Eigenschaftszeile bytegleich erhalten

View File

@@ -0,0 +1,64 @@
## MODIFIED Requirements
### Requirement: Korpusabdeckung der Ereignis-Traps
Der Testkorpus SHALL die Ereignissteuerung mit Programmen abdecken, die
ohne echte Wartezeit und mit reproduzierbarer Ausgabe laufen: Zeit-Trap,
Tasten-Trap einschließlich der Frage, ob die Taste im Eingabestrom
verbleibt, benutzerdefiniertes Ereignis, die drei Maskierungszustände je
Quelle, der Hauptschalter `EVENT` sowie Rückkehr und Nicht-Wiedereintritt
eines Handlers. Ein Korpusprogramm mit Zeitbezug SHALL seinen
Zeitverlauf im Programmkopf deklarieren; ohne Deklaration MUST der
Harness den Lauf abweisen statt auf die Systemuhr auszuweichen.
Die drei Zustände SHALL für KEY und UEVENT ebenso ausdrücklich im Korpus vorkommen wie für TIMER, einschließlich der Lesbarkeit einer unter KEY STOP gemerkten Taste.
#### Scenario: Zeit-Trap im Korpus
- **WHEN** ein Korpusprogramm mit deklariertem Zeitverlauf und `ON TIMER` ausgeführt wird
- **THEN** entspricht der Bildschirminhalt byte-genau der Sollausgabe und der Lauf benötigt keine messbare Wartezeit
#### Scenario: Fehlende Zeitdeklaration
- **WHEN** ein Korpusprogramm zeitgesteuerte Traps nutzt, aber keinen Zeitverlauf deklariert
- **THEN** schlägt der Harness mit einer Meldung fehl, die die fehlende Deklaration nennt
#### Scenario: KEY STOP im Korpus
- **WHEN** der Korpus einen F1-Druck unter KEY STOP und ein späteres ON ausführt
- **THEN** prüft seine Sollausgabe sowohl die normal lesbare Taste als auch genau eine spätere Handlerausführung
### Requirement: Korpusabdeckung der Formularprogramme
Der Testkorpus SHALL Formularprogramme mit byte-genauer
Bildschirm-Sollausgabe führen. Ihre Ereignisfolge (Tasten, Maus, Zeit)
SHALL im Programmkopf deklariert und vom Harness eingespeist werden,
sodass ein Lauf ohne Terminal und ohne Wartezeit auskommt und zweimal
dasselbe Ergebnis liefert. Abgedeckt SHALL sein: Fokus- und
Tabreihenfolge, Access-Key, Klick über die Maus, Menüauswahl, ein
Listen-Steuerelement, ein Timer und ein modaler Dialog.
Der Access-Key-Nachweis SHALL mindestens ein gewöhnliches Control betreffen und MUST NOT allein durch einen Menü-Access-Key ersetzt werden. Rückwärtstab und die beobachtbare Reihenfolge LostFocus vor GotFocus SHALL enthalten sein.
#### Scenario: Formularprogramm im Korpus
- **WHEN** ein Formular-Korpusprogramm mit deklarierter Ereignisfolge ausgeführt wird
- **THEN** entspricht der Bildschirminhalt byte-genau der Sollausgabe
#### Scenario: Wiederholbarkeit
- **WHEN** dasselbe Programm zweimal ausgeführt wird
- **THEN** ist die Ausgabe beide Male identisch
#### Scenario: Control-Access-Key und Rückwärtstab
- **WHEN** ein Formular-Korpus Alt+O für eine OK-Schaltfläche und anschließend Rückwärtstab deklariert
- **THEN** weist die Sollausgabe den Click und den Fokuswechsel in korrekter Reihenfolge nach
### Requirement: Kompatibilitätsnachweis an Fremdprogrammen
Die Formularprogramme aus dem öffentlichen Bestand des Vorbilds SHALL
sich ohne Diagnosen oder Non-Features übersetzen lassen und bedienbar
sein. Jeder entdeckte fehlende Sprach-, Projekt-, Formular- oder
Laufzeitpfad MUST implementiert und als Befund festgehalten werden.
Der Nachweis SHALL den verwendeten öffentlichen Bestand mit Revision und Programmeinstiegen benennen sowie reproduzierbare Eingabesequenzen und sichtbare Zustandsänderungen führen. Ein bloß erfolgreiches Starten MUST NOT als Bediennachweis gelten.
#### Scenario: Fremdprogramm übersetzt
- **WHEN** ein Formularprogramm des öffentlichen Bestands übersetzt wird
- **THEN** entstehen keine Diagnosen und das Programm ist bedienbar
#### Scenario: Bediennachweis ist wiederholbar
- **WHEN** ein dokumentiertes Fremdprogramm erneut mit seiner Prüfsequenz ausgeführt wird
- **THEN** werden dieselben relevanten Formularzustände beziehungsweise Abschlussereignisse erreicht

View File

@@ -0,0 +1,22 @@
## MODIFIED Requirements
### Requirement: Compile-Zeit-Abweisung deklarierter Non-Features
Die Semantik MUST jede Verwendung der in docs/sprachreferenz.md
(„Abweichungen") deklarierten Non-Features — Hardware-Nähe (`PEEK`,
`POKE`, `INP`, `OUT`, `WAIT`, `BLOAD`/`BSAVE`, `VARPTR`-Familie, `CALL
ABSOLUTE`/`INTERRUPT`, `IOCTL`), `CHAIN`, die weiterhin ausgeschlossenen Grafik-Anweisungen (`PSET`,
`PRESET`, `CIRCLE`, `DRAW`, Grafik-`GET`/`PUT`, `PALETTE`, `WINDOW`,
`PMAP`, `PCOPY`, `POINT`), `SOUND`/
`PLAY` und die Event-Geräte `COM`/`PEN`/`PLAY`/`STRIG` — zur Compile-Zeit
mit der Meldung „Feature unavailable" abweisen. Dokumentierte Features
außerhalb dieser Liste MUST NOT abgewiesen werden (Guiding Principle).
Die dokumentierte Zellenpuffer-Untermenge SCREEN 013, LINE, PAINT und VIEW SHALL verfügbar bleiben.
#### Scenario: Hardware-Zugriff
- **WHEN** `POKE 100, 1` oder `x = PEEK(100)` kompiliert wird
- **THEN** erscheint die Compile-Diagnose „Feature unavailable"
#### Scenario: Erlaubte Grafikuntermenge
- **WHEN** ein Programm SCREEN 1, LINE, PAINT und VIEW in gültiger Syntax verwendet
- **THEN** wird es ohne Feature-unavailable-Diagnose übersetzt

View File

@@ -0,0 +1,30 @@
## MODIFIED Requirements
### Requirement: Automatischer Abgleich Inventar gegen Implementierung
Ein automatischer Test SHALL das Inventar gegen den tatsächlichen
Implementierungsstand prüfen und bei jeder Divergenz fehlschlagen. Als
`implementiert` markierte Elemente MUST im Frontend eine Signatur besitzen
und MUST NOT auf einen „nicht verfügbar"-Marker absenken; als `offen`
markierte Elemente MUST auf einen solchen Marker absenken oder ohne
Signatur sein; als `Non-Feature` markierte Elemente MUST zur Compile-Zeit
mit „Feature unavailable" abgewiesen werden. Der Test MUST das
divergierende Element, seinen Inventarstatus und den vorgefundenen
Zustand benennen.
Klassentabellen oder deklarierte Signaturnamen allein MUST NOT als Laufzeitnachweis gelten. Für implementierte Elemente SHALL ein erreichbarer Frontend-/Absenkungspfad und ein ausführbares Laufzeitziel geprüft werden. Für deklarierte Ereignisse SHALL ein Test den Auslöser bis zum BASIC-Handler führen.
#### Scenario: Inventar hinkt der Implementierung hinterher
- **WHEN** `LOCATE` implementiert wurde, im Inventar aber noch `offen` steht
- **THEN** schlägt der Abgleich fehl und nennt `LOCATE`, Status `offen` und den Befund „implementiert"
#### Scenario: Inventar behauptet zu viel
- **WHEN** ein Eintrag `implementiert` trägt, das Element aber weiterhin auf den „nicht verfügbar"-Marker absenkt
- **THEN** schlägt der Abgleich fehl und nennt das Element
#### Scenario: Abdeckungsstand ist abrufbar
- **WHEN** der Abgleich läuft
- **THEN** nennt er die Zahl der Einträge je Status als Abdeckungsstand
#### Scenario: Deklariertes Ereignis ohne Auslöser
- **WHEN** ein als implementiert geführtes Formularereignis bei seiner dokumentierten Hosteingabe keinen Handler ausführt
- **THEN** schlägt der Abgleich mit Name, Status und fehlendem Auslösepfad fehl

View File

@@ -0,0 +1,15 @@
## 1. Normtexte
- [ ] 1.1 Grafik-Abweisung und FRM-Schreibmodi gemäß Delta präzisieren; verifizieren mit tbc check für erlaubte Grafik, benannter Ablehnung weiterhin verbotener Formen und bytegleichem FRM-Rundlauf mit explizitem Default.
## 2. Nachweise
- [ ] 2.1 Inventarprüfung gegen erreichbare HIR-/Dispatch-Pfade erweitern; verifizieren mit künstlich offen/implementiert/verboten gesetzten Einträgen und einer fehlenden Runtime-Bindung, jeweils mit Namen und Status im Fehler.
- [ ] 2.2 Fehlende FORM_KEYPRESS/FORM_KEYUP-Auslöser ergänzen und registrierte Form-Ereignisse auf realen Quellenpfad prüfen; verifizieren durch BASIC-Handler nach Hosteingabe, nicht durch direktes queue_form_event allein.
- [ ] 2.3 Maskierungs- und Formular-Korpus ergänzen; verifizieren mit KEY/UEVENT in ON/OFF/STOP, normal lesbarer STOP-Taste, Control-Access-Key, BackTab, Fokusreihenfolge und zweimal identischer Ausgabe.
- [ ] 2.4 Öffentlichen Fremdprogrammbestand in dokumentierter Revision prüfen; verifizieren durch diagnosefreie Übersetzung und reproduzierbare deklarierte Eingabesequenzen bis zu sichtbaren Zustandsänderungen. Nicht vorhandene externe Voraussetzungen im Ergebnisbericht benennen, nicht abhaken.
## 3. Gesamtabnahme
- [ ] 3.1 Alle F01F25 nach Integration der Korrektur-Changes mit Regressionstest oder geklärtem Normtext abgleichen; verifizieren durch separaten Ergebnisreport mit Fundstellen pro F-ID.
- [ ] 3.2 Gesamten Korpus und Spec-Validierung ausführen; verifizieren mit cargo test --workspace, openspec validate --all --strict und git diff --check; Golden Files nicht ungeprüft neu aufzeichnen.