Ereigniszustellung und Hostgrenzen korrigieren und Change archivieren

This commit is contained in:
2026-09-05 19:15:31 +02:00
parent 57d6386a5a
commit 644a86212f
39 changed files with 2013 additions and 271 deletions

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,167 @@
//! Ursprüngliche Reproduktionen: fünf behobene Befunde und eine Kontrollprobe.
//! Separater Cargo-Treiber; siehe review.md. Alle sechs Prüfungen müssen bestehen.
use tb_frontend::forms::{FormCatalog, ObjectClass};
use tb_runtime::host::{CaptureHost, Ereignis, Host, MausArt, MausEreignis};
use tb_runtime::screen::TextScreen;
use tb_runtime::value::Value;
use tb_vm::interp::{RunEvent, Vm};
fn vm(src: &str) -> Vm {
let mut catalog = FormCatalog::default();
catalog.add("Form1", ObjectClass::Form, None, false);
catalog.add("Text1", ObjectClass::TextBox, Some("Form1"), false);
catalog.add("Text2", ObjectClass::TextBox, Some("Form1"), false);
catalog.add("Timer1", ObjectClass::Timer, Some("Form1"), false);
catalog.add("Menu1", ObjectClass::Menu, Some("Form1"), false);
catalog.add("Item1", ObjectClass::Menu, Some("Menu1"), false);
Vm::new(
tb_vm::compile_source_with_forms("FORM1", src, &catalog)
.unwrap_or_else(|d| panic!("{d:?}")),
)
}
fn stop(vm: &mut Vm, host: &mut dyn Host) {
assert!(matches!(vm.run(host), RunEvent::Stopped { .. }));
}
fn string(vm: &Vm, name: &str) -> String {
match vm.inspect(name) {
Some(Value::Str(s)) => s.to_string(),
other => panic!("{other:?}"),
}
}
fn key(s: &str) -> Ereignis {
Ereignis::Taste(s.into(), 0)
}
fn mouse() -> Ereignis {
Ereignis::Maus(MausEreignis {
art: MausArt::Druck,
taste: 1,
shift: 0,
zeile: 1,
spalte: 1,
})
}
#[test]
fn review_signal_waehrend_blockierender_eingabe() {
struct Input {
reads: usize,
}
impl Host for Input {
fn present(&mut self, _: &TextScreen) {}
fn next_event(&mut self, block: bool) -> Option<Ereignis> {
if !block {
return None;
}
self.reads += 1;
Some(if self.reads == 1 {
Ereignis::Signal(2)
} else {
Ereignis::Ende
})
}
fn jetzt_ms(&mut self) -> u64 {
0
}
fn warten(&mut self, _: Option<u64>) -> Option<Ereignis> {
self.next_event(true)
}
}
let mut results = vec![];
for statement in ["INPUT s$", "LINE INPUT s$", "s$ = INPUT$(1)"] {
let src =
format!("ON SIGNAL(2) GOSUB H\nSIGNAL(2) ON\n{statement}\nEND\nH:\nPRINT \"H\"\nEND");
let mut vm = Vm::new(tb_vm::compile_source("TEST", &src).unwrap());
let result = vm.run(&mut Input { reads: 0 });
println!(
"{statement}: {result:?}; Ausgabe={:?}",
tb_runtime::snapshot::text(&vm.rt.screen)
);
results.push(result);
}
assert!(results.iter().all(|result| *result == RunEvent::Ended));
}
#[test]
fn review_objektargument_darf_gotfocus_nicht_vorziehen() {
let mut vm = vm("DIM SHARED s$\nText1.TabIndex = 0\nText2.TabIndex = 1\nForm1.Show\nText1.SetFocus\nSTOP\nDOEVENTS\nEND\nSUB Text1_LostFocus()\nSHARED s$\nCALL Mark(Text1)\ns$ = s$ + \"l\"\nEND SUB\nSUB Mark(c AS CONTROL)\nSHARED s$\ns$ = s$ + \"L\"\nEND SUB\nSUB Text2_GotFocus()\nSHARED s$\ns$ = s$ + \"G\"\ns$ = s$ + \"g\"\nEND SUB");
let mut host = CaptureHost::default();
stop(&mut vm, &mut host);
host.ereignis(key("\t"));
assert_eq!(vm.run(&mut host), RunEvent::Ended);
assert_eq!(string(&vm, "s"), "LlGg");
}
#[test]
fn review_gleichzeitig_key_und_uevent_behalten_prioritaet() {
let mut vm = vm("ON KEY(15) GOSUB K\nKEY 15, CHR$(0) + CHR$(20)\nKEY(15) ON\nON UEVENT GOSUB U\nUEVENT ON\nSTOP\nDOEVENTS\nEND\nK:\ns$ = s$ + \"K\"\nRETURN\nU:\ns$ = s$ + \"U\"\nRETURN");
let mut host = CaptureHost::default();
stop(&mut vm, &mut host);
host.ereignis(key("t"));
vm.rt.pump(&mut host, false);
vm.rt.traps.melden(tb_runtime::traps::Quelle::UEvent);
assert_eq!(vm.run(&mut host), RunEvent::Ended);
assert_eq!(string(&vm, "s"), "KU");
}
#[test]
fn review_modale_eingabe_vor_eof_wird_zugestellt() {
let mut vm = vm("DIM SHARED s$\nForm1.Show 1\nEND\nSUB Form_MouseDown(Button AS INTEGER, Shift AS INTEGER, X AS SINGLE, Y AS SINGLE)\nSHARED s$\ns$ = \"M\"\nUNLOAD Form1\nEND SUB");
let mut host = CaptureHost::default();
host.ereignis_nach(2, mouse());
host.ereignis_nach(2, Ereignis::Ende);
assert_eq!(vm.run(&mut host), RunEvent::Ended);
assert_eq!(string(&vm, "s"), "M");
}
#[test]
fn review_menu_schliessen_stellt_pending_trap_vor_erneutem_warten_zu() {
struct MenuHost {
time: u64,
waits: usize,
}
impl Host for MenuHost {
fn present(&mut self, _: &TextScreen) {}
fn next_event(&mut self, _: bool) -> Option<Ereignis> {
None
}
fn jetzt_ms(&mut self) -> u64 {
self.time
}
fn warten(&mut self, deadline: Option<u64>) -> Option<Ereignis> {
self.waits += 1;
if self.waits == 1 {
return Some(key("\u{1b}"));
}
self.time = deadline.unwrap();
None
}
}
let mut vm = vm("ON UEVENT GOSUB H\nUEVENT ON\nMenu1.Caption = \"&Datei\"\nItem1.Caption = \"&Ende\"\nForm1.Show\nSTOP\nSLEEP 60\nEND\nH:\ns$ = \"U\"\nRETURN");
let mut host = CaptureHost::default();
stop(&mut vm, &mut host);
vm.forms.handle_key("d", 4);
vm.rt.traps.melden(tb_runtime::traps::Quelle::UEvent);
let mut host = MenuHost { time: 0, waits: 0 };
assert_eq!(vm.run(&mut host), RunEvent::Ended);
println!(
"Menü/SLEEP: Zeit={}, Warteaufrufe={}, Handler={}",
host.time,
host.waits,
string(&vm, "s")
);
assert_eq!(host.waits, 1);
}
#[test]
fn review_maus_vor_getrappter_taste() {
let mut vm = vm("DIM SHARED s$\nON KEY(15) GOSUB H\nKEY 15, CHR$(0) + CHR$(20)\nKEY(15) ON\nForm1.Show\nSTOP\nDOEVENTS\nDOEVENTS\nEND\nH:\ns$ = s$ + \"K\"\nRETURN\nSUB Form_MouseDown(Button AS INTEGER, Shift AS INTEGER, X AS SINGLE, Y AS SINGLE)\nSHARED s$\ns$ = s$ + \"M\"\nEND SUB");
let mut host = CaptureHost::default();
stop(&mut vm, &mut host);
host.ereignis(mouse());
host.ereignis(key("t"));
assert_eq!(vm.run(&mut host), RunEvent::Ended);
assert_eq!(string(&vm, "s"), "MK");
}

