Virtual Embryo Challenge更新于 10-03 20:13(北京时间) / 每 5 分钟更新

← 工作原理 · 原文件 notes/reports/dev/2026-10-03_G45_carryover_limits.md(Markdown 源文件已渲染;链接到其他文档的会跳转,指向源码的只显示路径)

G45.6 / G45.7:跨环续做、执行上限与权重复用

1. 起因与范围变化

用户 2026-10-03 担心复杂算法(要训练的 OT / CFM / VAE)在 12 h 一环里跑不完:候选每次执行受视图 limits(30 min)限制,一个入选候选被重复执行约 9 次;环截止时在飞的 Engineer 工作没有交接。任务中途两次改口径:

  1. 用户:不要固定执行时限,改为“健康即继续”的监督 + 宽松兜底(360 min),终选预留按实测,健康邮件改每小时(之后用户再改为每 3 小时,timer 由主会话改并部署),T1 链不写 time_limit_min。
  2. Codex 审阅(P0 / P1,覆盖 18 号草案中冲突的部分):截止语义、封存事务、终选资源隔离(缺基线拒绝替换、基线先落盘、预留估算口径)、seed / 训练状态 / 缓存有效范围写进契约;权重缓存键;新会话续做、真实父节点、额度累计、segments[] 证据;健康监督先只观测;链级已暴露评测 seed。后置:--seeds 批量接口、每节点提前三 seed、续同一 opencode 会话、前五名固定重训(改为入围候选冷启动重现检查,只作诊断)。

中途写过的 --seeds 多输出路径、晋级读已有 seed 分数、前五名重训已全部撤回(代码里不留)。

2. 候选执行上限与健康监督(G45.7)

项实现
配置 limitscontroller.normalize_limits:{memory_gb, time_limit_min} 或 {<榜>: {...}} → cfg.limits = {board, memory_gb, time_limit_min}(进锁);默认 dataview.EXEC_LIMITS_DEFAULT = 28 GB / 360 min(views.LIMITS 同步改 360)。build_run_views 把它写进本 run 每个视图(含测试题视图)manifest 的 limits(dataview.set_limits);execute.program_limits 与 evalspec 仍从 manifest 读
健康监督 health.pySupervisor:每 collect_s 60 s 读进程组(累计 CPU、I/O、按 pid 归属的 GPU SM% / 显存(nvidia-smi pmon + compute-apps)、GPU 排队、进程状态 / wchan、cgroup 内存与余量、输出、可写目录 mtime、solution/progress.json、探针可用性),每 interval_s 300 s 汇总写 <exec_dir>/health.jsonl + health_latest.json。活动只认 CPU / 自己的 GPU 计算 / I/O / D 态 + I/O / GPU 排队,心跳与进度不算;没有内存逼近判据
模式exec_health.enforce: false(默认,只观测):30 min 无活动只记 would_hang,程序受 360 min 与阶段终点约束。enforce: true:30 min 无活动 → hung(候选失败);360 min 只在监督器失效(failed_after_s 600 s 读不到进程组)时生效(backstop → timeout)
新状态 hungarchive STATUSES / FAILED / EXEC_FAILED、final.CANDIDATE_FAILURES、tune.CANDIDATE_FAIL、viewcheck.INCONCLUSIVE_STATUSES、看板词表、报告失败列表
截止语义所有终止 SIGTERM → 宽限 term_grace_s 60 s → SIGKILL(kill_tree(grace_s));run_group(deadline_fn=...):控制器的 live_deadline 让 cutoff 前移时在飞的 Engineer 会话(cutoff − tail)和执行(阶段终点)一起提前
Engineer 停顿判定ActivityProbe 加 health.GpuActivity(每 60 s 一次 pmon,会话进程树自己的 GPU 计算算活动)
进度文件执行时 solution/progress.json 是 solution/ 里唯一 rw 绑定的文件(环境变量 VEC_PROGRESS_FILE),提交时排除
评测规格evalspec execution 加 health(仅当配置有 exec_health;旧锁不变)
健康邮件timer 由主会话改为每 3 h(OnCalendar=*-*-* 00/3:00:00,本任务不再改它);reports.health_report 每个运行中 run 列出在飞候选执行(health.inflight_summaries / summary_line),熔断与模型异常的统计窗口 = 邮件周期近 3 h(HEALTH_WINDOW_S)

