7.5 KiB
Einheiten: backend/kanban.py
stand: UNBEKANNT
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:
- Hält pro Stage einen Zähler aktiver Worker. beleg: "self.active: dict[str, int] = {}"
- Zählt zusätzlich die aktuell laufenden Research-Producer. beleg: "self.producers = 0"
- research_done ist nur True, wenn kein einziger Producer mehr läuft. beleg: "return self.producers <= 0"
- leave klemmt den Active-Zähler beim Dekrementieren bei Null ab. beleg: "self.active[stage] = max(0, self.active.get(stage, 0) - 1)"
- 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:
- wird-genutzt-von: backend/kanban.py::quiescent
- wird-genutzt-von: backend/kanban.py::_sleep_wake
- wird-genutzt-von: backend/kanban.py::_fail_package
- wird-genutzt-von: backend/kanban.py::_worker
- wird-genutzt-von: backend/kanban.py::run_flow
- wird-genutzt-von: backend/kanban.py::_progress
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:
- Repräsentiert genau eine Spalte, identifiziert durch Board- und Stage-Name, mit einem Processor. beleg: "self.process = process"
- Setzt serial automatisch mit, sobald drain True ist. beleg: "self.serial = serial or drain"
- Hält eine leere upstream-Liste, die chain_stages mit Vorgänger-Stages füllt. beleg: "self.upstream: list[str] = []" kanten:
- wird-genutzt-von: backend/kanban.py::chain_stages
- wird-genutzt-von: backend/kanban.py::_worker
- wird-genutzt-von: backend/kanban.py::_fail_package
- wird-genutzt-von: backend/kanban.py::run_flow
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:
- Setzt das upstream jeder Stage auf die zuvor gesehenen Stages. beleg: "s.upstream = list(seen)"
- Gibt die unveränderte Eingabeliste wieder zurück. beleg: "return stages" kanten:
- nutzt: backend/kanban.py::Stage
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:
- Eine leere Stages-Liste gilt sofort als quiescent. beleg: "return True"
- Sobald irgendeine der Stages aktive Worker hat, wird sofort False zurückgegeben. beleg: "return False"
- 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:
- ruft-auf: backend/kanban.py::Flow
- ruft-auf: backend/database.py::kanban_count
- wird-genutzt-von: backend/kanban.py::_worker
- wird-genutzt-von: backend/kanban.py::run_flow
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:
- Wartet auf das Wake-Event des Flows mit einem Polling-Timeout. beleg: "await asyncio.wait_for(flow.wake.wait(), timeout=_POLL)"
- Fängt TimeoutError ab, damit die Polling-Schleife weiterläuft. beleg: "except asyncio.TimeoutError:"
- Löscht das Wake-Event vor der Rückkehr. beleg: "flow.wake.clear()" kanten:
- nutzt: backend/kanban.py::Flow
- wird-genutzt-von: backend/kanban.py::_worker
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:
- Überspringt Karten, die nicht mehr auf der Stage des Processors sitzen (bereits advanced). beleg: "if cur is None or cur["stage"] != spec.stage:"
- 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,"
- Loggt eine Warnung, wenn eine Karte auf die Dead-Stage verschoben wird. beleg: "log.warning("kanban %s/%s: card %s → dead (%s)"," kanten:
- nutzt: backend/kanban.py::Flow
- nutzt: backend/kanban.py::Stage
- ruft-auf: backend/database.py::kanban_get_card
- ruft-auf: backend/database.py::kanban_fail_card
- wird-genutzt-von: backend/kanban.py::_worker
backend/kanban.py::_worker
anker: backend/kanban.py::_run
anker: backend/kanban.py::_idle_exit
anker: backend/kanban.py::_may_pull
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:
- 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"
- Hält ein claimed-Set mit Card-IDs, damit parallele Pulls dieselbe Karte nicht doppelt holen. beleg: "claimed: set[str] = set()"
- Bei AgentInfraError wird der ganze Flow pausiert (stop + infra_paused), statt zu dead-lettern. beleg: "flow.stop = True"
- Barrier-Worker pullen nur, wenn das Gate offen ist und alle Upstream-Stages quiescent sind. beleg: "return await quiescent(flow, spec.upstream)"
- Bricht beim Exit alle noch laufenden Package-Tasks ab und sammelt sie mit Exceptions ein. beleg: "await asyncio.gather(*tasks, return_exceptions=True)" kanten:
- nutzt: backend/kanban.py::Flow
- nutzt: backend/kanban.py::Stage
- ruft-auf: backend/kanban.py::quiescent
- ruft-auf: backend/kanban.py::_sleep_wake
- ruft-auf: backend/kanban.py::_fail_package
- ruft-auf: backend/database.py::kanban_pull
- ruft-auf: backend/database.py::kanban_count
- nutzt: backend/pipeline.py::AgentInfraError
- wird-genutzt-von: backend/kanban.py::run_flow
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:
- Trägt den Flow für die Laufzeit in das Modul-Dict active_flows ein. beleg: "active_flows[flow.topic] = flow"
- Startet pro Stage einen Worker mit inflight=1 für serial-Stages, sonst WORKER_INFLIGHT. beleg: "1 if s.serial else WORKER_INFLIGHT,"
- Startet den Progress-Reporter nur, wenn ein set_p-Callback übergeben wurde. beleg: "progress = asyncio.create_task(_progress(flow, set_p
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:
- Läuft in einer Schleife, bis das Stop-Flag des Flows gesetzt ist. beleg: "while not flow.stop:"
- Holt pro Board die aktuellen Stage-Counts aus der Datenbank. beleg: "counts = await db.kanban_stage_counts(flow.topic)"
- 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())"
- Reicht die Gesamtzahl als formatierten deutschen String an set_p weiter. beleg: "set_p(f"Kanban: {total} Karten im Fluss")"
- Schläft eine Sekunde zwischen den Iterationen. beleg: "await asyncio.sleep(1.0)" kanten:
- nutzt: backend/kanban.py::Flow
- ruft-auf: backend/database.py::kanban_stage_counts
- wird-genutzt-von: backend/kanban.py::run_flow