Die kb_dir-Fixture repointet config.ROOT nicht - ein Test schreibt dadurch ins echte Repo #44
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Erledigt in 4.1.1 (
23307c3). Der Befund unten ist die Diagnose, die stehen bleibt; die Entscheidungen sind getroffen und umgesetzt. Ein Prozess-Nachtrag ging als 4.1.2 (41f5dfe) hinterher — siehe „Nachtrag" am Ende.Der Befund
tools/chemenu/tests/conftest.pyhatte zwei Fixtures, die eine Baumwurzel aufbauen, und nur eine von beiden richteteconfig.ROOTmit aus:config.ROOT(vor 4.1.1)raw_dirmonkeypatch.setattr(config, "ROOT", tmp_path)kb_dirconfig.ROOTtraf das echte Repokb_dirbaute seinen Baum untertmp_path / "kb"auf und gab ihn zurück. Das reicht für jeden Test, der den Pfad als Argument weiterreicht (run_lint(kb_dir)). Es reicht nicht, sobald der Code unter Test eine Datei überconfig.ROOToderconfig.KB_DIRauflöst statt über das übergebene Verzeichnis — und das tat er an mehreren Stellen.Wie es aufgefallen ist
Beim Bau der Migrations-Gate-Tests für 4.1.0. Ein Test rief
kb_state.write_kb_state(...), das überkb_state_file() -> config.ROOT / ".wikitool-kb.json"auflöst. Ergebnis:Der Applied-Ledger des echten Repos war überschrieben —
applied: []statt der beiden Einträge für3.0.0-authoring-conventionsund4.0.0-link-taxonomy. Zurückgeholt mitgit checkout --, die Tests auf eine lokalerooted_kb-Fixture umgestellt.Das war eine unversionierte, git-verfolgte Datei, also war der Schaden reversibel und sofort sichtbar. Bei einer gitignorierten Datei — oder in einem Baum mit uncommitteten Änderungen — wäre er es nicht gewesen.
Der zweite, stillere Fall
Derselbe Mechanismus machte Prüfungen wirkungslos, statt Dateien zu überschreiben.
lint_core._collection_of()löste eine Seite gegenconfig.KB_DIRauf, nicht gegen das anrun_lint()übergebene Verzeichnis:Für eine Seite unter
tmp_path/kb/wirft dasValueError, die Funktion gabNonezurück, und die Label-Autorisierungsprüfung übersprang die Kante stillschweigend. Das hieß:unauthorised_labelswar in der Suite faktisch nicht getestet — jeder Test, der es hätte auslösen können, bekam eine leere Liste zurück und behauptete nichts.Das war die gefährlichere Hälfte. Ein überschriebenes
.wikitool-kb.jsonsieht man ingit status. Eine Prüfung, die unter Test nie feuert, sieht man gar nicht, und ihr grüner Testlauf liest sich wie eine Zusicherung.Warum das nicht die Autouse-Fixture erledigt
hermetic_environmentneutralisiert die Umgebung —HOME,XDG_CONFIG_HOME, die Git-Konfiguration,CHEMENU_ROOT.config.ROOTfällt bei fehlendemCHEMENU_ROOTauf_PACKAGE_ROOTzurück, also auf den Checkout, aus dem die Tests laufen. Das ist für die meisten Tests richtig — sie brauchen die ausgeliefertentypes/— und genau deshalb greift die Isolation hier nicht von selbst.configs Modul-__getattr__löst die abgeleiteten Pfade bei jedem Zugriff auf,monkeypatch.setattr(config, "ROOT", tmp_path)ziehtKB_DIRalso mit. Der Mechanismus war vorhanden;kb_dirbenutzte ihn nur nicht.Entschieden und umgesetzt
kb_dirrepointet selbst (Variante 1, nicht die getrennterooted_kb-Fixture). Genau das, wasraw_dirschon tat:config.ROOTauf das eigenetmp_path, plususe_shipped_type_specs(). Der befürchtete Umfang wurde gemessen statt geschätzt — kein einziger Test kippte (863 vorher). Variante 2 wurde verworfen, weil sie die Falle stehen lässt und die Auswahl zwischen zwei fast identischen Fixturen Wissen wäre, das nirgends steht.rooted_kbist ersatzlos entfallen._collection_of()löst gegen den übergebenen Baum auf, undauthorised_labels()bekommt denselben Baum gereicht statt aufconfig.KB_DIRzurückzufallen. Das ist der eigentliche Fix: eine Funktion, die ein Verzeichnis entgegennimmt, löst dagegen auf — keine Fixture kann diese Form von außen reparieren.Regressionstest:
test_unauthorised_label_is_judged_in_a_tree_that_is_not_the_configured_kblintet einen Baum, von demROOTbewusst wegzeigt. Gegen den alten Code schlägt er fehl (verifiziert), gegen den neuen nicht. Ein Test unter der reparierten Fixture allein hätte nichts bewiesen — dort istkb_dir == config.KB_DIR, und der Defekt bliebe unsichtbar.Wächter: ja, zweistufig.
repository_tree_guard(session)git status --porcelainpro Laufper_test_tree_guardCHEMENU_TREE_GUARD=eachgit statuspro TestDer Session-Wächter vergleicht vorher gegen nachher statt einen sauberen Baum zu verlangen, sagt also nichts über unveröffentlichte Arbeit des Entwicklers. Er benennt den Verursacher nicht — dafür ist
CHEMENU_TREE_GUARD=eachda. Ohne git oder außerhalb eines Repositorys sind beide still. Beide gegen eine absichtlich schreibende Probe verifiziert.Bewusst nicht abgedeckt: die stillere Hälfte. Eine Prüfung, die unter Test nichts tut, schreibt keine Datei — kein Wächter dieser Bauart sieht das. Dagegen hilft nur ein Test, der das Finding tatsächlich auslöst. Diese Grenze steht in
testing-conventions.md, damit sie nicht als abgedeckt missverstanden wird.Checkliste
_collection_of()gegen den übergebenen Baum aufgelöstunauthorised_labelstatsächlich auslöstinstructions/dev/testing-conventions.mdergänzt (Abschnitt „Which tree a test writes into", ein Checklisten-Schritt, drei Fixture-Tests intest_hermetic_env.py)--patch→ 4.1.1Verifiziert
867 Tests grün, auch im gehärteten
env -i-Lauf;docs verifyundinstructions verifyOK; der Regressionstest gegen den alten Code als fehlschlagend nachgewiesen; beide Wächter gegen eine schreibende Probe getestet.Nachtrag: der Prozessfehler beim Schließen (4.1.2)
Dieser Issue wurde zunächst mit einem Abschlusskommentar geschlossen, während der Body weiter als offene Arbeit dastand — „Zu entscheiden" über eine längst getroffene Entscheidung, ungehakte Checkliste. Das ist genau der Fehler, den
instructions/dev/issue-tracking.mdSchritt 2 verhindern soll.Daraus wurde die Regel geschärft statt nur der Fall repariert (4.1.2,
41f5dfe):wikitoolkennt den Tracker nicht und soll ihn nicht kennenlernen (es wird an Instanzen ausgeliefert, die kein Board haben;instructions/dev/wird vondist exportgepruned). Es gibt also keine mechanische Prüfung für einen Body, der offen klingt, obwohl er es nicht ist — das fängt nur eine Session ab, die dieser Datei folgt.stack-dev/SKILL.mdsagt es beim Aufgreifen mit.Vorgeschichte
Gefunden am 2026-09-03 beim Bau der Migrations-Gate-Tests für 4.1.0 (#40). Geschlossen am 2026-09-03 mit 4.1.1; Prozess-Nachtrag als 4.1.2.
Umgesetzt in 4.1.1 (
23307c3)Entscheidung 1, nicht 2.
kb_dirrepointet jetzt selbst —config.ROOTauf das eigenetmp_path, plususe_shipped_type_specs(), also genau das, wasraw_dirschon tat. Der befürchtete Umfang war messbar statt geschätzt: kein einziger Test kippte (863 vorher, alle grün nachher). Die lokalerooted_kb-Umgehung aus 4.1.0 ist damit ersatzlos entfallen; die Auswahl zwischen zwei fast identischen Fixturen war Wissen, das nirgends stand.Punkt 3 ist der eigentliche Fix.
_collection_of()löst gegen das anrun_lint()übergebene Verzeichnis auf, undauthorised_labels()bekommt denselben Baum gereicht statt aufconfig.KB_DIRzurückzufallen. Regressionstest:test_unauthorised_label_is_judged_in_a_tree_that_is_not_the_configured_kblintet einen Baum, von demROOTbewusst wegzeigt — gegen den alten Code schlägt er fehl (verifiziert), gegen den neuen nicht. Das ist der Beleg, den die Checkliste verlangt hat: hätte die Fixture allein gefixt, wärekb_dir == config.KB_DIRund der Defekt weiterhin unsichtbar.Punkt 4, Wächter: ja, zweistufig.
repository_tree_guard(session)git status --porcelainpro Laufper_test_tree_guardCHEMENU_TREE_GUARD=eachgit statuspro TestDer Session-Wächter vergleicht vorher gegen nachher statt einen sauberen Baum zu verlangen, sagt also nichts über unveröffentlichte Arbeit. Er benennt den Verursacher nicht — dafür ist
CHEMENU_TREE_GUARD=eachda. Ohne git oder außerhalb eines Repositorys sind beide still. Beide gegen eine absichtlich schreibende Probe verifiziert.Was der Wächter nicht abfängt: die stillere Hälfte. Eine Prüfung, die unter Test nichts tut, schreibt keine Datei. Dagegen hilft nur ein Test, der das Finding tatsächlich auslöst — steht so auch in den Conventions.
Dokumentiert in
instructions/dev/testing-conventions.md, neuer Abschnitt „Which tree a test writes into" plus ein Schritt in der Checkliste: eine neue Fixture, die einen Baum baut, repointetROOT. Dazu drei Tests intest_hermetic_env.py, die für beide Korpus-Fixturen festhalten, dass sie es tun.Version:
--patch(4.1.1) — Punkt 3 ist ein Fix ohne Interface-Änderung, der Rest ist Test und Doku._collection_of()gegen den übergebenen Baumunauthorised_labelstatsächlich auslösttesting-conventions.mdergänzt--patch-BumpVerifiziert: 867 Tests grün, auch im gehärteten
env -i-Lauf;docs verifyundinstructions verifyOK.Changelog: Body auf den Endstand gezogen — er stand nach dem Schließen noch als offene Arbeit da (Abschnitt „Zu entscheiden", ungehakte Checkliste), obwohl entschieden und umgesetzt. „Zu entscheiden" → „Entschieden und umgesetzt" mit der jeweiligen Begründung, Checkliste abgehakt, Abschnitt „Verifiziert" ergänzt, Diagnose ins Präteritum. Der vorige Kommentar bleibt als Umsetzungsbericht stehen; inhaltlich neu ist nichts.
Changelog: Abschnitt „Nachtrag: der Prozessfehler beim Schließen (4.1.2)" ergänzt — der falsch geschlossene Body dieses Issues war der Anlass,
issue-tracking.mdSchritt 2/3/7 zu schärfen (Body = Plan-File, fortlaufend aktuell; Abschluss ist die letzte Aktualisierung; ein Kommentar pro Session-Umfang) und den Abschnitt „What no tool checks" aufzunehmen. Kopfzeile verweist darauf. Sonst unverändert.