Audit N-12: goal pushback preamble reaches breakpoint resume via --resume-note
ci / test (push) Failing after 35m34s
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:
1 parent
bc19f09dff
commit
159d7112ae
4 files changed
+421
-11
No files matched your search
@@ -1950,7 +1950,8 @@ def _llm_declared_done(text):
|
||||
def run_goal_loop(server, state, state_path, task, goal_text, verify,
|
||||
max_rounds, model, effort, sandbox, cfg=None,
|
||||
timeout=None, verify_timeout=600, start_round=1,
|
||||
last_verify="", last_turn_status=None, last_turn_text=""):
|
||||
last_verify="", last_turn_status=None, last_turn_text="",
|
||||
resume_note=None):
|
||||
"""目标循环(长任务版,对齐 Codex App 的可续跑语义)。
|
||||
|
||||
两种验收形态(2026-10-27 起,console goal 模式走新形态):
|
||||
@@ -1980,6 +1981,12 @@ def run_goal_loop(server, state, state_path, task, goal_text, verify,
|
||||
判 failed,60 分钟工作无续跑口)。
|
||||
goal-unmet 穷尽轮次后不允许 goal-resume(按既有规则须新建
|
||||
任务),goal-resume 只接受 goal-running / interrupted / running。
|
||||
resume_note(N-12 审计推回前言续跑缺口):仅断点续跑分支
|
||||
传,贴进本次续跑第一轮的 prompt 最前面(仅展示,不写回
|
||||
task["goal"]、不进存储的 goal、不改轮次与验收;后续轮次
|
||||
的 prompt 不再重复贴,同一 thread 的历史里已有它)。不传
|
||||
时行为与旧版逐字相同;开新目标分支绝不传它(新目标自带
|
||||
goal_text,不需要也不接受断点备注)。
|
||||
"""
|
||||
task["goal"] = goal_text
|
||||
task["verify_command"] = verify
|
||||
@@ -2019,6 +2026,9 @@ def run_goal_loop(server, state, state_path, task, goal_text, verify,
|
||||
# max_rounds=0 是不限轮次:while 循环只在模型宣告、预算耗尽
|
||||
# 或取消时结束;非 0 时按旧语义穷尽轮次即 unmet。
|
||||
round_no = int(start_round) - 1
|
||||
# N-12:断点备注只贴本次续跑的第一轮 prompt(thread 历史
|
||||
# 会把它带到后续轮次,不必每轮重复贴、避免 token 膨胀)。
|
||||
_first_turn = True
|
||||
# 连续失败轮次护栏:回合瞬间失败(如密钥缺失)时不限轮次
|
||||
# 循环会无限空转(2026-10-29 实弹:断点后 274 轮全失败,
|
||||
# state 文件持续增长)。3 轮连续失败即走中断出口,任务
|
||||
@@ -2072,6 +2082,12 @@ def run_goal_loop(server, state, state_path, task, goal_text, verify,
|
||||
f"目标:{goal_text}\n"
|
||||
f"验收命令:{verify}\n"
|
||||
f"上一轮验收输出:\n{last_verify}")
|
||||
# N-12:断点备注只贴第一轮 prompt(见 _first_turn 注释),
|
||||
# 绝不写回 goal_text——task["goal"] 在循环开始时已按原
|
||||
# goal_text 落盘,备注只是本次续跑的展示前缀。
|
||||
if _first_turn and resume_note:
|
||||
prompt = "%s\n\n%s" % (resume_note, prompt)
|
||||
_first_turn = False
|
||||
try:
|
||||
result = run_task_turn(server, state, task, prompt,
|
||||
model, effort, sandbox, timeout=timeout,
|
||||
@@ -2362,6 +2378,10 @@ def main():
|
||||
"传了会把原验收命令覆盖成 None")
|
||||
p.add_argument("--max-rounds", type=int, default=None,
|
||||
help="不传时沿用原任务的 goal_max_rounds;传了则覆盖总轮次")
|
||||
p.add_argument("--resume-note", default=None,
|
||||
help="断点续跑备注(N-12):仅断点续跑(不传 --goal-text)时用,"
|
||||
"只贴进本次续跑第一轮的 prompt,不写回存储的 goal、不改"
|
||||
"轮次与验收;与 --goal-text 同传时忽略(开新目标分支不用它)")
|
||||
p.add_argument("--verify-timeout", type=int, default=600,
|
||||
help="验测命令上限(秒);0 表示不限时,默认 600")
|
||||
p = sub.add_parser("parallel"); p.add_argument("--jobs", required=True)
|
||||
@@ -3258,7 +3278,8 @@ def main():
|
||||
start_round=int(t.get("goal_rounds_done") or 0) + 1,
|
||||
last_verify=t.get("goal_last_verify") or "",
|
||||
last_turn_status=t.get("goal_last_turn_status"),
|
||||
last_turn_text=last_result.get("text", "") or "")
|
||||
last_turn_text=last_result.get("text", "") or "",
|
||||
resume_note=args.resume_note)
|
||||
elif args.cmd == "parallel":
|
||||
jobs = json.load(open(args.jobs))
|
||||
# 依赖 + 优先级:先拓扑排序,缺依赖/成环直接报错不跑;
|
||||
|
||||
Reference in new issue
Block a user