From 0868a2c71fba6e36474ae46b759a7dc4b9800b73 Mon Sep 17 00:00:00 2001 From: eric Date: Sat, 21 Feb 2026 20:28:22 +0000 Subject: [PATCH] Fix standup race condition: lock + daily guard + remove standup from TaskBeat whitelist - _standup_lock: threading.Lock prevents concurrent standup runs - DB check: if a standup task already exists for today, abort - TaskBeat whitelist: remove 'standup' type so TaskBeat never re-runs standup tasks (standup is always driven by trigger_daily_standup(), not TaskBeat) --- app.py | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/app.py b/app.py index ed9ad5c..4c14047 100644 --- a/app.py +++ b/app.py @@ -2108,7 +2108,7 @@ def process_beat_tasks(): # Konvertiere zu Dict-Format für Legacy-Kompatibilität pending_tasks = [] for db_task in db_tasks: - if db_task.get('type') in ('agent_created', 'manual', 'orchestrated', 'agent_delegated', 'telegram', 'agent_subtask', 'standup', 'broadcast'): + if db_task.get('type') in ('agent_created', 'manual', 'orchestrated', 'agent_delegated', 'telegram', 'agent_subtask', 'broadcast'): pending_tasks.append(db_task) for task in pending_tasks: @@ -2405,12 +2405,43 @@ def start_orchestrator_beat(): # ── DAILY STANDUP ───────────────────────────────────────────────────────────── +_standup_lock = threading.Lock() + def trigger_daily_standup(): """ Tägliches Standup: Orchestrator fragt alle Team-Members nach Updates und delegiert anschließend Wissensupdates an alle Agenten. + Gegen Doppel-Trigger gesichert: nur ein Standup pro Tag möglich. """ - logger.info("[DailyStandup] Starte tägliches Standup...") + # Nur einen gleichzeitigen Standup erlauben + if not _standup_lock.acquire(blocking=False): + logger.warning("[DailyStandup] Bereits aktiv — zweiter Trigger ignoriert.") + return + + try: + # Prüfe ob heute schon ein Standup läuft oder abgeschlossen ist + today_str = datetime.now().strftime('%Y-%m-%d') + try: + con = sqlite3.connect(EMAIL_JOURNAL_DB) + existing = con.execute( + "SELECT id FROM tasks WHERE type='standup' AND created_at LIKE ? AND status != 'error'", + (f"{today_str}%",) + ).fetchone() + con.close() + except Exception: + existing = None + + if existing: + logger.warning("[DailyStandup] Standup für heute (Task #%s) bereits vorhanden — abgebrochen.", existing['id'] if existing else '?') + return + + logger.info("[DailyStandup] Starte tägliches Standup...") + _trigger_daily_standup_inner() + finally: + _standup_lock.release() + + +def _trigger_daily_standup_inner(): # Team-Members aus DB holen try: