fix(generative): pipeline_runs-Insert vom Inline-Eval entkoppeln + Test-Traces nach tmp (#198 P1+P2)#218
Merged
Merged
Conversation
…st-Traces nach tmp (#198 P1+P2) P1 (Insert-Lücke): Bei deaktiviertem Inline-Eval (Profil fast/balanced oder ATOMIC_AGENT_INLINE_EVAL=0) kehrte orchestrator.main() VOR dem pipeline_runs-Insert zurück. Der Lauf schrieb einen vollständigen Trace, bekam aber keine DB-Zeile — war also keiner Pipeline-Version zuordenbar und verschwand aus versions-gefilterten Ansichten. Token-Berechnung + run_meta + DB-Insert laufen jetzt UNBEDINGT vor dem inline_eval-skip-Return; die eval-abhängigen note_evals bleiben Stage-8-exklusiv. get_db(DB_PATH) statt get_db() liest den Modul-Pfad zur Laufzeit (Test-Isolation). P2 (Test-Verschmutzung): Neue autouse-Fixture in generative/conftest.py biegt Trace-Backend, base._RUN_DIR und db.DB_PATH pro Test auf ein isoliertes tmp-Verzeichnis um — kein Test schreibt mehr ins produktive .cache/runs/ bzw. atomic_analytics.db. Tests: test_pipeline_run_persist_no_eval (P1, RED auf master: 0 statt 1 Zeile), test_trace_isolation (P2). Suite generative/lib/decision_engine/shared grün.
…-Override, run_totals defensiv (#198 Nachbesserung)
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.
Setzt Punkte 1+2 aus #198 um. Punkt 3 wartet auf eine Maintainer-Entscheidung (siehe unten), Punkt 4 läuft über #187 — daher bewusst kein
closes #198.Problem
P1 — Insert-Lücke (strukturell): In
generative/orchestrator.pystand derpipeline_runs-Insert INNERHALB von Stage 8 (Inline-Eval). Istinline_eval_enabled()False (Profilfast/balancedoderATOMIC_AGENT_INLINE_EVAL=0), kehrtemain()VOR dem Insert zurück. Solche Läufe schrieben einen vollständigen Trace, bekamen aber keine DB-Zeile — waren also keiner Pipeline-Version zuordenbar und verschwanden aus allen versions-gefilterten Ansichten.P2 — Test-Verschmutzung: pytest nutzt dieselbe
_trace()-Infrastruktur wie die Pipeline. Tests, dieagents.base.call_claude_full(...)mit gestubbtem Backend aufrufen (z. B.test_call_record,test_eval_cache_namespace), schrieben echte Trace-Dateien ins produktivegenerative/.cache/runs/(agent=test/verifier/critic,<ts>.jsonl).Fix
P1: Token-Berechnung +
run_meta+ DB-Insert laufen jetzt unbedingt vor deminline_eval-skip-Return. Der Insert hängt an nichts Eval-Spezifischem (nur Trace-Tokens + Run-Zähler); die eval-abhängigennote_evalsbleiben Stage-8-exklusiv.get_db(_db.DB_PATH)stattget_db()liest den Modul-Pfad zur Laufzeit (wiecalibration.collect), damit Tests ihn auf eine tmp-DB umbiegen können. Kein Doppel-Insert.Präzisierung — der Verhaltenswechsel gilt auch bei aktivem Inline-Eval, nicht nur im deaktivierten Fall: Der Insert lag zuvor INNERHALB des Stage-8-
try, NACH der Eval-Schleife. Schlug die Eval früh fehl (Exception vor dem Insert), ging die DB-Zeile auch bei aktivem Inline-Eval verloren — sie wurde nur vomexceptals[eval-warn]geschluckt. Jetzt läuft der Insert VOR Stage 8 und überlebt Stage-8-Frühausfälle. Der Eval-Overhead bleibt weiterhin außerhalb vonrun_meta/DB (nur am Run-Ende geprintet).P2: Neue autouse-Fixture
isolate_pipeline_side_effectsingenerative/conftest.pybiegttracing._backend,base._RUN_DIRunddb.DB_PATHpro Test auf ein isoliertestmp_path_factory-Verzeichnis um (bewusst NICHTtmp_path— viele Tests listen ihr eigenestmp_path). Analog zum bestehenden Sicherheitsnetz ingenerative/gui/tests/conftest.py. Tests, die selbst einen Backend/DB-Pfad setzen, überschreiben die Fixture per monkeypatch (last-wins).RED-Beweis (TDD)
P1 —
test_pipeline_run_persist_no_eval.pyfährtmain()mitfast-Profil (Inline-Eval AUS) auf dem echten Beispiel-PDF (LLM/Netz gestubbt, Harness austest_ci_smoke_e2e). Auf master-Stand (Fix gestasht):Mit Fix: 1 Zeile mit korrekter
pipeline_version→ grün.P2 — Im frischen Worktree existierte kein
runs/-Verzeichnis. Nach zwei Polluter-Tests OHNE Fixture:test_trace_isolation.pybeweist die GREEN-Seite: ein getracter Call landet in tmp, das produktiveruns/bleibt unberührt.Suite-Ergebnis
pytest generative lib/decision_engine/tests shared/tests -q→ 5697 passed, 3 skipped, 4 deselected, 0 failed (Baseline master: 5693 passed; +4 neue Tests: 2× P1/P2, 2× Nachbesserung).ruff format --check·ruff check→ clean.Nebenbefund (nicht Teil dieses PRs):
main(--no-llm)mutiertconfig.ENABLE_LLM = Falseals Modul-Global ohne Restore — ein bestehender Prozess-State-Leak. Der neue P1-Test snapshottet/restauriert den Wert selbst, damit er keine Folge-Tests (z. B.test_verifier_prepass) in den no-LLM-Modus zieht. Eine saubere Kapselung inmain()wäre ein eigener kleiner Folge-Fix.Nachbesserung (Commit
58227e6, extern verifizierte Funde)Kontrollreview (Fable) + Runtime-Funktionsprüfung deckten Rest-Lücken in der P2-Isolation und einen Crash-Pfad auf.
orchestrator.pybleibt unangetastet (Konfliktschonung ggü. #216/#220) — Fix 4 sitzt an der Wurzel ineval_agent_stats.py.base._LLM_CACHE_DIR(.cache/llm/) nicht ab. Der in-process-main()-Test persistierte Fake-Backend-Antworten unter ECHTEN Cache-Keys im produktiven LLM-Cache. →_LLM_CACHE_DIRin die autouse-Fixture aufgenommen (tmp).rotate_run_caches()ammain()-Ende rotiert das ECHTEconfig.CACHE_DIR(Caps llm=2000 / runs=600, ältestes wird gelöscht). Ein Suite-Lauf auf einer gefüllten Maintainer-Maschine hätte reale Cache-Dateien gelöscht. → Fixture bindetcache_rotation.rotate_run_cachesper Wrapper auf ein tmp-cache_root(echte Rotations-Logik bleibt exerziert, nur Zielverzeichnis ist tmp).tests/test_e2e_baseline.py(slow-gated) startet die Pipeline als Subprozess ohne DB-Override → in-process-Fixture greift nicht. → dem Subprozess-EnvATOMIC_DB_PATH=tmpmitgegeben (ENV existiert bereits, steuertdb.DB_PATHzur Import-Zeit). Trace-/LLM-Cache-/Rotations-Senke kennen keine ENV-Steuerung; einATOMIC_CACHE_DIR-Umbau inconfig.pyhätte breiten Import-Rippel → bewusst als Residuum im Testkommentar + hier dokumentiert (echter Lauf → legitime, rotations-gedeckelte Laufdaten, keine schädliche Verschmutzung).run_totalsdefensiv: Der Token-Block ammain()-Ende rufteval_agent_stats.run_totals()ungeschützt;trace_path.read_text()konnte OSError/UnicodeDecodeError werfen und den Lauf am Ende crashen. → Datei-Lesen inrun_totalstoleriert jetzt Lese-/Decode-Fehler (→ Nullen, wie der Docstring-Contract behauptet). Deckt beide Aufrufstellen ab, ohneorchestrator.pyanzufassen.db-Query-Helfer bindenDB_PATHals Default-Argument zur DEF-Zeit; no-arg-Aufrufer (query_*()) umgehen dendb.DB_PATH-Monkeypatch (lesen die reale DB, read-only). Vorbestehend — jetzt imconftest-Docstring präzise abgegrenzt (abgedeckte vs. nicht abgedeckte Senken).TDD:
test_cache_isolation.py— zwei Tests snapshotten das REALE.cache/llm+.cache/runsrein lesend und beweisen, dass ein gecachter Call bzw. eine Rotation ausschließlich das tmp-Ziel trifft und die produktiven Datei-Mengen (davor == danach) unverändert bleiben.Cross-Model-Review (Mistral
magistral-medium-latest)Voller Diff geprüft. Keine belastbaren Funde — jede aufgeworfene Sorge (Doppel-Insert, undefinierte Variablen im Eval-Block,
run_totals-Exception,get_db-Laufzeitpfad, Fixture-Teardown/init_db,pdf_key-split) wurde vom Modell selbst als sicher bzw. unverändert ggü. Original verifiziert. Fazit wörtlich: „es gibt keine konkreten Bugs".Offener Punkt 3 (Maintainer-Entscheidung, nicht in diesem PR)
total_tokens-KPI im Dashboard: Lifetime-Summe nur DB-gejoint vs. Orphans ausweisen. Betriffteval_dashboard*.py/.html(parallele Arbeiten — hier bewusst nicht angefasst).SUM(pipeline_runs.tokens_total). Konsistent mit den übrigen KPIs. Dieser PR schließt die größte Orphan-Klasse (eval-lose Voll-Läufe); verbleibende Orphans sind echte Leerläufe (0 Notes, frühe Returns bei „keine Konzepte"/„alles Artefakte") — deren Tokens aus einer Note-Produktions-KPI auszuschließen ist vertretbar.Merge-Reihenfolge
Nach #216 (
fix/210-cli-timeout-retry) mergen. #216 ändertorchestrator.pyebenfalls (Extractor-Failure-Tracking,main()-Exit-Code 3,return exit_codeam eval-skip,sys.exit(main())).Konfliktflächen-Einschätzung: gering. Meine zwei Hunks — (1) Insert-Block eingefügt zwischen frühem Token-Print und
# --- Stage 8-Kommentar, (2) alter Token/Insert-Block INNERHALB des Eval-tryentfernt — überlappen keine der #216-Hunks (Fertig-Warnblock ~L2412,return→return exit_code~L2496,sys.exit(main())~L2614). #216s eval-skip-Return liegt im unberührten Spalt zwischen meinen beiden Hunks → erwartetes sauberes 3-Wege-Auto-Merge. Semantisch nach beidem korrekt: der eval-skip-Pfad machtreturn exit_code, NACHDEM mein Insert-Block gelaufen ist.