fix: 断点丢失时 goal-resume 拒绝成 failed,阻断自动恢复空转

2026-10-29 长任务实弹:Docker goal 断点续跑在 host-state 里找
不到任务时抛 KeyError,worker 把无结论异常退出判 interrupted,
自动恢复再排队、再 KeyError,12 秒一轮空转 49 次(人工暂停
拦下,第 50 次人工续跑又烧掉),文件 32 章原样未动。
修法:host.py goal-resume 先查任务在不在 state 里,不在就打印
拒绝行、正常退出;sandbox 三处解析(本地、Docker resume、
Docker 断点)见拒绝行即判 failed 终态,不进自动恢复循环。
单测:host 缺任务拒绝不炸 + 三处解析拒绝判 failed。
This commit is contained in:
host committed 2026-10-29 14:14:13 +08:00
1 parent e48b756f96
commit a5f2e4e091
4 files changed
+138 -3

No files matched your search

+66
View File
@@ -0,0 +1,66 @@
"""host.py goal-resume 断点丢失守卫:state 里没有任务时拒绝不炸。
2026-10-29 长任务实弹:断点续跑在 state 里找不到任务时抛
KeyError traceback,worker 把无结论异常退出判成 interrupted,
自动恢复再排队、再 KeyError,12 秒一轮空转到 50 次上限。
守卫后:打印拒绝行、正常返回,state 文件不动、线程不恢复。
"""
import io
import json
import os
import sys
import tempfile
import unittest
from contextlib import redirect_stdout
from pathlib import Path
from unittest import mock
REPO_ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(REPO_ROOT))
import host
class FakeServer(object):
def __init__(self):
self.resumed = []
def initialize(self):
pass
def close(self):
pass
def resume_thread(self, thread_id):
self.resumed.append(thread_id)
class GoalResumeMissingTest(unittest.TestCase):
def test_missing_task_refuses_without_traceback(self):
with tempfile.TemporaryDirectory() as tmp:
state_path = os.path.join(tmp, "host-state.json")
cfg_path = os.path.join(tmp, "host-config.json")
with open(state_path, "w", encoding="utf-8") as f:
json.dump({"tasks": {"g": {"name": "g",
"thread_id": "x",
"status": "goal-running"}},
"automations": []}, f)
with open(cfg_path, "w", encoding="utf-8") as f:
json.dump({}, f)
fake = FakeServer()
argv = ["host.py", "--state", state_path,
"--config", cfg_path,
"goal-resume", "--task", "missing-task"]
out = io.StringIO()
with mock.patch.object(sys, "argv", argv):
with mock.patch.object(host, "Server",
lambda **kw: fake):
with redirect_stdout(out):
host.main()
self.assertIn("goal-resume 拒绝", out.getvalue())
self.assertIn("missing-task", out.getvalue())
self.assertEqual(fake.resumed, [])
with open(state_path, encoding="utf-8") as f:
saved = json.load(f)
self.assertEqual(list(saved["tasks"].keys()), ["g"])