Audit N-12: goal pushback preamble reaches breakpoint resume via --resume-note
ci / test (push) Failing after 35m34s

Goal tasks pushed back by the audit loop lost the packet preamble on
the next round: claim-time _apply_audit_loop_packet pasted it into the
in-memory prompt/goal, but the next round collided with the existing
host task name, fell back to breakpoint goal-resume (no --goal-text),
and the host reused the stored goal, discarding the preamble.

Fix (minimal, direction (c) trimmed): host goal-resume gains
--resume-note, accepted only by the breakpoint branch and prepended
to the first turn prompt of this resume only -- never written back
to the stored goal, never touching rounds or verification. The worker
stashes the same preamble as the memory-only _audit_loop_resume_note
at claim time and forwards it on breakpoint resume (Local/Docker
goal_resume_task and the popen goal-resume command, each guarded to
ignore the note when goal_text is set for a new-goal resume).
Accounting untouched: audit 3-round cap, goal_max_rounds/quota, and
DB goal columns are unchanged; non-goal pushback is unchanged.

Tests: cloud/tests/test_audit_n12.py (9 tests) -- host first-turn-only
note + stored-goal/rounds, worker exists-fallback end-to-end with
--resume-note, non-goal regression, goal-column accounting, and
Local/Docker/popen command shapes. Full cloud suite: 1760 OK
(skipped=11), baseline 1751 + 9 new; tests/test_host.py: 62 OK.
This commit is contained in:
host committed 2026-11-04 18:02:23 +00:00
1 parent bc19f09dff
commit 159d7112ae
4 files changed
+421 -11

No files matched your search

+24 -7
View File
@@ -2194,10 +2194,12 @@ class Worker:
packet 对 goal 任务同步贴两者),但 goal 任务下一轮
实际会撞宿主任务名已存在、经 run_one 的已存在回落走
断点续跑(goal-resume 不传 --goal-text,宿主用存储的
goal 与 goal_rounds_done+1 继续),所以包前言贴了却
不进续跑循环——这是审计推回对 goal 任务的已知效果
缺口,不是轮次会计错误,本批不改行为、留待后续批
定夺。轮次会计本身有界:审计轮次硬上限 3(MAX_
goal 与 goal_rounds_done+1 继续)。N-12 修复:认领贴
前言时另存 _audit_loop_resume_note,断点续跑时作
--resume-note 传给 host,host 只贴进本次续跑第一轮的
prompt,不写回存储的 goal、不改轮次与验收——前言从此
到达续跑循环,库内原 goal 与宿主存储的 goal 均不动。
轮次会计本身有界:审计轮次硬上限 3(MAX_
PUSHBACKS),3 轮后必升级;goal 旧形态总回合数受
goal_max_rounds 约束(续跑不重置计数),新形态受配额
约束;两组计数器独立、不叠加成无界循环。
@@ -2720,7 +2722,10 @@ class Worker:
顺序是目录树 → 推回包 → 重跑前言 → 项目上下文 → 原
prompt,推回包是最需要 agent 先看到的一段。库里的
原 prompt 与包列都不动,包列只在 pass/escalate 时由
接线清理、pushback 时由接线覆盖。
接线清理、pushback 时由接线覆盖。另存
_audit_loop_resume_note(N-12):同一份前言的副本,
仅内存键,供断点续跑时作 --resume-note 传给 host;
非 goal 任务与开新目标续跑忽略它。
"""
if task.get("coordinator_parts"):
return task
@@ -2748,6 +2753,14 @@ class Worker:
if task.get("goal_mode") or task.get("verify_command"):
new_task["goal"] = preamble + "\n\n" + (
task.get("goal") or task.get("prompt") or "")
# N-12:前言另存一份作断点续跑备注(仅内存键,不进库)。
# goal 任务下一轮撞“已存在”走断点续跑时,host 只用存储
# 的原 goal(见 _run_attempt 的 goal_resume 调用),内存
# goal 里的前言到不了 agent;_run_attempt 在断点续跑时
# 把这份备注作 --resume-note 传给 host,host 只贴进本次
# 续跑第一轮的 prompt,不写回存储的 goal。非 goal 任务
# 与开新目标续跑忽略此键(sandbox 侧的配对守卫)。
new_task["_audit_loop_resume_note"] = preamble
new_task["_audit_loop_packet_injected"] = True
self._audit(task["project_id"], "worker_audit_loop_packet_injected",
task["id"])
@@ -5553,7 +5566,9 @@ class Worker:
network=tenant_network,
extra_env=_merge_env(proxy_env,
gateway_env),
model_provider=gateway_provider),
model_provider=gateway_provider,
resume_note=task.get(
"_audit_loop_resume_note")),
quota_check)
else:
result = self._docker_call_with_quota_poll(
@@ -5620,7 +5635,9 @@ class Worker:
result = self.executor.goal_resume_task(
task, _resume_text,
extra_env=gateway_env,
model_provider=gateway_provider)
model_provider=gateway_provider,
resume_note=task.get(
"_audit_loop_resume_note"))
elif isinstance(self.executor, LocalExecutor):
result = self.executor.goal_task(
task, seed_dir=seed_host_dir,