docs verify prüft die beiden Tabellen in tools/CONTRACT.md nicht getrennt - Fehlerkontrakt gar nicht #91

Closed
opened 2026-09-11 09:34:27 +00:00 by torben · 1 comment
Owner

Befund

check_cli_readme() in tools/chemenu/commands/docs_verify.py glich die registrierten CLI-Kommandos gegen tools/CONTRACT.md ab. Der Regex, mit dem es die dokumentierten Kommandos einsammelte, kannte keine Abschnittsgrenzen:

# tools/chemenu/commands/docs_verify.py:179 (vor dem Fix)
TABLE_CELL_RE = re.compile(r"^\|\s*`([^`]+)`", re.MULTILINE)

def documented_commands(readme_text: str) -> list[str]:
    return [match.group(1).strip() for match in TABLE_CELL_RE.finditer(readme_text)]

tools/CONTRACT.md hat aber zwei Tabellen mit Kommandonamen in der ersten Spalte: § Commands (ab Zeile 44) und § Error contracts (ab Zeile 188). Der Check sah beide als einen Topf. Zwei Konsequenzen, beide gemessen:

  1. Eine gelöschte Zeile in § Commands fiel nicht auf, solange der Name noch in § Error contracts stand.
  2. § Error contracts wurde für nichts erzwungen. Gemessen am Baum vor dem Fix (55 registrierte Kommandos): § Commands war vollständig, in § Error contracts fehlten 10 Kommandos - und docs verify war grün: budget reset, instructions list, instructions verify, log append, migrate status, sources rebuild-index, sources trace, types describe, upload show, version notes.

Ursache der 10 Lücken (beim Umsetzen gefunden)

Keine zehn unabhängigen Auslassungen: acht der zehn Kommandos teilten sich in § Error contracts eine Zeile der Form `a` / `b` mit einem Geschwisterkommando - TABLE_CELL_RE liest je Zeile nur das erste Backtick-Wort, der Rest der Gruppe war für den (ohnehin ungeprüften) Check unsichtbar. log append war die neunte Lücke aus einem anderen Grund: seine Zeile stand im Quelltext ohne Zeilenumbruch hinter der von index rebuild / sources rebuild-index verschmolzen - für Menschen kaum als Tabelle lesbar, für die zeilenanfang-verankerte Regex unsichtbar. Beim Aufteilen dieser Gruppen fielen zusätzlich drei sachlich falsche Zeilen auf: sources coverage, types list, instructions list und budget status schlagen nie fehl, die alte gemeinsame Zeile behauptete pauschal das Gegenteil, weil sie das Verhalten des jeweils benachbarten Kommandos mit übernahm. Alle betroffenen Zeilen wurden einzeln aufgeteilt und gegen den tatsächlichen Code (run_budget.py, instructions_cmd.py, log_append.py, migrate_cmd.py, provenance_cmd.py, types_cmd.py, upload_cmd.py/upload.py, version_cmd.py) nachgesehen statt aus dem Nachbarn übernommen.

Umsetzung

check_cli_readme() liest beide Tabellen jetzt über ihre ##-Überschrift ab (neue Funktion section_text()), unabhängig voneinander und je Tabelle in beide Richtungen. Fehlt eine der beiden Überschriften, meldet die Funktion das explizit statt stillschweigend auf "ganzes Dokument" zurückzufallen.