View File

@@ -0,0 +1,191 @@
# Verifikation: ereigniszustellung-und-hostgrenzen
Stand nach Behebung: 05.09.2026, aktueller Arbeitsbaum auf `57d6386`.
**Alle fünf Verifikationsbefunde sind behoben.** Die ursprünglichen sechs
Review-Proben bestehen unverändert in ihren Erwartungen; zusätzlich wurden
sie in die Workspace-Regressionen übernommen und um verwandte Pfade ergänzt.
| Dimension | Ergebnis |
| --- | --- |
| Vollständigkeit | 12/12 Tasks erledigt; ursprüngliche Zuordnung der 9 Delta-Requirements und 33 Szenarien um Korrektur-Nachweise ergänzt. |
| Korrektheit | 419 Workspace-Tests und alle sechs separaten Review-Proben bestanden; keine offenen V1V5-Befunde. |
| Kohärenz | Gemeinsame Zustellung auch während Eingabe und nach Menüschluss; synchrone LOAD-Wirkungen getrennt, klassische Quellpriorität erhalten. |
## Korrekturen und Regressionen
| Befund | Behebung | Nachweis in `crates/tb-vm/tests/events.rs` |
| --- | --- | --- |
| V1 | Fortsetzbarer Konsolenleser für INPUT, LINE INPUT, INPUT$ und RANDOMIZE ohne Argument. Frame hält Teiltext/Restlänge, Stack hält Operanden und Referenzen. Trap-Unwinding erhält gemeinsam genutzte Locals; Fehler und RETURN label verwerfen abgebrochene Eingaben. | Ursprüngliche Signalprobe; Teiltext/BYREF/Ausdruck, verschachtelte Eingaben mit STOP/CONT, Timer, Redo, lokaler Fehlerhandler, RANDOMIZE und RETURN label. |
| V2 | Implizite Objektaufrufe stellen ausschließlich notwendige LOAD-Handler zu. Deren Rückkehr zieht keine unabhängigen Forms-Ereignisse vor. | LlGg mit Control-Argument, Property-Lesen/-Schreiben, Refresh und LOAD; bestehende Load-vor-Show- und Unload-Tests. |
| V3 | Nach dem Schließen des Menüs wird die gemeinsame Auswahl vor jedem erneuten Warten wiederholt. | Esc bei Zeit 0, UEVENT zugestellt nach genau einem Warteaufruf; bestehende Menüsperrtests einschließlich Forms-Timer. |
| V4 | Modale und modellose Schleifen beenden bei EOF erst, wenn kein Handler mehr zugestellt wurde. | Ursprüngliche MouseDown/Unload-Probe; jeweils zwei Mausereignisse vor EOF ergeben MM in beiden Schleifen. |
| V5 | Getrappte Tasten am Queue-Anfang werden vor der klassischen Quellenauswahl gemeldet. | Gleichzeitiges KEY/UEVENT ergibt KU; Maus vor getrappter Taste bleibt MK. |
## Ursprüngliche Befunde — historischer Stand vor der Behebung
Die folgenden Ist-Beobachtungen und Ursachen dokumentieren den fehlgeschlagenen
Review vor diesen Korrekturen. Alle fünf sind durch die Nachweise oben geschlossen.
### V1 — Während blockierender Eingabe werden eintreffende Traps nicht zugestellt (hoch)
**Reproduktion:** `ON SIGNAL(2) GOSUB H`, `SIGNAL(2) ON`, danach INPUT.
Der Host liefert erst im blockierenden `next_event(true)` Signal(2), beim
nächsten Aufruf Eingabeende. Der Handler H würde `PRINT "H"` und END ausführen.
**Ist:** INPUT, LINE INPUT und INPUT$(1) enden jeweils mit Fehler 62, H läuft
nicht. Ohne nachfolgende Eingabe kann die Laufzeit stattdessen erneut
blockieren, obwohl der Signal-Trap schon ansteht.
**Soll:** Vor dem erneuten Blockieren muss der erlaubte Zustellpunkt genutzt
werden; H beendet das Programm, Ausgabe H, kein Fehler 62.
**Ursache:** Der einmalige VM-Check vor der Eingabeinstruktion genügt nicht.
[RtState::naechste_taste](../../../../crates/tb-runtime/src/builtins.rs#L209)
pumpt innerhalb seiner Schleife weitere Ereignisse und blockiert erneut,
ohne zur VM-Zustellung zurückzukehren. Dasselbe betrifft die drei genannten
Eingabefunktionen über ihren gemeinsamen Leser.
**Vertrag:** `ereignissteuerung` / Abschließende Liste der Zustellpunkte,
Signal-Trap auf Betriebssystemsignalen; Design D2; Task 2.1.
**Korrektur:** Blockierende Eingabe so fortsetzbar machen, dass nach einem
Nicht-Tasten-Ereignis und vor dem nächsten Host-Warten die gemeinsame
VM-Zustellung laufen kann. Eingabepuffer, Referenzen und Operanden erhalten.
### V2 — Control-Argumente stellen wartende Forms-Handler mitten in einer Anweisung zu (hoch)
**Reproduktion:** Fokuswechsel Text1 → Text2. Text1_LostFocus ruft als erste
wirksame Anweisung `CALL Mark(Text1)` auf; Mark hängt L an eine gemeinsame
Variable, LostFocus danach l. Text2_GotFocus hängt G und g an.
**Ist:** `GgLl`. **Soll:** `LlGg`.
**Ursache:** [Instr::PushObject](../../../../crates/tb-vm/src/interp.rs#L1344)
ruft nach dem Ablegen eines bereits geladenen Control-Objekts bedingungslos
`dispatch_next_form_event` auf. Dadurch läuft GotFocus noch vor dem eigentlichen
CALL, obwohl weder DOEVENTS noch modales Warten vorliegt. Der Schutz am ersten
Stmt-Eintritt und `forms_frei` werden damit umgangen.
**Vertrag:** Fokus, Tabreihenfolge und Access-Keys / Handlerwirkungen in
Fokusreihenfolge; Abschließende Liste der Zustellpunkte; Design D1/D2;
Tasks 1.2 und 2.1.
**Korrektur:** Unabhängig wartende Ereignisse aus Objekt-Opcodes heraus nicht
zustellen. Notwendige synchrone Load-/Unload-Wirkungen gezielt behandeln;
alle Aufrufer von `dispatch_next_form_event` gegen dieselben Zustellregeln prüfen.
### V3 — Menüschließen während SLEEP führt zu erneutem Warten trotz bereitem Trap (hoch)
**Reproduktion:** Menü offen, UEVENT anstehend, `SLEEP 60`. Der erste
Host-Warteaufruf liefert Esc bei unveränderter virtueller Zeit 0.
**Ist:** Esc schließt das Menü; die VM wartet ein zweites Mal bis Hostzeit
60.000 ms. Erst dann läuft der UEVENT-Handler.
**Soll:** Nach Esc läuft der freigegebene Handler ohne zweiten Warteaufruf
und beendet SLEEP sofort. Bei einer Wartephase ohne eigene Deadline droht
entsprechend Warten auf ein weiteres externes Ereignis.
**Ursache:** Die klassische Trap-Auswahl in
[Vm::zustellen](../../../../crates/tb-vm/src/interp.rs#L625) erfolgt vor der
Eingabeverarbeitung. Nachdem Esc die Menüsperre aufgehoben hat, kehrt die
Methode ohne erneute Auswahl zurück;
[SLEEP](../../../../crates/tb-vm/src/interp.rs#L707) ruft unmittelbar wieder
`warten` auf.
**Vertrag:** Menüsystem / Traps ruhen im geöffneten Menü und werden danach
fortgesetzt; Abschließende Liste der Zustellpunkte / SLEEP endet durch
Ereignis; Design D2/D3; Tasks 2.1/2.2.
**Korrektur:** Nach einer Eingabe, die die Zustellbarkeit verändert, bereite
Ereignisse vor jedem weiteren blockierenden Warten erneut auswählen.
### V4 — Modale Schleife verliert bereits angenommenes Ereignis unmittelbar vor EOF (hoch)
**Reproduktion:** `Form1.Show 1`; nach der zweiten Anzeige liefert der Host
MouseDown und unmittelbar danach Eingabeende. Der MouseDown-Handler hängt M
an und entlädt das Formular.
**Ist:** VM meldet Ended, die gemeinsame Variable bleibt leer; der Handler
wurde nicht ausgeführt. **Soll:** Das vor EOF eingetroffene Mausereignis wird
noch wirksam, Ergebnis M, Formular entladen.
**Ursache:** In der [modalen Run-Schleife](../../../../crates/tb-vm/src/interp.rs#L890)
setzt `zustellen` einen Handlerframe auf. Direkt danach wird ungeachtet von
`dispatched` wegen `rt.ende` zurückgekehrt. Das Ereignis ist bereits aus der
Queue entfernt und wird stillschweigend verloren.
**Vertrag:** Mausereignisse in Zellenkoordinaten / Reihenfolge bis zur
beobachtbaren Handlerausführung; Design D1/D2; Tasks 1.2/2.1.
**Korrektur:** EOF als Ende der Eingabequelle behandeln und bereits angenommene
Ereignisse/Handler geordnet abarbeiten, bevor die modale Ausführung endet.
## Ursprüngliche weitere Designabweichung
### V5 — Gleichzeitig wartende klassische Quellen erhalten eine andere Priorität (mittel)
**Reproduktion:** KEY(15) ON und UEVENT ON. Vor demselben Zustellpunkt liegen
eine passende Taste in der Eingabequeue und ein gemeldeter UEVENT vor.
Die Handler hängen K bzw. U an.
**Ist:** `UK`. **Soll:** `KU` gemäß der bestehenden klassischen Quellpriorität.
**Ursache:** [Vm::zustellen](../../../../crates/tb-vm/src/interp.rs#L631) wählt
bereits gemeldete Traps aus, bevor die wartende Taste durch `taste_trappen`
gemeldet wird. Die Prioritätsauswahl in Traps sieht die Taste deshalb noch
nicht. Früher erfolgte die Tastenprüfung vor der gemeinsamen Trap-Auswahl.
**Vertrag:** Design / Non-Goals: keine Änderung der festgelegten Priorität
gleichzeitig fälliger klassischer Quellen; D1/D2; Task 2.1.
**Korrektur:** Fälligkeitserfassung und Trap-Auswahl so koordinieren, dass die
bestehende Quellpriorität erhalten bleibt, ohne die Reihenfolge zwischen
Maus- und Tasteneingaben wieder nach Quellarten umzusortieren.
## Abdeckung und positive Nachweise
| Delta-Requirement | Nachweis / Einschränkung |
| --- | --- |
| Ereignisquellen mit Zeit vom Host | Host-Warteschnittstelle, eingefrorene virtuelle Zeit, klassischer Timer und Forms-Timer-SLEEP getestet. Keine reale Rückfalluhr in der VM; Zustellung nach Menüschluss durch V3-Regression abgesichert. |
| Mausereignisse in Zellenkoordinaten | Mapper-/Bounds-Tests, leere Verbraucher, KM/MK-Korpus und gemischte Eingaben. V4-Regressionen sichern EOF in modalen und modellosen Schleifen ab. |
| Abschließende Liste der Zustellpunkte | DOEVENTS-Wert, SLEEP, vorbereitete und erst beim Warten eintreffende INPUT-Ereignisse getestet; V1V3 geschlossen. |
| Getrappte Tasten verlassen den Eingabestrom | ON/OFF/STOP × INKEY$/INPUT/LINE INPUT/INPUT$, mehrfaches STOP-Merken und Dialogrestpuffer getestet. |
| Signal-Trap auf Betriebssystemsignalen | Beide Kennungen, Maskierung und ungültige Werte getestet. V1-Regression sichert Signalzustellung während blockierender Eingabe ab. |
| Fokus, Tabreihenfolge und Access-Keys | Tab/Shift-Tab, BackTab-Mapper, Access-Keys und einfacher LostFocus→GotFocus-Lauf belegt. V2-Regressionen sichern Control-Argumente und verwandte Objektaufrufe ab. |
| Menüsystem | Menüeigenschaften/-auswahl und Sperre an normalen Grenzen getestet. V3-Regression sichert Freigabe innerhalb von SLEEP ab. |
| Zellenpuffer ohne Terminalabhängigkeit | Separater Workspace kompiliert FRM/VM und liefert KM; 16 Pakete im normalen Dependency-Graph, keines davon crossterm/ratatui/signal-hook. CLI schaltet das Feature ausdrücklich ein. |
| Ereigniszustellung an Anweisungsgrenzen | Bestehende SUB-/GOSUB-/BYREF-/RESUME-Tests und Host ohne erlaubte Uhrabfragen grün. V2/V5-Regressionen sichern zulässige Handlerbeginn-Reihenfolge und klassische Priorität ab. |
Erneut ausgeführt:
- `cargo test --workspace`: **419 bestanden, 0 fehlgeschlagen, 1 ignoriert**.
- `openspec validate ereigniszustellung-und-hostgrenzen --strict`: gültig.
- Isolierter Einbetter mit CaptureHost: **KM**, Dependency-Prüfung erfolgreich.
- Separate [Review-Proben](review-probes.rs): **6 bestanden, 0 fehlgeschlagen**.
Die Kontrollprobe prüft Maus unmittelbar vor getrappter Taste.
- Workspace-Ereignistests: **25 bestanden**, einschließlich Frist-Watchdog.
- Keine bestehenden Sollausgaben neu aufgezeichnet oder geändert.
Warnings-denied Clippy, Formatprüfungen und Compile-/VM-Benchmarks wurden
nach der Behebung erneut erfolgreich ausgeführt; Zahlen siehe
[verification.md](verification.md). Kein interaktiver Terminaltest und keine
Windows-/Linux-Matrix. Die Proben speisen Ereignisse
gezielt über Host ein und benötigen weder echte Signale noch reale Wartefristen.
## Review-Proben reproduzieren
Im Repository-Wurzelverzeichnis ausführen. Das erzeugt ausschließlich einen
separaten temporären Cargo-Treiber und referenziert den aktuellen Produktcode:
```sh
python3 - <<'PY'
from pathlib import Path
import json, subprocess, tempfile
root = Path.cwd()
p = Path(tempfile.mkdtemp(prefix='tb-review-'))
manifest = '[package]\nname="tb-review"\nversion="0.0.0"\nedition="2021"\n[workspace]\n'
manifest += '[[test]]\nname="befunde"\npath=' + json.dumps(str(root / 'openspec/changes/archive/2026-09-05-ereigniszustellung-und-hostgrenzen/review-probes.rs')) + '\n[dependencies]\n'
for name in ['tb-vm', 'tb-runtime', 'tb-ui', 'tb-frontend']:
manifest += name + ' = { path = ' + json.dumps(str(root / 'crates' / name)) + ' }\n'
(p / 'Cargo.toml').write_text(manifest)
subprocess.run(['cargo', 'test', '--manifest-path', str(p / 'Cargo.toml'), '--test', 'befunde', '--', '--nocapture'], check=True, timeout=120)
PY
```
Alle sechs Proben müssen auf dem korrigierten Stand bestehen. Ihre Erwartungen
wurden auch in den regulären Workspace-Testlauf übernommen.

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,25 @@
## 1. Hosteingabe und Maskierung
- [x] 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.
- [x] 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
- [x] 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.
- [x] 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.
- [x] 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
- [x] 3.1 Gezielte geordnete Ereignisprogramme zum Korpus ergänzen; verifizieren mit cargo test --workspace und Vergleich der bestehenden Snapshots ohne automatisches Neuaufzeichnen.
- [x] 3.2 Zeit-/Zustellverträge und Backend-Feature dokumentieren; verifizieren mit Compile-/VM-Benchmarks und openspec validate ereigniszustellung-und-hostgrenzen --strict.
Abnahmenachweise: [verification.md](verification.md).
## 4. Verifikationsbefunde
- [x] 4.1 V1: Blockierende Konsoleneingabe fortsetzbar machen; Signale, Timer, Teiltexte und Handler-Rückkehr absichern.
- [x] 4.2 V2: Synchrone Load-Wirkungen von wartenden Forms-Ereignissen trennen; Control-Argumente und Geschwisterpfade prüfen.
- [x] 4.3 V3: Nach Menüschluss vor erneutem Warten bereit gewordene Ereignisse zustellen.
- [x] 4.4 V4: Vor EOF angenommene Ereignisse im modalen Lauf vollständig abarbeiten.
- [x] 4.5 V5: Klassische Quellpriorität bei gleichzeitig wartenden Tasten und UEVENT erhalten.

View File

@@ -0,0 +1,82 @@
# Implementierungsnachweis — 05.09.2026
Change: `ereigniszustellung-und-hostgrenzen`, Ausgangscommit `57d6386`.
Alle zwölf Implementierungs-/Abnahmetasks einschließlich der fünf
[Verifikationsbefunde](review.md) sind abgeschlossen. Die vier betroffenen
Hauptspezifikationen sind synchronisiert; der Change wurde am 05.09.2026 archiviert.
## Befunde und Umsetzung
| Befund / Tasks | Umsetzung | Ausführbarer Nachweis |
| --- | --- | --- |
| F04 / 1.1 | SIGNAL(1) und SIGNAL(2) werden im gemeinsamen RtState-Eingang gemeldet; nur ungetrapptes SIGINT wird Abbruch. Zusätzlich erkennt der Parser SIGNAL(n) ON/OFF/STOP. | VM-Test `beide_signale_werden_zugestellt_und_beginnen_in_quellreihenfolge` einschließlich ungültiger konstanter/dynamischer Kennungen; Runtime-Tests für STOP/OFF beider Quellen und SIGINT ohne Trap. |
| F05 / 1.1 | ON verbraucht die Taste; STOP merkt den Trap einmal und belässt die Eingabe auch nach erneutem ON lesbar. Die Markierung bleibt beim Dialogaufruf erhalten. | VM-Test `key_on_off_stop_gelten_fuer_alle_konsoleneingaben`: drei Zustände × INKEY$, INPUT, LINE INPUT, INPUT$. Runtime-Test mit drei STOP-Tasten; VM-Regression für verbleibende Dialogeingaben. |
| F06 / 1.2, 2.1 | Eine Eingabequeue für Tasten/Maus bis zur Annahme. Forms-Eingaben und ihre Handler werden geordnet verarbeitet; erster Handler-Statement-Eintritt geschützt, gleiche Forms-Prozedur nicht rekursiv zugestellt. | VM-Tests für aM, Ma, aMbTM einschließlich KEY-Trap; Fokusfolge LlGg; explizites DOEVENTS ergibt arbr; UNLOAD im eigenen Handler ergibt ur. |
| F07 / 2.1 | Gemeinsame Zustellung; separate Menüsperre in Traps ohne Änderung der ON/OFF/STOP-Zustände. Bereits wartende Forms-Timer bleiben gesperrt, Menübedienung kann fortfahren. | VM-Menütest über gewöhnliche Zuweisung, DOEVENTS, SLEEP und LINE INPUT: Zähler bleibt 1, nach Esc werden UEVENT und Forms-Timer nachgeholt, Ergebnis 111. |
| F08 / 2.2 | Host besitzt Zeit und Deadline-Warten; keine Instant-/Sleep-/Yield-Rückfallebene in der VM. Auch Forms-SLEEP, modales Warten und sichtbare Forms nach Programmende nutzen den Host. Zeitabfragen nur bei tatsächlichem Zeitbedarf. | Host, der jede unerwartete Uhrabfrage/Warteoperation ablehnt; eingefrorene Uhr über reale 10 ms mit zwei identischen Hostdeadlines; Forms-Timer beendet SLEEP 60 bei Hostzeit 100 ms. Bestehender klassischer SLEEP-/TIMER-Test. |
| F18 / 1.1 | BackTab wird als TAB mit SHIFT normalisiert, auch wenn crossterm kein separates SHIFT-Flag liefert. | Terminal-Mapper-Test `backtab_ist_tab_mit_shift_auch_ohne_modifier`; bestehende Forms-Tests für Rückwärtstab, übersprungene Controls und Access-Keys. |
| F20 / 2.3 | tb-ui ist standardmäßig terminalfrei; `terminal` aktiviert Backendmodule und optionale Dependencies. CLI aktiviert dieses Feature ausdrücklich. | Separater Workspace `tests/terminalfrei`: FRM laden/kompilieren, Forms ausführen, CaptureHost-Ausgabe KM prüfen. Normaler Dependency-Graph: 16 Pakete, keines davon crossterm, ratatui oder signal-hook. CLI-Kompilation und CLI-Prozesstests erfolgreich. |
| 3.1 | Zwei kleine geordnete Ereignisprogramme mit ausdrücklich festgelegten Ausgaben ergänzt. | `eingabe-taste-maus.frm` → KM; `eingabe-maus-taste.frm` → MK. Beide laufen zweimal im bestehenden Korpusharness. Keine bestehende .out-Datei verändert. |
| 3.2 | Zeit-, Zustell-, Eingabequeue- und Backend-Verträge einschließlich Einbetter-Migration dokumentiert. | [VM-Design](../../../../docs/tbvm-design.md#ausführungsmodell--unterbrechbarkeit), Checks und Benchmarks unten. |
Die neuen VM-Regressionen liegen in
[crates/tb-vm/tests/events.rs](../../../../crates/tb-vm/tests/events.rs).
Bestehende Trap-Rückkehr-, GOSUB-, Fehler-/RESUME-, BYREF- und Modalitätstests
wurden im vollständigen Workspace-Testlauf mit ausgeführt.
## Behebung der Verifikationsbefunde
V1V5 sind geschlossen. Der [aktualisierte Report](review.md) ordnet jede
Korrektur den ursprünglichen Beobachtungen und ihren Regressionen zu.
Die 25 Tests in `events.rs` decken nun auch fortsetzbare Eingabe samt Teiltext,
Ausdrucksoperanden, BYREF, verschachtelten Handlern, STOP/CONT, Timer,
Fehler-Unwinding und RETURN label ab. Objektaufrufe bewahren die Fokusfolge,
Menüschluss gibt Traps sofort frei, beide Formularschleifen arbeiten Eingaben
vor EOF ab und die klassische KEY-/UEVENT-Priorität bleibt erhalten.
## Ausgeführte Prüfungen
- `cargo test --workspace`: **419 bestanden, 0 fehlgeschlagen, 1 ignoriert**.
Ignoriert bleibt ausschließlich `erzeuge_sollausgaben`; keine automatische
Neuaufzeichnung von Golden Files.
- Separater Treiber der ursprünglichen `review-probes.rs`: **6 bestanden, 0 fehlgeschlagen**.
- `cargo clippy -p tb-runtime -p tb-ui -p tb-vm -p tb-cli --all-targets --all-features -- -D warnings`: erfolgreich, ohne Warnungen.
- `cargo fmt --all -- --check` und Formatprüfung des separaten Einbetters: erfolgreich.
- `cargo run --manifest-path tests/terminalfrei/Cargo.toml --offline`: erfolgreich,
`Terminalfreier FRM-/VM-Lauf: KM`.
- `cargo tree --manifest-path tests/terminalfrei/Cargo.toml --offline --edges normal --prefix none --format '{p}'`:
mit expliziter Prüfung auf ausgeschlossene Terminalpakete, erfolgreich.
- `openspec validate ereigniszustellung-und-hostgrenzen --strict`: erfolgreich.
- `git diff --check`: erfolgreich.
## Release-Benchmarks
`cargo bench -p tb-vm --bench compile --bench vm`, auf diesem Rechner:
| Last | Ergebnis |
| --- | --- |
| Compile: 508 Zeilen | 0,83 ms, Budget 50 ms eingehalten |
| Compile: 49.760 Zeilen in 20 Modulen | 58 ms, Budget 1.000 ms eingehalten |
| INTEGER: 10 Mio. Iterationen | 1.048 ms, 9,5 Mio. Iterationen/s |
| DOUBLE: 5 Mio. Iterationen | 504 ms, 9,9 Mio. Iterationen/s |
| SUB/BYREF: 1 Mio. Aufrufe | 123 ms, 8,2 Mio. Aufrufe/s |
| String-Funktionen: 200.000 Runden | 99 ms, 2,0 Mio. Runden/s |
Der Compile-Projektbenchmark übersetzt 20 Module einzeln; er prüft keinen
Mehrmodul-Linker. Die VM-Zahlen sind lokale Durchsatzmessungen, keine
zugesicherte Performance auf anderer Hardware.
## Synchronisation und Archivierung
Neun bestehende Requirements in vier Hauptspezifikationen wurden um elf
Szenarien ergänzt; alle bisherigen Szenarien bleiben erhalten. Vollständiger
Abgleich aller Delta-Requirements erfolgreich; `openspec validate --specs
--strict`: 15 bestanden, 0 fehlgeschlagen.
## Grenzen
Terminaleingaben wurden über den Mapper und vorbereitete Hostereignisse
geprüft. Kein interaktiver Terminal-/Fremdprogrammvergleich und keine
Windows-/Linux-Ausführung. Der neue Vertrag verlangt von Einbettern eine
Implementierung von Host::warten; CaptureHost modelliert eine endliche
Ereignisquelle und explizites virtuelles Fortschreiten bis zur Deadline.