3. 训练一次、多 seed 复用(G45.7)

  • ARTIFACTS.json 条目加 per_view: true(必须在子目录);EXECUTION.json 加 seed_affects_training(bool)与 train_seed(int)。
  • 提交:per_view 文件不进 git、不按提交存;声明了但提交时不在的文件自动转 per_view(记 commit_skipped.txt)。
  • 执行(execute.prepare_weights):缓存键 weights_key = sha256(solution 树、训练 seed、view_content、env_key(venv、pip freeze、modeling 快照)、额外参数如 --ablate)。命中 → 只读硬链回放(replay);未命中 → 持该键的 flock(锁在运行时目录,不在 run 目录)、权重子目录 rw 绑定(目录内指向产物库的硬链先换成私有副本)、成功后验收并不可变登记(runenv.store_per_view,索引 artifacts/per_view/<tree>/<key>.json,首写者为准,进账本)。已登记的权重缺失 / 哈希不符 → interrupted。artifacts="fresh"(视图盲检查伪装与复核、冷启动重现检查)训练但不回放、不登记。
  • 四种执行的核实:--ablate 对照走 evaluate(extra_args) → 键含参数 → 训练自己的一组(对照不能用完整机制的权重,按 Codex 改);晋级 seed 1/2、终选护栏 seed(含基线)走 evaluate → 同键回放;--continue-from:continuation.import_runs 原先只复制产物库顶层文件,补上递归复制 per_view 索引(硬链 + 账本)。单元测试 WeightsTest 逐项覆盖。
  • 冷启动重现检查(repro_check,默认开):终选在 holdout / record 诊断之后、仍有时间时,对入围的训练型候选 seed 0 fresh 再跑,记 repro_json、REPRO.json、repro_check 事件,只作诊断。

4. 终选资源保护与 seed(Codex P0-3、item 8)

  • final.py:配置了护栏但没有基线提交 → 记 final_guard_refused,不写 final/,不做任何候选审查 / 执行(原为“跳过护栏、按排名放候选”);有护栏时基线先落盘,再做候选的视图盲检查和审查。
  • 终选预留按实测(Controller.final_reserve_estimate / update_final_reserve,主循环与收尾循环每 60 s):未做的视图检查、未审查、缺的晋级 seed、护栏全部任务(最坏候选 + 基线)、final 冷启动(× 1.5)与基线 final 运行、每次执行 60 s 评分排队,串行累加;执行时间用多视图合计 + 评分时间(evaluate 新增 wall_s,进 seed_scores);reserve = max(配置, 估计 + 10 min),上限半个挂钟;Budget.extend_reserve 只前移 cutoff,存 meta,resume 后保持。
  • 链级已暴露 seed:assign_fresh_seeds 在 launch(inputs 锁定、锚点预建之前)沿 --continue-from 链收集各环护栏 fresh_seeds 与 chain_exposed_seeds,重叠就换成最小未用整数(3–7 → 8–12),集合写进 config.chain_exposed_seeds(进锁)。

