# -*- coding: utf-8 -*- """Гейт кода в закрытии задачи (commands.code_change_check) — hermetic, временный git-репо. Запуск: ./venv/bin/python tests/test_code_gate.py Кейс 2026-10-18: Коди закрыл задачу итогом «x» до конца правки tg_inbox, без теста и коммита. Проверяет: задача без правок кода — закрывается как раньше; незакоммиченная правка после клейма — отказ; старая чужая грязь (до клейма) — не мешает; код без теста — отказ, с --no-test «причина» — можно, причина в итоге; код + зелёный тест — закрыта, хеш и тест в итоге; красный тест — отказ; SIMBIOT_CODE_GATE=off — гейт выключен. """ import os import subprocess import sys import tempfile import time sys.path.insert(0, "/root/simbiot-harness") _tmp = tempfile.mkdtemp(prefix="simbiot_codegate_") os.environ["SIMBIOT_DB"] = os.path.join(_tmp, "harness.db") REPO = os.path.join(_tmp, "repo") os.environ["SIMBIOT_CODE_REPO"] = REPO os.environ.pop("SIMBIOT_CODE_GATE", None) from harness import commands, es 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 git(*a): subprocess.run(["git", "-C", REPO, "-c", "user.name=t", "-c", "user.email=t@t", *a], check=True, capture_output=True) def write(rel, text, mtime=None): p = os.path.join(REPO, rel) os.makedirs(os.path.dirname(p), exist_ok=True) open(p, "w").write(text) if mtime: os.utime(p, (mtime, mtime)) os.makedirs(REPO) git("init", "-q") write("harness/a.py", "x = 1\n") write("scripts/old.sh", "echo old\n") git("add", ".") git("commit", "-q", "-m", "init") write("scripts/old.sh", "echo old dirty\n", mtime=time.time() - 3600) # чужая старая грязь N = [0] def task(): N[0] += 1 tid = f"t-{N[0]}" es.append_event("task", tid, "TaskSubmitted", {"id": tid, "title": "правка", "size": "S"}) es.append_event("task", tid, "TaskClaimed", {"level": "min"}) time.sleep(1.1) # git --since с точностью до секунды return tid def done(tid, answer="сделана правка харнеса", **kw): try: commands.complete_task_with_delivery(tid, answer, level="min", **kw) return True, "" except ValueError as e: return False, str(e) def answer_of(tid): import json return [json.loads(e["data"])["answer"] for e in es.get_events(0, 10**6) if e["event_type"] == "TaskCompleted" and e["aggregate_id"] == tid][-1] t = task() ok, why = done(t, "ответил в чат, код не трогал") check("без правок кода — закрывается как раньше, итог без приписки", ok and "Код:" not in answer_of(t), why) check("старая чужая грязь (до клейма) не мешает", ok) t = task() write("harness/a.py", "x = 2\n") ok, why = done(t) check("незакоммиченная правка — отказ с файлом", not ok and "harness/a.py" in why, why) git("add", "harness/a.py") git("commit", "-q", "-m", "a=2") ok, why = done(t) check("код без теста — отказ", not ok and "без теста" in why, why) ok, why = done(t, no_test_reason="правка константы, тест невозможен") check("--no-test с причиной — закрыта, причина в итоге", ok and "без теста: правка константы" in answer_of(t), why) t = task() write("harness/a.py", "x = 3\n") write("tests/test_a.py", "print('PASS a ok')\n") git("add", ".") git("commit", "-q", "-m", "a=3 + test") ok, why = done(t) ans = answer_of(t) if ok else "" check("код + зелёный тест — закрыта", ok, why) check("в итоге хеш коммита и тест", "Код: " in ans and "тесты PASS: tests/test_a.py" in ans, ans) t = task() write("harness/a.py", "x = 4\n") write("tests/test_a.py", "print('FAIL a broken')\nraise SystemExit(1)\n") git("add", ".") git("commit", "-q", "-m", "a=4 broken test") ok, why = done(t) check("красный тест — отказ", not ok and "не проходит" in why, why) # чужая незакоммиченная правка того же времени: тик её не трогал — не блокирует N[0] += 1 tid = f"t-{N[0]}" es.append_event("task", tid, "TaskSubmitted", {"id": tid, "title": "правка", "size": "S"}) es.append_event("task", tid, "TaskClaimed", {"level": "min", "session_id": "s-min-777"}) time.sleep(1.1) es.append_event("trace", "min-777", "ToolCalled", {"trace_id": "min-777", "tool": "edit", "input": '{"path": "/x/repo/harness/b.py"}'}) es.append_event("trace", "min-777", "ToolCalled", {"trace_id": "min-777", "tool": "shell", "input": '{"command": "./venv/bin/python cli.py tg-reply m-1 \\"ok\\""}'}) es.append_event("trace", "min-777", "ToolCalled", {"trace_id": "min-777", "tool": "shell", "input": '{"command": "grep -n tg_reply cli.py 2>/dev/null | head"}'}) es.append_event("trace", "min-777", "ToolCalled", {"trace_id": "min-777", "tool": "shell", "input": '{"command": "sed -i s/a/b/ scripts/mine.sh"}'}) write("cli.py", "# чужая правка cli\n") # запускали и читали, но не правили — не своё write("scripts/mine.sh", "echo b\n") # правил sed -i — своё write("harness/b.py", "y = 1\n") # своя правка (в трассе) write("scripts/other.sh", "echo чужое\n") # чужая правка (не в трассе) ok, why = done(tid) check("своя незакоммиченная правка (в трассе) — отказ", not ok and "harness/b.py" in why, why) check("чужая правка того же времени не в списке", "scripts/other.sh" not in why, why) check("запуск и grep cli.py — не правка cli.py", "cli.py" not in why, why) check("sed -i в shell — своя правка", "scripts/mine.sh" in why, why) git("add", "harness/b.py", "scripts/mine.sh") write("tests/test_a.py", "print('PASS b ok')\n") git("add", "tests/test_a.py") git("commit", "-q", "-m", "b + test") ok, why = done(tid) check("свою закоммитил — закрыта, чужая грязь не мешает", ok, why) os.environ["SIMBIOT_CODE_GATE"] = "off" ok, why = done(t) check("SIMBIOT_CODE_GATE=off — гейт выключен", ok, why) print(f"\n{'OK' if not fails else 'FAILED'}: {len(fails)} fail(s)") sys.exit(1 if fails else 0)