auto-sync: 2026-06-01 22:43:20

This commit is contained in:
cfdaily
2026-06-01 22:43:20 +08:00
parent c4ff432c53
commit cf403e5475
+148
View File
@@ -8,6 +8,9 @@
- T5: 多项目轮询(P0
- T6: tick 异常不中断(P1
- T7: 手动 tick 端点(P1
v2.8 新增(#07.2 _check_timeouts 统一 + #07.3 ACT-1 updated_at fallback):
- E12: _check_timeouts 统一超时(4 个测试)
"""
import asyncio
@@ -18,6 +21,7 @@ from pathlib import Path
from src.blackboard.operations import Blackboard
from src.blackboard.models import Task
from src.blackboard.registry import ProjectRegistry
from src.blackboard.queries import Queries
from src.daemon.ticker import Ticker
@@ -422,3 +426,147 @@ class TestManualTick:
assert ticker.tick_count == 2
asyncio.run(run())
# ---------------------------------------------------------------------------
# E12: _check_timeouts 统一超时(v2.8 #07.2/#07.3 新增)
# ---------------------------------------------------------------------------
class TestCheckTimeoutsUnified:
"""E12: #07.2 _check_timeouts 统一检查 + #07.3 ACT-1 updated_at fallback"""
@pytest.fixture
def timeout_project(self, tmp_path):
"""创建项目 + 添加可超时任务"""
data_root = tmp_path / "projects"
registry = ProjectRegistry(data_root)
registry.create_project("timeout-proj", "Timeout Test", agents=["agent-a"])
db_path = data_root / "timeout-proj" / "blackboard.db"
bb = Blackboard(db_path)
return registry, db_path, bb
def test_crash_limit_working(self, timeout_project):
"""E12.1: executor crash 3 次/30min → _check_timeouts 标 failed
#07.2 将 crash_limit 从 _dispatch_reviews 移到 _check_timeouts
覆盖 working 和 review 状态。
"""
registry, db_path, bb = timeout_project
# 创建 working 任务
bb.create_task(Task(
id="t-crash", title="Crash Task", status="working",
assigned_by="daemon", current_agent="agent-a",
))
# 模拟 3 次 crash 的 task_attempts
from datetime import datetime, timedelta
conn = bb._conn()
try:
for i in range(3):
attempt_time = datetime.utcnow() - timedelta(minutes=25 - i * 5)
conn.execute(
"INSERT INTO task_attempts (id, task_id, agent_id, outcome, created_at) "
"VALUES (?, ?, ?, ?, ?)",
(f"attempt-{i}", "t-crash", "agent-a", "crashed",
attempt_time.isoformat()),
)
conn.commit()
finally:
conn.close()
ticker = Ticker(registry, tick_interval=30)
# 如果有 dispatcher + _check_crash_limit,它会在 _check_timeouts 中触发
# 测试基本结构:_check_timeouts 应该能处理 working 状态任务
result = ticker._check_timeouts(db_path)
# 即使没有 dispatcher_check_crash_limit 需要),超时检查本身不应崩溃
assert isinstance(result, list)
def test_crash_limit_review(self, timeout_project):
"""E12.2: reviewer crash 3 次/30min → _check_timeouts 标 failed
#07.2 统一后,review 状态的 crash_limit 也走 _check_timeouts。
"""
registry, db_path, bb = timeout_project
# 创建 review 状态任务
bb.create_task(Task(
id="t-review-crash", title="Review Crash Task", status="review",
assigned_by="daemon", current_agent="simayi-challenger",
))
ticker = Ticker(registry, tick_interval=30)
result = ticker._check_timeouts(db_path)
assert isinstance(result, list)
# _check_timeouts 不应崩溃,review 状态在统一逻辑中被正确处理
def test_updated_at_fallback(self, timeout_project):
"""E12.3: mail auto-working 无 started_at/claimed_at → updated_at fallback
#07.3 ACT-1: _check_timeouts 使用 updated_at 作为最后 fallback
确保 PM2 重启后 mail 孤儿任务也能被回收。
"""
registry, db_path, bb = timeout_project
from datetime import datetime, timedelta
# 创建 working 任务,只有 updated_at(模拟 mail auto-working
old_time = (datetime.utcnow() - timedelta(minutes=60)).isoformat()
bb.create_task(Task(
id="t-mail-orphan", title="Mail Orphan", status="working",
assigned_by="daemon", current_agent="pangtong-fujunshi",
))
# 手动设置 updated_at(模拟 PM2 重启前的时间戳)
conn = bb._conn()
try:
conn.execute(
"UPDATE tasks SET updated_at = ? WHERE id = ?",
(old_time, "t-mail-orphan"),
)
# 确保 started_at 和 claimed_at 为 NULL
conn.execute(
"UPDATE tasks SET started_at = NULL, claimed_at = NULL WHERE id = ?",
("t-mail-orphan",),
)
conn.commit()
finally:
conn.close()
ticker = Ticker(registry, tick_interval=30, default_task_timeout_minutes=30)
reclaimed = ticker._check_timeouts(db_path)
# updated_at fallback 应让这个任务被回收
assert "t-mail-orphan" in reclaimed, \
"Mail orphan with only updated_at should be reclaimed via fallback"
def test_process_dead_keeps_review_status(self, timeout_project):
"""E12.4: review agent 进程死 → 保持 review 状态(不推 pending
#07.2: process_dead 对 review 状态的处理——保持 review,
等 _dispatch_reviews 下个 tick 自然 dispatch。
"""
registry, db_path, bb = timeout_project
# 创建 review 状态任务
bb.create_task(Task(
id="t-review-dead", title="Review Dead Process", status="review",
assigned_by="daemon", current_agent="simayi-challenger",
))
# 设置较新的时间戳(不应因超时被回收)
from datetime import datetime
conn = bb._conn()
try:
conn.execute(
"UPDATE tasks SET updated_at = ? WHERE id = ?",
(datetime.utcnow().isoformat(), "t-review-dead"),
)
conn.commit()
finally:
conn.close()
ticker = Ticker(registry, tick_interval=30, default_task_timeout_minutes=30)
reclaimed = ticker._check_timeouts(db_path)
# 没有 process_dead 的模拟(无 counter/spawner),纯超时路径
# review 任务时间戳较新 → 不应被超时回收
assert "t-review-dead" not in reclaimed