Skip to content

Master Plan Optimierung

MadGapun edited this page Jun 2, 2026 · 3 revisions

Master-Plan — Analyse und Optimierung

Stand: 2026-06-01 · Aktualisiert nach jedem Plan-Review

Diese Seite enthaelt die kritische Analyse des Master-Plans. Sie listet Risiken, Trade-offs, Optimierungs-moeglichkeiten und konkrete Anpassungen, die in den Plan eingeflossen sind — viele davon aus realen Bugfix-Sessions retroaktiv extrahiert.


Identifizierte Risiken (R1–R14)

R1 — Scraper-URL-Aging ohne Erkennung

Risiko: Portale aendern URL-Formate (z.B. Workday-SPA-Migration, FERCHAU-Site-Restrukturierung). Scraper liefern dann 0 Treffer aber Server-200 — silent failure. Maßnahme:

  • scraper_health-System mit Auto-Reactivate-Backoff (B3 im Plan) deaktiviert nach 5+ stillen Laeufen automatisch.
  • quellen_health_check macht aktiven Probe-Check (B4).
  • URL-Aging-Auto-Cleanup (B11, beta.73) sortiert tote Einzel-Stellen aus.
  • Offen (B13): Probe-URLs sind teilweise veraltet (Bundesagentur 403, obwohl Production-Scraper laeuft). Probe-Endpoint-Inventar muss gepflegt werden.

R2 — SQLite-Lock-Konflikt zwischen Auto-Engine und Bulk-Tools

Risiko: _run_auto_refetch_descriptions haelt waehrend HTTP-GET (15s Timeout pro Stelle) die DB-Connection. Parallele Bulk-Reads (stellen_bulk_bewerten Dry-Run) hingen 4 Minuten in MCP-Client-Timeout. War #646. Maßnahme:

  • WAL-Mode + busy_timeout 5s aktiv (A15).
  • stellen_bulk_bewerten 90s-Wall-Clock-Budget (C4, beta.74).
  • stellen_auto_aussortieren Hard-Cap 10 + 180s-Budget (C6, beta.74).
  • Offen: Auto-Engine-Steps koennten Connection-pro-Iteration statt Long-Held nutzen — eigenes Issue empfohlen.

R3 — Ollama-Cold-Start ueberschreitet MCP-Timeout

Risiko: Lokales LLM braucht im Cold-State 50–60s. Erste match_job_to_skills-Anfrage nach >5min Inaktivitaet timeoutete. War #638. Maßnahme:

  • keep_alive=60m im Ollama-Request (F4).
  • LLMService.warmup() vor Bulk-Operationen, idempotent (F4).
  • Risiko bleibt: wenn Ollama-Prozess komplett aus ist (Reboot), erste Anfrage zahlt Cold-Load. Hier hilft nur User-Geduld.

R4 — DSGVO bei oeffentlichen GitHub-Issues

Risiko: Issues sind oeffentlich einsehbar. Personennamen / Firmennamen / Mail-Adressen in Repro-Beispielen sind DSGVO-relevant fuer Dritte. Maßnahme:

  • scripts/scrub_pii.py --check vor jedem gh issue create (Pflicht-Workflow in CLAUDE.md).
  • Replace-Mapping: <USER>/<PERSON>/<FIRMA>/<email-anonymisiert>/<telefon>.
  • Edit-History-Falle dokumentiert: anonymisierte Bodies haben Vorgaenger-Versionen sichtbar. Bei wirklich sensitiven Faellen Issue komplett loeschen (GraphQL deleteIssue).
  • Historische Bereinigung: am 2026-05-10 ~155 historische Issue-Bodies + 9 Comments nachtraeglich anonymisiert.

R5 — Schema-Migration ALTER-only

Risiko: Daten-Migrationen sind komplex und brechen User-DBs. Maßnahme:

  • Im Release-Workflow festgelegt: SCHEMA_VERSION hochzaehlen, neue Spalten in _migrate UND in SCHEMA_SQL (CREATE TABLE) ergaenzen — ALTER-only, keine Daten-Konvertierungen.
  • Backup vor Schema-Upgrade ist Pflicht (Ordner data/backups/).
  • Schema v1 → v45 ohne Datenverlust (zuletzt v44 documents.lifecycle, v45 tasks + dismiss_reasons.is_active + search_criteria.keywords_minus).