Akzeptanzkriterien

  • documented_commands()-Ersatz liest die Kommandotabelle abschnittsweise (§ Commands / § Error contracts getrennt geführt). Regressionstest: test_a_row_deleted_from_commands_is_caught_even_if_error_contracts_still_has_it.
  • Abschnittserkennung hängt an den ##-Überschriften, nicht an Zeilennummern, und schlägt mit klarer Meldung fehl, wenn eine Überschrift fehlt - kein stillschweigendes "alles gescannt". Tests: test_a_renamed_commands_heading_is_reported_not_silently_scanned, test_a_renamed_error_contracts_heading_is_reported_not_silently_scanned, plus drei Unit-Tests für section_text() selbst.
  • Jedes registrierte Kommando hat eine Zeile in § Error contracts, beide Richtungen geprüft. Tests: test_error_contracts_is_enforced_against_registered_commands, test_a_phantom_error_contract_row_is_reported.
  • Die 10 Kommandos haben eine Fehlerkontrakt-Zeile mit echtem Inhalt (Exit-1-Bedeutung, Atomarität, Retry-Policy), kein Platzhalter.
  • Gegen jede der 10 nachgetragenen Zeilen wurde das tatsächliche Verhalten im Code nachgesehen (siehe "Ursache der 10 Lücken" oben) - dabei auch die vier sachlich falschen Nachbarzeilen korrigiert, die sonst als Kollateralschaden stehen geblieben wären.
  • Die docs verify-Zeile in tools/CONTRACT.md (§ Commands) nennt jetzt beide Tabellen, beide Richtungen je Tabelle, und dass nur die Präsenz des Namens geprüft wird, nie der Zellentext.
  • pytest in tools/ grün (1193 passed, inkl. 9 neuer Tests in test_docs_verify.py); docs verify und instructions verify grün.
  • Versionsbump --minor (5.0.0-beta.12 → 5.0.0-beta.13) mit Changelog-Body, der die Fremdinstanz-Ausnahme (eigenes, abweichendes tools/CONTRACT.md) explizit benennt - gegen version-parts.md geprüft: additiv, drop-in in beide Richtungen, kein neues Boundary-Crossing.

Nicht in diesem Paket: Flag-Parität

