断点镜像状态标签回落:host-state 条目写 status 不是 goal_status
生产实弹:极小 goal 任务的 timeline 里 checkpoint goal_status 为
None——host.py 的任务条目只有 status(goal-running/goal-met),
没有 goal_status 键,镜像原样读 goal_status 永远是空。修法:
entry.get('goal_status') or entry.get('status')。轮次、thread_id、
state_sha 等关键字段不受影响(熔断只看轮次),纯标签 fidelity
修复;单测加 status-only 条目镜像断言。
This commit is contained in:
1 parent
585242fc8f
commit
8c719c7e62
2 files changed
+25
-2
No files matched your search
@@ -240,6 +240,24 @@ class CheckpointMirrorTestCase(unittest.TestCase):
|
|||||||
conn.close()
|
conn.close()
|
||||||
self.assertEqual(rows, [(1, "round"), (2, "round")])
|
self.assertEqual(rows, [(1, "round"), (2, "round")])
|
||||||
|
|
||||||
|
def test_mirror_falls_back_to_status_label(self):
|
||||||
|
# host-state 条目写的是 status 不是 goal_status:实弹里
|
||||||
|
# 镜像到的 goal_status 全是 None,修法是取 status 回落。
|
||||||
|
with open(self.state_path, "w", encoding="utf-8") as fh:
|
||||||
|
json.dump({"version": 1, "tasks": {"T1": {
|
||||||
|
"goal_rounds_done": 2, "thread_id": "th2",
|
||||||
|
"status": "goal-met",
|
||||||
|
"goal_last_verify": ""}}}, fh)
|
||||||
|
self.assertEqual(self.worker._mirror_checkpoint_once(self.task), 2)
|
||||||
|
conn = sqlite3.connect(self.db_path)
|
||||||
|
try:
|
||||||
|
row = conn.execute(
|
||||||
|
"SELECT round_no, thread_id, goal_status"
|
||||||
|
" FROM task_checkpoints WHERE task_id = 'T1'").fetchone()
|
||||||
|
finally:
|
||||||
|
conn.close()
|
||||||
|
self.assertEqual(row, (2, "th2", "goal-met"))
|
||||||
|
|
||||||
def test_rebuild_without_checkpoint_is_false(self):
|
def test_rebuild_without_checkpoint_is_false(self):
|
||||||
with open(self.state_path, "w", encoding="utf-8") as fh:
|
with open(self.state_path, "w", encoding="utf-8") as fh:
|
||||||
json.dump({"version": 1, "tasks": {}}, fh)
|
json.dump({"version": 1, "tasks": {}}, fh)
|
||||||
|
|||||||
+7
-2
@@ -1104,6 +1104,11 @@ class Worker:
|
|||||||
if not isinstance(entry, dict):
|
if not isinstance(entry, dict):
|
||||||
return -1
|
return -1
|
||||||
rounds = int(entry.get("goal_rounds_done") or 0)
|
rounds = int(entry.get("goal_rounds_done") or 0)
|
||||||
|
# host-state 条目写的是 status(goal-running/goal-met…),
|
||||||
|
# 不是 goal_status:实弹里镜像到的 goal_status 全是
|
||||||
|
# None——取 status 作回落,标签才不是空的(轮次与
|
||||||
|
# thread_id 等关键字段不受影响,熔断只看轮次)。
|
||||||
|
goal_status = entry.get("goal_status") or entry.get("status")
|
||||||
state_sha = hashlib.sha256(
|
state_sha = hashlib.sha256(
|
||||||
json.dumps(entry, sort_keys=True,
|
json.dumps(entry, sort_keys=True,
|
||||||
default=str).encode("utf-8")).hexdigest()
|
default=str).encode("utf-8")).hexdigest()
|
||||||
@@ -1121,7 +1126,7 @@ class Worker:
|
|||||||
" verify_out = ?, created_at = ?"
|
" verify_out = ?, created_at = ?"
|
||||||
" WHERE task_id = ? AND round_no = ?",
|
" WHERE task_id = ? AND round_no = ?",
|
||||||
(entry.get("thread_id"),
|
(entry.get("thread_id"),
|
||||||
entry.get("goal_status"),
|
goal_status,
|
||||||
state_sha,
|
state_sha,
|
||||||
(entry.get("goal_last_verify") or "")[:500],
|
(entry.get("goal_last_verify") or "")[:500],
|
||||||
time.time(), task["id"], rounds))
|
time.time(), task["id"], rounds))
|
||||||
@@ -1131,7 +1136,7 @@ class Worker:
|
|||||||
" thread_id, goal_status, state_sha, verify_out,"
|
" thread_id, goal_status, state_sha, verify_out,"
|
||||||
" created_at) VALUES(?, ?, ?, ?, ?, ?, ?)",
|
" created_at) VALUES(?, ?, ?, ?, ?, ?, ?)",
|
||||||
(task["id"], rounds, entry.get("thread_id"),
|
(task["id"], rounds, entry.get("thread_id"),
|
||||||
entry.get("goal_status"), state_sha,
|
goal_status, state_sha,
|
||||||
(entry.get("goal_last_verify") or "")[:500],
|
(entry.get("goal_last_verify") or "")[:500],
|
||||||
time.time()))
|
time.time()))
|
||||||
self._import_host_steps(conn, task, state_path)
|
self._import_host_steps(conn, task, state_path)
|
||||||
|
|||||||
Reference in new issue
Block a user