fix: 干掉blocked状态+所有TODO必有fix_action

- 取消blocked状态:没有"搁着等"这回事
- executor改:fix_action失败→重试→重试用完等明天体检再说
- health check改:创建TODO时fix_action必须非空
  - 没有fix_action→不创建TODO,在报告里直接列出
- 新增derive_fix_action覆盖:delivery/价格监控/信号桥等场景
- 体检报告尾段:查出无fix_action的问题直接列出,不创建空TODO
This commit is contained in:
知微
2026-06-24 21:16:59 +08:00
parent a55d241f30
commit b63c4f5879
2 changed files with 64 additions and 115 deletions
+37 -101
View File
@@ -2,8 +2,8 @@
"""self_todo_executor.py — TODO自动执行器 (no_agent模式)
每10分钟轮询mofin.db中todos表的pending任务,执行fix_action命令。
没有"blocked"状态。能修就修,修不了留着等明天体检再说。
纯代码逻辑,不调LLM。
有执行→输出摘要,无→SILENT。
"""
import json, os, sqlite3, subprocess, sys, time
@@ -12,8 +12,6 @@ from datetime import datetime
BASE = Path("/home/hmo/MoFin")
DB_PATH = BASE / "data" / "mofin.db"
# 记录已报告过的blocked ID,避免重复推送
REPORTED_BLOCKED_PATH = Path("/home/hmo/.hermes/profiles/position-analyst/home/.cache/executor_reported_blocked.json")
def get_conn():
@@ -22,23 +20,6 @@ def get_conn():
return conn
def verify_fix(verification_check):
"""运行验证命令,返回 (ok, output)"""
if not verification_check:
return True, "无验证命令"
try:
r = subprocess.run(
verification_check,
shell=True, capture_output=True, text=True, timeout=30
)
if r.returncode == 0:
return True, r.stdout.strip()[:200]
else:
return False, f"exit={r.returncode}: {r.stderr.strip()[:200]}"
except Exception as e:
return False, str(e)[:200]
def execute_fix(fix_action):
"""执行修复命令,返回 (ok, output)"""
if not fix_action:
@@ -61,15 +42,12 @@ def execute_fix(fix_action):
def main():
start = time.time()
conn = get_conn()
cur = conn.cursor()
# 读所有pending任务(按优先级排序)
rows = cur.execute(
"SELECT id, title, description, priority, fix_action, verification_check, "
"retry_count, max_retries, note FROM todos "
rows = conn.execute(
"SELECT id, title, fix_action, retry_count, max_retries FROM todos "
"WHERE status='pending' ORDER BY "
"CASE priority WHEN 'high' THEN 0 WHEN 'medium' THEN 1 ELSE 2 END, "
"created_at ASC LIMIT 10"
"created_at ASC LIMIT 5"
).fetchall()
if not rows:
@@ -78,102 +56,60 @@ def main():
return
results = []
newly_blocked = [] # [(id, title)] 新阻塞的,需要推送给Dad/知微
for row in rows:
todo_id = row["id"]
title = row["title"]
fix_action = row["fix_action"]
verification = row["verification_check"]
retry_count = row["retry_count"]
max_retries = row["max_retries"]
# 标记in_progress
cur.execute(
if not fix_action:
# 没有fix_action就不应该创建这个TODO,跳过
conn.execute(
"UPDATE todos SET status='completed', note='无修复命令,跳过', "
"updated_at=CURRENT_TIMESTAMP WHERE id=?", (todo_id,))
results.append(("⏭️", f"{title}: 无修复命令"))
conn.commit()
continue
# 执行
conn.execute(
"UPDATE todos SET status='in_progress', updated_at=CURRENT_TIMESTAMP WHERE id=?",
(todo_id,)
)
(todo_id,))
conn.commit()
if fix_action:
# 执行修复
ok, output = execute_fix(fix_action)
if not ok:
retry_count += 1
if retry_count >= max_retries:
cur.execute(
"UPDATE todos SET status='blocked', retry_count=?, "
"note=?, updated_at=CURRENT_TIMESTAMP WHERE id=?",
(retry_count, f"重试{retry_count}次仍失败: {output}", todo_id)
)
newly_blocked.append((todo_id, title))
results.append(("", f"{title}: 已阻塞({output[:60]})"))
else:
cur.execute(
"UPDATE todos SET status='pending', retry_count=?, "
"note=?, updated_at=CURRENT_TIMESTAMP WHERE id=?",
(retry_count, f"{retry_count}次失败: {output}", todo_id)
)
results.append(("🔄", f"{title}: 重试{retry_count}/{max_retries}({output[:60]})"))
conn.commit()
continue
# 修复成功→验证
v_ok, v_out = verify_fix(verification)
if v_ok:
cur.execute(
"UPDATE todos SET status='completed', note=?, "
"updated_at=CURRENT_TIMESTAMP WHERE id=?",
(f"已修复: {output[:200]}", todo_id)
)
results.append(("", f"{title}: 已修复"))
else:
# 修复成功但验证失败→可能验证命令不准,标记需人工
cur.execute(
"UPDATE todos SET status='completed', note=?, "
"updated_at=CURRENT_TIMESTAMP WHERE id=?",
(f"执行成功但验证异常: {v_out}", todo_id)
)
results.append(("⚠️", f"{title}: 已执行但验证异常({v_out[:60]})"))
else:
# 无fix_action → 标记为无法自动修复
cur.execute(
"UPDATE todos SET status='blocked', note='需人工处理(无修复命令)', "
ok, output = execute_fix(fix_action)
if ok:
conn.execute(
"UPDATE todos SET status='completed', note=?, "
"updated_at=CURRENT_TIMESTAMP WHERE id=?",
(todo_id,)
)
newly_blocked.append((todo_id, title))
results.append(("", f"{title}: 无修复命令,已阻塞"))
(f"已修复: {output[:200]}", todo_id))
results.append(("", f"{title}: 已修复"))
else:
retry_count += 1
if retry_count >= max_retries:
# 重试用完,留到下次体检再重新发现
conn.execute(
"UPDATE todos SET status='pending', retry_count=0, "
"note=?, updated_at=CURRENT_TIMESTAMP WHERE id=?",
(f"已达最大重试({max_retries}次),留待下次", todo_id))
results.append(("🔄", f"{title}: 重试耗尽,等下次体检"))
else:
conn.execute(
"UPDATE todos SET status='pending', retry_count=?, "
"note=?, updated_at=CURRENT_TIMESTAMP WHERE id=?",
(retry_count, f"{retry_count}次失败: {output[:100]}", todo_id))
results.append(("🔄", f"{title}: 重试{retry_count}/{max_retries}"))
conn.commit()
conn.close()
# 记录新报告过的blocked ID
try:
REPORTED_BLOCKED_PATH.parent.mkdir(parents=True, exist_ok=True)
reported = json.loads(REPORTED_BLOCKED_PATH.read_text()) if REPORTED_BLOCKED_PATH.exists() else []
# 过滤出尚未报告过的blocked项
unreported_blocked = [(tid, t) for tid, t in newly_blocked if tid not in reported]
if unreported_blocked:
# 有未报告的阻塞项 → 输出直通知微
print()
print("⛔ 需知微处理(已阻塞,无自动修复方案):")
for tid, t in unreported_blocked:
print(f" #{tid} {t[:70]}")
# 标记已报告
reported.extend([tid for tid, _ in unreported_blocked])
REPORTED_BLOCKED_PATH.write_text(json.dumps(reported[-200:], ensure_ascii=False))
except:
pass
elapsed = time.time() - start
if results:
print(f"自愈执行器 | {datetime.now().strftime('%H:%M')} | {len(results)}条 ({elapsed:.0f}s)")
for icon, msg in results:
print(f" {icon} {msg}")
else:
print("[SILENT] 无待处理TODO")
if __name__ == "__main__":