fix: pytest sammelt aus der Repo-Wurzel wieder, plus sieben Folgefixe - #151
Merged
Conversation
added 8 commits
August 18, 2026 22:05
…raus, Abdeckung groesser statt kleiner
Befund PB-PYTEST-WURZEL-LAEUFT-NICHT-01. `pytest` aus der Wurzel brach mit 740 Sammelfehlern ab.
Gemessene Ursache, kein Testfehler: die entpackten sdist-Baeume unter `dist_pkgtest*/` tragen
dieselben Modul-Basenamen wie `tests/` ("import file mismatch: imported module 'test_signature'
has this __file__ attribute: dist_pkgtest/tree/tests/test_signature.py"). `build/` und `dist/`
sind pytest-Default, die `dist_*`-Varianten NICHT — der Default matcht `dist` exakt.
`norecursedirs` statt `testpaths`, und zwar als bewusst SCHMALERER Eingriff: ausgeschlossen wird,
was Bauartefakt ist, statt die Sammlung auf ein Verzeichnis zu verengen. Die naheliegende
Begruendung dagegen (scripts/test_manifest_gate.py wuerde verloren) ist GEMESSEN widerlegt — die
Datei sammelt null Tests, sie ist ein Gate-Skript mit test-artigem Namen; der Kommentar sagt das
so, statt eine schoenere Begruendung zu behaupten.
Wirkung beidseitig gemessen (Config von origin/main gegen diesen Zweig, gleicher Baum):
vorher 2212 gesammelt + 740 Fehler, Abbruch
nachher 2295 gesammelt, 0 Fehler
Die Sammelmenge WAECHST — es geht nichts still verloren.
…kann nicht mehr still altern Befund PB-COSIGN-SIGN-SIDE-NEVER-RAISE-COVERAGE-01 (4. adversariale Linse des 4.0.0-Gates). DIE INSTANZ: `_NAME_PATTERN` ist eine Allowlist von Namensfamilien, und die gesamte `cosign_*`- Seite stand nie darin. Sie ist keine reine Erzeuger-Seite: `cosign_checkpoint` und `cosign_checkpoint_mldsa` nehmen eine vom LOG gelieferte, also untrusted, Note entgegen — genau die Eingabe, gegen die diese Eigenschaft schuetzt. Nenner gemessen 91 -> 97, alle sechs `cosign_*` drin; die Eigenschaft haelt (die Code-Klasse hatten iter1-5 bereits geschlossen). DIE KLASSE: der bestehende Bodentest wacht nur gegen KOLLAPS des Nenners (>= 65). Ein Nenner von 91 sieht gesund aus, auch wenn sechs Verbraucher-Surfaces danebenliegen — deshalb rutschten vier Nachbarn derselben Klasse nacheinander durch. Neu ist deshalb die andere Richtung: jede oeffentliche Funktion der gescannten Module muss ENTWEDER im Nenner stehen ODER namentlich als ausserhalb gefuehrt sein (122 Namen, nach Familie begruendet: Erzeuger / beschreibend / Urteile ueber bereits validierte Strukturen / Krypto-Primitive). Eine neue oeffentliche Funktion, die keins von beidem ist, laesst den Test failen und erzwingt die Entscheidung. EMPFINDLICHKEIT BEWIESEN, nicht behauptet: eine Klassifikation aus _OUT_OF_SCOPE entfernt -> Test FAILT mit dem Namen; byte-genau zurueckgelegt -> 6 passed. Nachbarsuiten 52 passed, ruff clean.
… verglichen BEFUND PB-EXPECTED-ORIGIN-ASCII-INKONSISTENZ-01. Die Log-Seite erzwingt fuer `origin` seit dem Homoglyph-Fix druckbares ASCII (positive Regel, nicht Kategorie-Aufzaehlung). Der vom Aufrufer gesetzte `--expected-origin` lief an dieser Regel vorbei: ein Pin mit KELVIN SIGN oder unsichtbarem Zeichen wurde stumm gegen die geprueften Werte gehalten und ergab ein "passt nicht", das wie eine Aussage ueber die NOTE aussah, obwohl die ERWARTUNG kaputt war. Bewusst BERICHTEND, nicht ablehnend: der Repo-eigene Test OriginVergleichIstExakt pinnt, dass ein Beinah-Treffer ein Verdikt mit unberuehrtem `inclusion_ok` liefern muss - ein hartes Nein waere dort ein Rueckschritt gewesen und wurde von genau diesem Test widerlegt. Das neue Feld `expected_origin_wellformed` (True/False/None) sagt es, ohne den Vergleich anzufassen. Der fail-closed-Pfad traegt DIESELBE Form. Ohne das brach die CLI dort mit KeyError ab und drei verschiedene Ursachen lieferten byte-identische Ausgabe; der Test, der diese Unterscheidbarkeit sichert, hat es gefangen. Sensitivitaet gemessen, nicht behauptet - drei eingepflanzte Defekte, jeder rot: Praedikat immer True (12 rot) - Feld auf dem fail-closed-Pfad entfernt (2 rot) - Praedikat wirft statt zu urteilen (never-raise-Familie rot). Die neue Funktion steht deshalb ausdruecklich IM Nenner der never-raise-Familie, nicht daneben. Volle Suite: 2292 passed, 7 skipped. ruff clean.
…Verifizierer akzeptiert
BEFUND PB-CHECKPOINT-CONSTRUCTOR-TYPEERROR-01 — nachgemessen statt geglaubt. Sein
eigener Kern (roher TypeError aus checkpoint_note/key_id/vkey/sign_checkpoint und
den vier cosign_*-Flaechen bei Nicht-bytes) ist durch das 4.0.0-Re-Gate iter5
LAENGST geschlossen: alle acht Flaechen werfen typisiert, gegen sieben Eingabearten
gemessen. Der Befund wird deshalb nicht "gefixt", sondern als erledigt belegt.
Dieselbe Messung legte einen NACHBARN derselben Klasse frei, den niemand gesucht
hatte: `checkpoint_note(root=b"")` kam durch, denn `b""` IST bytes. Die dritte
Notenzeile wurde damit leer, `sign_checkpoint` signierte das anstandslos, und
`verify_checkpoint` wie `_note_text_of` lehnten die frisch signierte Note danach
als "at least 3 non-empty lines" ab. Der Aufrufer haelt eine signierte Note in
der Hand, die KEIN Verifizierer akzeptiert, auch keiner ausserhalb dieser
Bibliothek. Der realistische Weg dorthin ist kein Tippfehler:
`root_bytes_from_b64("")` liefert `b""` und nicht `None`.
KLASSE STATT INSTANZ. Der Riss entsteht, weil Emit- und Verify-Regeln an zwei
Stellen gehalten werden; jede Regel, die nur die Pruefseite kennt, erzeugt ihn
erneut. tests/test_emitter_parser_rundlauf.py prueft deshalb generativ ueber ALLE
Emitter des Moduls: was ein Emitter ANNIMMT, darf sein eigener Parser nicht
malformed nennen. Der Sweep ueber sign_checkpoint (root/origin/tree_size),
cosign_checkpoint, vkey und cosign_vkey fand genau zwei auffaellige Faelle, beide
mit dieser einen Wurzel.
Anti-Tautologie ist Teil des Tests: eine Rundlauf-Eigenschaft ist ueber der leeren
Menge wahr. Jeder Emitter muss nachweisen, dass er ueberhaupt etwas angenommen hat.
Sensitivitaet gemessen, drei Proben, jede rot: Fix zurueckgenommen (5 rot) -
Emitter lehnt alles ab, Anti-Tautologie greift (4 rot) - Parser gelockert, sodass
der Rundlauf verstummt: der namentliche Pin haelt trotzdem (5 rot). Genau dafuer
steht er neben der generativen Eigenschaft.
EHRLICHE GRENZE, absichtlich nicht weiter zugezogen: ein nicht-leerer Root
falscher Laenge (5 Bytes) laeuft weiter durch, weil er den Rundlauf besteht - ein
Aufrufer-Fehler an den eigenen Wurzel-Bytes, kein angreifer-gelieferter Wert. Wo
das Format eine Laenge verlangt, steht sie schon (_mldsa_cosigned_message: 32).
Volle Suite: 2299 passed, 7 skipped. ruff clean.
…nie hatte Befund PB-PYTEST-WURZEL-LAEUFT-NICHT-01 war mit fdfc4e6 gefixt, aber von KEINEM Test gedeckt - der Fix lebte allein in pyproject.toml. Eine Konfigurationszeile ohne Test faellt still zurueck: sie verschwindet bei einem Merge-Konflikt, einem Werkzeugwechsel oder einer aufgeraeumten Liste, ohne dass etwas rot wird. Der Abschluss-Vertrag der Befund-Queue verlangt aus demselben Grund zwei aufloesbare Belege; ohne sie waere "geschlossen" ein Haken ohne Deckung gewesen. Drei absichtlich verschieden gebaute Tests: die Wirkung (die real erstickenden Verzeichnisse werden ausgeschlossen) - die Anti-Tautologie (ohne `dist_*` faellt die Deckung, sonst maesse der erste Test nur den pytest-Default, in dem `dist` EXAKT matcht und `dist_pkgtest` eben nicht) - die Gegenrichtung (ein zu breiter Riegel versteckt echte Tests: sauber, aber blind). DIE QUELLE IST PYTEST SELBST, und das ist eine Korrektur an meinem ersten Entwurf. Der las pyproject.toml mit `tomllib` und uebersprang sich auf diesem Interpreter (Python 3.10, tomllib ab 3.11) in DREI gruen aussehenden SKIPs - auch unter eingepflanztem Defekt. Er mass nichts und sah wie eine Deckung aus; die Sensitivitaetsprobe hat ihn gefangen, nicht der Lauf. `pytestconfig.getini` liest den Wert, den pytest WIRKLICH benutzt, und ein fehlender Wert ist hier ein Fehlschlag, nie ein Skip. Sensitivitaet gemessen, drei Proben, jede rot: `dist_*` entfernt (2 rot) - `tests` zusaetzlich ausgeschlossen (1 rot, die Gegenrichtung) - Liste geleert (3 rot).
…ei Tagen hat Befund PBNACHBAR-EMIT-TYPBODEN-01, am 2026-08-18 gegen den heutigen Stand nachgemessen statt aus der alten Akte uebernommen. Ergebnis: HALB geschlossen. `load_signer` hat den Boden am 16.08. bekommen, `save_signer` steht zwanzig Zeilen darueber, nimmt denselben aufrufer-gelieferten Pfad und hatte ihn nicht - `save_signer(echterSchluessel, 1)` warf einen rohen TypeError aus os.open. DER URSPRUENGLICHE BELEG MASS DEN FALSCHEN GEGENSTAND: er rief `save_signer(None, 1)` und schloss aus dem rohen Fehler auf den Pfad - der haette ebenso vom None-Schluessel kommen koennen. Mit echtem Schluessel gemessen liegt er wirklich am Pfad. Der Befund stimmte, seine Begruendung nicht ganz. WARUM ES DURCHRUTSCHTE, und das ist die Klasse: die never-raise-Familieneigenschaft entdeckt ueber Namensfamilien - `load_` steht darin, `save_` nicht. In der begruendeten Ausschlussmenge liegt `save_signer` als ERZEUGER, "baut aus EIGENEN, bereits geprueften Werten". Das trifft auf sein key-Argument zu und auf seinen PFAD gerade nicht. Eine Begruendung, die fuer EIN Argument stimmt, deckt die Funktion nicht. Die Ausschlussmenge bleibt deshalb richtig; der Beleg steht dort, wo der Gegenstand liegt. Der Boden selbst ist jetzt EINE Funktion, die beide benutzen - zwei Kopien waeren die naechste Drift. MEIN EIGENER TEST WAR ZUERST BEINAHE TAUTOLOGISCH, und die Sensitivitaetsprobe hat es gezeigt, nicht der Lauf: er zaehlte ein Zielverzeichnis vorher und nachher auf und blieb gruen, als ich einen Schreibvorgang VOR die Schranke setzte - bei `path=1` entsteht dort ohnehin nie eine Datei. Beobachtet wird jetzt der Gegenstand selbst: ein Attrappen-Schluessel merkt sich, ob `private_bytes` gefragt wurde. Der Seed darf gar nicht erst entstehen. Sensitivitaet gemessen, beide Proben rot: Boden entfernt (6 rot) - Boden hinter den Rohwert geschoben (1 rot, genau der neue Beobachter). Volle Suite: 2305 passed, 7 skipped. ruff clean.
…llen drei Stellen OWNER-ENTSCHEID 2026-08-18 zu PB-GLEICHE-KLASSE-BLEIBT-UMGEKEHRT-ENTSCHIEDEN-01, Karte OA-43cbdbb292, Option 1: "vereinheitlichen auf Kappe-vor-Arbeit wie 2c52596, das ist der dokumentierte Hausstandard des Budget-Moduls. Einzige Ausnahme, wenn die Kappe an der Gegenstelle ohne die begrenzte Arbeit nicht berechenbar ist." DIE AUSNAHME GREIFT AN KEINER DER DREI STELLEN, und das ist geprueft statt angenommen: die Kappe zaehlt Elemente, `len(proof_list)` ist ohne das Dekodieren berechenbar und exakt gleich `len(proof)`. Ein eigener Test haelt genau diese Vorbedingung fest — faellt sie, ist dieser Fix der falsche und es muesste nach Option 3 entschieden werden. WIRKUNG GEMESSEN, nicht geschaetzt, und die Groesse ist bewusst die AUFRUFZAHL des base64-Dekodierers statt der Laufzeit (eine Zeitmessung haengt an Last und Cache; an einer Zeitmessung ist die erste Nachmessung dieses Befunds schon einmal gescheitert): recompute_merkle_root_b64 195002 -> 2 Aufrufe, 365/942/5714 ms -> 35/36/37 ms bei Proof-Elementen von 32/320/3200 Byte verify_bundle 195004 -> 4 Aufrufe, 410/5418 ms -> 32/35 ms verify_sample_opening lehnt vor dem Dekodieren ab (0,06 ms), belegt daran, dass ungueltiges base64 in der Liste nie beruehrt wird Die Zeit skaliert danach NICHT mehr mit der Elementgroesse — das ist der Beleg, dass die Liste nicht mehr durchlaufen wird, und nicht nur, dass es schneller ist. VERDIKTE AENDERN SICH NICHT: ein Beweis ueber der Kappe konnte nie verifizieren. EINE Fehlerklasse aendert sich: eine Eingabe, die GLEICHZEITIG ueber der Kappe liegt UND kaputtes base64 traegt, ergab einen Format-Fehler (Exit 2) und ergibt jetzt ein Verdikt (Exit 1). Genau diese Folge hat den ersten Anlauf (2c52596) vor dem 4.0.0-Release zurueckgenommen; der Owner hat sie jetzt bewusst entschieden. Sie steht im CHANGELOG unter [Unreleased], zusammen mit den beiden anderen oeffentlichen Aenderungen dieses Zweigs (leerer checkpoint-Root, save_signer- Typboden) und dem neuen expected_origin_wellformed-Feld — die fehlende CHANGELOG-Disziplin war ein eigener Befund der Regressions-Linse. Sensitivitaet gemessen, drei Proben, jede rot: Kappe wieder hinter die Arbeit (2 rot) · Kappe auf 0, Anti-Tautologie (1 rot) · persample-Kappe zurueckgenommen (1 rot). Der Anti-Tautologie-Test war im ersten Entwurf selbst vakuum-wahr — er leitete seine Erwartung aus der geprueften Kappe ab und blieb bei Kappe 0 gruen; die Probe hat das gezeigt, nicht der Lauf. Er nennt jetzt eine feste Zahl. Volle Suite: 2311 passed, 7 skipped. ruff clean.
…alb des Nenners
Deep-Gate-Linse 2, Befund 1 (Lauf 2026-08-18, NORMAL 3L/3I). `split_key_binding`
und `holder_key_from_cnf` stehen beide in `kbjwt.__all__` und nahmen ihr
PRIMAERargument aus einer halter-gelieferten Praesentation — untrusted Eingabe.
Gemessen verliessen 7 von 7 bzw. 6 von 6 feindliche Formen sie als ROHER
AttributeError (`.endswith` / `.get`).
DER DEFEKT WAR BEKANNT UND AN DER FALSCHEN STELLE GESCHLOSSEN: `verify_key_binding`
traegt seit einem frueheren Durchgang einen aufrufer-seitigen Boden mit genau
diesem Kommentar ("never a raw AttributeError from split_key_binding's string
operations"). Der Aufrufer war gedeckt, die QUELLE nicht — und stand danach in der
Ausschlussmenge der never-raise-Eigenschaft unter ERZEUGER ("baut aus EIGENEN,
bereits geprueften Werten"). Diese Begruendung trifft auf ihr key-Argument zu und
auf ihre untrusted Primaereingabe gerade nicht. Dieselbe Form, die die Eigenschaft
selbst als Wurzel des cosign_*-Vorfalls dokumentiert: ein Verbraucher liegt
ausserhalb des Nenners, und nichts sagt es.
ZWEI VERSCHIEDENE ANTWORTEN, jede aus dem Vertrag der Funktion:
split_key_binding -> typisierter BundleFormatError (ein Parser, wie seine
Geschwister in dieser Datei)
holder_key_from_cnf -> None, denn ihr Docstring sagt genau das ("Returns None if
there is no usable OKP/Ed25519 confirmation key") und sie
gibt schon fuer fehlendes cnf, Nicht-dict-jwk und falsches
kty None zurueck. Ein Wurf waere hier die INKONSISTENTE
Antwort.
Beide sind aus _OUT_OF_SCOPE heraus und im NENNER — ohne das haette der naechste
weggenommene Boden wieder niemanden gestoert.
Sensitivitaet, drei Proben: split-Boden weg (rot) · holder-Boden weg (rot) · beide
zurueck in _OUT_OF_SCOPE UND Boden weg (GRUEN, und das ist der Punkt: die Probe
stellt den urspruenglichen Blindfleck nach und zeigt, dass erst die Aufnahme in den
Nenner den Riegel beissen laesst).
Im selben Commit korrigiert (Linse 1, Befund 3): die Begruendung der
require_-Nenner-Oeffnung nannte einen Pruefer `require_wellformed_expected_origin`,
den es im Baum NICHT gibt — er war der erste Entwurf und wurde durch das
berichtende `expected_origin_wellformed` ersetzt. Gemessen bewirkt die Oeffnung
heute nichts (Nenner 91 -> 98, alle sieben Zugaenge cosign_*/expected_origin_
wellformed, kein einziges require_*). Sie bleibt als richtige Form stehen; was
nicht bleiben durfte, ist eine Begruendung, die auf etwas Nichtexistierendes zeigt.
Gueltige Eingaben unveraendert. Volle Suite: 2311 passed, 7 skipped.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Ausfuehrung des Owner-GO vom 2026-08-20T01:10:23Z (Karte OA-1c8275d896, Option 1: GO fuer Push und PR).
Acht Commits, die seit Tagen lokal lagen und von keinem frueheren GO gedeckt waren:
Der Merge wartet auf gruene CI.