# Verifikation: forms-zustand-und-bedienung

Abnahme vom 05.09.2026, umgesetzt auf Basis von `644a862`.
Nach erfolgreicher Verifikation am selben Tag synchronisiert und archiviert. **V1, V2 und V3 sind behoben. Keine
offenen Befunde im Change.** Beide Delta-Requirements, alle acht Szenarien,
die ergänzten Abnahmetasks und D1–D3 wurden erneut gegen Code und Tests geprüft.

| Dimension | Ergebnis |
| --- | --- |
| Vollständigkeit | 7/7 Tasks vollständig umgesetzt; beide Delta-Requirements mit allen acht Szenarien abgedeckt. Task 4.2 einschließlich Mindestmaßen und verwandten Zeichenpfaden erfüllt. |
| Korrektheit | 428 Workspace-Tests und alle vier Review-Proben bestanden. Keine kritischen Befunde, Warnungen oder offenen Verbesserungsvorschläge. |
| Kohärenz | D1–D3 umgesetzt. Die stabile Sortierung erhält die Auswahl; Simple-ComboBox und ListBox verwenden denselben Listenrenderer. |

## V3 — behoben; historischer Befund vor der Korrektur

Die folgenden Beobachtungen gelten für den vorherigen Stand. Die Korrektur
und ihre erweiterten Nachweise stehen im nächsten Abschnitt.

### V3 (ursprünglich mittel): Listenrenderer überschreitet zulässige Mindestmaße

**Reproduktion:** Sichtbare Simple-ComboBox mit Width = 1, Height = 5,
Left = 2, Top = 2 in einem ausreichend großen Formular. Gegenüber dem
Bildschirm mit ausgeblendetem Control werden auch die Zellen in Spalte 4,
Zeilen 4–7 überschrieben; die einzige erlaubte Controlspalte ist Spalte 3.
Eine ListBox mit Width = 1 hat denselben horizontalen Überlauf. Bei einer
ListBox mit Width = 6, Height = 1 wird zusätzlich die vollständige Zeile
unterhalb des Controls gezeichnet. Benachbarte Inhalte können dadurch
überschrieben werden.

