Skip to content

fix(shared): PDF-Pfade mit Apostroph-Varianten ueber Glob-Fallback aufloesen#214

Merged
TillQuandel merged 2 commits into
masterfrom
fix/186-pdf-apostroph
Jul 12, 2026
Merged

fix(shared): PDF-Pfade mit Apostroph-Varianten ueber Glob-Fallback aufloesen#214
TillQuandel merged 2 commits into
masterfrom
fix/186-pdf-apostroph

Conversation

@TillQuandel

@TillQuandel TillQuandel commented Jul 12, 2026

Copy link
Copy Markdown
Owner

Problem

PDF-Pfade mit typografischem Apostroph (U+2019) statt geradem ' (oder umgekehrt) liessen die Existenz-Pruefung an 3 Stellen mit einer nackten Fehler: PDF nicht gefunden-Meldung scheitern -- ohne Diagnose, welches Zeichen das Matching bricht:

  • extractive/orchestrator.py:76
  • generative/eval_chunk_recall.py:213
  • generative/gui/app.py:784

Fix-Ansatz

Neuer Helper shared/path_safety.resolve_source_path(source) -> Path (importierbar von extractive UND generative, da shared/ bereits von beiden genutzt wird):

  • Existiert source exakt: unveraendert zurueckgeben.
  • Sonst Glob-Fallback im selben Verzeichnis: Apostroph-/Anfuehrungszeichen-Varianten (' ’ ‘ \ ´) im Dateinamen werden durch ?ersetzt (Rest viaglob.escape`), Kandidaten danach strikt nachgefiltert (siehe Nachbesserung Punkt 1).
  • Genau 1 Treffer: verwenden + Hinweis auf stderr.
  • 0 oder >1 Treffer: FileNotFoundError mit repr(str(source)), bei Mehrdeutigkeit inkl. Kandidatenliste.

Alle Aufrufer auf try: resolve_source_path(...) / except FileNotFoundError umgestellt (Fehlertext bleibt je Aufrufer im bisherigen Format: sys.exit, print+return 1, JSONResponse). Die Funktion wirft FileNotFoundError mit fertig formatierter Meldung, damit alle Aufrufer denselben Text bekommen statt ihn zu duplizieren (SSoT).

In generative/gui/app.py wird pdf nach erfolgreicher Aufloesung auf den resolvten String gesetzt, damit ein Glob-Fallback-Treffer auch tatsaechlich fuer den Pipeline-Lauf (nicht nur fuer die Fehlermeldung) verwendet wird.

Bekannte Grenze

Apostroph-/Anfuehrungszeichen-Varianten im Verzeichnisnamen selbst (nicht im Dateinamen) werden nicht geheilt -- der Glob-Fallback sucht nur innerhalb von path.parent, nicht rekursiv ueber Verzeichnis-Namensvarianten. In diesem Fall bleibt es bei der repr()-Diagnose (kein automatischer Fallback), analog zum Verhalten bei fehlenden Dateien.

RED-Beweis (vor dem Fix)

shared/tests/test_path_safety.py importierte die noch nicht existierende Funktion:

ImportError: cannot import name 'resolve_source_path' from 'shared.path_safety'

Tests

6 Tests in shared/tests/test_path_safety.py (tmp_path-Fixture): exakter Treffer, gerader Apostroph findet Datei mit U+2019 und umgekehrt, Fehlermeldung enthaelt repr(str(source)), Mehrdeutigkeit listet beide Kandidaten, kein Fallback ohne Apostroph-Zeichen im Namen. Plus die Nachbesserungs-Tests unten.

Cross-Model-Review (Mistral magistral-medium-latest)

1 Fund (niedrig, generative/gui/app.py): Vorschlag, den stderr-Fallback-Hinweis zusaetzlich in die JSONResponse aufzunehmen. Der von Mistral gezeigte "korrigierte" Code war identisch zum Original -> laut Fake-Fund-Kriterium verworfen. 0 reale Funde, 0 Fixes noetig.

Nachbesserung (Cross-Model-Review Fable+Qwen, 3x konvergent + Runtime-Pruefung)

Drei extern verifizierte Funde nachgebessert:

  1. [MITTEL, 3x konvergent] resolve_source_path: Das ?-Glob matcht JEDES Zeichen, nicht nur Quote-Varianten -- real belegt an PorstXs-Buch.pdf / Porst_s-Buch.pdf / Porst5s-Buch.pdf, die bei einer Apostroph-Query faelschlich als "Apostroph-Variante" durchgingen; auch Verzeichnisse zaehlten als Kandidat. Fix: Kandidaten nach dem Glob zusaetzlich streng gefiltert -- Name muss nach Normalisierung aller Quote-Zeichen exakt dem normalisierten Query-Namen entsprechen, plus is_file()-Filter. 4 neue RED-Tests (3 Fehlmatch-Faelle + Verzeichnis-Fall).
  2. [MITTEL] generative/orchestrator.py (Haupt-CLI-Pfad, --source ohne --load-drafts): nutzte noch einen nackten sys.exit(f"Datei nicht gefunden: {source_path}") statt resolve_source_path -- dieselbe Bugklasse wie die 3 im Issue genannten Stellen, dort im Review uebersehen. Jetzt auf resolve_source_path umgestellt (gleiches Muster wie die anderen 3 Stellen; Hunk bewusst minimal wegen paralleler orchestrator-PRs fix(generative): Timeout-Retry im legacy-Profil + verlorene Konzepte sichtbar (#210) #216/fix(generative): pipeline_runs-Insert vom Inline-Eval entkoppeln + Test-Traces nach tmp (#198 P1+P2) #218/feat(generative): Stage-Outcome-Events + Drop-Gründe an den Gate-Punkten (#197 Schritt 1+2) #220). 2 neue Tests (test_orchestrator_source_resolution.py, mit autouse-Fixture gegen _LLM_RUNTIME_SETTINGS-Leak: orchestrator.main setzt sonst globalen Backend-State, der test_phoenix_span.py bricht).
  3. [NIEDRIG] generative/gui/app.py: resolve_source_path lief vor dem _allowed_roots-Check -- der Glob-Fallback haette so Verzeichnisse ausserhalb der erlaubten Roots durchsuchen koennen (Defense-in-Depth-Aufweichung, im Threat-Model toleriert aber unnoetig). Fix: Root-Check auf den Elternordner des rohen Pfads laeuft jetzt VOR resolve_source_path; der bestehende Check auf den aufgeloesten Pfad bleibt zusaetzlich bestehen. 1 neuer Test (spy auf resolve_source_path, darf fuer nicht-erlaubte Verzeichnisse nicht aufgerufen werden).

Suite-Ergebnis (nach Nachbesserung)

pytest generative lib/decision_engine/tests shared/tests -q: 5706 passed, 0 failed, 3 skipped, 4 deselected (5699 vom bisherigen PR-Stand + 7 Nachbesserungs-Tests). ruff format --check . und ruff check .: sauber.

Hinweis: Das Haupt-venv hat den extractive-Extra (pdfplumber/gliner) nicht installiert -- vorbestehend, nicht Teil dieses Fixes; extractive/tests ist entsprechend nicht Teil der kanonischen Suite (AGENTS.md).

Closes #186

Tilltime added 2 commits July 12, 2026 02:01
…floesen (closes #186)

Typografischer Apostroph (U+2019) vs. gerades ' im PDF-Pfad liess die
Existenz-Pruefung an 3 Stellen (extractive/orchestrator.py,
generative/eval_chunk_recall.py, generative/gui/app.py) mit einer nackten
"PDF nicht gefunden"-Meldung scheitern -- ohne erkennbar zu machen, welches
Zeichen das Matching bricht.

Neuer Helper shared/path_safety.resolve_source_path(): bei exaktem Treffer
unveraendert zurueckgegeben; sonst Glob-Fallback im selben Verzeichnis mit
Apostroph-/Anfuehrungszeichen-Varianten (' U+2019 U+2018 ` U+00B4) als
Wildcard (Rest via glob.escape). Genau 1 Treffer -> verwenden + Hinweis auf
stderr; 0 oder >1 Treffer -> FileNotFoundError mit repr(str(source)) und bei
Mehrdeutigkeit den Kandidaten. Alle 3 Aufrufer auf try/except umgestellt.
…hestrator-CLI-Pfad angeschlossen (#186 Nachbesserung)
@TillQuandel TillQuandel merged commit 7ef77e1 into master Jul 12, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

PDF-Pfade mit typografischem Apostroph scheitern mit 'PDF nicht gefunden' — repr()-Diagnose + Glob-Fallback

2 participants