5. 跨环续做(G45.6,carryover.py)

  • 带走:会话在交接点(cutoff − node_tail)被切断且没留下可执行程序(gen_failed / gen_empty)→ carried_over(engineer);切断但可运行 → 照常立即执行,快照只作证据(handoff_sealed 事件);权威执行被搜索阶段截止切断(interrupted + budget_cut)→ carried_over(execution,封存权重目录里的最后检查点)。
  • 封存事务 carryover.seal:进程组已回收、打分 client 已移除后,复制 solution/(跳过 .h5ad / 符号链接 / 超过 2 GB)、PLAN、原始轨迹、检查点,写 HANDOFF.json(交接摘要、改动文件、METHOD / run.py 状态、已用查分、会话统计、最后 30 条消息节选、segments[])与 SEAL.json(逐文件 sha256),全部只读,登记账本;节点 Archive.interrupt(status="carried_over")(新增状态,属 SET_ASIDE)。
  • 导入:continuation.import_runs 对 carried_over 行 copy_verified(先按 SEAL 逐文件校验,失败不复制、记 carryover_rejected),通过的写 meta carryover_verified;continued_from.json 加 carryover 段。
  • 续做:build_resume_queue(已校验、未被续做、最旧优先、每环 ≤ max_resume_per_ring 4)→ 主循环先 next_resume 再抽签;resume 是新 op(本环节点,计 max_nodes),父节点 = 被带走节点的真实父节点;父节点审查 fail → resume_refused。resume_origin 列(JSON):run、id、imported_id、kind、segments、seal_sha256、commit(Codex 要的“origin 记上一环 run id 与节点 id”放在这一列,而不是 origin_run:后者在全代码里表示“导入的、不是本环的工作”,会把续做节点排除在 max_nodes、节点估时、报告的“本环新节点”之外)。
  • engineer 类:不调 Researcher;新会话;工作区 = 快照的 solution/,已写的 METHOD 保留;提示 = 本环完整提示 + carryover.resume_block(续做标记、交接摘要、最后消息、只读快照路径、剩余额度);快照只读挂载;额度 = 每节点额度 − 各段已用之和。execution 类:同一提交直接 execute_node。
  • 证据:续做节点的 trajectory_paths 以各段快照的原始轨迹开头;package 把祖先链里续做节点的快照(轨迹、HANDOFF、SEAL、PLAN)并入 lineage_trajectories,MANIFEST.json 加 lineage_segments。
  • 配置 carryover: {enabled: false, max_resume_per_ring: 4, max_file_mb: 2048};T1 / T2 链配置打开。
  • fake 后端:fake_faults.slow_engineer / slow_program / train_weights(按节点 id)供端到端用。

6. Codex 实现审阅(notes/reports/reviews/2026-10-03_codex_g45_impl.md)的修复

