# -*- coding: utf-8 -*- """Guard бюджет-стабов (e-261001074527-f0c1b4, hermetic, без LLM/TG). Запуск: ./venv/bin/python tests/test_budget_stub_guard.py Ночной тик 20727 пачкой закрыл 5 L-задач отпиской воркера «бюджет исчерпан». Гвард: complete_task_with_delivery со стоп-фразой → ValueError + авто-requeue в ready (TaskPromoted), TaskCompleted НЕ пишется. Честные результаты со встречным упоминанием бюджета гвард не трогает. Проверяет: looks_like_budget_stub на ночных текстах (стоп-фраза воркера, end_turn-сообщение tool-budget, шаблон ночной доставки) — true; честные результаты («уточнил бюджет», «бюджет рекламной кампании исчерпан») — false; complete со стабом → ValueError + задача вернулась в ready; повторный complete с реальным результатом → done. CLI requeue по guard-сообщению работает (задача не теряется). """ import os import sys _DST = "/tmp/simbiot-test-guard.db" for p in (_DST, _DST + "-wal", _DST + "-shm"): if os.path.exists(p): os.unlink(p) os.environ["SIMBIOT_DB"] = _DST sys.path.insert(0, "/root/simbiot-harness") from harness import es from harness import commands es.init_db() fails = [] def check(name, cond, extra=""): print(("PASS" if cond else "FAIL"), name, ("| " + str(extra)) if not cond else "") if not cond: fails.append(name) def task_state(tid): model = es.run_projections() t = model["tasks"].get(tid) return t["status"] if t else None def task_events(tid): db = es._conn() try: rows = db.execute("SELECT event_type FROM events WHERE aggregate_id=?", (tid,)).fetchall() return [r["event_type"] for r in rows] finally: db.close() # ── 1. Predicate: ночные шаблоны распознаются ── nightly = [ "✅ Готово, тик завершён: бюджет исчерпан", "тик завершён: бюджет шагов исчерпан (30 tool-вызовов). Выше — результат того, что успело выполниться.", "тиковый бюджет шагов исчерпан (30 tool-вызовов): вызов отменён; новые вызовы инструментов отключены до конца тика", ] for i, txt in enumerate(nightly): check(f"1.{i}: stub распознан", commands.looks_like_budget_stub(txt), txt[:50]) # ── 2. Predicate: честные результаты не трогаем ── honest = [ "Утверждён оффер: 10 мест × $99.9/мес. Задача закрыта, счётчик на лендинг — следующая задача.", "Проверил рекламный кабинет: бюджет рекламной кампании исчерпан, переключил на резервный.", "Проанализировал траты за месяц; бюджет месяца исчерпан частично — отчёт готов.", "Рефакторинг завершён из 3 шагов. Всё влезло, бюджет не трогали.", ] for i, txt in enumerate(honest): check(f"2.{i}: честный не стаб", not commands.looks_like_budget_stub(txt), txt[:50]) # ── 3. Живая задача: complete со стабом → ValueError + requeue в ready ── tid, _sz = commands.submit_task(title="Тест guard", minutes=30, details="тестовая задача гвирда бюджет-стабов") es.append_event("task", tid, "TaskClaimed", {"level": "hour", "session_id": "t-guard"}) es.run_projections() check("3.0: задача running", task_state(tid) == "running") stub = "тик завершён: бюджет исчерпан" try: commands.complete_task_with_delivery(tid, stub, level="hour") check("3.1: complete со стабом отклонён", False, "нет ValueError") except ValueError as e: check("3.1: complete со стабом отклонён", "ПЕРЕВЕД" in str(e).upper() or "ready" in str(e), str(e)[:80]) check("3.2: после guard — задача в ready", task_state(tid) == "ready", task_state(tid)) check("3.3: TaskCompleted НЕ записан", "TaskCompleted" not in task_events(tid), task_events(tid)) check("3.4: requeue-событие есть", "TaskPromoted" in task_events(tid)) # ── 4. Тот же тик: повторный complete с реальным результатом → done ── es.append_event("task", tid, "TaskClaimed", {"level": "hour", "session_id": "t-guard2"}) tid2, delivered = commands.complete_task_with_delivery( tid, "Реальный результат: расчёт суммы бюджета проекта завершён", level="hour") check("4.0: честный complete после requeue", task_state(tid) == "done", task_state(tid)) check("4.1: TaskCompleted записан", task_events(tid).count("TaskCompleted") == 1) # ── 5. Честный «бюджет … исчерпан» в реальном результате → TaskCompleted ок ── tid3, _sz3 = commands.submit_task(title="Тест честного", minutes=30, details="тест: результат содержит бюджет-фразу по делу") es.append_event("task", tid3, "TaskClaimed", {"level": "hour", "session_id": "t-guard3"}) try: commands.complete_task_with_delivery( tid3, "Проверил кабинет: бюджет рекламной кампании исчерпан, отчёт готов", level="hour") check("5.0: честный результат проходит", task_state(tid3) == "done", task_state(tid3)) except ValueError as e: check("5.0: честный результат проходит", False, str(e)[:80]) print() print("FAILS:", len(fails)) sys.exit(1 if fails else 0)