Naheliegend, weil Typer die deklarierten Parameter eines Kommandos kennt und ein neues --flag heute für docs verify vollständig unsichtbar ist (AGENTS.md sagt selbst „a README goes stale on every new flag"). Trägt aber in dieser Form nicht: eine Tabellenzelle ist eine Einzeiler-Beschreibung und nennt nie alle Flags - die stehen in den Prosa-Abschnitten darunter. Ein solcher Check bräuchte ein Modell der Abschnittsgrenzen je Kommando, nicht nur je Tabelle, und müsste entscheiden, welche Flags dokumentationspflichtig sind (--help? geerbte?).

Weiterhin offene Designfrage, eigenes Issue, falls es aufgegriffen wird. Der hier gebaute abschnittsweise Leser (section_text()) ist die Vorarbeit dafür.

Verifiziert

tools/wikitool docs verify, tools/wikitool instructions verify, volle pytest-Suite (1193 passed). Publiziert als Commit 2030844.

Modell-Handover

Design, Umsetzung, Tests und Versionsbump liefen in einer Sitzung auf Sonnet 5 bei Effort high - kein Moment, an dem die Sitzung zwischen Design und mechanischer Phase umschalten musste, weil das Issue bereits vollständig spezifiziert war (kind/build, keine offenen Designfragen). Die Closing-Phase (dieser Body, stack-close) lief ebenfalls auf Sonnet 5.

## Befund `check_cli_readme()` in `tools/chemenu/commands/docs_verify.py` glich die registrierten CLI-Kommandos gegen `tools/CONTRACT.md` ab. Der Regex, mit dem es die dokumentierten Kommandos einsammelte, kannte keine Abschnittsgrenzen: ```python # tools/chemenu/commands/docs_verify.py:179 (vor dem Fix) TABLE_CELL_RE = re.compile(r"^\|\s*`([^`]+)`", re.MULTILINE) def documented_commands(readme_text: str) -> list[str]: return [match.group(1).strip() for match in TABLE_CELL_RE.finditer(readme_text)] ``` `tools/CONTRACT.md` hat aber **zwei** Tabellen mit Kommandonamen in der ersten Spalte: § Commands (ab Zeile 44) und § Error contracts (ab Zeile 188). Der Check sah beide als einen Topf. Zwei Konsequenzen, beide gemessen: 1. **Eine gelöschte Zeile in § Commands fiel nicht auf**, solange der Name noch in § Error contracts stand. 2. **§ Error contracts wurde für nichts erzwungen.** Gemessen am Baum vor dem Fix (55 registrierte Kommandos): § Commands war vollständig, in § Error contracts fehlten **10** Kommandos - und `docs verify` war grün: `budget reset`, `instructions list`, `instructions verify`, `log append`, `migrate status`, `sources rebuild-index`, `sources trace`, `types describe`, `upload show`, `version notes`. ## Ursache der 10 Lücken (beim Umsetzen gefunden) Keine zehn unabhängigen Auslassungen: acht der zehn Kommandos teilten sich in § Error contracts eine Zeile der Form `` `a` / `b` `` mit einem Geschwisterkommando - `TABLE_CELL_RE` liest je Zeile nur das *erste* Backtick-Wort, der Rest der Gruppe war für den (ohnehin ungeprüften) Check unsichtbar. `log append` war die neunte Lücke aus einem anderen Grund: seine Zeile stand im Quelltext ohne Zeilenumbruch hinter der von `index rebuild` / `sources rebuild-index` verschmolzen - für Menschen kaum als Tabelle lesbar, für die zeilenanfang-verankerte Regex unsichtbar. Beim Aufteilen dieser Gruppen fielen zusätzlich drei sachlich falsche Zeilen auf: `sources coverage`, `types list`, `instructions list` und `budget status` schlagen nie fehl, die alte gemeinsame Zeile behauptete pauschal das Gegenteil, weil sie das Verhalten des jeweils benachbarten Kommandos mit übernahm. Alle betroffenen Zeilen wurden einzeln aufgeteilt und gegen den tatsächlichen Code (`run_budget.py`, `instructions_cmd.py`, `log_append.py`, `migrate_cmd.py`, `provenance_cmd.py`, `types_cmd.py`, `upload_cmd.py`/`upload.py`, `version_cmd.py`) nachgesehen statt aus dem Nachbarn übernommen. ## Umsetzung `check_cli_readme()` liest beide Tabellen jetzt über ihre `##`-Überschrift ab (neue Funktion `section_text()`), unabhängig voneinander und je Tabelle in beide Richtungen. Fehlt eine der beiden Überschriften, meldet die Funktion das explizit statt stillschweigend auf "ganzes Dokument" zurückzufallen. ## Akzeptanzkriterien - [x] `documented_commands()`-Ersatz liest die Kommandotabelle abschnittsweise (§ Commands / § Error contracts getrennt geführt). Regressionstest: `test_a_row_deleted_from_commands_is_caught_even_if_error_contracts_still_has_it`. - [x] Abschnittserkennung hängt an den `##`-Überschriften, nicht an Zeilennummern, und schlägt mit klarer Meldung fehl, wenn eine Überschrift fehlt - kein stillschweigendes "alles gescannt". Tests: `test_a_renamed_commands_heading_is_reported_not_silently_scanned`, `test_a_renamed_error_contracts_heading_is_reported_not_silently_scanned`, plus drei Unit-Tests für `section_text()` selbst. - [x] Jedes registrierte Kommando hat eine Zeile in § Error contracts, beide Richtungen geprüft. Tests: `test_error_contracts_is_enforced_against_registered_commands`, `test_a_phantom_error_contract_row_is_reported`. - [x] Die 10 Kommandos haben eine Fehlerkontrakt-Zeile mit echtem Inhalt (Exit-1-Bedeutung, Atomarität, Retry-Policy), kein Platzhalter. - [x] Gegen jede der 10 nachgetragenen Zeilen wurde das tatsächliche Verhalten im Code nachgesehen (siehe "Ursache der 10 Lücken" oben) - dabei auch die vier sachlich falschen Nachbarzeilen korrigiert, die sonst als Kollateralschaden stehen geblieben wären. - [x] Die `docs verify`-Zeile in `tools/CONTRACT.md` (§ Commands) nennt jetzt beide Tabellen, beide Richtungen je Tabelle, und dass nur die Präsenz des Namens geprüft wird, nie der Zellentext. - [x] `pytest` in `tools/` grün (1193 passed, inkl. 9 neuer Tests in `test_docs_verify.py`); `docs verify` und `instructions verify` grün. - [x] Versionsbump `--minor` (5.0.0-beta.12 → 5.0.0-beta.13) mit Changelog-Body, der die Fremdinstanz-Ausnahme (eigenes, abweichendes `tools/CONTRACT.md`) explizit benennt - gegen `version-parts.md` geprüft: additiv, drop-in in beide Richtungen, kein neues Boundary-Crossing. ## Nicht in diesem Paket: Flag-Parität Naheliegend, weil Typer die deklarierten Parameter eines Kommandos kennt und ein neues `--flag` heute für `docs verify` vollständig unsichtbar ist (`AGENTS.md` sagt selbst „a README goes stale on every new flag"). Trägt aber in dieser Form nicht: eine Tabellenzelle ist eine Einzeiler-Beschreibung und nennt nie alle Flags - die stehen in den Prosa-Abschnitten darunter. Ein solcher Check bräuchte ein Modell der Abschnittsgrenzen **je Kommando**, nicht nur je Tabelle, und müsste entscheiden, welche Flags dokumentationspflichtig sind (`--help`? geerbte?). Weiterhin offene Designfrage, eigenes Issue, falls es aufgegriffen wird. Der hier gebaute abschnittsweise Leser (`section_text()`) ist die Vorarbeit dafür. ## Verifiziert `tools/wikitool docs verify`, `tools/wikitool instructions verify`, volle `pytest`-Suite (1193 passed). Publiziert als Commit `2030844`. ## Modell-Handover Design, Umsetzung, Tests und Versionsbump liefen in einer Sitzung auf Sonnet 5 bei Effort `high` - kein Moment, an dem die Sitzung zwischen Design und mechanischer Phase umschalten musste, weil das Issue bereits vollständig spezifiziert war (`kind/build`, keine offenen Designfragen). Die Closing-Phase (dieser Body, `stack-close`) lief ebenfalls auf Sonnet 5.
torben added the prio/plannedsize/Marea/processkind/build labels 2026-09-11 09:34:27 +00:00
Author
Owner

Changelog: Alle Akzeptanzkriterien umgesetzt und abgehakt. Neuer Abschnitt "Ursache der 10 Lücken" ergänzt das Befund: acht der zehn Lücken waren `a` / `b`-gruppierte Zeilen (nur das erste Backtick-Wort zählte), eine (log append) eine verschmolzene Zeile ohne Zeilenumbruch; beim Aufteilen wurden vier zusätzliche sachlich falsche Nachbarzeilen korrigiert (sources coverage, types list, instructions list, budget status schlagen nie fehl). Versionsbump 5.0.0-beta.12 → 5.0.0-beta.13, Commit 2030844.

**Changelog:** Alle Akzeptanzkriterien umgesetzt und abgehakt. Neuer Abschnitt "Ursache der 10 Lücken" ergänzt das Befund: acht der zehn Lücken waren `` `a` / `b` ``-gruppierte Zeilen (nur das erste Backtick-Wort zählte), eine (`log append`) eine verschmolzene Zeile ohne Zeilenumbruch; beim Aufteilen wurden vier zusätzliche sachlich falsche Nachbarzeilen korrigiert (`sources coverage`, `types list`, `instructions list`, `budget status` schlagen nie fehl). Versionsbump 5.0.0-beta.12 → 5.0.0-beta.13, Commit `2030844`.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: torben/chemenu#91