条改动位置测试(agent/search/tests/test_g45_carryover_limits.py)
P0-1 截止收尾controller.work_phase / bind_phase(节点线程、晋级线程绑定启动时的阶段;live_deadline、carry_on、evaluate、llm_call 都按它),_spawn、_promoter;execute.LiveDeadline / live_stop(前移的截止传到训练锁、admission、GPU 槽、评分请求);loop() 收尾:cutoff 后等在飞节点回收、封存、结算(宽限 + SETTLE_EXTRA_S,不晚于 stop_at − 60 s)ReviewP0Deadline.test_bound_phase_survives_the_final、test_live_deadline_reaches_waits、test_final_waits_for_search_work_to_settle
P0-2 多环去重archive.insert_imported 重映射 resume_origin.imported_id;controller.work_key(稳定身份 = 封存它的 run#node)、build_resume_queue(已续做的工作不再续;名额只数 origin_run IS NULL 的 resume)、next_resume(resume_origin.run/id = 直接上一段,记 work)ReviewP0MultiRing.test_three_rings;fake 三环 §7
P0-3 权重验收execute._run_sandboxed:store_per_view 有任何 problem → crash、不打分;runenv.load_per_view:索引不完整 / 读不出 → ArtifactError(→ interrupted),不退回训练ReviewP0Weights.test_acceptance_failure_is_not_scored、test_incomplete_index_is_interrupted
P0-4 祖先污染continuation.ancestor_integrity(递归:TAINTED、ops.verify_run、目录 / 锁存在);接入 describe_source(launch)、controller.package(失败拒绝打包)、scripts/prepare_submission.py(祖先问题列为不能提交)ReviewP0Ancestors.test_recursive_taint
P1-9 指纹拆分evalspec.search_hash(去掉护栏 fresh seed 与只为它们建的锚点、视图 limits、time_limit_min、threads、health),spec() 输出 search_hash;scoring_def.search_hash_of(旧锁从锁里的规格 + 磁盘视图重算);continuation 按 search_hash 判可比ReviewP1.test_search_hash_ignores_guard_seeds_and_time_limit、test_view_limits_do_not_change_the_search_view_hash;fake 三环 B、C 都 comparable=True
P1-6 额度carryover.used_from_log(复用评分服务口径)、controller.queries_used;queries_used_total 有未知段 → None → 续做额度 0ReviewP1.test_unknown_quota_grants_nothing
P1-10 宽限runenv.kill_tree:整组在宽限期内退出才算完,到期 KILL,返回残留 pid;run_group 记 reap_left;执行有残留 → 不登记权重、不封存(reap_incomplete),会话有残留(CallResult.reap_left)→ 不封存ReviewP1.test_grace_covers_the_whole_group
追加:线程上限limits.threads(默认 8,进锁、写进视图 manifest、进评测规格 execution.programs.*.threads,不进 search_hash);runenv.thread_env 经 base_env 给执行与 Engineer 沙箱设 OMP_NUM_THREADS / OPENBLAS_NUM_THREADS / MKL_NUM_THREADS / NUMEXPR_MAX_THREADS / NUMEXPR_NUM_THREADS / VECLIB_MAXIMUM_THREADS / BLIS_NUM_THREADS;权重缓存键的 env_key 含线程数;search_spec.md 预算条加半句LimitsTest.test_thread_budget

后置(已知限制,未做):5(segments 逐段完整取证:Engineer 封存只收 Engineer 轨迹,execution 封存无轨迹;package 只打包当前 imported_id 的快照,不逐段解析 segments[].run/node)、7(封存 / 导入不是可恢复的原子事务:SEAL 写完到节点结算之间崩溃会被 recover 记成普通 interrupted)、8(seed / guard 阶段的截止没有扣除候选 final 冷启动预留;估算未计护栏 holdout 执行)、11(缓存键未纳入注入的 GPU 环境 / sitecustomize)。kg_task 接线按更正未合入(research 服务无 kg_task 时 kg 保持 disabled)。

6b. Codex 二审(notes/reports/reviews/2026-10-03_codex_g45_impl_r2.md)的修复

条改动位置测试(test_g45_carryover_limits.py)
P0-1 等待资源时截止execute.run_program / _run_sandboxed:程序未开始、admission 放弃、GPU 槽放弃、admission 后无时间 → interrupted 带 budget_cut=True;训练锁等待超时(prepare_weights 记 budget_cut)带 True,产物损坏不带ReviewR2P0WaitCut.test_admission_and_gpu_waits、test_train_lock_vs_damage、test_wait_cut_node_is_carried_over(真实 execute_node → run_program,admission 与 GPU 两条分支都落到 carried_over 并封存校验通过)
P1-1 T1 锚点命名evalspec.anchor_seed(按字段解析 sN,兼容 B:s3:d0)、search_hashReviewP1.test_search_hash_guard_seeds_only、test_search_hash_real_proxy10_labels(真实 proxy10.describe())、test_old_round2_lock_guard_seed_swap(真实 r2-D-s0/s1 锁,只读;VM 上没有该 run 会跳过,Spark 上运行)
P1-2 在途评分截止agent/scoring/service.py:GroupDeadline、request_stop(内部请求的 deadline_key)、控制 op set_deadline(只降不升)、FifoGate 等待每 2 s 重读、_run_worker 循环 communicate 每 2 s 重读并在新截止杀 worker;execute.LiveDeadline.key 与 score_prediction / check_prediction 带 deadline_key;控制器 push_deadline(开跑时与每次前移 cutoff 时);stop_promoter 上限(cutoff + 宽限 + SETTLE_EXTRA_S,超时 promotion_abandoned、结算 failed,promote 末尾不再覆盖)ReviewR2P1Deadlines.test_group_deadline_wakes_a_queued_request、test_set_deadline_op_lowers_only、test_live_deadline_names_its_group、test_stop_promoter_is_bounded
P1-3 时间 / 线程进比较search_hash 不再排除视图 limits、time_limit_min、threads(只排除护栏新 seed 及其锚点和 health);search_hash_of 只用锁里的规格;T1 链配置 continue_rescore_top_k: 40test_search_hash_guard_seeds_only、test_old_round2_lock_guard_seed_swap(改时限后不可比)
P1-4 在途查分结算服务 remove_client 把 client 留在 retired,新 op settle_client;控制器先移除 client 再 settled_count(90 s),结不清 queries_settled=False → queries_used 为 NoneReviewR2P1Quota.test_pending_request_across_the_seal、test_unsettled_count_is_unknown
P1-5 旧 resume_originarchive.insert_imported 补 work = run#id;controller.resume_work 去重兼容旧记录ReviewR2P1OldResume.test_migration
P2-1 忙循环runenv.kill_tree:leader 退出后按 0.5 s 间隔 sleep / pollReviewP1.test_grace_covers_the_whole_group(行为不变)

取舍(P1-3):时间上限与线程数候选看得见,可能改变输出,所以留在比较里。代价:今晚 T1 第一环从 r2-D-s0 / s1(30 min、无线程变量)接续时 search_hash 不同,导入节点只重评排名前 40 个(加各自种子),其余成为只作历史的节点;重评要额外执行约 40 次候选(每次在 proxy10 与 X3 上各一次)。

后置(已知限制):二审 P1-6(v6 及更早祖先只查 TAINTED、不做内容校验;今晚两个来源是 v8,不触发);一审 P1-5 / P1-7 / P1-8 / P1-11(见 §6)。

6c. Codex 三审(notes/reports/reviews/2026-10-03_codex_g45_impl_r3.md)的修复

条改动位置测试(test_g45_carryover_limits.py)
1 settle_client 封口service.Client.closed;remove_client 在服务锁内弹出、置 closed、转 retired;score 的准入(pending += 1)同一锁内先查 closed,关闭后拒绝(refused “client closed”);controller.queries_used 只认 queries_settled is True,服务不答复也算未知,去掉日志回退ReviewR3Quota.test_no_request_admitted_after_close、ReviewR2P1Quota.test_unsettled_count_is_unknown(加了“无结算证明不回退日志”)
2 计数归一化controller.final_count:_engineer 写进 info["queries_used"](即节点列)前归一化,未结清为 None;Engineer 与 execution 两种 HANDOFF 都沿用节点列 / 该值test_unsettled_count_is_unknown(final_count)
3 set_deadline 确认push_deadline 记待确认值,响应确认(返回的 stop_at ≤ cutoff)才清除;retry_deadline 每 DEADLINE_RETRY_S 30 s 重发(挂在 update_final_reserve,主循环 / 收尾 / 重评都会调用);confirm_deadline 在进入终选前最多重试 60 s,失败记 deadline_unconfirmed;客户端 execute.service_live:LiveDeadline 过去 30 s 仍无应答就返回 interruptedReviewR3Deadline.test_retry_until_confirmed、test_retry_is_bounded_and_final_confirmation、test_client_stops_waiting_at_the_moved_bound
4 晋级弃置 / 视图锁promo_commit(锁 promo_commit_lock + 代次 promo_gen):promote 的两处结果写入都经它,弃置时代次加一,迟到写入丢弃;abandoned_threads 保留旧线程;view_check(lock_timeout=...),终选用 FinalSelector.vc_lock_timeout()(≤ 300 s 且不过终选截止),超时 BudgetExhausted → 跳过该候选ReviewR2P1Deadlines.test_stop_promoter_is_bounded(加迟到提交被丢弃)、ReviewR3Promotion.test_view_lock_timeout
5 导入重评rescore_node:interrupted + budget_cut → 保持 imported_unscored、留在 rescore_queue、事件 rescore_cut;continuation.describe_source 读来源 run 的 rescore_queue,import_runs 把仍是 imported_unscored 的这些节点重新入队(不论可比与否);rescore_imported 在后台线程跑,主线程期间更新终选预留ReviewR3Rescore.test_cut_rescore_stays_queued、test_open_obligations_are_imported
6 seed / guard 截止FinalSelector.cold_final_s / place_reserve_s:补跑 seed、旧式护栏、非劣护栏的截止扣 max(1.5 × 基线 final, 候选冷启动 final 估计) + 120 s;冷启动估计 = 训练过的(weights train / fresh / 无 per_view)最长单视图 seed-0 实测 × 1.5,没有则 final_cold_fraction(新配置键,默认 0.25)× limits.time_limit_min;没有 limits 的旧锁不加默认(行为不变)ReviewR3FinalReserve.test_place_reserve
7 共用收尾截止settle_deadline() 首次调用时固定 cutoff + 宽限 + 180 s(≤ stop_at − 60 s),节点收尾与 stop_promoter 共用ReviewR3FinalReserve.test_one_settle_deadline

6d. Codex 四审(notes/reports/reviews/2026-10-03_codex_g45_impl_r4.md)的修复

条改动位置测试(test_g45_carryover_limits.py)
1 额度封口service.settle_client 只对 closed=True 的 client 给 settled(应答多 closed 字段);controller.close_client 检查 remove_client 应答、最多 3 次重试(“no client” 视为已移除),关闭无法证明时不结算、计数为未知ReviewR4Quota.test_open_client_is_never_settled、test_close_client_checks_the_answer
2 冷启动估算共用(最要紧)final.cold_final_estimate(模块级):无 per-view 权重 = 最长单视图实测 × 1.5;有 per-view 且有训练实测 = 最长训练实测 × 1.5;有 per-view 只有回放 = 0.25 × time_limit_min;旧锁无 limits = 0。FinalSelector.cold_final_s 和控制器 final_reserve_estimate 都用它,后者再加同一个 PLACE_CHECK_S(120 s)ReviewR4FinalReserve.test_guard_runs_with_replay_only_candidates(Codex 复现:5 个已晋级 replay-only 候选、每视图 30 s 执行 + 5 s 评分;用控制器算出的预留作为终选全部时间,护栏 10 个 fresh-seed 作业全部运行)、test_no_per_view_weights_uses_the_measurement、test_shared_estimate
3 截止确认持续失败confirm_deadline 失败后 reap_scoring_workers:反复列出并 SIGKILL + stop 本 run 的评分 worker scope(vec-score-<run>-*),直到 10 s 内无新出现(最多 120 s),事件 scoring_workers_reaped,然后才进入终选ReviewR4Deadline.test_unconfirmed_deadline_reaps_workers
4 晋级代次(P2)_promoter 启动时绑定代次,循环条件与 promote(nid, gen) 都用它;promote 的启动登记(running)也经 promo_commit;异常分支改走 promo_commitReviewR4PromotionGeneration.test_stale_thread_registers_nothing

关于默认 0.25 × 360 = 90 分钟是否过大:按上面的划分,默认值只用于“声明了 per-view 权重、但本 run 里从没有一次训练执行被测到”的候选。正常情况下这类程序的第一次权威执行就是训练(weights: train),会有实测;只回放过的典型来源是导入节点,而今晚的导入因线程数 / 环境改变,权重缓存键不同,重评时会重新训练、得到实测。没有 per-view 权重的程序(包括 replay-only 字样之外的所有普通程序)一律用实测 × 1.5,不会落到 90 分钟。所以 90 分钟只在少见情况下生效,且现在已计入总预留,不会再出现“预留不足、护栏全部跳过”。

剩余的已知限制(第 3 条):reap 只杀已经起了 worker 的请求;服务内仍在排队、尚未起 worker 的 search 请求按它们发送时的截止运行(服务不确认截止通常意味着服务已不可用,这时也没有排队请求)。

7. 回归

四审修复后(最新):Spark bash scripts/test_on_spark.sh -j 2 70 个模块全部通过(failed modules: 0);test_g45_carryover_limits 70 项(VM 上 1 项跳过:r2-D 旧锁只在 Spark 上);VM 另跑 test_objective_ops、test_proxy10、test_continuation_promotion、test_ni_guard、test_x3_guard 全过。

三审修复后:Spark bash scripts/test_on_spark.sh -j 2 70 个模块全部通过(failed modules: 0);test_g45_carryover_limits 63 项全过(Spark 上无跳过,r2-D 旧锁测试运行)。VM 上 test_objective_ops、test_proxy10、test_x3_guard、test_continuation_promotion、test_ni_guard 也单独跑过(test_continuation_promotion.test_rescore_node 曾因 rescore_imported 新的后台线程用到 hb 失败,已改为可选)。

二审修复后(Spark bash scripts/test_on_spark.sh -j 2,同步的是当前工作树):68 个模块全部通过,failed modules: 0。 其中 agent.search.tests.test_g45_carryover_limits 54 项全过、无跳过(test_old_round2_lock_guard_seed_swap 在 Spark 上读到了真实的 r2-D 锁并通过,test_search_hash_real_proxy10_labels 用真实 proxy10.describe());agent.search.tests.* 全部、agent.notify.notify_test、agent.notify.quota_test、agent.outer.outer_test / quota_watch_test / watcher_test、agent.research.test_kg_proxy(1 项跳过)/ test_service、agent.relay.relay_test、agent.scoring.test_service 均 OK。(Codex 二审在只读沙箱里看到的 36 项报错是无法建临时目录,不是代码问题。)

以下为一审修复时的记录:

  • VM:test_g45_carryover_limits(43 个)、test_execution_contract、test_run_correctness、test_continuation_promotion、test_objective_ops、test_proxy10、test_score_parts、test_roles_controller、test_g39_search_fixes、test_x3_guard、agent.notify.notify_test 全过。
  • 既有测试的两处调整(新语义所致):test_run_correctness.test_budget_cut_kill_is_interrupted 显式设 exec_health.term_grace_s: 8(默认宽限改 60 s,被测程序忽略 SIGTERM);test_x3_guard 的假 run_program 接受 artifacts,并断言视图盲检查用 fresh。
  • Spark 全量 bash scripts/test_on_spark.sh -j 2(最后一次,含 agent.notify.notify_test 与本任务全部改动、健康邮件窗口 3 h):65 个模块 62 OK、3 个失败,都不在本任务改动范围内,是并行会话正在改的文件: 本任务涉及的模块(agent.search.tests.* 全部、agent.notify.notify_test、agent.dashboard.*、agent.report.report_test、agent.scoring.test_service 等)全部 OK;前一次全量里唯一与本任务有关的失败(test_x3_guard 的假 run_program 不接受 artifacts)已修。
    • agent.outer.outer_test:新建的未跟踪模块 agent/outer/,测试读 .claude/skills/outer-loop/SKILL.md,该目录被 .gitignore(.claude/)排除,test_on_spark.sh 不同步它(环境问题);
    • agent.research.test_kg_proxy:kg_proxy 正按 Codex 审阅改成固定模板,测试与模块版本不一致(build_queries 不存在、run() 签名不同);
    • agent.relay.relay_test:test_anthropic_json_injects_key_and_logs_usage 用量字段不符,agent/relay 本任务未改(可能与并行的用量 / 定价改动有关)。

8. fake 三环端到端(Spark,VEC_RUNS_ROOT=~/vec/scratch/g45_runs,flock heavy.lock)

环run结果
A search_t1_g45_carry_fake.yaml20261003-171426-search-t1-g45-carry-fake节点 3(慢 Engineer,删掉 run.py 写 helper.py)在交接点被切 → carried_over(engineer;HANDOFF:run_py false、method_state placeholder、改动文件 (deleted) run.py, METHOD.md, helper.py、queries_used 0);节点 5(run.py sleep 900 s)在 cutoff 被切 → carried_over(execution);收尾日志 “cut-off reached with 1 node(s) in flight [5]: waiting up to 240 s …”,节点 5 结算后才进终选;package / verify ok
B …_ring2.yaml --continue-from A20261003-172936-search-t1-g45-carry-fake-r2护栏 seed 3–7 → 8–12;comparable=True(完整 hash 不同、search_hash 相同 e0b90d45…);两个快照校验通过并复制;resume 队列 [3, 5] 先于抽签:节点 6(engineer,按 slow_engineer.ops: [resume] 仍慢)再次被切 → carried_over;节点 7(execution,同一提交重跑)又被 cutoff 切 → carried_over;新程序训练 per_view 权重(artifacts/per_view 8 个树目录);package / verify ok
C …_ring3.yaml --continue-from B(max_resume_per_ring: 2)20261003-174729-search-t1-g45-carry-fake-r3护栏 seed → 13–17,chain_exposed_seeds 3–17;comparable=True;队列只有 B 的再带走节点 [6, 7](工作 …r2#6、…r2#7),A 的节点 3、5(工作 ringA#3/#5,经 B 再次导入)没有被再次续做;B 的两个历史 resume 节点不占 C 的 2 个名额;节点 18 续做 B#6:提交里有上一段的 helper.py,轨迹路径以 nodes/6/carryover/trajectory/engineer.jsonl 开头,打分 48.48、审查 pass;节点 19(execution)再次被切 → carried_over;package / verify ok(终选给基线,lineage_segments 为空:胜者无祖先链)

未在端到端里直接看到的:晋级 / 护栏执行的 weights: replay(fake 程序的复跑目录被清理,seed_entry 原先不记模式——已补 seed_scores.<seed>.weights_modes,下次 run 可见;回放路径由 WeightsTest 在真实 run_program 上覆盖);冷启动重现检查(三环的终选都只剩基线,没有入围训练型候选)。

9. 文件清单

新增:agent/search/health.py、agent/search/carryover.py、agent/search/tests/test_g45_carryover_limits.py、agent/configs/dev/search_t1_g45_carry_fake{,_ring2,_ring3}.yaml、本报告。

修改:agent/search/{controller,execute,runenv,archive,budget,continuation,dataview,views,evalspec,final,viewcheck,roles,tune,fake_llm,scoring_def}.py、agent/search/CONTRACT.md、agent/search/tests/{test_run_correctness,test_x3_guard}.py、agent/prompts/{search_spec,ideas_T1__val}.md、agent/configs/experiments/chain/{t1_D_12h,t2_embryo_interp_12h,t2_heart_extrap_12h}.yaml、agent/notify/{reports,cli,__init__}.py、agent/notify/README.md、agent/notify/systemd/vec-notify-health.{timer,service}、agent/dashboard/glossary.py、agent/report/collect.py、scripts/prepare_submission.py、notes/architecture/10_harness_era_architecture.md(§4.6 跨环续做、§4.8 候选执行上限 / 训练一次多 seed 复用 / 终选预留 / 链级 seed / 搜索可比性指纹、§9 表与 §9.1–9.3)、notes/architecture/18_carryover_and_seed_policy.md(定稿)、notes/plan/tasks/G39-G43_plan_2026-10-03.md(§7.2 G45 表 45.6、45.7)。

10. 遗留

  1. 健康监督只观测(enforce: false):上线后看 would_hang 的误判率再决定是否打开;打开会改变评测规格(不改 search_hash)。
  2. limits 默认改 360 min 与 threads 8 改变完整评测规格;接续旧 run 时由 search_hash 保持可比(旧锁从磁盘视图重算),但旧 run 目录必须还在。
  3. Codex 实现审阅 5 / 7 / 8 / 11 后置(见 §6)。
  4. 续做时父节点审查 fail 直接拒绝(resume_refused),没有做“先自动复审继承物”。
  5. --seeds 批量接口、每节点提前三 seed、续同一 opencode 会话按审阅不做。
  6. G39-G43 计划 §7.2 外另有一处(E03 停止原因那行)也用了编号 “45.6” 指 stop_reason,与本次新增的 45.6(跨环续做)编号冲突,未改动那一行,需要人定编号。
  7. Engineer 会话内的试跑由会话自己的停顿探针管(已加 GPU 计算为活动);vec-score 本身不运行程序,没有单独的执行监督。
  8. fake 三环与旧 ring 的 run 目录留在 Spark ~/vec/scratch/g45_runs/(4 个 run,含第一次因 event(kind=…) 参数冲突在 ring 1 收尾报错的 20261003-165615-…,该 bug 已修),可删。