R6 — Tag-Lock bei kaputten Releases

Risiko: GitHub Releases sind tag-gelocked. Ein zu existierendem Tag laesst sich nicht neu erstellen, nur editieren. v1.6.0/v1.6.1 wurden so verbrannt. Maßnahme:

  • Vor git tag Pflicht-Check: Frontend gebaut, Tests gruen, CHANGELOG aktuell.
  • Bei kaputtem Release: NIE Tag-Lock loesen — neue Patch-Version (vX.Y.Z+1) erstellen. Wurde bei beta.68/beta.69 praktiziert (→ beta.70 als saubere Version).

R7 — Direkter DB-Bypass durch Claude

Risiko: Claude koennte direkt in SQLite schreiben und damit Lifecycle-Logik (Audit, Lerneffekt, Statistik) umgehen. Maßnahme:

  • Anti-DB-Bypass-Pattern (#514, H9): Alle Mutationen laufen ueber MCP-Tools.
  • Server-Instructions in server.py machen das transparent.
  • pbp_capabilities listet alle verfuegbaren Tools — wenn Claude eines vermisst, pbp_grenze_melden statt DB-Workaround.

R8 — Phantom-Bewerbungen aus CV-Dateinamen

Risiko: bewerbungs_dokumente_erkennen(auto_erstellen=True) legte Bewerbungen bei "Ausfuehrlich", "freelancer", "SC", "SL" an — generische CV-Varianten-Namen wurden als Firmen interpretiert. War #642. Maßnahme:

  • _extract_firma_from_filename neu aufgebaut: Trenner-Logik (; vor -), Blacklist (umlaut-normalisiert), Kuerzel-Filter.
  • 50+ Test-Cases mit echten CV-Dateinamen.
  • Lehre: Auto-Erstellung mit Heuristik ist gefaehrlich. Im Zweifel manueller Bestaetigungs-Step.

R9 — Bericht-Designprinzip "lieber weglassen"

Risiko: Kennzahlen mit unzuverlaessiger Datenbasis (z.B. "30min/Bewerbung" als Aufwand-Schaetzung) sind irrefuehrend. Maßnahme:

  • Doku-Prinzip in CLAUDE.md: "Kennzahlen deren Datenbasis nicht zuverlaessig ist, kommen nicht in den Bericht."
  • Konkrete Faelle aus v1.6.8 dokumentiert: "Aktive Filter-Arbeit", "Geschaetzter Zeitaufwand", "Bewerbungs-Trichter" — alle drei raus weil Datenbasis ueberlappt.

R10 — Stellen-URL-Regression bei Scraper-Refactor

Risiko: Beim Refactoring zur Adapter-Architektur (#499) ist der URL-Fallback fuer XING/Stepstone verloren gegangen — Scraper schrieben leere URLs. War #645. Maßnahme:

  • Hard-Guard in save_jobs: leere URLs aus Scraper-Quellen werden mit WARN + auto-is_search_url=True gemarkt (A11).
  • Garantie: jobs.url ist entweder Detail-URL oder mit Markierung Such-URL. Nie wieder leeres Feld ohne Indikator.
  • Lehre: Defense-in-Depth in der DB-Schicht zusaetzlich zum Scraper-Fix.

R11 — Workday-SPA-URLs liefern 200 OK auch fuer vergebene Stellen

Risiko: Bei Workday-Sites ist das HTML nur ein JS-Skeleton. Selbst vergebene Stellen liefern 200 OK, der eigentliche "Job not found" kommt erst aus dem JS-API-Call. War der Hintergrund warum Markus die Nexperia-Stelle b9f0bbe25d09 manuell als veraltet aussortieren musste. Maßnahme:

  • services/url_health.py mit Workday-API-Cross-Check (B11, beta.73): wenn wday/cxs/{tenant}/{site}/job/{path} 404 liefert, ist die Stelle weg — auch wenn HTML 200 OK ist.
  • Title-Token-Cross-Check fuer statisches HTML als zweiter Sicherheitsnetz.

R12 — Wiki-Lock-Out nach Beta-Iterationen

Risiko: Wiki war auf Stand beta.45, der Code auf beta.72 — 27 Iterationen Luecke. Neue User landen auf veraltetem Wiki und sehen Tool-Counts/Features die nicht stimmen. Maßnahme:

  • Beim Release-Workflow ergaenzt (CLAUDE.md): Wiki-Sync ist Teil des Release-Process.
  • Master-Plan-First (siehe CLAUDE.md): Plan-Eintrag UND Wiki-Stub muessen vor dem Release stehen.
  • Periodischer Audit: alle 5 Beta-Iterationen Wiki-Diff gegen Master-Plan pruefen.

R13 — Tool-Count-Drift in Tests

Risiko: test_mcp_registry.py hat hart-codierten Tool-Count. Bei jedem neuen Tool muss der Test angepasst werden, sonst rot. War aktuell bei beta.73 + beta.74. Maßnahme:

  • Bewusst belassen — der Hard-Cap zwingt zum Wiki-Update wenn ein neues Tool kommt.
  • Im Master-Plan: jedes neue Tool bekommt eigene Position mit Issue + Wiki-Stub. Test-Anpassung passiert dann im gleichen Commit.

R14 — Lokale KI optional, aber kritische Pfade haengen daran

Risiko: stellen_auto_aussortieren braucht Ollama. Wenn User kein Ollama hat, fehlt eine Kern-Funktion. Maßnahme:

  • Fail-Fast mit klarer Meldung wenn ollama_available=False oder user_state != "active".
  • Routing-Tabelle hat fuer jeden TaskKind einen Fallback (LOCAL > CLAUDE > MANUAL).
  • Setup-Wizard fuehrt durch Ollama-Installation als optionalen Schritt.
  • Offen: explizite "PBP ohne Ollama"-Walkthrough im Wiki — heute steht es nur als 1 FAQ-Eintrag.

Optimierungen am Plan (O1–O8)

O1 — Master-Plan-First-Disziplin retroaktiv eingefuehrt

Vorher: PBP wurde agil entwickelt (Issue → Code → Wiki nachreichen wenn dran gedacht wird). Nachher: Ab beta.74 verbindlich in CLAUDE.md: vor jedem Code-Change MUSS ein Master-Plan-Eintrag existieren. Bestehende Features wurden retroaktiv eingeordnet (~150 Items).

O2 — Hybrid-Granularitaet: Cluster-Master + Issue-Level-Sub

Vorher diskutiert: alles auf einer Seite (zu lang) ODER 590 Issues einzeln (unuebersichtlich). Nachher: Master-Plan auf Cluster-Ebene (A-J) + pro Cluster ein Plan-{cluster}.md mit Theme-gebuendelten Issue-Listen. Maximale Uebersicht ohne Verlust an Tiefe.

O3 — services/url_health.py als wiederverwendbares Pure-Python-Modul

Vorher: URL-Checks waren ad-hoc in verschiedenen Scrapern verstreut. Nachher: Zentrales Modul mit 7 Status-Werten, Workday-SPA-Sonderfall, Marker-Bibliothek. Nutzbar von MCP-Tools UND Auto-Engine (B11, C14).

O4 — Wall-Clock-Budgets als Pattern fuer alle Bulk-Tools

Vorher: Tools liefen so lange wie sie brauchten — MCP-Client-Timeout (4 Min) brach sie willkuerlich ab. Nachher: Pattern in beta.74 etabliert (C4/C6/H11): Pre-Loop-Budget-Check + status='teilweise'/'timeout' mit Teil-Ergebnis. Idempotent fortsetzbar.

O5 — Ollama als Hintergrund-KI statt On-Demand-Service

Vorher: Ollama wurde bei jedem Call frisch geladen (Cold-Start 50-60s). Nachher: keep_alive=60m + Pre-Warmup vor Bulk-Operationen + regelmaessiger Heartbeat. Ollama ist immer "warm". (F4, F5)

O6 — Anti-DB-Bypass als Hard-Rule, nicht als Soft-Guideline

Vorher: Claude koennte theoretisch via sqlite-MCP / filesystem-MCP direkt in pbp.db schreiben. Nachher: MCP-Server-Instructions in server.py machen explizit klar: "Niemals direkt in die SQLite-Datei schreiben oder ueber andere MCP-Tools an PBP-Daten gehen." Plus pbp_grenze_melden als Edge-Case-Reporter.

O7 — Frontend-Build-Output mit committen

Vorher: Frontend wurde im CI gebaut. Nachher: Built-Assets unter src/bewerbungs_assistent/static/dashboard/assets/ mit committed. Dadurch laeuft INSTALLIEREN.bat ohne Node-Setup — User-freundlicher.

O8 — Issue-Tracking statt Repo-Tracking fuer User-Wuensche

Vorher: User-Feedback im Chat → vergessen oder ad-hoc umgesetzt. Nachher: Pro User-Feedback ein GitHub-Issue (oeffentlich + DSGVO-gescrubbed). Issues sind die Quelle fuer den Master-Plan. Nichts geht verloren.


Reihenfolge-Trade-offs

Diskutierte Reihenfolge Bewertung
Erst Plugin-Plattform (J), dann Domain-Features Verworfen — Plugins gegen instabile API → Plugin-Wildwuchs. Erst API stabilisieren.
Frontend (G) parallel zu MCP-Tools (H) Behalten — beide nutzen dieselben Backend-Services, aber sind verschiedene Konsumenten. Parallel-Entwicklung passt.
Quellen-Strategie (B5) vor Scraper-Plugin-Architektur (B1) Verworfen — Empfehlungs-System braucht funktionierende Scraper. Architektur zuerst.
Lokale KI (F) als optional behandeln Verworfen — F5 macht Ollama zur permanenten Backbone fuer Hintergrund-Tasks. Optional ist nur die Pflicht-Installation, nicht das Nutzen.
Wiki nachholen statt mit-pflegen Verworfen — Wiki-Lock-Out (R12) zeigt: 27 Beta-Iterationen Luecke war zu viel. Wiki ist Teil des Release-Workflows.
B18 Playwright-Adapter fuer gulp/kimeta/heise_jobs sofort Zurueckgestellt 2026-06-01 — ohne Live-Browser-Inspection nur geratene Selektoren = instabile Scraper (vgl. R10 / #645). Adzuna (B17) deckt die Coverage erstmal ab. B18 reaktivieren wenn (a) Adzuna sich als unzureichend erweist oder (b) Live-Iteration mit Network-Tab moeglich ist (JSON-API-Reverse-Engineering bevorzugt vor Playwright).

Annahmen die noch zu pruefen sind

  • Code-Signing (I8): Ohne Code-Signing warnt Windows bei jedem Install. Ist das Aufwand wert (~99 EUR/Jahr Cert)? Markus klaert.
  • Auto-Updater (I9): Squirrel.Windows / electron-updater / eigenes? PBP ist Python-Backend + Static-Frontend — kein Electron. Welcher Updater passt? Vorschlag: Custom Bash/Batch der das ZIP entpackt und Python-venv neu macht.
  • Plugin-Plattform (J1) Stabilitaets-Pflicht: Welche API-Garantien geben wir Plugin-Autoren? Semver fuer die Ingest-API noetig.
  • Mail-Add-Ons (J2/J3): Microsoft 365 vs. Exchange-on-Prem vs. Thunderbird → drei verschiedene Welten. Reihenfolge: Thunderbird zuerst (FOSS, eigene API), Outlook spaeter.
  • Newsletter-Ingest (J5): Wie identifizieren wir Newsletter? Vorschlag: User markiert eine Mail einmal als "Job-Newsletter von Stepstone" → System lernt Sender/Subject-Muster.
  • Community-Tagesimpulse (J7): GitHub-Issue-basierter Workflow oder eigene API? Vorschlag: Issues mit Label tagesimpuls, periodisch ins Wiki gesynced.
  • Live-Updates Frontend (G8): WebSocket vs. Server-Sent-Events vs. Polling? Vorschlag: SSE — einfacher, Reverse-Proxy-freundlich, kein Bidirectional-State noetig.

Aenderungs-Historie

Datum Aenderung
2026-06-01 Erstanlage. Plan A–J mit Risiken R1–R14 und Optimierungen O1–O8. Retroaktiv aus 590 Issues + CLAUDE.md + Bugfix-Sessions (#642, #645, #646) extrahiert.

Clone this wiki locally