**Ursache:** [list_lines](../../../../crates/tb-ui/src/forms.rs#L2047) verwendet
`box_lines`, das Breite und Höhe auf mindestens 2 erhöht. Für Width < 3
kehrt der Listenrenderer ohne Begrenzung zurück; im ListBox-Pfad fehlt
auch eine abschließende Höhenbegrenzung. `write_at` begrenzt ausschließlich
auf den Bildschirm. [Width und Height](../../../../crates/tb-frontend/src/forms.rs#L202)
lassen ausdrücklich den Wert 1 zu.

**Vertrag:** Task 4.2 verlangt die Prüfung von Kürzung und Größenbegrenzung
für die Simple-ComboBox. Der neue gemeinsame Renderer übernimmt die bereits
vorhandene Mindestmaß-Lücke auch für die nun dargestellte Simple-Liste.
Die bestehenden Regressionen prüfen Breite 6 und kleine Combo-Höhen,
aber weder Breite 1 noch die ListBox-Höhe 1.

**Behebung:** Die gemeinsame Listenausgabe auf die tatsächlich angeforderte
Breite und Höhe begrenzen. Mindestbreite 1 für beide Klassen und Mindesthöhe 1
für ListBox durch Bildschirmprüfungen absichern; normale Maße beibehalten.

**Nachweis:** `listen_bleiben_auch_bei_minimalmassen_im_controlrechteck` in
[review-probes.rs](review-probes.rs) prüft den tatsächlichen Bildschirm vor
und nach Einblenden des Controls. Die Kontrollfälle ComboBox 15×5 und
ListBox 6×4 bleiben innerhalb ihrer Grenzen; die drei Mindestmaßfälle
schlugen vor der Behebung fehl und bestehen jetzt mit unveränderten Erwartungen.

## Behebung und Regressionen

- **V1:** Gemeinsame Sortierung für Laufzeit- und initiale Property-Zugriffe.
  Sortierung ohne Beachtung der Groß-/Kleinschreibung; ursprüngliche Indizes
  erhalten die Auswahl auch zwischen gleichen Texten. Regressionen prüfen
  beide Klassen, alle ComboBox-Stile, Basis-/Arrayindex 0 und dynamische
  Arrayelemente, wiederholtes Sortieren sowie anschließendes Einfügen/Entfernen.
  Der Formular-Korpus prüft jetzt das Einschalten von Sorted nach dem Befüllen
  mit unveränderter Sollausgabe.
- **V2:** Simple verwendet den bestehenden Listenrenderer samt Rahmen und
  Auswahlmarkierung. Der Bildschirmtest prüft tatsächliche Einträge, Auswahl,
  Entfernen und erhaltenen Eingabetext. V3 ergänzt die Mindestmaßabnahme.
- **V3:** `list_lines` begrenzt sämtliche Rahmenzeilen auf die angeforderte
  Breite und Höhe, auch vor dem frühen Rücksprung für schmale Controls.
  Alle Aufrufer (ListBox, Simple-ComboBox, DirListBox und FileListBox)
  verwenden diese Korrektur. Die gemeinsame Textkürzung zählt dieselben
  Zellenbreiten wie TextScreen und behandelt Steuerzeichen als Leerzeichen.
  `TextScreen::print_line` verwendet den vorhandenen Zellenschreibpfad ohne
  Umbruch oder Scrollen; `write_at` nutzt ihn für Forms-Ausgaben.
  Das normale BASIC-PRINT behält Umbruch und Scrollen.

Die zusätzliche Regression
`listenrenderer_haelt_zellgrenzen_auch_an_bildschirmraendern_ein` vergleicht
alle Bildschirmzellen außerhalb des jeweiligen Controlrechtecks einschließlich
Farben und Unicode-Fortsetzungsmarkierung. **3.360 Kombinationen bestehen:**
ListBox, Combo-Stile 0/1/2, DirListBox, FileListBox und DriveListBox;
Basis- und dynamische Arrayelemente; Breiten 1/2/3/6/15/254 und Höhen
1/2/3/5/254; vier Positionen einschließlich rechtem/unterem Bildschirmrand
und vollständig außerhalb; leere und gefüllte Listen mit langen Texten,
breiten Unicode-Zeichen und CR/LF. Vor der Korrektur schlug dieser Test
bereits bei einer ListBox 1×1 fehl.

`ui_zeile_bleibt_am_rand_ohne_umbruch_oder_scrollen` prüft zusätzlich
vollständig passende und nicht passende breite Zeichen, Farben, CR/LF
sowie den rechten/unteren Rand und einen eigenen VIEW-PRINT-Bereich.
Bestehende Zeichenbild-, Auswahl-, Timer-, VM- und Korpustests bestehen
unverändert in ihren Erwartungen.

## Ursprüngliche Befunde — historischer Stand vor der Behebung

Die folgenden Ist-Beobachtungen dokumentieren den fehlgeschlagenen Review.
Beide Befunde sind durch die Nachweise oben geschlossen.

### V1: Nachträgliches Sorted lässt den Listeninhalt unsortiert

**Reproduktion:** In ListBox oder ComboBox zuerst `b`, dann `a` hinzufügen,
Eintrag 0 (`b`) auswählen, anschließend `Sorted = -1` setzen und `c`
hinzufügen. **Ist:** `List = [b, a, c]`, ListIndex = 0.
**Soll:** sortierte Liste `[a, b, c]` mit weiterhin ausgewähltem `b`, Index 1.

Der [Property-Setter](../../../../crates/tb-ui/src/forms.rs#L430) speichert
SORTED ohne Sortierung des vorhandenen Inhalts. [ADDITEM](../../../../crates/tb-ui/src/forms.rs#L1025)
verwendet anschließend `partition_point`, das bereits sortierte Daten
voraussetzt. Der Fehler tritt bei beiden Klassen auf.

**Vertrag:** Delta „Steuerelemente mit Listeninhalt“, Sortierung bei
Sorted = −1 und Konsistenz von List/ListIndex/Text; Task 1.1.
Die Tests setzen Sorted ausschließlich vor dem Befüllen und decken den
zulässigen Wechsel an einer gefüllten Liste nicht ab.

**Behebung:** Beim Einschalten der Sortierung vorhandene Einträge sortieren
und die Auswahl ihrem bisherigen Eintrag zuordnen; anschließend Einfügen
und Entfernen weiterhin konsistent halten. Regression für beide Klassen,
einschließlich der Auswahl, ergänzen.

### V2: ComboBox.Style 1 zeichnet keine Listeneinträge

**Reproduktion:** Sichtbare ComboBox mit Style = 1, Width = 15, Height = 5,
Text = `frei`; ADDITEM `apfel` und `birne`. **Ist:** Der Bildschirm zeigt
`frei` und einen leeren eingerahmten Listenbereich. **Soll:** Die beiden
Einträge erscheinen in diesem ausreichend großen Bereich.

Der [Renderer](../../../../crates/tb-ui/src/forms.rs#L2184) hängt für Style 1
nur `box_lines` mit leerem Inhalt an. Er liest die vorhandenen Listeneinträge
für diesen Bereich nicht. Die Probe prüft zuerst, dass das Control über den
Text `frei` tatsächlich sichtbar ist, und danach die beiden Listeneinträge.

**Vertrag:** Delta „Steuerelemente mit Listeninhalt“ verlangt die Darstellung
aller drei ComboBox-Stile; die [Forms-Referenz](../../../../docs/forms-referenz.md#L180)
zeigt für Simple einen sichtbaren Listeneintrag. Task 1.1 verlangt die Prüfung
aller Stile. Der bestehende Stiltest prüft nur Zeilenzahl und Präfix;
die neuen Auswahltests prüfen nur die oberste Zeile.

**Behebung:** Den vorhandenen Listeninhalt samt Auswahl im Simple-Bereich
darstellen und dessen Inhalt mit einem Bildschirmtest absichern. Die bereits
festgelegten Zeichenformen beibehalten.

Beide Lücken bestanden bereits vor diesem Change. Es handelt sich um
verbleibende Abweichungen im übernommenen Listen-Requirement, nicht um neu
eingeführte Regressionen. Die Korrekturen wurden nach diesem Review umgesetzt.

## Szenarioabdeckung und positive Nachweise

| Delta-Szenario | Nachweis |
| --- | --- |
| Element hinzufügen | Bestehender Sorted-Listentest und neue Auswahlmatrix: a/b, ListCount und List(i). Nachträgliches Einschalten einschließlich stabiler Auswahl durch V1-Regressionen abgesichert. |
| Keine Auswahl | ListIndex −1 bei leerer Liste und nach Entfernen der Auswahl. |
| Auswahl ausdrücklich aufheben | Modellmatrix für ListBox und ComboBox-Stile 0/1/2; Formular-Korpus. |
| Eintrag vor Auswahl einfügen | Sortierte/unsortierte Modellmatrix prüft Indexverschiebung und erhaltenen Text. |
| Timer feuert im Abstand | Zwei Ereignisse nach 250 ms aktiver Zeit, mehrere Timer und Arrayelemente, identische Wiederholung. |
| Interval 0 schaltet ab | Modelltest, Arrayindex-0-Reset und Änderung im VM-Handler. |
| Späte Aktivierung | VM: direkte Property, Control-Parameter und Enabled; Aktivierung bei 1000, still bei 1099, einmal bei 1100 ms. Zusätzliche erfolgreiche Probe für spät geladenes Timer-Arrayelement. |
| Abschalten verwirft anstehende Ereignisse | Interval/Enabled-Reset, Array-Entladen und Handlerwechsel mit altem Rückstau. |

Erneut ausgeführt:

- `cargo test --workspace`: **428 bestanden, 0 fehlgeschlagen, 1 ignoriert**.
  Ignoriert bleibt der Generator für Sollausgaben; keine Golden Files geändert.
- `openspec validate forms-zustand-und-bedienung --strict`: gültig.
- `git diff --check`: erfolgreich.
- [Zusätzliche Review-Proben](review-probes.rs): **4 bestanden, 0 fehlgeschlagen**.

`cargo clippy -p tb-ui -p tb-vm -p tb-runtime --all-targets --all-features -- -D warnings`
und `cargo fmt --all -- --check` wurden nach der Behebung erfolgreich ausgeführt. Kein interaktiver Terminal-/VBDOS-Vergleich und
keine Windows-/Linux-Matrix. Die Darstellungsprobe prüft den echten Zellenpuffer
ohne Terminal; Timerprüfungen verwenden ausschließlich virtuelle Hostzeit.

## Review-Proben reproduzieren

Im Repository-Wurzelverzeichnis ausführen; erzeugt einen separaten temporären
Cargo-Treiber. Auf dem korrigierten Stand bestehen alle vier Proben.

```sh
python3 - <<'PY'
from pathlib import Path
import json, subprocess, tempfile
root = Path.cwd()
p = Path(tempfile.mkdtemp(prefix='tb-forms-review-'))
manifest = '[package]\nname="tb-forms-review"\nversion="0.0.0"\nedition="2021"\n[workspace]\n'
manifest += '[[test]]\nname="probes"\npath=' + json.dumps(str(root / 'openspec/changes/archive/2026-09-05-forms-zustand-und-bedienung/review-probes.rs')) + '\n[dependencies]\n'
for name in ['tb-ui', 'tb-vm', 'tb-runtime', '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'), '--offline', '--test', 'probes', '--', '--nocapture'], check=True, timeout=120)
PY
```
