Files
planer/phase0/vorschau.html
2026-07-22 16:12:23 +02:00

1427 lines
50 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!doctype html>
<html lang="de">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Planer-Vorschau: Creator (Teilscan)</title>
<style>
:root {
--bg: #f6f7f9; --karte: #ffffff; --text: #1a202c; --dezent: #64748b;
--rand: #e2e8f0; --akzent: #2563eb; --kern: #166534; --kern-bg: #dcfce7;
--rand-flag: #92400e; --rand-bg: #fef3c7; --code-bg: #f1f5f9;
}
@media (prefers-color-scheme: dark) {
:root {
--bg: #0f1420; --karte: #1a2130; --text: #e2e8f0; --dezent: #94a3b8;
--rand: #2d3748; --akzent: #60a5fa; --kern: #86efac; --kern-bg: #14532d;
--rand-flag: #fcd34d; --rand-bg: #451a03; --code-bg: #111827;
}
}
* { box-sizing: border-box; }
body { margin: 0; padding: 2rem 1rem 4rem; background: var(--bg); color: var(--text);
font: 15px/1.55 system-ui, sans-serif; }
main { max-width: 860px; margin: 0 auto; }
h1 { font-size: 1.4rem; margin: 0 0 .3rem; }
.untertitel { color: var(--dezent); margin: 0 0 2rem; font-size: .92rem; }
.bereich { background: var(--karte); border: 1px solid var(--rand); border-radius: 10px;
padding: 1rem 1.2rem; margin-bottom: 1rem; }
.bereich > h2 { font-size: 1.08rem; margin: 0; }
.bereich > p { color: var(--dezent); margin: .25rem 0 .8rem; font-size: .92rem; }
details { border-top: 1px solid var(--rand); }
summary { cursor: pointer; padding: .55rem .2rem; list-style: none; display: flex;
align-items: baseline; gap: .5rem; }
summary::before { content: "▸"; color: var(--dezent); font-size: .8em; transition: transform .12s; }
details[open] > summary::before { transform: rotate(90deg); }
summary:hover { color: var(--akzent); }
.flag { font-size: .7rem; font-weight: 600; padding: .1rem .45rem; border-radius: 99px;
text-transform: uppercase; letter-spacing: .04em; }
.flag.kern { color: var(--kern); background: var(--kern-bg); }
.flag.rand { color: var(--rand-flag); background: var(--rand-bg); }
.feature-besch { color: var(--dezent); font-size: .9rem; margin: 0 0 .4rem 1.15rem; }
.teil { margin: 0 0 .35rem 1.15rem; }
.teil > summary { font-size: .93rem; padding: .3rem .2rem; }
.einheit { margin: .3rem 0 .6rem 1.15rem; border: 1px solid var(--rand); border-radius: 8px;
background: var(--bg); }
.einheit > summary { font-family: ui-monospace, monospace; font-size: .82rem; padding: .45rem .6rem; }
.einheit-inhalt { padding: .2rem .8rem .7rem; font-size: .9rem; }
.einheit-inhalt p { margin: .3rem 0; }
.fakt { margin: .45rem 0; }
.beleg { display: block; font-family: ui-monospace, monospace; font-size: .78rem;
background: var(--code-bg); border-left: 3px solid var(--akzent);
padding: .25rem .5rem; margin-top: .2rem; border-radius: 0 4px 4px 0;
overflow-x: auto; white-space: pre; }
.kanten { color: var(--dezent); font-size: .82rem; margin-top: .5rem; }
.kanten code { font-size: .78rem; }
.hinweis { color: var(--dezent); font-size: .85rem; margin-top: 2rem;
border-top: 1px solid var(--rand); padding-top: 1rem; }
</style>
</head>
<body>
<main>
<h1>Creator — Projektansicht (Phase-0-Vorschau)</h1>
<p class="untertitel">Teilscan: kanban.py, pipeline.py, config.py · alle Belege mechanisch
verifiziert · so ähnlich würde die Projektübersicht im Planer aussehen</p>
<div id="wurzel"></div>
<p class="hinweis">Wegwerf-Vorschau aus Phase 0 — kein Frontend-Code des Planers.
Aufklapp-Logik: Bereich → Feature → Teilfeature → Einheiten (Fakten mit Code-Beleg, Kanten).</p>
</main>
<script>
const DATEN = {
"bereiche": [
{
"name": "Kanban-Engine (kartenbasierte Themenverarbeitung)",
"beschreibung": "Verteilt einen Themen-Run als Kanban-Board über mehrere Stages und zieht Karten worker-gesteuert durch die Verarbeitung.",
"features": [
{
"titel": "Themen-Run starten und überwachen",
"flag": "kern",
"beschreibung": "Initialisiert pro Topic den Laufzeit-Zustand, fährt Worker pro Stage hoch und meldet den Live-Fortschritt der Karten im Fluss.",
"teilfeatures": [
{
"titel": "Kann für ein Topic einen Lauf starten, Worker hochfahren und live registrieren",
"einheiten": [
"backend/kanban.py::run_flow"
]
},
{
"titel": "Kann den Laufzeit-Zustand pro Topic (aktive Worker, Producer, Wachsignal) führen",
"einheiten": [
"backend/kanban.py::Flow"
]
},
{
"titel": "Kann sekündlich die Gesamtzahl der Karten im Fluss als deutschen Statustext melden",
"einheiten": [
"backend/kanban.py::_progress"
]
}
]
},
{
"titel": "Board und Stages modellieren",
"flag": "kern",
"beschreibung": "Bildet die Spalten eines Kanban-Boards mit Verarbeitung und Verhaltens-Flags ab und verknüpft Vorgänger-Stages automatisch.",
"teilfeatures": [
{
"titel": "Kann eine Kanban-Spalte mit zugeordneter Verarbeitung und Verhaltens-Flags (Serial, Drain, Barrier, Gate) beschreiben",
"einheiten": [
"backend/kanban.py::Stage"
]
},
{
"titel": "Kann Vorgänger-Spalten automatisch verketten, sodass Barrier-Worker ihre Quellen kennen",
"einheiten": [
"backend/kanban.py::chain_stages"
]
}
]
},
{
"titel": "Karten worker-gesteuert abarbeiten",
"flag": "kern",
"beschreibung": "Pullt Karten aus der Queue der eigenen Stage, hält mehrere Pakete gleichzeitig und wartet reaktiv auf neue Arbeit.",
"teilfeatures": [
{
"titel": "Kann Karten pullen, mehrere Pakete gleichzeitig halten und beim Stopp alle laufenden Tasks sauber abbrechen",
"einheiten": [
"backend/kanban.py::_worker"
]
},
{
"titel": "Kann reaktiv auf neue Arbeit warten, statt dauerhaft zu pollen",
"einheiten": [
"backend/kanban.py::_sleep_wake"
]
},
{
"titel": "Kann Ruhe über aktive Worker und Queue-Bestand zuverlässig erkennen und zum Beenden auflösen",
"einheiten": [
"backend/kanban.py::quiescent"
]
}
]
},
{
"titel": "Fehler im Karten-Lauf behandeln",
"flag": "kern",
"beschreibung": "Wiederholt fehlgeschlagene Karten mit exponentiellem Backoff und verschiebt erschöpfte Karten in die Dead-Letter-Stage.",
"teilfeatures": [
{
"titel": "Kann fehlgeschlagene Karten mit Backoff wiederholen oder als Dead-Letter ablegen, ohne bereits weitergerückte Karten zu bestrafen",
"einheiten": [
"backend/kanban.py::_fail_package"
]
}
]
}
]
},
{
"name": "Pipeline-Steuerung (Guide-Laufzeitverwaltung)",
"beschreibung": "Orchestriert parallele Agent-Aufrufe für einen Guide, verwaltet Stornierung und Fortschritt und stellt Helfer für Prompt-Bau sowie Fehlerformatierung bereit.",
"features": [
{
"titel": "Guide-Abbruch handhaben",
"flag": "kern",
"beschreibung": "Markiert einen Guide als abgebrochen, beendet laufende Agent-Slots und gibt die Sperre für einen Neustart wieder frei.",
"teilfeatures": [
{
"titel": "Kann einen laufenden Guide abbrechen, zugehörige Agent-Slots stoppen und den Datensatz als fehlgeschlagen markieren",
"einheiten": [
"backend/pipeline.py::cancel_guide"
]
},
{
"titel": "Kann den Stornierungsstatus eines Guides abfragen",
"einheiten": [
"backend/pipeline.py::is_guide_cancelled"
]
},
{
"titel": "Kann nach einem Abbruch die Sperre lösen, damit der Guide neu gestartet werden kann",
"einheiten": [
"backend/pipeline.py::clear_guide_cancelled"
]
}
]
},
{
"titel": "Live-Fortschritt und Statusmeldungen",
"flag": "kern",
"beschreibung": "Schreibt Fortschritt, Schrittnummer und Fehler persistiert und meldet nebenläufige Jobs mit Live-Zähler an einen Reporter.",
"teilfeatures": [
{
"titel": "Kann Fortschritt, Schrittnummer und Fehlermeldung im Guide-Datensatz persistieren",
"einheiten": [
"backend/pipeline.py::_set_progress",
"backend/pipeline.py::_set_step",
"backend/pipeline.py::_fail"
]
},
{
"titel": "Kann nebenläufige Jobs mit Live-Zähler an einen Reporter melden und auch bei Ausnahmen weiterzählen",
"einheiten": [
"backend/pipeline.py::_gather_progress"
]
},
{
"titel": "Kann Info-Meldungen mit Topic-Präfix einheitlich loggen",
"einheiten": [
"backend/pipeline.py::_log"
]
}
]
},
{
"titel": "Parallele Agent-Slots orchestrieren",
"flag": "kern",
"beschreibung": "Startet mehrere Agent-Slots parallel, sammelt bis zum Quorum gültige Ergebnisse und reagiert auf Stornierung und Infrastrukturfehler.",
"teilfeatures": [
{
"titel": "Kann mehrere Agent-Slots parallel starten und auf eine konfigurierbare Quorum-Anzahl gültiger Ergebnisse warten, mit Hedging, Grace-Periode und Infrastruktur-Retries",
"einheiten": [
"backend/pipeline.py::_race"
]
},
{
"titel": "Kann Pipeline-Parameter einmal bündeln und durch alle Pipeline-Glieder reichen",
"einheiten": [
"backend/pipeline.py::GenContext"
]
}
]
},
{
"titel": "Fehler klassifizieren und formatieren",
"flag": "rand",
"beschreibung": "Erkennt Infrastruktur- von inhaltlichen Fehlern und baut kompakte Fehlertexte für Anzeige und Vergleich.",
"teilfeatures": [
{
"titel": "Kann Infrastruktur- von inhaltlichen Fehlern anhand einer Markerliste unterscheiden",
"einheiten": [
"backend/pipeline.py::_is_infra"
]
},
{
"titel": "Kann Agent-Fehler aus Returncode und Ausgaben zu einer kompakten Meldung formatieren",
"einheiten": [
"backend/pipeline.py::_claude_error"
]
},
{
"titel": "Kann aus einer Liste von Slot-Ergebnissen den ersten verwertbaren Fehler auswählen und melden",
"einheiten": [
"backend/pipeline.py::_gather_error"
]
}
]
},
{
"titel": "Prompt-Bau und Schemavalidierung",
"flag": "rand",
"beschreibung": "Stellt Helfer bereit, um Prompts aus Vorlagen aufzubauen, Timeouts zu berechnen und Agent-Antworten zu validieren.",
"teilfeatures": [
{
"titel": "Kann Prompt-Vorlagen aus dem Templates-Verzeichnis laden und mit Argumenten ausfüllen",
"einheiten": [
"backend/pipeline.py::_prompt"
]
},
{
"titel": "Kann zusätzliche Nutzeranweisungen als klar markierten Block an einen Prompt anhängen",
"einheiten": [
"backend/pipeline.py::_extra"
]
},
{
"titel": "Kann die Timeout-Dauer eines Schritts als Basis plus skalierten Zuschlag pro Eintrag berechnen",
"einheiten": [
"backend/pipeline.py::_timeout"
]
},
{
"titel": "Kann die Probleme-Liste einer Agent-Antwort bereinigen oder leer zurückgeben",
"einheiten": [
"backend/pipeline.py::_problems_schema"
]
}
]
}
]
},
{
"name": "Provider-Konfiguration",
"beschreibung": "Lädt Umgebung und Provider-Defaults, wählt pro Rolle den effektiven Provider samt Modell und überschreibt Tuning-Werte zur Startzeit.",
"features": [
{
"titel": "Umgebung und Defaults laden",
"flag": "kern",
"beschreibung": "Lädt eine .env-Datei und stellt den Default-Provider als Pflicht-Voraussetzung für den Start sicher.",
"teilfeatures": [
{
"titel": "Kann eine .env-Datei laden und damit Umgebungsvariablen setzen, auch über bereits gesetzte Werte hinweg",
"einheiten": [
"backend/config.py::_load_env"
]
},
{
"titel": "Kann das System ohne konfigurierten Default-Provider gar nicht starten",
"einheiten": [
"backend/config.py::DEFAULT_PROVIDER"
]
}
]
},
{
"titel": "Provider-Stacks und Rollen-Routing",
"flag": "kern",
"beschreibung": "Stellt mehrere unabhängige Provider-Stacks bereit und routet Agent-Rollen optional auf alternative Provider um.",
"teilfeatures": [
{
"titel": "Kann mehrere unabhängige Provider-Stacks (Cloud-Auth, API-Key, lokal) mit Modellen und Authentifizierung bereitstellen",
"einheiten": [
"backend/config.py::PROVIDERS"
]
},
{
"titel": "Kann die vier Agent-Rollen prozessweit auf alternative Provider umleiten",
"einheiten": [
"backend/config.py::ROLE_ROUTING"
]
},
{
"titel": "Kann für eine Rolle das effektive (Provider, Modell)-Paar unter Berücksichtigung von Overrides und \"provider:model\"-Syntax liefern",
"einheiten": [
"backend/config.py::resolve_role"
]
}
]
},
{
"titel": "Schritt-Timeouts und Tuning-Overrides",
"flag": "rand",
"beschreibung": "Definiert pro Agent-Schritt eine Timeout-Paarung und erlaubt numerische Tuning-Overrides per JSON zur Startzeit.",
"teilfeatures": [
{
"titel": "Kann pro Agent-Schritt ein Timeout-Paar (Basis, Pro-Eintrag) zentral definieren",
"einheiten": [
"backend/config.py::TIMEOUTS"
]
},
{
"titel": "Kann numerische Tuning-Konstanten aus einer JSON-Umgebungsvariablen zur Startzeit überschreiben",
"einheiten": [
"backend/config.py::_apply_param_overrides"
]
}
]
}
]
}
],
"einheiten": {
"backend/kanban.py::Flow": {
"id": "backend/kanban.py::Flow",
"beschreibung": "Hält den Laufzeit-Zustand eines Topic-Runs (aktive Worker pro Stage, Producer-Zähler, Wake-Event), damit andere Einheiten Quieszenz und Producer-Lebenszyklus prüfen können.",
"fakten": [
{
"text": "Hält pro Stage einen Zähler aktiver Worker.",
"beleg": "self.active: dict[str, int] = {}"
},
{
"text": "Zählt zusätzlich die aktuell laufenden Research-Producer.",
"beleg": "self.producers = 0"
},
{
"text": "research_done ist nur True, wenn kein einziger Producer mehr läuft.",
"beleg": "return self.producers <= 0"
},
{
"text": "leave klemmt den Active-Zähler beim Dekrementieren bei Null ab.",
"beleg": "self.active[stage] = max(0, self.active.get(stage, 0) - 1)"
},
{
"text": "active_in meldet True, sobald irgendeine der angegebenen Stages aktive Worker hat.",
"beleg": "return any(self.active.get(s, 0) > 0 for s in stages)"
}
],
"kanten": [
{
"typ": "wird-genutzt-von",
"ziel": "backend/kanban.py::quiescent"
},
{
"typ": "wird-genutzt-von",
"ziel": "backend/kanban.py::_sleep_wake"
},
{
"typ": "wird-genutzt-von",
"ziel": "backend/kanban.py::_fail_package"
},
{
"typ": "wird-genutzt-von",
"ziel": "backend/kanban.py::_worker"
},
{
"typ": "wird-genutzt-von",
"ziel": "backend/kanban.py::run_flow"
},
{
"typ": "wird-genutzt-von",
"ziel": "backend/kanban.py::_progress"
}
],
"anker": []
},
"backend/kanban.py::Stage": {
"id": "backend/kanban.py::Stage",
"beschreibung": "Beschreibt eine Spalte des Kanban-Boards mit Board-Name, Stage-Name, Processor und Verhaltens-Flags (barrier, serial, gate, drain), die der Worker beim Pull-Verhalten auswertet.",
"fakten": [
{
"text": "Repräsentiert genau eine Spalte, identifiziert durch Board- und Stage-Name, mit einem Processor.",
"beleg": "self.process = process"
},
{
"text": "Setzt serial automatisch mit, sobald drain True ist.",
"beleg": "self.serial = serial or drain"
},
{
"text": "Hält eine leere upstream-Liste, die chain_stages mit Vorgänger-Stages füllt.",
"beleg": "self.upstream: list[str] = []"
}
],
"kanten": [
{
"typ": "wird-genutzt-von",
"ziel": "backend/kanban.py::chain_stages"
},
{
"typ": "wird-genutzt-von",
"ziel": "backend/kanban.py::_worker"
},
{
"typ": "wird-genutzt-von",
"ziel": "backend/kanban.py::_fail_package"
},
{
"typ": "wird-genutzt-von",
"ziel": "backend/kanban.py::run_flow"
}
],
"anker": []
},
"backend/kanban.py::chain_stages": {
"id": "backend/kanban.py::chain_stages",
"beschreibung": "Füllt für jede Stage deren upstream-Liste mit allen Stages, die in der übergebenen Liste davor stehen, damit Barrier-Worker ihre Vorgänger kennen.",
"fakten": [
{
"text": "Setzt das upstream jeder Stage auf die zuvor gesehenen Stages.",
"beleg": "s.upstream = list(seen)"
},
{
"text": "Gibt die unveränderte Eingabeliste wieder zurück.",
"beleg": "return stages"
}
],
"kanten": [
{
"typ": "nutzt",
"ziel": "backend/kanban.py::Stage"
}
],
"anker": []
},
"backend/kanban.py::quiescent": {
"id": "backend/kanban.py::quiescent",
"beschreibung": "Liefert True genau dann, wenn kein Worker in den angegebenen Stages aktiv ist UND keine Karte dort in der Queue liegt — die Barrier-/Exit-Bedingung.",
"fakten": [
{
"text": "Eine leere Stages-Liste gilt sofort als quiescent.",
"beleg": "return True"
},
{
"text": "Sobald irgendeine der Stages aktive Worker hat, wird sofort False zurückgegeben.",
"beleg": "return False"
},
{
"text": "True nur, wenn die Datenbank für keine der Stages eine queued Card meldet.",
"beleg": "return await db.kanban_count(flow.topic, list(stages)) == 0"
}
],
"kanten": [
{
"typ": "ruft-auf",
"ziel": "backend/kanban.py::Flow"
},
{
"typ": "ruft-auf",
"ziel": "backend/database.py::kanban_count"
},
{
"typ": "wird-genutzt-von",
"ziel": "backend/kanban.py::_worker"
},
{
"typ": "wird-genutzt-von",
"ziel": "backend/kanban.py::run_flow"
}
],
"anker": []
},
"backend/kanban.py::_sleep_wake": {
"id": "backend/kanban.py::_sleep_wake",
"beschreibung": "Wartet mit Polling-Timeout auf das Wake-Event des Flows und räumt es danach auf, damit der Worker-Loop reaktiv aber nicht busy-waitend bleibt.",
"fakten": [
{
"text": "Wartet auf das Wake-Event des Flows mit einem Polling-Timeout.",
"beleg": "await asyncio.wait_for(flow.wake.wait(), timeout=_POLL)"
},
{
"text": "Fängt TimeoutError ab, damit die Polling-Schleife weiterläuft.",
"beleg": "except asyncio.TimeoutError:"
},
{
"text": "Löscht das Wake-Event vor der Rückkehr.",
"beleg": "flow.wake.clear()"
}
],
"kanten": [
{
"typ": "nutzt",
"ziel": "backend/kanban.py::Flow"
},
{
"typ": "wird-genutzt-von",
"ziel": "backend/kanban.py::_worker"
}
],
"anker": []
},
"backend/kanban.py::_fail_package": {
"id": "backend/kanban.py::_fail_package",
"beschreibung": "Behandelt Package-Fehler mit exponentiellem Backoff bzw. Dead-Letter, aber nur für Karten, die der Processor noch nicht weitergerückt hat.",
"fakten": [
{
"text": "Überspringt Karten, die nicht mehr auf der Stage des Processors sitzen (bereits advanced).",
"beleg": "if cur is None or cur[\"stage\"] != spec.stage:"
},
{
"text": "Ruft db.kanban_fail_card mit MAX_CARD_RETRIES und RETRY_BACKOFF auf.",
"beleg": "await db.kanban_fail_card(flow.topic, spec.board, c[\"card_id\"], error,"
},
{
"text": "Loggt eine Warnung, wenn eine Karte auf die Dead-Stage verschoben wird.",
"beleg": "log.warning(\"kanban %s/%s: card %s → dead (%s)\","
}
],
"kanten": [
{
"typ": "nutzt",
"ziel": "backend/kanban.py::Flow"
},
{
"typ": "nutzt",
"ziel": "backend/kanban.py::Stage"
},
{
"typ": "ruft-auf",
"ziel": "backend/database.py::kanban_get_card"
},
{
"typ": "ruft-auf",
"ziel": "backend/database.py::kanban_fail_card"
},
{
"typ": "wird-genutzt-von",
"ziel": "backend/kanban.py::_worker"
}
],
"anker": []
},
"backend/kanban.py::_worker": {
"id": "backend/kanban.py::_worker",
"beschreibung": "Pullt Karten aus der Queue der eigenen Stage, hält bis zu `inflight` Packages gleichzeitig am Laufen und behandelt sowohl Backoff/Dead-Letter als auch Infra-Pausen, bis der gesamte Flow quiescent ist.",
"fakten": [
{
"text": "Bei drain wird die gesamte Queue in einem Package gezogen, sonst KANBAN_BATCH auf einmal.",
"beleg": "batch = 100_000 if spec.drain else KANBAN_BATCH"
},
{
"text": "Hält ein claimed-Set mit Card-IDs, damit parallele Pulls dieselbe Karte nicht doppelt holen.",
"beleg": "claimed: set[str] = set()"
},
{
"text": "Bei AgentInfraError wird der ganze Flow pausiert (stop + infra_paused), statt zu dead-lettern.",
"beleg": "flow.stop = True"
},
{
"text": "Barrier-Worker pullen nur, wenn das Gate offen ist und alle Upstream-Stages quiescent sind.",
"beleg": "return await quiescent(flow, spec.upstream)"
},
{
"text": "Bricht beim Exit alle noch laufenden Package-Tasks ab und sammelt sie mit Exceptions ein.",
"beleg": "await asyncio.gather(*tasks, return_exceptions=True)"
}
],
"kanten": [
{
"typ": "nutzt",
"ziel": "backend/kanban.py::Flow"
},
{
"typ": "nutzt",
"ziel": "backend/kanban.py::Stage"
},
{
"typ": "ruft-auf",
"ziel": "backend/kanban.py::quiescent"
},
{
"typ": "ruft-auf",
"ziel": "backend/kanban.py::_sleep_wake"
},
{
"typ": "ruft-auf",
"ziel": "backend/kanban.py::_fail_package"
},
{
"typ": "ruft-auf",
"ziel": "backend/database.py::kanban_pull"
},
{
"typ": "ruft-auf",
"ziel": "backend/database.py::kanban_count"
},
{
"typ": "nutzt",
"ziel": "backend/pipeline.py::AgentInfraError"
},
{
"typ": "wird-genutzt-von",
"ziel": "backend/kanban.py::run_flow"
}
],
"anker": [
"backend/kanban.py::_run",
"backend/kanban.py::_idle_exit",
"backend/kanban.py::_may_pull"
]
},
"backend/kanban.py::run_flow": {
"id": "backend/kanban.py::run_flow",
"beschreibung": "Startet die übergebenen Producer und je einen Worker pro Stage, lässt sie bis zur globalen Quieszenz laufen, respawnt sie bei Bedarf und meldet den Flow live in active_flows an bzw. ab.",
"fakten": [
{
"text": "Trägt den Flow für die Laufzeit in das Modul-Dict active_flows ein.",
"beleg": "active_flows[flow.topic] = flow"
},
{
"text": "Startet pro Stage einen Worker mit inflight=1 für serial-Stages, sonst WORKER_INFLIGHT.",
"beleg": "1 if s.serial else WORKER_INFLIGHT,"
},
{
"text": "Startet den Progress-Reporter nur, wenn ein set_p-Callback übergeben wurde.",
"beleg": ""
}
],
"kanten": [],
"anker": []
},
"backend/kanban.py::_progress": {
"id": "backend/kanban.py::_progress",
"beschreibung": "Pollt sekündlich die Stage-Counts aller Boards aus der Datenbank und meldet die Gesamtzahl laufender Karten über das set_p-Callback zurück, bis das Flow-Stop-Flag gesetzt ist.",
"fakten": [
{
"text": "Läuft in einer Schleife, bis das Stop-Flag des Flows gesetzt ist.",
"beleg": "while not flow.stop:"
},
{
"text": "Holt pro Board die aktuellen Stage-Counts aus der Datenbank.",
"beleg": "counts = await db.kanban_stage_counts(flow.topic)"
},
{
"text": "Summiert alle Karten über alle Boards und Stages zu einer Gesamtzahl.",
"beleg": "total = sum(n for stages in counts.values() for n in stages.values())"
},
{
"text": "Reicht die Gesamtzahl als formatierten deutschen String an set_p weiter.",
"beleg": "set_p(f\"Kanban: {total} Karten im Fluss\")"
},
{
"text": "Schläft eine Sekunde zwischen den Iterationen.",
"beleg": "await asyncio.sleep(1.0)"
}
],
"kanten": [
{
"typ": "nutzt",
"ziel": "backend/kanban.py::Flow"
},
{
"typ": "ruft-auf",
"ziel": "backend/database.py::kanban_stage_counts"
},
{
"typ": "wird-genutzt-von",
"ziel": "backend/kanban.py::run_flow"
}
],
"anker": []
},
"backend/pipeline.py::cancel_guide": {
"id": "backend/pipeline.py::cancel_guide",
"beschreibung": "Verwaltet den Cancel-Zustand eines Guides (Markieren, Abfragen, Zurücksetzen) und markiert den Guide-Datensatz als fehlgeschlagen unter Erhalt des Fortschritts.",
"fakten": [
{
"text": "Beim Markieren wird der Guide zur internen Cancel-Menge hinzugefügt, der Agent-Scope gelöscht und laufende Subprozesse beendet.",
"beleg": "_cancelled.add(guide_id)"
},
{
"text": "Nach dem Abbruch wird der Guide-Datensatz auf status=\"error\" gesetzt, der Fortschritt bleibt erhalten und es wird eine UTC-Zeitmarke geschrieben.",
"beleg": "await update_guide(guide_id, status=\"error\", progress=None, error_msg=\"Cancelled — progress is preserved\""
},
{
"text": "`is_guide_cancelled` meldet ausschließlich über die Modul-interne Menge, nicht aus dem Datensatz.",
"beleg": "return guide_id in _cancelled"
},
{
"text": "`clear_guide_cancelled` entfernt den Guide aus der Cancel-Menge und räumt den zugehörigen Agent-Scope auf, sodass ein Neustart nicht blockiert wird.",
"beleg": "clear_scope(f\"{guide_id}-\") # clear scope → restart not blocked"
}
],
"kanten": [
{
"typ": "ruft-auf",
"ziel": "backend/pipeline.py::is_guide_cancelled"
},
{
"typ": "ruft-auf",
"ziel": "backend/pipeline.py::clear_guide_cancelled"
},
{
"typ": "nutzt",
"ziel": "backend/database.py::update_guide"
},
{
"typ": "nutzt",
"ziel": "backend/agents.py::cancel_scope"
},
{
"typ": "nutzt",
"ziel": "backend/agents.py::kill_process"
},
{
"typ": "nutzt",
"ziel": "backend/agents.py::clear_scope"
}
],
"anker": [
"backend/pipeline.py::is_guide_cancelled",
"backend/pipeline.py::clear_guide_cancelled"
]
},
"backend/pipeline.py::_set_progress": {
"id": "backend/pipeline.py::_set_progress",
"beschreibung": "Schreibt Fortschritts- und Schrittinformationen sowie Fehlermeldungen eines Guides persistiert in den Datensatz.",
"fakten": [
{
"text": "`_set_progress` aktualisiert ausschließlich das Fortschrittsfeld zusammen mit einer UTC-Zeitmarke.",
"beleg": "await update_guide(guide_id, progress=progress, updated_at=now)"
},
{
"text": "`_set_step` schreibt zusätzlich den numerischen Schritt zusammen mit dem Fortschritt und einem Zeitstempel.",
"beleg": "await update_guide(guide_id, step=step, progress=progress, updated_at=now)"
},
{
"text": "`_fail` setzt den Guide auf den Fehlerstatus, leert den Fortschritt und hinterlegt die übergebene Fehlermeldung.",
"beleg": "await update_guide(guide_id, status=\"error\", progress=None, error_msg=msg, updated_at=now)"
}
],
"kanten": [
{
"typ": "nutzt",
"ziel": "backend/database.py::update_guide"
}
],
"anker": [
"backend/pipeline.py::_set_step",
"backend/pipeline.py::_fail"
]
},
"backend/pipeline.py::_prompt": {
"id": "backend/pipeline.py::_prompt",
"beschreibung": "Stellt Hilfsfunktionen für Prompt-Bau, Logging, Fehlerklassifikation, Timeout-Berechnung und JSON-Schemavalidierung bereit.",
"fakten": [
{
"text": "`_prompt` lädt ein Prompt-Template aus `TEMPLATES_DIR/Prompt/<name>.md` und formatiert es mit den übergebenen Schlüsselwortargumenten.",
"beleg": "template = (TEMPLATES_DIR / \"Prompt\" / f\"{name}.md\").read_text(encoding=\"utf-8\")"
},
{
"text": "`_extra` hängt zusätzliche Nutzeranweisungen in einem klar markierten Block an, sofern welche vorhanden sind.",
"beleg": "return f\"\\\\n\\\\nADDITIONAL INSTRUCTIONS FROM THE USER:\\\\n{instructions}\\\\n\" if instructions else \"\""
},
{
"text": "`_is_infra` erkennt Transport- bzw. Infrastrukturfehler anhand einer festen Markerliste, um sie von inhaltlichen Fehlern zu unterscheiden.",
"beleg": "return any(m in (err or \"\") for m in _INFRA_MARKERS)"
},
{
"text": "`_claude_error` formatiert eine Fehlermeldung aus Returncode, stdout und stderr, mit Fallback auf das Ende der stdout-Ausgabe.",
"beleg": "return f\"{label} (exit {returncode}, no output)\""
},
{
"text": "`_timeout` berechnet die Timeout-Dauer eines Schritts als Basis plus skalierten Zuschlag pro Eintrag.",
"beleg": "return base + per * n"
},
{
"text": "`_problems_schema` liefert eine leere Liste bei `{\"ok\": True}`, eine bereinigte Problemliste bei vorhandenen Problemen, sonst `None`.",
"beleg": "if data.get(\"ok\") is True:"
},
{
"text": "`_runde_schema` liefert im Finalmodus nur dann ein Ergebnis, wenn keine Restfragen offen sind.",
"beleg": "if include is None or rest is None or (final and rest):"
},
{
"text": "`_enum_map_schema` liefert eine Parser-Factory, die bei ungültigen IDs oder Werten strikt `None` zurückgibt.",
"beleg": "if value not in allowed:"
},
{
"text": "`_yesno_schema` ist die konkrete Ausprägung der Enum-Map-Factory für das Triage-Feld \"relevant\" mit den Werten \"ja\"/\"nein\".",
"beleg": "_yesno_schema = _enum_map_schema(\"relevant\", _YESNO) # triage gate ∈ ja/nein"
}
],
"kanten": [
{
"typ": "nutzt",
"ziel": "backend/config.py::TEMPLATES_DIR"
},
{
"typ": "nutzt",
"ziel": "backend/config.py::TIMEOUTS"
}
],
"anker": [
"backend/pipeline.py::_extra",
"backend/pipeline.py::_log",
"backend/pipeline.py::_claude_error",
"backend/pipeline.py::_is_infra",
"backend/pipeline.py::_gather_error",
"backend/pipeline.py::_timeout",
"backend/pipeline.py::_problems_schema",
"backend/pipeline.py::_str_list",
"backend/pipeline.py::_runde_schema",
"backend/pipeline.py::_enum_map_schema",
"backend/pipeline.py::_yesno_schema"
]
},
"backend/pipeline.py::_race": {
"id": "backend/pipeline.py::_race",
"beschreibung": "Startet parallele Agent-Slots, sammelt eine konfigurierbare Quorum-Anzahl gültiger Ergebnisse und behandelt dabei Timeouts, Infrastrukturfehler, Hedging, Grace-Periode und Stornierung.",
"fakten": [
{
"text": "Slots, die länger als `hedge_s` ohne Ergebnis laufen, bekommen genau einen parallelen Zwilling mit dem Suffix `-h`.",
"beleg": "hedged.add(i)"
},
{
"text": "Sobald das Quorum steht und die Grace-Frist abgelaufen ist, werden laufende Agents beendet und die bis dahin gesammelten Ergebnisse zurückgegeben.",
"beleg": "if deadline is not None and len(results) >= quorum and loop.time() >= deadline:"
},
{
"text": "Infrastrukturfehler werden mit eigenem Zähler und wachsendem Backoff bis zu `_INFRA_MAX_RETRIES` wiederholt; bei Erschöpfung wird `AgentInfraError` ausgelöst.",
"beleg": "raise AgentInfraError("
},
{
"text": "Eine aktive Stornierung führt sofort zum Abbruch ohne Neustart und Rückgabe von `None`.",
"beleg": "if cancelled and cancelled():"
},
{
"text": "Im `finally`-Block werden alle noch laufenden Tasks storniert und ihre Subprozesse beendet, auch beim vorzeitigen Abbruch.",
"beleg": "for task, i in tasks.items():"
}
],
"kanten": [
{
"typ": "nutzt",
"ziel": "backend/agents.py::run_agent"
},
{
"typ": "nutzt",
"ziel": "backend/agents.py::kill_process"
},
{
"typ": "nutzt",
"ziel": "backend/pipeline.py::_is_infra"
},
{
"typ": "nutzt",
"ziel": "backend/pipeline.py::_claude_error"
},
{
"typ": "nutzt",
"ziel": "backend/pipeline.py::_log"
},
{
"typ": "nutzt",
"ziel": "backend/pipeline.py::_timeout"
},
{
"typ": "nutzt",
"ziel": "backend/config.py::MAX_CONCURRENT_GENERATIONS"
}
],
"anker": [
"backend/pipeline.py::AgentInfraError"
]
},
"backend/pipeline.py::GenContext": {
"id": "backend/pipeline.py::GenContext",
"beschreibung": "Bündelt Pipeline-Parameter (Topic, Provider, Stornierungsprüfung, Guide-ID) zu einem Wert, der durch die Pipeline-Glieder weitergereicht wird.",
"fakten": [
{
"text": "`GenContext` kapselt die langen Argumentlisten der Pipeline-Aufrufe in einem Wert.",
"beleg": "\"\"\"Pipeline parameters passed through — saves long argument signatures.\"\"\""
},
{
"text": "`is_cancelled` ist eine parameterlose Funktion, die den aktuellen Stornierungszustand abfragt.",
"beleg": "is_cancelled: Callable[[], bool]"
},
{
"text": "`run_single_slot` führt genau einen Agent-Aufruf als Rennen mit Quorum 1 aus und übersetzt das Ergebnis in einen `(status, wert)`-Tripel.",
"beleg": "res = await _race(ctx.topic, label, slots, 1, timeout, ctx.provider, cancelled=ctx.is_cancelled)"
},
{
"text": "Bei gesetztem Quorum und Stornierung gibt `run_single_slot` `(CANCELLED, None)` zurück; bei verfehltem Quorum `(FAILED, None)`.",
"beleg": "if res is None:"
}
],
"kanten": [
{
"typ": "ruft-auf",
"ziel": "backend/pipeline.py::_race"
}
],
"anker": [
"backend/pipeline.py::run_single_slot"
]
},
"backend/pipeline.py::_gather_progress": {
"id": "backend/pipeline.py::_gather_progress",
"beschreibung": "Führt Coroutinen nebenläufig aus, meldet nach jeder Vervollständigung den Live-Fortschritt und liefert die Ergebnisse in Eingabereihenfolge samt Ausnahmen.",
"fakten": [
{
"text": "Vor dem Start der Coroutinen wird der Initialwert `done` an den Reporter übergeben.",
"beleg": "await report(done, total)"
},
{
"text": "Jeder abgeschlossene Job ruft den Reporter auch dann auf, wenn die Coroutine eine Ausnahme wirft, dank `try/finally`.",
"beleg": "finally:"
},
{
"text": "`asyncio.gather` wird mit `return_exceptions=True` aufgerufen, damit ein Fehler in einer Coroutine die übrigen nicht abbricht.",
"beleg": "return await asyncio.gather(*[wrap(c) for c in coros], return_exceptions=True)"
}
],
"kanten": [],
"anker": []
},
"backend/pipeline.py::is_guide_cancelled": {
"id": "backend/pipeline.py::is_guide_cancelled",
"beschreibung": "Beantwortet die Frage, ob ein Guide abgesagt wurde, ausschließlich anhand der Modul-internen Cancel-Menge.",
"fakten": [
{
"text": "Die Prüfung liest ausschließlich die Modul-interne Cancel-Menge und nicht den Datensatz.",
"beleg": "return guide_id in _cancelled"
}
],
"kanten": [],
"anker": []
},
"backend/pipeline.py::clear_guide_cancelled": {
"id": "backend/pipeline.py::clear_guide_cancelled",
"beschreibung": "Entfernt einen Guide aus der internen Cancel-Menge und räumt den zugehörigen Agent-Scope auf, damit ein Neustart nicht blockiert wird.",
"fakten": [
{
"text": "Entfernt die Guide-ID aus der Modul-internen Cancel-Menge.",
"beleg": "_cancelled.discard(guide_id)"
},
{
"text": "Löscht den Agent-Scope unter dem Prefix \"{guide_id}-\" mit Hinweis auf Restart-Freigabe.",
"beleg": "clear_scope(f\"{guide_id}-\") # clear scope → restart not blocked"
}
],
"kanten": [
{
"typ": "nutzt",
"ziel": "backend/agents.py::clear_scope"
}
],
"anker": []
},
"backend/pipeline.py::_set_step": {
"id": "backend/pipeline.py::_set_step",
"beschreibung": "Schreibt zusätzlich zum Fortschritt den numerischen Schritt eines Guides in den Datensatz und stempelt die Aktualisierungszeit.",
"fakten": [
{
"text": "Persistiert step, progress und updated_at in einem update_guide-Aufruf.",
"beleg": "await update_guide(guide_id, step=step, progress=progress, updated_at=now)"
},
{
"text": "Setzt den Zeitstempel als UTC-ISO-String.",
"beleg": "now = datetime.now(timezone.utc).isoformat()"
}
],
"kanten": [
{
"typ": "nutzt",
"ziel": "backend/database.py::update_guide"
}
],
"anker": []
},
"backend/pipeline.py::_fail": {
"id": "backend/pipeline.py::_fail",
"beschreibung": "Setzt den Guide-Datensatz konsistent auf den Fehlerstatus mit übergebener Meldung und löscht den Fortschritt.",
"fakten": [
{
"text": "Schreibt status=\"error\", leert progress und hinterlegt die übergebene Fehlermeldung.",
"beleg": "await update_guide(guide_id, status=\"error\", progress=None, error_msg=msg, updated_at=now)"
},
{
"text": "Stempelt den Datensatz mit einer UTC-Zeitmarke.",
"beleg": "now = datetime.now(timezone.utc).isoformat()"
}
],
"kanten": [
{
"typ": "nutzt",
"ziel": "backend/database.py::update_guide"
}
],
"anker": []
},
"backend/pipeline.py::_extra": {
"id": "backend/pipeline.py::_extra",
"beschreibung": "Erzeugt einen klar markierten Block mit zusätzlichen Nutzeranweisungen für einen Prompt oder liefert einen leeren String, falls keine Anweisungen vorliegen.",
"fakten": [
{
"text": "Mit Anweisungen wird ein Block mit fester Überschrift eingefügt.",
"beleg": "return f\"\\\\n\\\\nADDITIONAL INSTRUCTIONS FROM THE USER:\\\\n{instructions}\\\\n\" if instructions else \"\""
},
{
"text": "Ohne Anweisungen ist das Ergebnis der leere String.",
"beleg": "if instructions else \"\""
}
],
"kanten": [],
"anker": []
},
"backend/pipeline.py::_log": {
"id": "backend/pipeline.py::_log",
"beschreibung": "Schreibt eine einheitlich formatierte Info-Meldung mit Topic-Präfix über den Pipeline-Logger.",
"fakten": [
{
"text": "Nutzt den Modul-Logger mit dem Namen \"creator.pipeline\".",
"beleg": "log = logging.getLogger(\"creator.pipeline\")"
},
{
"text": "Setzt Topic in eckige Klammern vor die eigentliche Meldung.",
"beleg": "log.info(\"[%s] %s\", topic, msg)"
}
],
"kanten": [
{
"typ": "wird-genutzt-von",
"ziel": "backend/pipeline.py::_race"
}
],
"anker": []
},
"backend/pipeline.py::_claude_error": {
"id": "backend/pipeline.py::_claude_error",
"beschreibung": "Baut eine kompakte Fehlermeldung aus Returncode, stdout und stderr eines Agent-Aufrufs mit abgestufter Fallback-Strategie.",
"fakten": [
{
"text": "Zieht zuerst den gestrippten stderr heran.",
"beleg": "stderr = (stderr or \"\").strip()"
},
{
"text": "Bevorzugt stderr bis 1000 Zeichen als Fehlermeldung.",
"beleg": "return f\"{label}: {stderr[:1000]}\""
},
{
"text": "Bei leerem stderr wird das Ende der stdout als Fallback genutzt.",
"beleg": "tail = (stdout or \"\").strip()[-500:]"
},
{
"text": "Bei komplett fehlender Ausgabe erscheint nur Label und Exitcode mit \"no output\".",
"beleg": "return f\"{label} (exit {returncode}, no output)\""
}
],
"kanten": [
{
"typ": "wird-genutzt-von",
"ziel": "backend/pipeline.py::_race"
},
{
"typ": "wird-genutzt-von",
"ziel": "backend/pipeline.py::_gather_error"
}
],
"anker": []
},
"backend/pipeline.py::_is_infra": {
"id": "backend/pipeline.py::_is_infra",
"beschreibung": "Klassifiziert eine Fehlermeldung als Transport- bzw. Infrastrukturfehler anhand einer festen Markerliste.",
"fakten": [
{
"text": "Die Markerliste enthält HTTP 429, HTTP 5xx, rate_limit, Timeout after und diverse Netzfehler.",
"beleg": "_INFRA_MARKERS = (\"HTTP 429\", \"HTTP 5\", \"rate_limit\", \"Timeout after\","
},
{
"text": "Ein Marker-Treffer genügt, um die Meldung als Infra-Fehler zu werten.",
"beleg": "return any(m in (err or \"\") for m in _INFRA_MARKERS)"
},
{
"text": "Der Docstring grenzt Infra-Fehler von inhaltlichem Fehlschlag ab.",
"beleg": "\"\"\"Transport-/Infra-Fehler (retry + pause) statt inhaltlichem Fehlschlag.\"\"\""
}
],
"kanten": [
{
"typ": "wird-genutzt-von",
"ziel": "backend/pipeline.py::_race"
}
],
"anker": []
},
"backend/pipeline.py::_gather_error": {
"id": "backend/pipeline.py::_gather_error",
"beschreibung": "Wählt aus einer Liste von Slot-Ergebnissen den ersten Fehler aus und formatiert ihn als kompakten Fehlertext.",
"fakten": [
{
"text": "Iteriert die Ergebnisliste in Reihenfolge und meldet den ersten Treffer.",
"beleg": "for r in results:"
},
{
"text": "Ausnahmen werden als Klassenname plus Meldung ausgegeben.",
"beleg": "return f\"{label}: {type(r).__name__}: {r}\""
},
{
"text": "Nicht-null-Returncodes werden an `_claude_error` zur Formatierung delegiert.",
"beleg": "return _claude_error(label, returncode, stdout, stderr)"
},
{
"text": "Ohne verwertbares Ergebnis wird ein pauschaler Fehlertext geliefert.",
"beleg": "return f\"{label}: no usable result\""
}
],
"kanten": [
{
"typ": "nutzt",
"ziel": "backend/pipeline.py::_claude_error"
}
],
"anker": []
},
"backend/pipeline.py::_timeout": {
"id": "backend/pipeline.py::_timeout",
"beschreibung": "Berechnet die Timeout-Dauer eines Pipeline-Schritts als Basis plus skalierten Zuschlag pro Eintrag aus der zentralen TIMEOUTS-Tabelle.",
"fakten": [
{
"text": "Liest Basis und Zuschlag pro Eintrag aus TIMEOUTS für den angegebenen Schritt.",
"beleg": "base, per = TIMEOUTS[step]"
},
{
"text": "Liefert die Summe aus Basis und Zuschlag multipliziert mit n.",
"beleg": "return base + per * n"
}
],
"kanten": [
{
"typ": "nutzt",
"ziel": "backend/config.py::TIMEOUTS"
}
],
"anker": []
},
"backend/pipeline.py::_problems_schema": {
"id": "backend/pipeline.py::_problems_schema",
"beschreibung": "Validiert Agent-Antworten für das Probleme-Feld und liefert je nach Form eine leere Liste, die bereinigte",
"fakten": [],
"kanten": [],
"anker": []
},
"backend/config.py::_load_env": {
"id": "backend/config.py::_load_env",
"beschreibung": "Lädt KEY=VALUE-Zeilen aus einer .env-Datei in os.environ, wobei die Datei bereits gesetzte Umgebungsvariablen überschreibt.",
"fakten": [
{
"text": "Liest die .env-Datei UTF-8-codiert ein.",
"beleg": "text = path.read_text(encoding=\"utf-8\")"
},
{
"text": "Bricht den Ladevorgang still ab, wenn die Datei nicht lesbar ist.",
"beleg": "except OSError:"
},
{
"text": "Überspringt Leerzeilen, Kommentarzeilen und Zeilen ohne Gleichheitszeichen.",
"beleg": "if not line or line.startswith(\"#\") or \"=\" not in line:"
},
{
"text": "Entfernt umgebende Anführungszeichen aus den Werten.",
"beleg": "value = value.strip().strip('\"').strip(\"'\")"
},
{
"text": "Schreibt jeden gültigen Schlüssel in os.environ (Datei gewinnt gegen vererbte Env).",
"beleg": "os.environ[key] = value"
}
],
"kanten": [],
"anker": []
},
"backend/config.py::DEFAULT_PROVIDER": {
"id": "backend/config.py::DEFAULT_PROVIDER",
"beschreibung": "Stellt sicher, dass DEFAULT_PROVIDER aus der Umgebung gesetzt ist, und bricht den Start sonst mit einer RuntimeError ab.",
"fakten": [
{
"text": "Liest DEFAULT_PROVIDER aus der Umgebung mit leerem Default.",
"beleg": "DEFAULT_PROVIDER = os.getenv(\"DEFAULT_PROVIDER\", \"\")"
},
{
"text": "Bricht den Start ab, wenn DEFAULT_PROVIDER nicht gesetzt ist.",
"beleg": "if not DEFAULT_PROVIDER:"
},
{
"text": "Die Fehlermeldung nennt `minimax` als konkreten Beispielwert.",
"beleg": "DEFAULT_PROVIDER=minimax)\""
}
],
"kanten": [],
"anker": []
},
"backend/config.py::PROVIDERS": {
"id": "backend/config.py::PROVIDERS",
"beschreibung": "Stellt die unabhängigen Provider-Stacks (claude, minimax, lokal) mit Rollen-Modellen, CLI-Aufrufen und Authentifizierungs-Umgebungsvariablen bereit.",
"fakten": [
{
"text": "Mehrere voneinander unabhängige Provider-Stacks sind in einem Dict zusammengefasst.",
"beleg": "PROVIDERS = {"
},
{
"text": "Der `lokal`-Stack prüft Ollama-Erreichbarkeit über eine HTTP-URL.",
"beleg": "\"check_url\": \"http://localhost:11434/api/tags\","
},
{
"text": "Der `minimax`-Stack nutzt den ENV-Key `MINIMAX_API_KEY` zur Authentifizierung.",
"beleg": "\"env_key\": \"MINIMAX_API_KEY\","
},
{
"text": "Der `claude`-Stack nutzt OAuth-Authentifizierung statt eines API-Keys.",
"beleg": "\"env_key\": None, # auth via CLAUDE_CODE_OAUTH_TOKEN"
}
],
"kanten": [
{
"typ": "wird-genutzt-von",
"ziel": "backend/config.py::resolve_role"
}
],
"anker": []
},
"backend/config.py::ROLE_ROUTING": {
"id": "backend/config.py::ROLE_ROUTING",
"beschreibung": "Bildet die vier Agent-Rollen quick/judge/guide/fast auf optionale prozessweite Override-Provider aus Umgebungsvariablen ab.",
"fakten": [
{
"text": "Vier Agent-Rollen können prozessweit auf einen anderen Provider-Stack umgeleitet werden.",
"beleg": "\"quick\": os.getenv(\"ROLE_QUICK\", \"\"),"
},
{
"text": "Fehlende Env-Variablen ergeben einen leeren Override-String.",
"beleg": "os.getenv(\"ROLE_QUICK\", \"\"),"
}
],
"kanten": [
{
"typ": "wird-genutzt-von",
"ziel": "backend/config.py::resolve_role"
}
],
"anker": []
},
"backend/config.py::resolve_role": {
"id": "backend/config.py::resolve_role",
"beschreibung": "Liefert für eine Rolle das konkrete (provider, model)-Paar, wobei ein per Env gesetzter Rollen-Override Vorrang hat und sonst der Lauf-Provider genutzt wird.",
"fakten": [
{
"text": "Ein gesetzter Rollen-Override aus ROLE_ROUTING hat Vorrang vor dem Lauf-Provider.",
"beleg": "target = ROLE_ROUTING.get(role, \"\") or run_provider"
},
{
"text": "Die Syntax \"provider:model\" erlaubt es, ein bestimmtes Modell zu pinnen.",
"beleg": "provider, _, model = target.partition(\":\")"
},
{
"text": "Unbekannte Provider fallen auf den Lauf-Provider zurück.",
"beleg": "if provider not in PROVIDERS:"
},
{
"text": "Fehlt eine explizite Modellangabe, wird das Rollen-Default des Stacks genutzt.",
"beleg": "model = PROVIDERS.get(provider, {}).get(role, \"\")"
}
],
"kanten": [
{
"typ": "nutzt",
"ziel": "backend/config.py::ROLE_ROUTING"
},
{
"typ": "nutzt",
"ziel": "backend/config.py::PROVIDERS"
}
],
"anker": []
},
"backend/config.py::TIMEOUTS": {
"id": "backend/config.py::TIMEOUTS",
"beschreibung": "Definiert pro Agent-Schritt ein (Basis, pro-Block)-Timeout-Paar, das unabhängig vom Provider gilt.",
"fakten": [
{
"text": "Jeder Agent-Schritt hat individuelle Timeout-Werte als (Basis, pro-Block)-Tupel.",
"beleg": "\"research_mapping\": (600, 3), # n = pre-merged entries"
},
{
"text": "Die Judge-Caps wurden am 2026-07-04 reduziert.",
"beleg": "# Judge caps tightened 2026-07-04: judge p50 is 672 s;"
},
{
"text": "Der `writer` skaliert mit 60 s pro Sektion.",
"beleg": "\"writer\": (450, 60), # per section"
},
{
"text": "QA/Repair-Wellen (`qa_judge`) skalieren nicht mit n.",
"beleg": "\"qa_judge\": (600, 0),"
}
],
"kanten": [
{
"typ": "wird-genutzt-von",
"ziel": "backend/config.py::_apply_param_overrides"
}
],
"anker": []
},
"backend/config.py::_apply_param_overrides": {
"id": "backend/config.py::_apply_param_overrides",
"beschreibung": "Liest ein JSON-Override-Dict aus der Umgebungsvariable CREATOR_PARAMS und überschreibt damit numerische Tuning-Konstanten dieses Moduls zur Prozess-Startzeit.",
"fakten": [
{
"text": "Bricht die Override-Anwendung still ab, wenn CREATOR_PARAMS nicht gesetzt ist.",
"beleg": "if not raw:"
},
{
"text": "Weist ungültiges JSON in CREATOR_PARAMS mit SystemExit zurück.",
"beleg": "except ValueError:"
},
{
"text": "Timeout-Overrides werden über das Präfix `TIMEOUT_` in TIMEOUTS eingespielt.",
"beleg": "if key.startswith(\"TIMEOUT_\"):"
},
{
"text": "Unbekannte Schlüssel oder boolesche Werte werden mit SystemExit abgewiesen.",
"beleg": "isinstance(g[key], bool):"
},
{
"text": "Numerische Overrides behalten den ursprünglichen Typ des Ziels.",
"beleg": "g[key] = type(g[key])(val)"
}
],
"kanten": [
{
"typ": "nutzt",
"ziel": "backend/config.py::TIMEOUTS"
}
],
"anker": []
}
}
};
const wurzel = document.getElementById("wurzel");
const eht = id => DATEN.einheiten[id];
const esc = s => s.replace(/[&<>"]/g, c => ({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;"}[c]));
for (const b of DATEN.bereiche) {
const bx = document.createElement("section");
bx.className = "bereich";
bx.innerHTML = `<h2>${esc(b.name)}</h2><p>${esc(b.beschreibung)}</p>`;
for (const f of b.features) {
const fd = document.createElement("details");
fd.innerHTML = `<summary><strong>${esc(f.titel)}</strong>
<span class="flag ${f.flag}">${f.flag}</span></summary>
<p class="feature-besch">${esc(f.beschreibung)}</p>`;
for (const t of f.teilfeatures) {
const td = document.createElement("details");
td.className = "teil";
td.innerHTML = `<summary>${esc(t.titel)}</summary>`;
for (const id of t.einheiten) {
const e = eht(id);
const ed = document.createElement("details");
ed.className = "einheit";
if (!e) { ed.innerHTML = `<summary>${esc(id)} (nicht im Teilscan)</summary>`; td.appendChild(ed); continue; }
const fakten = e.fakten.map(fa => `<div class="fakt">${esc(fa.text)}
${fa.beleg ? `<code class="beleg">${esc(fa.beleg)}</code>` : ""}</div>`).join("");
const kanten = e.kanten.length
? `<div class="kanten">Kanten: ${e.kanten.map(k => `${k.typ} → <code>${esc(k.ziel)}</code>`).join(" · ")}</div>` : "";
ed.innerHTML = `<summary>${esc(id)}</summary>
<div class="einheit-inhalt"><p><em>${esc(e.beschreibung)}</em></p>${fakten}${kanten}</div>`;
td.appendChild(ed);
}
fd.appendChild(td);
}
bx.appendChild(fd);
}
wurzel.appendChild(bx);
}
</script>
</body>
</html>