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.