Erledigt mit Stack 4.7.2 (e00eae0). Die Suite hat einen Boden: fail_under = 85 in tools/.coveragerc, gesetzt gegen gemessene 87,0 % — und gesetzt erst, nachdem die Zahl
beobachtet war. Genau die Reihenfolge, für die dieses Issue existiert hat.
Drei Schritte, alle drei geliefert:
Messen ohne Schwelle (a243a4a, Stack 1.8.1) — CI installiert pytest-cov neben pytest, misst chemenu/ ohne chemenu/tests/, lädt den Bericht als Artefakt hoch.
Beobachten — 38 grüne CI-Läufe zwischen Lauf 87 und Lauf 163.
Untergrenze setzen (e00eae0, Stack 4.7.2) — fail_under = 85, in eigenem Commit, mit
der beobachteten Zahl als Begründung.
Die Beobachtung, auf der die Schwelle steht
Lauf 87 (2026-08-31, Stack 1.8.1)
Lauf 163 (2026-09-04, Stack 4.7.1)
Coverage
86,9 %
87,0 %
Statements
5105
6498 (+27 %)
Tests
730
975 (+245)
Verbindlich ist jeweils die von CI gemessene Zahl, nie eine lokale: die lokale misst einen Baum,
der einen Commit hinter dem veröffentlichten liegt — beim ersten Mal hat genau das 5100/729
statt 5105/730 ergeben.
Das Paar trägt die Schwelle, nicht der Einzelwert. Zwischen den beiden Messungen ist der
gemessene Code um ein Viertel gewachsen und die Suite um ein Drittel, und die Quote hat sich um
einen Zehntelpunkt bewegt. Eine Untergrenze auf dieser Grundlage ist etwas anderes als eine
gegriffene Zahl.
Entschieden: 85, nicht 87
Der ursprüngliche Auslöser für Schritt 3 war ein Datum — 2026-09-14, gesetzt am 31.08. als
Schätzung, wie lange es dauert, bis genug Beobachtung zusammenkommt. Die Schätzung ist überholt
worden, weil der Stack schneller lief als sie: 1.8.1 → 4.7.1 in vier Tagen, 38 grüne Läufe,
+27 % Statements. Das Datum war ein Proxy für „mehrere CI-Läufe über echten Commits", und der
Proxy war am 2026-09-04 eingelöst. Auf dem Datum zu beharren, wäre die Umkehrung des Fehlers
gewesen, den dieses Issue verhindern soll: dort eine Zahl ohne Beobachtung dahinter, hier ein
Warten ohne Grund dahinter.
Der Wert ist 85 und nicht der gemessene 87er, und das ist keine Bequemlichkeit. Der
Coverage-Bericht unterscheidet drei Sorten ungedeckter Zeilen (siehe unten), und nur eine
bedeutet Arbeit. Ein neuer dünner Typer-Wrapper senkt den Gesamtwert, ohne dass irgendetwas
schlechter geworden wäre — seine Logik liegt daneben und ist dort getestet. Eine Schwelle auf dem
gemessenen Wert würde an genau diesem Commit rot, und eine Schwelle, die aus einem Nicht-Grund
rot wird, wird gesenkt statt verdient. Die zwei Punkte sind der Platz, den die Taxonomie
verlangt.
fail_under steht in tools/.coveragerc und nicht als --cov-fail-under im CI-Schritt: so
sitzt die Zahl neben der Begründung, die sie erzeugt hat, und gilt für jeden --cov-Lauf statt
nur für den einen, den CI schreibt.
Die Modulverteilung — der bleibende Teil des Befunds
Der Gesamtwert ist die uninteressanteste Größe. Drei Sorten sind zu unterscheiden, bevor daraus
Arbeit wird:
Dünne Typer-Wrapper, deren Logik daneben liegt und dort getestet ist: eval_cmd.py (36 %), types_cmd.py (40 %), search.py (49 %), cli.py (54 %), links_cmd.py (61 %). Niedrige
Zahlen sind hier das Zeichen für einen guten Schnitt. search.py ist der Musterfall: ungedeckt
ist ausschließlich der Kommandokörper (Zeilen 117–176), während die Backends unter chemenu/search/, die die Arbeit tun, zwischen 91 % und 98 % liegen. Wer nur den Modulwert
liest, sieht hier Arbeit, wo keine ist.
Code an der Netz- oder Dateisystem-Naht, wo die interessante Hälfte über eine injizierbare
Naht getestet wird: version.pys fetch_latest() nimmt dafür einen fetcher, die echte
Netzzeile bleibt absichtlich ungedeckt (version.py gesamt 86,5 %).
Echte Lücken: provenance_cmd.py (44 %), migrate_cmd.py (65 %), type_resolver.py
(79 %). Das ist die einzige der drei Listen, die sich nicht bewegt hat, während alles um sie
herum wuchs — provenance_cmd.py steht bei unveränderten 44,1 %, migrate_cmd.py ist von
71 % gefallen, weil das Modul gewachsen ist und die neuen Zeilen ungetestet ankamen.
Die Schwelle friert das ein, sie schließt es nicht. Das ist Gitea #51, mit der Liste, den
Zeilennummern und der Begründung, warum ausgerechnet diese drei Module zählen.
Der Artefakt-Abruf: geklärt
Der Bericht ist abrufbar, bestätigt am 2026-09-04 für coverage-163 über die Run-Seite.
Der Widerspruch zwischen Kommentar 318 und 342 ist damit zugunsten von 318 aufgelöst und die
Ursache benannt statt vermutet: von den beiden dort genannten Erklärungen trifft die erste zu. upload-artifact@v3 legt über die ältere Artifact-API ab, die die Actions-Artefakt-Endpunkte
nicht lesen — daher total_count: 0 bei einem Artefakt, das tatsächlich da ist. Reproduziert
über 76 Läufe, auch für Lauf 163 einzeln.
Kein Folge-Issue. v3 funktioniert hier, nur die Listen-API sieht es nicht. Der Befund steht
an beiden Stellen, an denen ihn jemand sucht — EVALS.md und der Kommentar an der Coverage report-Stufe in .gitea/workflows/ci.yml —, jeweils mit dem Satz, dass eine leere
Liste kein fehlgeschlagener Upload ist. Ohne den wäre die naheliegende „Korrektur" der Wechsel
auf v4, der auf dieser Instanz eingeschränkt ist.
Nebenbefund, mitbehoben
dist export hat die Coverage-Ausgabe mit ausgeliefert: der erste Export nach der Messung trug
227 statt 162 Dateien, darunter den kompletten htmlcov/-Baum — eine Messung dieses Repos, in
fremden Instanzen. TOOLS_EXCLUDE_DIRS prunet nur Verzeichnisse, aber .coverage, coverage.xml und unter Parallelläufen .coverage.<host>.<pid> liegen als Dateien neben dem
Code. _copy_tree nimmt seither zusätzlich ein Dateinamen-Prädikat. Ohne das hätte der
CI-Export in jedem Lauf die Coverage des Laufs mitverpackt, weil „Tests" vor „Export the
distribution" läuft.
Akzeptanzkriterien
CI weist Coverage aus, der Lauf bleibt grün — Läufe 87 bis 166 grün.
Der Bericht ist als Artefakt abrufbar — bestätigt für coverage-163, über die
Run-Seite; die Listen-API sieht v3-Artefakte nicht, siehe oben.
Die Zahl steht dort, wo man sie wiederfindet — EVALS.md § „How much of the stack the
suite reaches", nicht nur hier im Issue.
Schritt 2: Zahl und Modulverteilung über echte Commits beobachtet — 38 CI-Läufe,
Ergebnis oben.
Schritt 3: Untergrenze gesetzt — fail_under = 85, eigener Commit, PATCH-Bump auf
4.7.2 (Version-Gate greift bei ^tools/).
Verifiziert
pytest -q --cov lokal: 975 passed, „Required test coverage of 85.0% reached. Total
coverage: 87.01 %" — die Untergrenze ist aktiv und wird eingehalten, nicht nur konfiguriert.
tools/wikitool docs verify und tools/wikitool instructions verify grün, vor und nach version release.
CI-Lauf 166 über e00eae0 grün, alle neun Schritte — der erste Lauf mit aktiver
Untergrenze.
Nachgezogen, weil derselbe Gedanke: docs/why-gates-are-code.md § „Numbers that come from
measurement, not intuition" führt die Untergrenze jetzt als zweiten Fall neben der
Iterationsgrenze (b4e4501). Die Grenze war erst falsch und wurde dann gemessen; der Boden
wurde absichtlich zurückgehalten, bis die Zahl existierte — dasselbe Argument, vorwärts statt
rückwärts gelaufen.
Vorgeschichte
Angelegt am 2026-08-30, als 630 Tests in CI liefen und niemand wusste, wie viel vom Stack sie
abdecken. Der Verdacht war nicht theoretisch: Lauf 52 hatte zwei Tests gefunden, die grün waren,
weil die Umgebung zufällig passte. Was gar nicht ausgeführt wird, fällt noch leichter durch.
Der Zuwachs von 630 auf 975 Tests ist nicht nur Fläche. 2.1.1 hat eine Umgebungsabhängigkeit
geschlossen, die genau hierher gehört: test_legacy_source_pages_flags_url_and_directory war
grün, weil der Checkout zufällig ein raw/documents/ besaß — die Korpus-Bereinigung leerte es,
Git verfolgt keine leeren Verzeichnisse, und der Test wurde in CI rot, während er lokal grün
blieb. Derselbe Befund wie in Lauf 52, dritter Fall. Die systematische Härtung dagegen ist die
autouse-Fixture hermetic_environment (#8); dass ihre Env-Liste von Hand gepflegt wird, ist #23.
Pfade in diesem Body sind am 2026-09-04 von wiki_tools/ auf chemenu/ nachgezogen (Rename in
2.0.0, #3). Der Beleg für upload-artifact@v3 nannte bis dahin einen privaten Hostnamen; er ist
neutral als torben/gitea-mcp@ci-build, ci-build.yaml, Läufe 42–45 formuliert. Teilerledigung
von #29.
## Ergebnis
**Erledigt mit Stack 4.7.2** (`e00eae0`). Die Suite hat einen Boden: `fail_under = 85` in
`tools/.coveragerc`, gesetzt gegen gemessene **87,0 %** — und gesetzt erst, nachdem die Zahl
beobachtet war. Genau die Reihenfolge, für die dieses Issue existiert hat.
Drei Schritte, alle drei geliefert:
1. **Messen ohne Schwelle** (`a243a4a`, Stack 1.8.1) — CI installiert `pytest-cov` neben
`pytest`, misst `chemenu/` ohne `chemenu/tests/`, lädt den Bericht als Artefakt hoch.
2. **Beobachten** — 38 grüne CI-Läufe zwischen Lauf 87 und Lauf 163.
3. **Untergrenze setzen** (`e00eae0`, Stack 4.7.2) — `fail_under = 85`, in eigenem Commit, mit
der beobachteten Zahl als Begründung.
## Die Beobachtung, auf der die Schwelle steht
| | Lauf 87 (2026-08-31, Stack 1.8.1) | Lauf 163 (2026-09-04, Stack 4.7.1) |
|---|---|---|
| Coverage | 86,9 % | **87,0 %** |
| Statements | 5105 | **6498** (+27 %) |
| Tests | 730 | **975** (+245) |
Verbindlich ist jeweils die von CI gemessene Zahl, nie eine lokale: die lokale misst einen Baum,
der einen Commit hinter dem veröffentlichten liegt — beim ersten Mal hat genau das 5100/729
statt 5105/730 ergeben.
**Das Paar trägt die Schwelle, nicht der Einzelwert.** Zwischen den beiden Messungen ist der
gemessene Code um ein Viertel gewachsen und die Suite um ein Drittel, und die Quote hat sich um
einen Zehntelpunkt bewegt. Eine Untergrenze auf dieser Grundlage ist etwas anderes als eine
gegriffene Zahl.
## Entschieden: 85, nicht 87
Der ursprüngliche Auslöser für Schritt 3 war ein Datum — **2026-09-14**, gesetzt am 31.08. als
Schätzung, wie lange es dauert, bis genug Beobachtung zusammenkommt. Die Schätzung ist überholt
worden, weil der Stack schneller lief als sie: 1.8.1 → 4.7.1 in vier Tagen, 38 grüne Läufe,
+27 % Statements. Das Datum war ein Proxy für „mehrere CI-Läufe über echten Commits", und der
Proxy war am 2026-09-04 eingelöst. **Auf dem Datum zu beharren, wäre die Umkehrung des Fehlers
gewesen, den dieses Issue verhindern soll**: dort eine Zahl ohne Beobachtung dahinter, hier ein
Warten ohne Grund dahinter.
Der Wert ist 85 und nicht der gemessene 87er, und das ist keine Bequemlichkeit. Der
Coverage-Bericht unterscheidet drei Sorten ungedeckter Zeilen (siehe unten), und nur eine
bedeutet Arbeit. Ein neuer dünner Typer-Wrapper senkt den Gesamtwert, ohne dass irgendetwas
schlechter geworden wäre — seine Logik liegt daneben und ist dort getestet. Eine Schwelle auf dem
gemessenen Wert würde an genau diesem Commit rot, und eine Schwelle, die aus einem Nicht-Grund
rot wird, wird gesenkt statt verdient. Die zwei Punkte sind der Platz, den die Taxonomie
verlangt.
`fail_under` steht in `tools/.coveragerc` und nicht als `--cov-fail-under` im CI-Schritt: so
sitzt die Zahl neben der Begründung, die sie erzeugt hat, und gilt für jeden `--cov`-Lauf statt
nur für den einen, den CI schreibt.
## Die Modulverteilung — der bleibende Teil des Befunds
Der Gesamtwert ist die uninteressanteste Größe. Drei Sorten sind zu unterscheiden, bevor daraus
Arbeit wird:
- **Dünne Typer-Wrapper**, deren Logik daneben liegt und dort getestet ist: `eval_cmd.py` (36 %),
`types_cmd.py` (40 %), `search.py` (49 %), `cli.py` (54 %), `links_cmd.py` (61 %). Niedrige
Zahlen sind hier das Zeichen für einen guten Schnitt. `search.py` ist der Musterfall: ungedeckt
ist ausschließlich der Kommandokörper (Zeilen 117–176), während die Backends unter
`chemenu/search/`, die die Arbeit tun, zwischen 91 % und 98 % liegen. Wer nur den Modulwert
liest, sieht hier Arbeit, wo keine ist.
- **Code an der Netz- oder Dateisystem-Naht**, wo die interessante Hälfte über eine injizierbare
Naht getestet wird: `version.py`s `fetch_latest()` nimmt dafür einen `fetcher`, die echte
Netzzeile bleibt absichtlich ungedeckt (`version.py` gesamt 86,5 %).
- **Echte Lücken**: `provenance_cmd.py` (44 %), `migrate_cmd.py` (65 %), `type_resolver.py`
(79 %). Das ist die einzige der drei Listen, die sich nicht bewegt hat, während alles um sie
herum wuchs — `provenance_cmd.py` steht bei unveränderten 44,1 %, `migrate_cmd.py` ist von
71 % **gefallen**, weil das Modul gewachsen ist und die neuen Zeilen ungetestet ankamen.
**Die Schwelle friert das ein, sie schließt es nicht.** Das ist Gitea **#51**, mit der Liste, den
Zeilennummern und der Begründung, warum ausgerechnet diese drei Module zählen.
## Der Artefakt-Abruf: geklärt
Der Bericht **ist** abrufbar, bestätigt am 2026-09-04 für `coverage-163` über die Run-Seite.
Der Widerspruch zwischen Kommentar 318 und 342 ist damit zugunsten von 318 aufgelöst und die
Ursache benannt statt vermutet: von den beiden dort genannten Erklärungen trifft die erste zu.
`upload-artifact@v3` legt über die ältere Artifact-API ab, die die Actions-Artefakt-Endpunkte
nicht lesen — daher `total_count: 0` bei einem Artefakt, das tatsächlich da ist. Reproduziert
über 76 Läufe, auch für Lauf 163 einzeln.
**Kein Folge-Issue.** v3 funktioniert hier, nur die Listen-API sieht es nicht. Der Befund steht
an beiden Stellen, an denen ihn jemand sucht — `EVALS.md` und der Kommentar an der
`Coverage report`-Stufe in `.gitea/workflows/ci.yml` —, jeweils mit dem Satz, dass eine leere
Liste kein fehlgeschlagener Upload ist. Ohne den wäre die naheliegende „Korrektur" der Wechsel
auf v4, der auf dieser Instanz eingeschränkt ist.
## Nebenbefund, mitbehoben
`dist export` hat die Coverage-Ausgabe mit ausgeliefert: der erste Export nach der Messung trug
227 statt 162 Dateien, darunter den kompletten `htmlcov/`-Baum — eine Messung *dieses* Repos, in
fremden Instanzen. `TOOLS_EXCLUDE_DIRS` prunet nur Verzeichnisse, aber `.coverage`,
`coverage.xml` und unter Parallelläufen `.coverage.<host>.<pid>` liegen als Dateien neben dem
Code. `_copy_tree` nimmt seither zusätzlich ein Dateinamen-Prädikat. Ohne das hätte der
CI-Export in jedem Lauf die Coverage des Laufs mitverpackt, weil „Tests" vor „Export the
distribution" läuft.
## Akzeptanzkriterien
- [x] **CI weist Coverage aus, der Lauf bleibt grün** — Läufe 87 bis 166 grün.
- [x] **Der Bericht ist als Artefakt abrufbar** — bestätigt für `coverage-163`, über die
Run-Seite; die Listen-API sieht v3-Artefakte nicht, siehe oben.
- [x] **Die Zahl steht dort, wo man sie wiederfindet** — `EVALS.md` § „How much of the stack the
suite reaches", nicht nur hier im Issue.
- [x] **Schritt 2: Zahl und Modulverteilung über echte Commits beobachtet** — 38 CI-Läufe,
Ergebnis oben.
- [x] **Schritt 3: Untergrenze gesetzt** — `fail_under = 85`, eigener Commit, PATCH-Bump auf
4.7.2 (Version-Gate greift bei `^tools/`).
## Verifiziert
- `pytest -q --cov` lokal: **975 passed**, „Required test coverage of 85.0% reached. Total
coverage: 87.01 %" — die Untergrenze ist aktiv und wird eingehalten, nicht nur konfiguriert.
- `tools/wikitool docs verify` und `tools/wikitool instructions verify` grün, vor und nach
`version release`.
- CI-Lauf **166** über `e00eae0` grün, alle neun Schritte — der erste Lauf mit aktiver
Untergrenze.
- Nachgezogen, weil derselbe Gedanke: `docs/why-gates-are-code.md` § „Numbers that come from
measurement, not intuition" führt die Untergrenze jetzt als zweiten Fall neben der
Iterationsgrenze (`b4e4501`). Die Grenze war erst falsch und wurde dann gemessen; der Boden
wurde absichtlich zurückgehalten, bis die Zahl existierte — dasselbe Argument, vorwärts statt
rückwärts gelaufen.
## Vorgeschichte
Angelegt am 2026-08-30, als 630 Tests in CI liefen und niemand wusste, wie viel vom Stack sie
abdecken. Der Verdacht war nicht theoretisch: Lauf 52 hatte zwei Tests gefunden, die grün waren,
weil die Umgebung zufällig passte. Was gar nicht ausgeführt wird, fällt noch leichter durch.
Der Zuwachs von 630 auf 975 Tests ist nicht nur Fläche. 2.1.1 hat eine Umgebungsabhängigkeit
geschlossen, die genau hierher gehört: `test_legacy_source_pages_flags_url_and_directory` war
grün, weil der Checkout zufällig ein `raw/documents/` besaß — die Korpus-Bereinigung leerte es,
Git verfolgt keine leeren Verzeichnisse, und der Test wurde in CI rot, während er lokal grün
blieb. Derselbe Befund wie in Lauf 52, dritter Fall. Die systematische Härtung dagegen ist die
autouse-Fixture `hermetic_environment` (#8); dass ihre Env-Liste von Hand gepflegt wird, ist #23.
Pfade in diesem Body sind am 2026-09-04 von `wiki_tools/` auf `chemenu/` nachgezogen (Rename in
2.0.0, #3). Der Beleg für `upload-artifact@v3` nannte bis dahin einen privaten Hostnamen; er ist
neutral als `torben/gitea-mcp@ci-build`, `ci-build.yaml`, Läufe 42–45 formuliert. Teilerledigung
von #29.
Schritt 1 umgesetzt in a243a4a, Stack 1.8.1. Das Issue bleibt offen — Schritt 3 (die
Schwelle) ist ausdrücklich ein eigener, späterer Commit.
Erste Messung, 2026-08-31: 86.9 % von 5100 Statements, 729 Tests.
Festgehalten in EVALS.md § „How much of the stack the suite reaches" — also nicht nur hier im
Issue, wie das Akzeptanzkriterium es als Minimum zuließ.
Die Module unten, nach den drei Kategorien getrennt, die das Issue verlangt:
Modul
Cover
Kategorie
commands/eval_cmd.py
36.0 %
dünner Typer-Wrapper
commands/provenance_cmd.py
44.1 %
echte Lücke
cli.py
52.3 %
dünner Wrapper
commands/types_cmd.py
52.4 %
dünner Wrapper
commands/version_cmd.py
61.8 %
Wrapper + Netzpfad in version.py
commands/search.py
68.6 %
teils Ausgabeformatierung
commands/migrate_cmd.py
71.1 %
echte Lücke
type_resolver.py
78.6 %
echte Lücke
version.py liegt bei 89.1 % — der Netzzugriff sitzt wie beschrieben hinter dem injizierbaren fetcher, die echte Netzzeile bleibt absichtlich ungetestet. Die Wrapper-Zeilen sind kein
Arbeitsvorrat: niedrige Zahlen dort sind das Zeichen für den guten Schnitt, das im Issue schon
vorweggenommen war. Arbeit ist ausschließlich die dritte Spalte.
Umsetzung, mit einer Abweichung vom Issue.pytest-cov steht nicht in tools/requirements.txt, CI installiert es neben pytest — wie gefordert. Die Konfiguration
liegt aber in tools/.coveragerc, nicht in tools/pytest.ini: coverage.py liest .coveragerc, setup.cfg, tox.ini und pyproject.toml, aber kein pytest.ini, wo ein [coverage:*]-Abschnitt still ignoriert würde. Gemessen wird wiki_tools/ ohne wiki_tools/tests/. Bericht als Artefakt coverage-<run id> über upload-artifact@v3 (nicht
v4), unter if: always() — eine rote Suite ist genau der Moment, in dem die Zahlen pro Modul
interessant sind. Kein --cov-fail-under.
Die Messung hängt bewusst nicht in addopts: das würde den nackten pytest -q überall dort
brechen, wo pytest-cov fehlt — also in jeder Instanz und in jedem lokalen Lauf ohne das
Zusatzpaket.
Dabei gefunden und mitbehoben:dist export hat die Coverage-Ausgabe mit ausgeliefert. Der
erste Export nach der Messung trug 227 statt 162 Dateien, darunter den kompletten htmlcov/-Baum — eine Messung dieses Repos, ausgeliefert in fremde Instanzen. TOOLS_EXCLUDE_DIRS
prunet nur Verzeichnisse, zwei Drittel der Ausgabe (.coverage, coverage.xml, unter
Parallelläufen .coverage.<host>.<pid>) liegen als Dateien neben dem Code. _copy_tree nimmt
jetzt zusätzlich ein Dateinamen-Prädikat. Ohne das hätte der CI-Export in jedem Lauf die
Coverage des Laufs mitverpackt, weil „Tests" vor „Export the distribution" läuft.
Was noch offen ist
Schritt 2: ein bis zwei Wochen die Zahl und vor allem die Modulverteilung beobachten.
Schritt 3: --cov-fail-under setzen, in eigenem Commit, mit der beobachteten Zahl als
Begründung. Sie friert den erreichten Stand ein, sie rechnet ihn nicht schön.
Neu gelabelt prio/2 → prio/3. Der Wert ist unverändert real, aber der nächste Schritt
ist auf einen Auslöser gestellt, und der Auslöser ist Zeit: mehrere CI-Läufe über echten
Commits, frühestens ab 2026-09-14. Vorher gesetzt wäre die Schwelle wieder das, was das
Issue verhindern wollte — eine Zahl ohne Beobachtung dahinter.
Schritt 1 umgesetzt in `a243a4a`, Stack **1.8.1**. Das Issue bleibt offen — Schritt 3 (die
Schwelle) ist ausdrücklich ein eigener, späterer Commit.
**Erste Messung, 2026-08-31: 86.9 % von 5100 Statements, 729 Tests.**
Festgehalten in `EVALS.md` § „How much of the stack the suite reaches" — also nicht nur hier im
Issue, wie das Akzeptanzkriterium es als Minimum zuließ.
Die Module unten, nach den drei Kategorien getrennt, die das Issue verlangt:
| Modul | Cover | Kategorie |
|---|---|---|
| `commands/eval_cmd.py` | 36.0 % | dünner Typer-Wrapper |
| `commands/provenance_cmd.py` | 44.1 % | **echte Lücke** |
| `cli.py` | 52.3 % | dünner Wrapper |
| `commands/types_cmd.py` | 52.4 % | dünner Wrapper |
| `commands/version_cmd.py` | 61.8 % | Wrapper + Netzpfad in `version.py` |
| `commands/search.py` | 68.6 % | teils Ausgabeformatierung |
| `commands/migrate_cmd.py` | 71.1 % | **echte Lücke** |
| `type_resolver.py` | 78.6 % | **echte Lücke** |
`version.py` liegt bei 89.1 % — der Netzzugriff sitzt wie beschrieben hinter dem injizierbaren
`fetcher`, die echte Netzzeile bleibt absichtlich ungetestet. Die Wrapper-Zeilen sind kein
Arbeitsvorrat: niedrige Zahlen dort sind das Zeichen für den guten Schnitt, das im Issue schon
vorweggenommen war. Arbeit ist ausschließlich die dritte Spalte.
**Umsetzung, mit einer Abweichung vom Issue.** `pytest-cov` steht nicht in
`tools/requirements.txt`, CI installiert es neben `pytest` — wie gefordert. Die Konfiguration
liegt aber in **`tools/.coveragerc`**, nicht in `tools/pytest.ini`: coverage.py liest
`.coveragerc`, `setup.cfg`, `tox.ini` und `pyproject.toml`, aber kein `pytest.ini`, wo ein
`[coverage:*]`-Abschnitt still ignoriert würde. Gemessen wird `wiki_tools/` ohne
`wiki_tools/tests/`. Bericht als Artefakt `coverage-<run id>` über `upload-artifact@v3` (nicht
v4), unter `if: always()` — eine rote Suite ist genau der Moment, in dem die Zahlen pro Modul
interessant sind. Kein `--cov-fail-under`.
Die Messung hängt bewusst nicht in `addopts`: das würde den nackten `pytest -q` überall dort
brechen, wo `pytest-cov` fehlt — also in jeder Instanz und in jedem lokalen Lauf ohne das
Zusatzpaket.
**Dabei gefunden und mitbehoben:** `dist export` hat die Coverage-Ausgabe mit ausgeliefert. Der
erste Export nach der Messung trug 227 statt 162 Dateien, darunter den kompletten
`htmlcov/`-Baum — eine Messung *dieses* Repos, ausgeliefert in fremde Instanzen. `TOOLS_EXCLUDE_DIRS`
prunet nur Verzeichnisse, zwei Drittel der Ausgabe (`.coverage`, `coverage.xml`, unter
Parallelläufen `.coverage.<host>.<pid>`) liegen als Dateien neben dem Code. `_copy_tree` nimmt
jetzt zusätzlich ein Dateinamen-Prädikat. Ohne das hätte der CI-Export in jedem Lauf die
Coverage des Laufs mitverpackt, weil „Tests" vor „Export the distribution" läuft.
## Was noch offen ist
- [ ] Schritt 2: ein bis zwei Wochen die Zahl und vor allem die Modulverteilung beobachten.
- [ ] Schritt 3: `--cov-fail-under` setzen, in eigenem Commit, mit der beobachteten Zahl als
Begründung. Sie friert den erreichten Stand ein, sie rechnet ihn nicht schön.
**Neu gelabelt `prio/2` → `prio/3`.** Der Wert ist unverändert real, aber der nächste Schritt
ist auf einen Auslöser gestellt, und der Auslöser ist Zeit: mehrere CI-Läufe über echten
Commits, frühestens ab **2026-09-14**. Vorher gesetzt wäre die Schwelle wieder das, was das
Issue verhindern wollte — eine Zahl ohne Beobachtung dahinter.
CI-Lauf 87 grün, alle neun Schritte. Auch Lauf 88 (Release) grün.
Zwei Nachträge zum Kommentar oben.
1. Die Baseline-Zahlen waren um einen Commit veraltet — korrigiert in 2b7b3cb.
Gemessen hatte ich lokal vor dem dist export-Fix, der noch Statements hinzufügte, und vor
dem Test, der ihn absichert. Verbindlich ist, was der veröffentlichte Baum produziert:
86.9 % von 5105 Statements, 730 Tests (CI-Lauf 87), nicht 5100/729.
Der Prozentwert ist derselbe, die Grundgesamtheit nicht. EVALS.md und der Changelog-Eintrag
nennen jetzt beide die von CI gemessene Zahl und die Laufnummer dazu.
2. Das Artefakt ist hochgeladen, aber über die API nicht auffindbar — offen.
Aus dem Log von Lauf 87, Schritt „Coverage report":
The least common ancestor is /workspace/torben/llm-wiki-test1/tools
With the provided path, there will be 63 files uploaded
Container for artifact "coverage-87" successfully created. Starting upload of file(s)
Total size of all the files uploaded is 486780 bytes
Artifact has been finalized. All files have been successfully uploaded!
Artifact coverage-87 has been successfully uploaded!
Der Upload meldet also Erfolg: 63 Dateien, 487 KB komprimiert aus 4.3 MB roh.
Aber: sowohl /repos/torben/llm-wiki-test1/actions/runs/87/artifacts als auch die
Repo-weite Artefaktliste liefern total_count: 0. Der Lauf ist abgeschlossen, es ist also kein
Timing-Effekt.
Damit ist das Akzeptanzkriterium „Der Bericht ist als Artefakt abrufbar" nicht bestätigt.
Die Erfolgsmeldung der Action ist ein Beleg dafür, dass sie hochgeladen hat, nicht dafür,
dass Gitea es herausgibt. Zwei plausible Ursachen, beide ungeprüft: upload-artifact@v3 legt
über die alte Artifact-API ab, während die REST-Liste nur v4-Artefakte kennt — oder die
Artefakt-Speicherung ist auf dieser Instanz nicht so konfiguriert, wie die Liste es erwartet.
Nächster Schritt, bevor irgendetwas an v4 geändert wird: auf der Run-Seite im Browser
nachsehen, ob das Artefakt dort zum Download angeboten wird
(https://gitea.nehmer.net/torben/llm-wiki-test1/actions/runs/87). Wenn ja, ist es nur die
Listen-API und das Kriterium ist erfüllt. Wenn nein, ist v3 auf dieser Instanz ein Upload ins
Leere, und das gehört in ein eigenes Issue — es beträfe dann jeden Workflow hier, nicht nur
Coverage.
**CI-Lauf 87 grün, alle neun Schritte.** Auch Lauf 88 (Release) grün.
Zwei Nachträge zum Kommentar oben.
**1. Die Baseline-Zahlen waren um einen Commit veraltet — korrigiert in `2b7b3cb`.**
Gemessen hatte ich lokal *vor* dem `dist export`-Fix, der noch Statements hinzufügte, und vor
dem Test, der ihn absichert. Verbindlich ist, was der veröffentlichte Baum produziert:
**86.9 % von 5105 Statements, 730 Tests** (CI-Lauf 87), nicht 5100/729.
Der Prozentwert ist derselbe, die Grundgesamtheit nicht. `EVALS.md` und der Changelog-Eintrag
nennen jetzt beide die von CI gemessene Zahl und die Laufnummer dazu.
**2. Das Artefakt ist hochgeladen, aber über die API nicht auffindbar — offen.**
Aus dem Log von Lauf 87, Schritt „Coverage report":
```
The least common ancestor is /workspace/torben/llm-wiki-test1/tools
With the provided path, there will be 63 files uploaded
Container for artifact "coverage-87" successfully created. Starting upload of file(s)
Total size of all the files uploaded is 486780 bytes
Artifact has been finalized. All files have been successfully uploaded!
Artifact coverage-87 has been successfully uploaded!
```
Der Upload meldet also Erfolg: 63 Dateien, 487 KB komprimiert aus 4.3 MB roh.
**Aber:** sowohl `/repos/torben/llm-wiki-test1/actions/runs/87/artifacts` als auch die
Repo-weite Artefaktliste liefern `total_count: 0`. Der Lauf ist abgeschlossen, es ist also kein
Timing-Effekt.
Damit ist das Akzeptanzkriterium „Der Bericht ist als Artefakt abrufbar" **nicht bestätigt**.
Die Erfolgsmeldung der Action ist ein Beleg dafür, dass sie hochgeladen *hat*, nicht dafür,
dass Gitea es herausgibt. Zwei plausible Ursachen, beide ungeprüft: `upload-artifact@v3` legt
über die alte Artifact-API ab, während die REST-Liste nur v4-Artefakte kennt — oder die
Artefakt-Speicherung ist auf dieser Instanz nicht so konfiguriert, wie die Liste es erwartet.
Nächster Schritt, bevor irgendetwas an v4 geändert wird: auf der Run-Seite im Browser
nachsehen, ob das Artefakt dort zum Download angeboten wird
(<https://gitea.nehmer.net/torben/llm-wiki-test1/actions/runs/87>). Wenn ja, ist es nur die
Listen-API und das Kriterium ist erfüllt. Wenn nein, ist v3 auf dieser Instanz ein Upload ins
Leere, und das gehört in ein eigenes Issue — es beträfe dann jeden Workflow hier, nicht nur
Coverage.
Stand 2026-09-01 — Schritt 1 ist erledigt, Schritt 2 läuft
Schritt 1 (messen ohne Schwelle) ist mit 1.8.1 geliefert. CI installiert pytest-cov neben pytest, misst chemenu/ ohne chemenu/tests/, lädt den Bericht als Artefakt hoch und setzt kein --cov-fail-under. Die Akzeptanzkriterien „CI weist Coverage aus" und „Bericht ist als Artefakt abrufbar" sind damit erfüllt.
Offen bleiben Schritt 2 und 3: die Zahl über ein paar Wochen beobachten — vor allem welche Module unten liegen — und danach in einem eigenen Commit eine Untergrenze setzen, die den erreichten Stand einfriert.
Zwei Korrekturen am Text oben:
Die Testzahl ist von 630 auf 752 gestiegen. Der Zuwachs ist nicht nur Fläche: 2.1.1 hat eine Umgebungsabhängigkeit geschlossen, die genau in die Argumentation dieses Issues gehört. test_legacy_source_pages_flags_url_and_directory war grün, weil der Checkout zufällig ein raw/documents/ besaß — die Korpus-Bereinigung leerte es, Git verfolgt keine leeren Verzeichnisse, und der Test wurde in CI rot, während er lokal grün blieb. Das ist derselbe Befund wie in Run 52, dritter Fall.
Der Verweis auf torben/gitea-mcp@ci-binford / binford-build.yaml als Beleg für upload-artifact@v3 nennt einen privaten Hostnamen. Die Sache stimmt weiterhin — v3 ist das, was auf dieser Gitea funktioniert —, der Beleg gehört aber neutral formuliert, bevor jemand den Text nach draußen kopiert. Pfade im Text: wiki_tools/ heißt seit 2.0.0 chemenu/.
## Stand 2026-09-01 — Schritt 1 ist erledigt, Schritt 2 läuft
**Schritt 1 (messen ohne Schwelle) ist mit 1.8.1 geliefert.** CI installiert `pytest-cov` neben `pytest`, misst `chemenu/` ohne `chemenu/tests/`, lädt den Bericht als Artefakt hoch und setzt kein `--cov-fail-under`. Die Akzeptanzkriterien „CI weist Coverage aus" und „Bericht ist als Artefakt abrufbar" sind damit erfüllt.
**Offen bleiben Schritt 2 und 3:** die Zahl über ein paar Wochen beobachten — vor allem *welche Module unten liegen* — und danach in einem eigenen Commit eine Untergrenze setzen, die den erreichten Stand einfriert.
**Zwei Korrekturen am Text oben:**
Die Testzahl ist von 630 auf **752** gestiegen. Der Zuwachs ist nicht nur Fläche: 2.1.1 hat eine Umgebungsabhängigkeit geschlossen, die genau in die Argumentation dieses Issues gehört. `test_legacy_source_pages_flags_url_and_directory` war grün, weil der Checkout zufällig ein `raw/documents/` besaß — die Korpus-Bereinigung leerte es, Git verfolgt keine leeren Verzeichnisse, und der Test wurde in CI rot, während er lokal grün blieb. Das ist derselbe Befund wie in Run 52, dritter Fall.
Der Verweis auf `torben/gitea-mcp@ci-binford` / `binford-build.yaml` als Beleg für `upload-artifact@v3` nennt einen privaten Hostnamen. Die Sache stimmt weiterhin — v3 ist das, was auf dieser Gitea funktioniert —, der Beleg gehört aber neutral formuliert, bevor jemand den Text nach draußen kopiert. Pfade im Text: `wiki_tools/` heißt seit 2.0.0 `chemenu/`.
Changelog: Body vollständig auf den Stand von 4.5.0 umgeschrieben (Triage-Sitzung 2026-09-04). Der Text hatte sich seit dem 2026-08-30 nicht bewegt, während drei Kommentare den echten Stand trugen — genau das Muster, das issue-tracking.md Schritt 2 verhindern soll, und genau der Fall, den #47 als Befund 2 beschreibt.
Was der alte Body falsch sagte:
„Wie viel vom Stack sie abdecken, weiß niemand — Coverage wurde nie gemessen." Gemessen seit 1.8.1: 86,9 % von 5105 Statements, 730 Tests (CI-Lauf 87), Suite inzwischen bei 752 Tests.
„630 Tests laufen in CI." Überholt.
pytest-cov „gehört nach tools/pytest.ini" — die Konfiguration liegt tatsächlich in tools/.coveragerc, mit Grund: coverage.py liest pytest.ini nicht.
Der Beleg für upload-artifact@v3 nannte einen privaten Hostnamen (ci-binford, binford-build.yaml); im Baum war er längst zu ci-build neutralisiert, im Issue nicht. Nachgezogen, ebenso wiki_tools/ → chemenu/ (Teilerledigung von #29).
Neu im Body, vorher nur in Kommentaren oder gar nicht:
Die Modulverteilung nach den drei Kategorien, die das Issue selbst verlangt — Wrapper, Netz-/FS-Naht, echte Lücken (provenance_cmd.py 44 %, migrate_cmd.py 71 %, type_resolver.py 79 %). Nur die dritte Spalte bedeutet Arbeit.
Der Nebenbefund, dass dist export die Coverage-Ausgabe mit ausgeliefert hat (227 statt 162 Dateien), und wie er behoben wurde.
Der Widerspruch zwischen Kommentar 318 und 342 ist im Body zugunsten von 318 aufgelöst: der Artefakt-Abruf ist nicht bestätigt. Der Upload meldet Erfolg, aber die Artefakt-API liefert total_count: 0 bei abgeschlossenem Lauf. Kommentar 342 hat das Kriterium für erfüllt erklärt, ohne dass die Gegenprobe gelaufen wäre. Es steht jetzt als offener Punkt mit dem konkreten nächsten Schritt (Run-Seite im Browser ansehen, bevor irgendetwas an v4 geändert wird).
Labels unverändert.prio/waiting bleibt bis zum Auslöser 2026-09-14; das Datum steht jetzt im Body statt nur in Kommentar 315.
**Changelog:** Body vollständig auf den Stand von 4.5.0 umgeschrieben (Triage-Sitzung 2026-09-04). Der Text hatte sich seit dem 2026-08-30 nicht bewegt, während drei Kommentare den echten Stand trugen — genau das Muster, das `issue-tracking.md` Schritt 2 verhindern soll, und genau der Fall, den #47 als Befund 2 beschreibt.
Was der alte Body falsch sagte:
- „Wie viel vom Stack sie abdecken, weiß niemand — Coverage wurde nie gemessen." Gemessen seit 1.8.1: **86,9 % von 5105 Statements, 730 Tests** (CI-Lauf 87), Suite inzwischen bei 752 Tests.
- „630 Tests laufen in CI." Überholt.
- `pytest-cov` „gehört nach `tools/pytest.ini`" — die Konfiguration liegt tatsächlich in `tools/.coveragerc`, mit Grund: coverage.py liest `pytest.ini` nicht.
- Der Beleg für `upload-artifact@v3` nannte einen privaten Hostnamen (`ci-binford`, `binford-build.yaml`); im Baum war er längst zu `ci-build` neutralisiert, im Issue nicht. Nachgezogen, ebenso `wiki_tools/` → `chemenu/` (Teilerledigung von #29).
Neu im Body, vorher nur in Kommentaren oder gar nicht:
- Die Modulverteilung nach den drei Kategorien, die das Issue selbst verlangt — Wrapper, Netz-/FS-Naht, **echte Lücken** (`provenance_cmd.py` 44 %, `migrate_cmd.py` 71 %, `type_resolver.py` 79 %). Nur die dritte Spalte bedeutet Arbeit.
- Der Nebenbefund, dass `dist export` die Coverage-Ausgabe mit ausgeliefert hat (227 statt 162 Dateien), und wie er behoben wurde.
- **Der Widerspruch zwischen Kommentar 318 und 342 ist im Body zugunsten von 318 aufgelöst:** der Artefakt-Abruf ist **nicht bestätigt**. Der Upload meldet Erfolg, aber die Artefakt-API liefert `total_count: 0` bei abgeschlossenem Lauf. Kommentar 342 hat das Kriterium für erfüllt erklärt, ohne dass die Gegenprobe gelaufen wäre. Es steht jetzt als offener Punkt mit dem konkreten nächsten Schritt (Run-Seite im Browser ansehen, bevor irgendetwas an v4 geändert wird).
**Labels unverändert.** `prio/waiting` bleibt bis zum Auslöser **2026-09-14**; das Datum steht jetzt im Body statt nur in Kommentar 315.
Der Artefakt-Abruf ist bestätigt.coverage-163 wird auf der Run-Seite zum Download
angeboten. Damit ist das Akzeptanzkriterium erfüllt, und die Ursache des Widerspruchs aus
Kommentar 318 ist benannt statt vermutet: von den beiden dort genannten Erklärungen trifft die
erste zu. upload-artifact@v3 legt über die ältere Artifact-API ab, die die
Actions-Artefakt-Endpunkte nicht lesen — deshalb total_count: 0 bei einem Artefakt, das
tatsächlich da ist. Reproduziert über 76 Läufe: auch list_run_artifacts für Lauf 163 meldet total_count: 0, während die Run-Seite die Datei herausgibt.
Kein Folge-Issue. v3 funktioniert auf dieser Instanz, nur die Listen-API sieht es nicht. Der
Befund steht jetzt an beiden Stellen, an denen ihn jemand sucht: in EVALS.md und im Kommentar
an der Coverage report-Stufe in .gitea/workflows/ci.yml, jeweils mit dem Satz, dass eine
leere Liste kein fehlgeschlagener Upload ist. Ohne den leitet ihn beim nächsten Mal jemand neu
her — und die naheliegende „Korrektur" wäre der Wechsel auf v4, der hier eingeschränkt ist.
Schritt 2 ist damit erledigt. Beobachtet wurden 38 CI-Läufe zwischen Lauf 87 und Lauf 163,
alle grün:
Lauf 87 (31.08., 1.8.1)
Lauf 163 (04.09., 4.7.1)
Coverage
86,9 %
87,0 %
Statements
5105
6498 (+27 %)
Tests
730
975 (+245)
Ein Viertel mehr gemessener Code, ein Drittel mehr Tests, ein Zehntelpunkt Bewegung in der Quote.
Das ist die Beobachtung, die eine Schwelle tragen kann.
Die Modulverteilung ist der interessantere Teil, und sie hat sich bewegt — die vollständige
Liste steht jetzt im Body. Zwei Dinge daraus:
search.py fällt von 68,6 % auf 48,5 % und ist kein Regress: ungedeckt ist der
Typer-Kommandokörper (117–176), die Backends unter chemenu/search/ liegen bei 91–98 %.
Kategorie 1 nach der Taxonomie dieses Issues. Wer nur den Gesamtwert pro Modul liest, hätte
hier Arbeit gesehen, wo keine ist — genau der Grund, warum das Issue die drei Kategorien
verlangt.
Die echten Lücken sind die einzige Liste, die sich nicht bewegt hat, während alles um sie
herum wuchs. provenance_cmd.py steht unverändert bei 44,1 %; migrate_cmd.py ist von 71 %
auf 65 % gefallen, weil das Modul gewachsen ist und die neuen Zeilen ungetestet kamen.
Eine Schwelle friert das ein, sie schließt es nicht. Der Arbeitsvorrat gehört in ein eigenes
Issue.
Schritt 3 bleibt offen, Labels bleiben. Die inhaltliche Bedingung („mehrere CI-Läufe über
echten Commits") ist erfüllt, das Datum 2026-09-14 steht trotzdem — es soll die Beobachtung
sein, die die Zahl trägt, nicht die Ungeduld. prio/waiting bis dahin. Empfehlung für die
Schwelle steht im Body: 85, nicht 87. 87 hat keine Luft, der nächste ehrliche
Wrapper-Commit macht CI rot, und dann wird die Schwelle gesenkt statt verdient — der Ausgang,
den dieses Issue verhindern soll.
Geliefert in fe55ad2. Prose-only plus Workflow-Kommentar, also kein Version-Bump: das
Version-Gate ist auf ^(tools/|types/|instructions/|AGENTS\.md$|[^/]+/CONTRACT\.md$) gescoped,
und weder EVALS.md noch .gitea/ fällt darunter.
## Schritt 2 abgeschlossen, Artefakt-Frage geklärt (2026-09-04, Stack 4.7.1)
**Der Artefakt-Abruf ist bestätigt.** `coverage-163` wird auf der Run-Seite zum Download
angeboten. Damit ist das Akzeptanzkriterium erfüllt, und die Ursache des Widerspruchs aus
Kommentar 318 ist benannt statt vermutet: von den beiden dort genannten Erklärungen trifft die
erste zu. `upload-artifact@v3` legt über die ältere Artifact-API ab, die die
Actions-Artefakt-Endpunkte nicht lesen — deshalb `total_count: 0` bei einem Artefakt, das
tatsächlich da ist. Reproduziert über 76 Läufe: auch `list_run_artifacts` für Lauf 163 meldet
`total_count: 0`, während die Run-Seite die Datei herausgibt.
**Kein Folge-Issue.** v3 funktioniert auf dieser Instanz, nur die Listen-API sieht es nicht. Der
Befund steht jetzt an beiden Stellen, an denen ihn jemand sucht: in `EVALS.md` und im Kommentar
an der `Coverage report`-Stufe in `.gitea/workflows/ci.yml`, jeweils mit dem Satz, dass eine
leere Liste kein fehlgeschlagener Upload ist. Ohne den leitet ihn beim nächsten Mal jemand neu
her — und die naheliegende „Korrektur" wäre der Wechsel auf v4, der hier eingeschränkt ist.
**Schritt 2 ist damit erledigt.** Beobachtet wurden 38 CI-Läufe zwischen Lauf 87 und Lauf 163,
alle grün:
| | Lauf 87 (31.08., 1.8.1) | Lauf 163 (04.09., 4.7.1) |
|---|---|---|
| Coverage | 86,9 % | **87,0 %** |
| Statements | 5105 | **6498** (+27 %) |
| Tests | 730 | **975** (+245) |
Ein Viertel mehr gemessener Code, ein Drittel mehr Tests, ein Zehntelpunkt Bewegung in der Quote.
Das ist die Beobachtung, die eine Schwelle tragen kann.
**Die Modulverteilung ist der interessantere Teil, und sie hat sich bewegt** — die vollständige
Liste steht jetzt im Body. Zwei Dinge daraus:
- `search.py` fällt von 68,6 % auf 48,5 % und ist **kein** Regress: ungedeckt ist der
Typer-Kommandokörper (117–176), die Backends unter `chemenu/search/` liegen bei 91–98 %.
Kategorie 1 nach der Taxonomie dieses Issues. Wer nur den Gesamtwert pro Modul liest, hätte
hier Arbeit gesehen, wo keine ist — genau der Grund, warum das Issue die drei Kategorien
verlangt.
- Die **echten Lücken** sind die einzige Liste, die sich nicht bewegt hat, während alles um sie
herum wuchs. `provenance_cmd.py` steht unverändert bei 44,1 %; `migrate_cmd.py` ist von 71 %
auf 65 % gefallen, weil das Modul gewachsen ist und die neuen Zeilen ungetestet kamen.
Eine Schwelle friert das ein, sie schließt es nicht. Der Arbeitsvorrat gehört in ein eigenes
Issue.
**Schritt 3 bleibt offen, Labels bleiben.** Die inhaltliche Bedingung („mehrere CI-Läufe über
echten Commits") ist erfüllt, das Datum **2026-09-14** steht trotzdem — es soll die Beobachtung
sein, die die Zahl trägt, nicht die Ungeduld. `prio/waiting` bis dahin. Empfehlung für die
Schwelle steht im Body: **85**, nicht 87. 87 hat keine Luft, der nächste ehrliche
Wrapper-Commit macht CI rot, und dann wird die Schwelle gesenkt statt verdient — der Ausgang,
den dieses Issue verhindern soll.
Geliefert in `fe55ad2`. Prose-only plus Workflow-Kommentar, also kein Version-Bump: das
Version-Gate ist auf `^(tools/|types/|instructions/|AGENTS\.md$|[^/]+/CONTRACT\.md$)` gescoped,
und weder `EVALS.md` noch `.gitea/` fällt darunter.
Geschlossen mit 4.7.2 (e00eae0). Body auf den Endstand umgeschrieben — er beschreibt jetzt
das Ergebnis statt des Plans, und die drei Schritte lesen sich als erledigt statt als Vorhaben.
Was sich gegenüber dem Stand von heute Nachmittag geändert hat:
Schritt 3 ist gesetzt, vor dem Datum.fail_under = 85 in tools/.coveragerc, gegen
gemessene 87,0 %. Der 2026-09-14 war ein Proxy für „mehrere CI-Läufe über echten Commits", und
der Proxy war eingelöst: 38 grüne Läufe, +27 % Statements, +245 Tests, ein Zehntelpunkt
Bewegung in der Quote. Auf dem Datum zu beharren, wäre das Spiegelbild des Fehlers gewesen, den
dieses Issue verhindern sollte — dort eine Zahl ohne Beobachtung, hier ein Warten ohne Grund.
Die Begründung für 85 statt 87 steht im Body.
Der Artefakt-Punkt ist abgehakt statt offen, mit benannter Ursache: v3 legt über die ältere
Artifact-API ab, die Listen-Endpunkte lesen die nicht. Festgehalten in EVALS.md und im
Workflow-Kommentar, damit die naheliegende „Korrektur" auf v4 niemandem mehr einfällt.
Der Arbeitsvorrat ist ausgezogen, statt in einem geschlossenen Issue zu verschwinden: #51 trägt die drei echten Lücken (provenance_cmd.py 44 %, migrate_cmd.py 65 %, type_resolver.py 79 %) mit Zeilennummern und der Begründung, warum gerade die zählen.
docs/why-gates-are-code.md nachgezogen (b4e4501): die Untergrenze steht dort jetzt als
zweiter Fall neben der Iterationsgrenze. Die Grenze war erst falsch und wurde dann gemessen,
der Boden wurde absichtlich zurückgehalten, bis die Zahl existierte — dasselbe Argument in
beide Richtungen. Keine andere docs/-Seite ist von dieser Änderung berührt.
Verifiziert, nicht angenommen: CI-Lauf 166 über e00eae0 grün, alle neun Schritte, mit Required test coverage of 85.0% reached. Total coverage: 87.01% im Log — die Untergrenze ist in
CI aktiv und wird eingehalten, nicht bloß konfiguriert. Lokal derselbe Befund bei 975 Tests.
Geschlossen mit **4.7.2** (`e00eae0`). Body auf den Endstand umgeschrieben — er beschreibt jetzt
das Ergebnis statt des Plans, und die drei Schritte lesen sich als erledigt statt als Vorhaben.
Was sich gegenüber dem Stand von heute Nachmittag geändert hat:
- **Schritt 3 ist gesetzt, vor dem Datum.** `fail_under = 85` in `tools/.coveragerc`, gegen
gemessene 87,0 %. Der 2026-09-14 war ein Proxy für „mehrere CI-Läufe über echten Commits", und
der Proxy war eingelöst: 38 grüne Läufe, +27 % Statements, +245 Tests, ein Zehntelpunkt
Bewegung in der Quote. Auf dem Datum zu beharren, wäre das Spiegelbild des Fehlers gewesen, den
dieses Issue verhindern sollte — dort eine Zahl ohne Beobachtung, hier ein Warten ohne Grund.
Die Begründung für 85 statt 87 steht im Body.
- **Der Artefakt-Punkt ist abgehakt statt offen**, mit benannter Ursache: v3 legt über die ältere
Artifact-API ab, die Listen-Endpunkte lesen die nicht. Festgehalten in `EVALS.md` und im
Workflow-Kommentar, damit die naheliegende „Korrektur" auf v4 niemandem mehr einfällt.
- **Der Arbeitsvorrat ist ausgezogen**, statt in einem geschlossenen Issue zu verschwinden:
#51 trägt die drei echten Lücken (`provenance_cmd.py` 44 %, `migrate_cmd.py` 65 %,
`type_resolver.py` 79 %) mit Zeilennummern und der Begründung, warum gerade die zählen.
- **`docs/why-gates-are-code.md` nachgezogen** (`b4e4501`): die Untergrenze steht dort jetzt als
zweiter Fall neben der Iterationsgrenze. Die Grenze war erst falsch und wurde dann gemessen,
der Boden wurde absichtlich zurückgehalten, bis die Zahl existierte — dasselbe Argument in
beide Richtungen. Keine andere `docs/`-Seite ist von dieser Änderung berührt.
Verifiziert, nicht angenommen: CI-Lauf **166** über `e00eae0` grün, alle neun Schritte, mit
`Required test coverage of 85.0% reached. Total coverage: 87.01%` im Log — die Untergrenze ist in
CI aktiv und wird eingehalten, nicht bloß konfiguriert. Lokal derselbe Befund bei 975 Tests.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Ergebnis
Erledigt mit Stack 4.7.2 (
e00eae0). Die Suite hat einen Boden:fail_under = 85intools/.coveragerc, gesetzt gegen gemessene 87,0 % — und gesetzt erst, nachdem die Zahlbeobachtet war. Genau die Reihenfolge, für die dieses Issue existiert hat.
Drei Schritte, alle drei geliefert:
a243a4a, Stack 1.8.1) — CI installiertpytest-covnebenpytest, misstchemenu/ohnechemenu/tests/, lädt den Bericht als Artefakt hoch.e00eae0, Stack 4.7.2) —fail_under = 85, in eigenem Commit, mitder beobachteten Zahl als Begründung.
Die Beobachtung, auf der die Schwelle steht
Verbindlich ist jeweils die von CI gemessene Zahl, nie eine lokale: die lokale misst einen Baum,
der einen Commit hinter dem veröffentlichten liegt — beim ersten Mal hat genau das 5100/729
statt 5105/730 ergeben.
Das Paar trägt die Schwelle, nicht der Einzelwert. Zwischen den beiden Messungen ist der
gemessene Code um ein Viertel gewachsen und die Suite um ein Drittel, und die Quote hat sich um
einen Zehntelpunkt bewegt. Eine Untergrenze auf dieser Grundlage ist etwas anderes als eine
gegriffene Zahl.
Entschieden: 85, nicht 87
Der ursprüngliche Auslöser für Schritt 3 war ein Datum — 2026-09-14, gesetzt am 31.08. als
Schätzung, wie lange es dauert, bis genug Beobachtung zusammenkommt. Die Schätzung ist überholt
worden, weil der Stack schneller lief als sie: 1.8.1 → 4.7.1 in vier Tagen, 38 grüne Läufe,
+27 % Statements. Das Datum war ein Proxy für „mehrere CI-Läufe über echten Commits", und der
Proxy war am 2026-09-04 eingelöst. Auf dem Datum zu beharren, wäre die Umkehrung des Fehlers
gewesen, den dieses Issue verhindern soll: dort eine Zahl ohne Beobachtung dahinter, hier ein
Warten ohne Grund dahinter.
Der Wert ist 85 und nicht der gemessene 87er, und das ist keine Bequemlichkeit. Der
Coverage-Bericht unterscheidet drei Sorten ungedeckter Zeilen (siehe unten), und nur eine
bedeutet Arbeit. Ein neuer dünner Typer-Wrapper senkt den Gesamtwert, ohne dass irgendetwas
schlechter geworden wäre — seine Logik liegt daneben und ist dort getestet. Eine Schwelle auf dem
gemessenen Wert würde an genau diesem Commit rot, und eine Schwelle, die aus einem Nicht-Grund
rot wird, wird gesenkt statt verdient. Die zwei Punkte sind der Platz, den die Taxonomie
verlangt.
fail_understeht intools/.coveragercund nicht als--cov-fail-underim CI-Schritt: sositzt die Zahl neben der Begründung, die sie erzeugt hat, und gilt für jeden
--cov-Lauf stattnur für den einen, den CI schreibt.
Die Modulverteilung — der bleibende Teil des Befunds
Der Gesamtwert ist die uninteressanteste Größe. Drei Sorten sind zu unterscheiden, bevor daraus
Arbeit wird:
eval_cmd.py(36 %),types_cmd.py(40 %),search.py(49 %),cli.py(54 %),links_cmd.py(61 %). NiedrigeZahlen sind hier das Zeichen für einen guten Schnitt.
search.pyist der Musterfall: ungedecktist ausschließlich der Kommandokörper (Zeilen 117–176), während die Backends unter
chemenu/search/, die die Arbeit tun, zwischen 91 % und 98 % liegen. Wer nur den Modulwertliest, sieht hier Arbeit, wo keine ist.
Naht getestet wird:
version.pysfetch_latest()nimmt dafür einenfetcher, die echteNetzzeile bleibt absichtlich ungedeckt (
version.pygesamt 86,5 %).provenance_cmd.py(44 %),migrate_cmd.py(65 %),type_resolver.py(79 %). Das ist die einzige der drei Listen, die sich nicht bewegt hat, während alles um sie
herum wuchs —
provenance_cmd.pysteht bei unveränderten 44,1 %,migrate_cmd.pyist von71 % gefallen, weil das Modul gewachsen ist und die neuen Zeilen ungetestet ankamen.
Die Schwelle friert das ein, sie schließt es nicht. Das ist Gitea #51, mit der Liste, den
Zeilennummern und der Begründung, warum ausgerechnet diese drei Module zählen.
Der Artefakt-Abruf: geklärt
Der Bericht ist abrufbar, bestätigt am 2026-09-04 für
coverage-163über die Run-Seite.Der Widerspruch zwischen Kommentar 318 und 342 ist damit zugunsten von 318 aufgelöst und die
Ursache benannt statt vermutet: von den beiden dort genannten Erklärungen trifft die erste zu.
upload-artifact@v3legt über die ältere Artifact-API ab, die die Actions-Artefakt-Endpunktenicht lesen — daher
total_count: 0bei einem Artefakt, das tatsächlich da ist. Reproduziertüber 76 Läufe, auch für Lauf 163 einzeln.
Kein Folge-Issue. v3 funktioniert hier, nur die Listen-API sieht es nicht. Der Befund steht
an beiden Stellen, an denen ihn jemand sucht —
EVALS.mdund der Kommentar an derCoverage report-Stufe in.gitea/workflows/ci.yml—, jeweils mit dem Satz, dass eine leereListe kein fehlgeschlagener Upload ist. Ohne den wäre die naheliegende „Korrektur" der Wechsel
auf v4, der auf dieser Instanz eingeschränkt ist.
Nebenbefund, mitbehoben
dist exporthat die Coverage-Ausgabe mit ausgeliefert: der erste Export nach der Messung trug227 statt 162 Dateien, darunter den kompletten
htmlcov/-Baum — eine Messung dieses Repos, infremden Instanzen.
TOOLS_EXCLUDE_DIRSprunet nur Verzeichnisse, aber.coverage,coverage.xmlund unter Parallelläufen.coverage.<host>.<pid>liegen als Dateien neben demCode.
_copy_treenimmt seither zusätzlich ein Dateinamen-Prädikat. Ohne das hätte derCI-Export in jedem Lauf die Coverage des Laufs mitverpackt, weil „Tests" vor „Export the
distribution" läuft.
Akzeptanzkriterien
coverage-163, über dieRun-Seite; die Listen-API sieht v3-Artefakte nicht, siehe oben.
EVALS.md§ „How much of the stack thesuite reaches", nicht nur hier im Issue.
Ergebnis oben.
fail_under = 85, eigener Commit, PATCH-Bump auf4.7.2 (Version-Gate greift bei
^tools/).Verifiziert
pytest -q --covlokal: 975 passed, „Required test coverage of 85.0% reached. Totalcoverage: 87.01 %" — die Untergrenze ist aktiv und wird eingehalten, nicht nur konfiguriert.
tools/wikitool docs verifyundtools/wikitool instructions verifygrün, vor und nachversion release.e00eae0grün, alle neun Schritte — der erste Lauf mit aktiverUntergrenze.
docs/why-gates-are-code.md§ „Numbers that come frommeasurement, not intuition" führt die Untergrenze jetzt als zweiten Fall neben der
Iterationsgrenze (
b4e4501). Die Grenze war erst falsch und wurde dann gemessen; der Bodenwurde absichtlich zurückgehalten, bis die Zahl existierte — dasselbe Argument, vorwärts statt
rückwärts gelaufen.
Vorgeschichte
Angelegt am 2026-08-30, als 630 Tests in CI liefen und niemand wusste, wie viel vom Stack sie
abdecken. Der Verdacht war nicht theoretisch: Lauf 52 hatte zwei Tests gefunden, die grün waren,
weil die Umgebung zufällig passte. Was gar nicht ausgeführt wird, fällt noch leichter durch.
Der Zuwachs von 630 auf 975 Tests ist nicht nur Fläche. 2.1.1 hat eine Umgebungsabhängigkeit
geschlossen, die genau hierher gehört:
test_legacy_source_pages_flags_url_and_directorywargrün, weil der Checkout zufällig ein
raw/documents/besaß — die Korpus-Bereinigung leerte es,Git verfolgt keine leeren Verzeichnisse, und der Test wurde in CI rot, während er lokal grün
blieb. Derselbe Befund wie in Lauf 52, dritter Fall. Die systematische Härtung dagegen ist die
autouse-Fixture
hermetic_environment(#8); dass ihre Env-Liste von Hand gepflegt wird, ist #23.Pfade in diesem Body sind am 2026-09-04 von
wiki_tools/aufchemenu/nachgezogen (Rename in2.0.0, #3). Der Beleg für
upload-artifact@v3nannte bis dahin einen privaten Hostnamen; er istneutral als
torben/gitea-mcp@ci-build,ci-build.yaml, Läufe 42–45 formuliert. Teilerledigungvon #29.
Schritt 1 umgesetzt in
a243a4a, Stack 1.8.1. Das Issue bleibt offen — Schritt 3 (dieSchwelle) ist ausdrücklich ein eigener, späterer Commit.
Erste Messung, 2026-08-31: 86.9 % von 5100 Statements, 729 Tests.
Festgehalten in
EVALS.md§ „How much of the stack the suite reaches" — also nicht nur hier imIssue, wie das Akzeptanzkriterium es als Minimum zuließ.
Die Module unten, nach den drei Kategorien getrennt, die das Issue verlangt:
commands/eval_cmd.pycommands/provenance_cmd.pycli.pycommands/types_cmd.pycommands/version_cmd.pyversion.pycommands/search.pycommands/migrate_cmd.pytype_resolver.pyversion.pyliegt bei 89.1 % — der Netzzugriff sitzt wie beschrieben hinter dem injizierbarenfetcher, die echte Netzzeile bleibt absichtlich ungetestet. Die Wrapper-Zeilen sind keinArbeitsvorrat: niedrige Zahlen dort sind das Zeichen für den guten Schnitt, das im Issue schon
vorweggenommen war. Arbeit ist ausschließlich die dritte Spalte.
Umsetzung, mit einer Abweichung vom Issue.
pytest-covsteht nicht intools/requirements.txt, CI installiert es nebenpytest— wie gefordert. Die Konfigurationliegt aber in
tools/.coveragerc, nicht intools/pytest.ini: coverage.py liest.coveragerc,setup.cfg,tox.iniundpyproject.toml, aber keinpytest.ini, wo ein[coverage:*]-Abschnitt still ignoriert würde. Gemessen wirdwiki_tools/ohnewiki_tools/tests/. Bericht als Artefaktcoverage-<run id>überupload-artifact@v3(nichtv4), unter
if: always()— eine rote Suite ist genau der Moment, in dem die Zahlen pro Modulinteressant sind. Kein
--cov-fail-under.Die Messung hängt bewusst nicht in
addopts: das würde den nacktenpytest -qüberall dortbrechen, wo
pytest-covfehlt — also in jeder Instanz und in jedem lokalen Lauf ohne dasZusatzpaket.
Dabei gefunden und mitbehoben:
dist exporthat die Coverage-Ausgabe mit ausgeliefert. Dererste Export nach der Messung trug 227 statt 162 Dateien, darunter den kompletten
htmlcov/-Baum — eine Messung dieses Repos, ausgeliefert in fremde Instanzen.TOOLS_EXCLUDE_DIRSprunet nur Verzeichnisse, zwei Drittel der Ausgabe (
.coverage,coverage.xml, unterParallelläufen
.coverage.<host>.<pid>) liegen als Dateien neben dem Code._copy_treenimmtjetzt zusätzlich ein Dateinamen-Prädikat. Ohne das hätte der CI-Export in jedem Lauf die
Coverage des Laufs mitverpackt, weil „Tests" vor „Export the distribution" läuft.
Was noch offen ist
--cov-fail-undersetzen, in eigenem Commit, mit der beobachteten Zahl alsBegründung. Sie friert den erreichten Stand ein, sie rechnet ihn nicht schön.
Neu gelabelt
prio/2→prio/3. Der Wert ist unverändert real, aber der nächste Schrittist auf einen Auslöser gestellt, und der Auslöser ist Zeit: mehrere CI-Läufe über echten
Commits, frühestens ab 2026-09-14. Vorher gesetzt wäre die Schwelle wieder das, was das
Issue verhindern wollte — eine Zahl ohne Beobachtung dahinter.
CI-Lauf 87 grün, alle neun Schritte. Auch Lauf 88 (Release) grün.
Zwei Nachträge zum Kommentar oben.
1. Die Baseline-Zahlen waren um einen Commit veraltet — korrigiert in
2b7b3cb.Gemessen hatte ich lokal vor dem
dist export-Fix, der noch Statements hinzufügte, und vordem Test, der ihn absichert. Verbindlich ist, was der veröffentlichte Baum produziert:
86.9 % von 5105 Statements, 730 Tests (CI-Lauf 87), nicht 5100/729.
Der Prozentwert ist derselbe, die Grundgesamtheit nicht.
EVALS.mdund der Changelog-Eintragnennen jetzt beide die von CI gemessene Zahl und die Laufnummer dazu.
2. Das Artefakt ist hochgeladen, aber über die API nicht auffindbar — offen.
Aus dem Log von Lauf 87, Schritt „Coverage report":
Der Upload meldet also Erfolg: 63 Dateien, 487 KB komprimiert aus 4.3 MB roh.
Aber: sowohl
/repos/torben/llm-wiki-test1/actions/runs/87/artifactsals auch dieRepo-weite Artefaktliste liefern
total_count: 0. Der Lauf ist abgeschlossen, es ist also keinTiming-Effekt.
Damit ist das Akzeptanzkriterium „Der Bericht ist als Artefakt abrufbar" nicht bestätigt.
Die Erfolgsmeldung der Action ist ein Beleg dafür, dass sie hochgeladen hat, nicht dafür,
dass Gitea es herausgibt. Zwei plausible Ursachen, beide ungeprüft:
upload-artifact@v3legtüber die alte Artifact-API ab, während die REST-Liste nur v4-Artefakte kennt — oder die
Artefakt-Speicherung ist auf dieser Instanz nicht so konfiguriert, wie die Liste es erwartet.
Nächster Schritt, bevor irgendetwas an v4 geändert wird: auf der Run-Seite im Browser
nachsehen, ob das Artefakt dort zum Download angeboten wird
(https://gitea.nehmer.net/torben/llm-wiki-test1/actions/runs/87). Wenn ja, ist es nur die
Listen-API und das Kriterium ist erfüllt. Wenn nein, ist v3 auf dieser Instanz ein Upload ins
Leere, und das gehört in ein eigenes Issue — es beträfe dann jeden Workflow hier, nicht nur
Coverage.
Stand 2026-09-01 — Schritt 1 ist erledigt, Schritt 2 läuft
Schritt 1 (messen ohne Schwelle) ist mit 1.8.1 geliefert. CI installiert
pytest-covnebenpytest, misstchemenu/ohnechemenu/tests/, lädt den Bericht als Artefakt hoch und setzt kein--cov-fail-under. Die Akzeptanzkriterien „CI weist Coverage aus" und „Bericht ist als Artefakt abrufbar" sind damit erfüllt.Offen bleiben Schritt 2 und 3: die Zahl über ein paar Wochen beobachten — vor allem welche Module unten liegen — und danach in einem eigenen Commit eine Untergrenze setzen, die den erreichten Stand einfriert.
Zwei Korrekturen am Text oben:
Die Testzahl ist von 630 auf 752 gestiegen. Der Zuwachs ist nicht nur Fläche: 2.1.1 hat eine Umgebungsabhängigkeit geschlossen, die genau in die Argumentation dieses Issues gehört.
test_legacy_source_pages_flags_url_and_directorywar grün, weil der Checkout zufällig einraw/documents/besaß — die Korpus-Bereinigung leerte es, Git verfolgt keine leeren Verzeichnisse, und der Test wurde in CI rot, während er lokal grün blieb. Das ist derselbe Befund wie in Run 52, dritter Fall.Der Verweis auf
torben/gitea-mcp@ci-binford/binford-build.yamlals Beleg fürupload-artifact@v3nennt einen privaten Hostnamen. Die Sache stimmt weiterhin — v3 ist das, was auf dieser Gitea funktioniert —, der Beleg gehört aber neutral formuliert, bevor jemand den Text nach draußen kopiert. Pfade im Text:wiki_tools/heißt seit 2.0.0chemenu/.Changelog: Body vollständig auf den Stand von 4.5.0 umgeschrieben (Triage-Sitzung 2026-09-04). Der Text hatte sich seit dem 2026-08-30 nicht bewegt, während drei Kommentare den echten Stand trugen — genau das Muster, das
issue-tracking.mdSchritt 2 verhindern soll, und genau der Fall, den #47 als Befund 2 beschreibt.Was der alte Body falsch sagte:
pytest-cov„gehört nachtools/pytest.ini" — die Konfiguration liegt tatsächlich intools/.coveragerc, mit Grund: coverage.py liestpytest.ininicht.upload-artifact@v3nannte einen privaten Hostnamen (ci-binford,binford-build.yaml); im Baum war er längst zuci-buildneutralisiert, im Issue nicht. Nachgezogen, ebensowiki_tools/→chemenu/(Teilerledigung von #29).Neu im Body, vorher nur in Kommentaren oder gar nicht:
provenance_cmd.py44 %,migrate_cmd.py71 %,type_resolver.py79 %). Nur die dritte Spalte bedeutet Arbeit.dist exportdie Coverage-Ausgabe mit ausgeliefert hat (227 statt 162 Dateien), und wie er behoben wurde.total_count: 0bei abgeschlossenem Lauf. Kommentar 342 hat das Kriterium für erfüllt erklärt, ohne dass die Gegenprobe gelaufen wäre. Es steht jetzt als offener Punkt mit dem konkreten nächsten Schritt (Run-Seite im Browser ansehen, bevor irgendetwas an v4 geändert wird).Labels unverändert.
prio/waitingbleibt bis zum Auslöser 2026-09-14; das Datum steht jetzt im Body statt nur in Kommentar 315.Schritt 2 abgeschlossen, Artefakt-Frage geklärt (2026-09-04, Stack 4.7.1)
Der Artefakt-Abruf ist bestätigt.
coverage-163wird auf der Run-Seite zum Downloadangeboten. Damit ist das Akzeptanzkriterium erfüllt, und die Ursache des Widerspruchs aus
Kommentar 318 ist benannt statt vermutet: von den beiden dort genannten Erklärungen trifft die
erste zu.
upload-artifact@v3legt über die ältere Artifact-API ab, die dieActions-Artefakt-Endpunkte nicht lesen — deshalb
total_count: 0bei einem Artefakt, dastatsächlich da ist. Reproduziert über 76 Läufe: auch
list_run_artifactsfür Lauf 163 meldettotal_count: 0, während die Run-Seite die Datei herausgibt.Kein Folge-Issue. v3 funktioniert auf dieser Instanz, nur die Listen-API sieht es nicht. Der
Befund steht jetzt an beiden Stellen, an denen ihn jemand sucht: in
EVALS.mdund im Kommentaran der
Coverage report-Stufe in.gitea/workflows/ci.yml, jeweils mit dem Satz, dass eineleere Liste kein fehlgeschlagener Upload ist. Ohne den leitet ihn beim nächsten Mal jemand neu
her — und die naheliegende „Korrektur" wäre der Wechsel auf v4, der hier eingeschränkt ist.
Schritt 2 ist damit erledigt. Beobachtet wurden 38 CI-Läufe zwischen Lauf 87 und Lauf 163,
alle grün:
Ein Viertel mehr gemessener Code, ein Drittel mehr Tests, ein Zehntelpunkt Bewegung in der Quote.
Das ist die Beobachtung, die eine Schwelle tragen kann.
Die Modulverteilung ist der interessantere Teil, und sie hat sich bewegt — die vollständige
Liste steht jetzt im Body. Zwei Dinge daraus:
search.pyfällt von 68,6 % auf 48,5 % und ist kein Regress: ungedeckt ist derTyper-Kommandokörper (117–176), die Backends unter
chemenu/search/liegen bei 91–98 %.Kategorie 1 nach der Taxonomie dieses Issues. Wer nur den Gesamtwert pro Modul liest, hätte
hier Arbeit gesehen, wo keine ist — genau der Grund, warum das Issue die drei Kategorien
verlangt.
herum wuchs.
provenance_cmd.pysteht unverändert bei 44,1 %;migrate_cmd.pyist von 71 %auf 65 % gefallen, weil das Modul gewachsen ist und die neuen Zeilen ungetestet kamen.
Eine Schwelle friert das ein, sie schließt es nicht. Der Arbeitsvorrat gehört in ein eigenes
Issue.
Schritt 3 bleibt offen, Labels bleiben. Die inhaltliche Bedingung („mehrere CI-Läufe über
echten Commits") ist erfüllt, das Datum 2026-09-14 steht trotzdem — es soll die Beobachtung
sein, die die Zahl trägt, nicht die Ungeduld.
prio/waitingbis dahin. Empfehlung für dieSchwelle steht im Body: 85, nicht 87. 87 hat keine Luft, der nächste ehrliche
Wrapper-Commit macht CI rot, und dann wird die Schwelle gesenkt statt verdient — der Ausgang,
den dieses Issue verhindern soll.
Geliefert in
fe55ad2. Prose-only plus Workflow-Kommentar, also kein Version-Bump: dasVersion-Gate ist auf
^(tools/|types/|instructions/|AGENTS\.md$|[^/]+/CONTRACT\.md$)gescoped,und weder
EVALS.mdnoch.gitea/fällt darunter.Geschlossen mit 4.7.2 (
e00eae0). Body auf den Endstand umgeschrieben — er beschreibt jetztdas Ergebnis statt des Plans, und die drei Schritte lesen sich als erledigt statt als Vorhaben.
Was sich gegenüber dem Stand von heute Nachmittag geändert hat:
fail_under = 85intools/.coveragerc, gegengemessene 87,0 %. Der 2026-09-14 war ein Proxy für „mehrere CI-Läufe über echten Commits", und
der Proxy war eingelöst: 38 grüne Läufe, +27 % Statements, +245 Tests, ein Zehntelpunkt
Bewegung in der Quote. Auf dem Datum zu beharren, wäre das Spiegelbild des Fehlers gewesen, den
dieses Issue verhindern sollte — dort eine Zahl ohne Beobachtung, hier ein Warten ohne Grund.
Die Begründung für 85 statt 87 steht im Body.
Artifact-API ab, die Listen-Endpunkte lesen die nicht. Festgehalten in
EVALS.mdund imWorkflow-Kommentar, damit die naheliegende „Korrektur" auf v4 niemandem mehr einfällt.
#51 trägt die drei echten Lücken (
provenance_cmd.py44 %,migrate_cmd.py65 %,type_resolver.py79 %) mit Zeilennummern und der Begründung, warum gerade die zählen.docs/why-gates-are-code.mdnachgezogen (b4e4501): die Untergrenze steht dort jetzt alszweiter Fall neben der Iterationsgrenze. Die Grenze war erst falsch und wurde dann gemessen,
der Boden wurde absichtlich zurückgehalten, bis die Zahl existierte — dasselbe Argument in
beide Richtungen. Keine andere
docs/-Seite ist von dieser Änderung berührt.Verifiziert, nicht angenommen: CI-Lauf 166 über
e00eae0grün, alle neun Schritte, mitRequired test coverage of 85.0% reached. Total coverage: 87.01%im Log — die Untergrenze ist inCI aktiv und wird eingehalten, nicht bloß konfiguriert. Lokal derselbe Befund bei 975 Tests.