# -*- coding: utf-8 -*- """e-261003094628 (повтор e-261001082241): guard мусорных заголовков задач (hermetic). Запуск: ./venv/bin/python tests/test_submit_title_guard.py Первопричина серии 'list': LLM-тик нижнего уровня звал `cli.py task list` / `cli.py task ` (хотел доску/детали задачи), а `task` принимает позиционный title → argparse молча подсунул слово 'list' / чужой id как заголовок и подал мусорную задачу. Min-уровень закрыл их как no-op и эскалировал — дважды. Проверяет: submit_task режет субкомандо-подобные заголовки и чужие id задач БЕЗ деталей (ValueError), пропускает нормальные подачи, одно слово с деталями, обычные слова не из стоп-листа; CLI `task list` → exit 1 с ERROR, а не тихая подача. """ import os import subprocess import sys # ── изоляция: своя БД в /tmp (prod не трогаем) ── _DST = "/tmp/simbiot-test-title-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 commands 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) # ── 1. Субкомандо-подобные заголовки без деталей — режутся ── for bad in ("list", "tasks", "status", "events", "help", "LIST", " Complete "): try: commands.submit_task(bad, "", minutes=5) check(f"мусорный заголовок {bad!r} отвергнут", False, "подача прошла") except ValueError: check(f"мусорный заголовок {bad!r} отвергнут", True) # ── 2. Чужой id задачи как заголовок без деталей — режется ── try: commands.submit_task("t-261003094008-ed226f", "", minutes=5) check("id задачи как заголовок отвергнут", False, "подача прошла") except ValueError: check("id задачи как заголовок отвергнут", True) # ── 3. Нормальные подачи — проходят ── for title, details in ( ("Разбор голосового m-973940607 + HTML-страница", "транскрипт в docs/"), ("list", "собрать список всех ключей в реестре"), # слово из стоп-листа, но с деталями ("фикс деплоя", ""), ): try: tid, size = commands.submit_task(title, details, minutes=5) ok = bool(tid) and size == "S" except ValueError as e: ok, tid = False, str(e) check(f"нормальная подача {title[:30]!r} прошла", ok, tid) # ── 4. CLI: `cli.py task list` — показывает доску, а не плодит мусорную задачу ── # Контракт изменён в origin/master (t-261003125513/e-261004134613): 'task list' # и 'task ' показывают доску/задачу (exit 0). Интент guard'а прежний: # НЕТ тихой подачи мусорной задачи с заголовком 'list'. env = dict(os.environ, SIMBIOT_DB=_DST) r = subprocess.run( ["./venv/bin/python", "cli.py", "task", "list"], capture_output=True, text=True, cwd="/root/simbiot-harness", env=env, ) out = (r.stdout + r.stderr).strip() check("CLI task list → exit 0 (показ доски)", r.returncode == 0, f"rc={r.returncode} out={out!r}") check("CLI task list → не ERROR-подача", "ERROR" not in out, out) model = commands.es.run_projections() check("мусорной задачи 'list' в сторе нет", not any(t.get("title", "").strip().lower() == "list" and not t.get("details") for t in model["tasks"].values())) print() if fails: print(f"FAILED: {len(fails)}") sys.exit(1) print("OK: все проверки прошли")