56 KiB
Web/WASM 移植效率手册
本文总结一种适用于大型原生项目移植到 Web/WASM 的高吞吐执行方法。它不依赖 FreeCAD、Qt 或某个具体 CI 平台;项目只需要提供任务账本、批次清单、上下文 manifest、增量 gate 和周期日志。
结论先行
移植效率通常不是由编译器并行度决定,而是由每个回合在真正实现前后重复做了多少管理工作决定。最有效的边界是:
- 外层调度器拥有任务领取、批次规划、上下文轮换和账本持久化。
- 单个代理回合只处理一个
(module, tier, family)批次。 - 批次、上下文和失败账本都采用幂等快路径;没有状态变化就不重建、不重哈希、不重放。
- 实现阶段只做当前批次的增量 gate;全量验证集中到明确的收口阶段。
- 真实编译、运行时 fixture 和 gate 是不可消除的工作,应该单独计时,不能把它们误判为调度低效。
如何定位慢点
每个周期日志至少记录:startedAt、completedAt、timedOut、命令开始/结束数、失败数、批次 ID 和准备耗时。用下列指标拆分问题:
新版 scheduler 还会在每个 command event 上记录 schedulerPhase、schedulerStartedAt、schedulerDurationSeconds,并在实时 status 中提供 lastStepPhase、lastStepDurationSeconds 和累计 phaseTiming。这使得当前慢步骤可以在回合运行中直接识别,而不必等待整回合结束。
| 指标 | 计算 | 目标 |
|---|---|---|
| 准备耗时 | prepare_end - prepare_start |
通常小于 1 秒 |
| 有效回合率 | effective_turns / completed_cycles |
大于 90% |
| 命令成功率 | (completed - failed) / completed |
大于 98% |
| 超时率 | timeouts / completed_cycles |
小于 5% |
| 批次耗时 | 周期结束时间减开始时间 | 由功能复杂度决定 |
| 管理开销占比 | (prepare + rotate + ledger) / batch_duration |
小于 5% |
如果准备耗时已经低于 1 秒而批次仍耗时数分钟,继续优化读取没有收益,应该优化批次规模、fixture 复用和 gate 的进程启动次数。
Timing v5:完整执行链与基线
一次实现周期按下面的所有权和时间边界统计,任何耗时只能进入一个桶:
| 阶段 | 所有者 | v5 计时桶 |
|---|---|---|
| 续接、批次规划、context 预检 | 外层 scheduler | preparation/* |
| 首次选择实现入口 | Codex | agent/initial-decision |
| 读取源码和现有 adapter | shell | source/read |
| 读取后设计 | Codex | agent/post-read |
| 生成并应用补丁 | Codex | agent/editing、agent/post-edit |
| 首错定位和修复 | Codex | agent/failure-recovery |
| link closure 和增量链接 | shell | build/preflight、build |
| focused gate | shell | focused-gate |
| gate 后证据/checkpoint | Codex | agent/post-gate、agent/synthesis |
| 子进程退出和 JSONL 排空 | scheduler | agent/finalization、scheduler/stream-drain |
| 无事件触发恢复 | scheduler | agent/idle-stop |
2026-09-05 在 timing-v4 最近 20 轮基线中:总 wall time 9978.459s,平均
498.923s/轮;命令执行 686.188s,外层准备 13.007s,粗粒度代理间隙
8743.321s,未归因 535.943s。代理间隙占 wall time 87.6%,准备仅占
0.13%。16 个唯一批次产生 4 个恢复轮,恢复轮累计 2075.315s,占 wall
time 20.8%。因此继续优化 planner 或 -j 不是主方向,主方向必须是减少回合内
重新理解、硬编码 gate、patch 冲突和同一批次恢复轮。
timing-v5 的对应措施:
- active prompt 从重复规则全文改为已预检工作包,直接注入 record 的 id/name/source
和现有 probe/facade/worker/gate 候选;子回合不再读取 59KB
nextTask.md、45KB 完整 task state 或无关前置卡。当前 24 条批次 prompt 约4.9KB。 - 同一 mutation epoch 内对规范化命令计数,记录重复 read/gate/build 的次数和耗时; 同一命令达到 3 次会收口回合,而不是继续空转。
- 两次
apply_patch校验冲突即收口,下一轮按当前文件重建小 patch;不允许连续数分钟 重放已失效的补丁上下文。 - preflight、build、focused gate 和 context validation 必须使用独立命令。复合命令仍会
标记为
build+preflight、build+focused-gate或focused-gate+context,不再把链接 时间误报为 preflight。 - JSONL stdout/stderr 排空由每线程 5 秒降为每线程 0.5 秒,固定回合尾开销目标从约
10 秒降至约 1 秒。实测发现主要的
10.907s实际位于turn.completed之后的 Codex tool-host 退出等待,因此 v5 在终态事件落盘后保留 0.5 秒 drain,再只终止该 已完成子进程并将退出归一为成功;不等待 tool-host 的固定延迟。 tools/wasm-probe-runtime.mjs统一 build/bin 归一、pthread worker CJS staging 和locateFile;worker 源文件变更时刷新 staging,不再由各 gate 复制易失效的 loader。- 批次工作包从 CMake probe 源和现有 artifact 推导准确 target/build tree,并注入完整
preflight 命令,避免先调用缺参数的 checker、再猜
configure/configure/bin。 - 每轮把 input/cached-input/output/reasoning-output token 写入 timing v2;源码读取先定位, 单文件只读命中前后 80 行、每条读取命令输出约 240 行,防止秒级 shell 命令产生数十万 token 的后续推理负担。
- 提示边界之外再加执行护栏:子回合 PATH 前置
tools/scheduler-bin。rgwrapper 拒绝 仓库根和大型目录参数,gitwrapper 拒绝未带-- <current-files>的 diff/status;在 大输出进入模型前以退出 64 阻断。构建和其它 git 子命令仍转发到真实工具。
首个完整 v5 样本 cycle 2586 用 184.053s 完成 24 条 Draft 批次,较 v4 最近 20 轮
均值 498.923s 低 63.1%,但仍有明确可压缩项:命令只占 3.254s;post-read
53.902s、一般 decision 36.345s、两次可避免首错的 failure-recovery 25.573s、
synthesis 16.407s、post-checkpoint 13.026s、tool-host finalization 10.907s。
因此下一阶段目标不是继续优化 3 秒 gate,而是用读取上限减少 post-read,用精确
target/build hint 消除两次首错,并用 terminal-event shutdown 消除 finalization;无构建
的同类 24 条批次目标应降到约 90-120 秒。单样本只用于定位,至少 5 轮后才用于确认趋势。
Post-read 根因复盘与结构修复
cycle 2590-2599 的逐事件复盘得到可复现数据:43 条 source/read 命令产生
1,354,448 字节输出,紧随其后的代理处理耗时 543.476s。其中同一个
DraftRuntimeProbe.cpp 被读取 29 次、同一个 module gate 16 次、同一个 Facade 15
次;cycle 2595 的单条搜索输出达到 990,824 字节。这说明慢点不是读取系统调用,
而是每批创建新会话后重新解析相同源码,以及每次工具返回后模型重新处理不断增长的
完整 turn context。
彻底修复分两层:
prepare_module_implementation_packet.py在外层预检后运行。它只打开当前 manifest 指向的官方源文件,用 Python AST 或 source line 提取每条 API 最多 640 字节的函数片段, 总预算 12KB;同时保存 support file hash、native name 命中、dispatcher 入口和 manifest-driven gate 检查。该步骤是确定性本地处理,不产生模型往返。- scheduler 按模块缓存
thread.started的 session id。相邻批次 module 相同则调用codex exec resume,直接复用已经理解的 probe/Facade/Worker/gate;硬上限 8 轮, cache 根据上一轮 input 增量预测下一轮 token,预测达到 800000、模块切换或失败时立即轮换。token 上限优先于轮数, 这样既消除跨批重复读取, 又限制长期上下文增长。
批次工作包会直接嵌入 implementation packet。恢复轮明确禁止重读 support files,只允许 针对新 record excerpts 和实际需要修改的命中行工作。与此同时,纯函数和 geometry-value 批次可从 24 自适应扩大到 32,进一步减少新会话数量;对象契约和重型 OCCT 批次仍保持 原内存上限。
真实运行验证:fresh cycle 2605 只执行 4 条命令和 1 次 source read,input token 为
153075、post-read 为 15.786s;此前批次常见 5-8 次读取、60-100 万 input token 和
50-130 秒 post-read。随后 cycle 2606 使用同一 Draft session 恢复,source read 与
post-read 均为 0,证明跨批重复源码理解已经消除。
恢复轮仍有 preflight/gate/checkpoint 之间的代理决策等待,因此进一步将这些确定性步骤移到
run_module_batch_pipeline.py。implementation packet 给出 target、build tree、artifact 和
generic module gate;Codex 只做源码判断/编辑并立即返回,scheduler 根据 native source 与
artifact mtime 决定是否增量构建,然后自行执行 preflight、sync、focused gate 和 incremental
context。失败流水线的首个 step 会注入同一 session 的下一轮;成功步骤不再消耗模型调用。
外层 pipeline 实测分成两类:无源码修改的恢复批次 cycle 2612/2613 中,代理分别只用
23.026s/20.025s、0 command、0 source read,随后 pipeline 分别约 2.8s 完成
preflight+gate+incremental context,完整周期约 24-27 秒。需要源码编辑的 cycle 2620 中,
代理只执行 3 个定点命令和 1 次 edit 后返回,pipeline 用 47.794s 增量构建、6.628s
同步、2.716s gate 和不足 0.1s 的 preflight/context 收口。真实编译成本保留,但构建
前后不再有模型决策等待。
最终结构样本 cycle 2622 更清楚:恢复会话依据 implementation packet 直接完成 1 次编辑,
sourceReads=0、post-read=0、代理 turn 26.026s;外层 pipeline 用 50.236s 构建、
7.247s 同步、2.754s gate 和各 0.04s 左右的 preflight/context,完整有效周期
87.153s。与此前同类需要构建的 216-295 秒周期相比缩短约 60%-70%,而真实 60 秒
构建/同步/gate 成本没有被隐藏。此时“代理读取后的处理”已经从主耗时项中消失,剩余主项
是可审计的真实构建成本。
构建复用采用“成功后信任”:首次没有 cache entry 时即使 artifact mtime 较新也必须构建; 只有一次真实 build+gate 成功后,才保存 native probe/CMake/build.ninja 输入 SHA-256 与 runtime/WASM SHA-256。后续只有输入摘要和 artifact 摘要同时匹配、且直接 link archive 没有更新时才跳过,mtime 仅用于诊断和依赖失效。一次恢复中 曾发现首次 mtime seed 跳过后 gate 失败,策略已收紧并通过真实重建恢复,失败记录保留。
Gate 只缓存 TypeScript 编译产物,不缓存 native 结果。cache key 包含直接 TS 文件、Node
版本和 TypeScript package;tsc 完整成功后才写 .complete marker。每批仍启动真实 Worker、
调用真实 WASM dispatcher 并生成新 evidence。Draft 首次样本为 compile 0.539s、Worker
ready 0.745s、dispatch 0.699s;下一批相同 TS 输入只跳过 compile,其他检查不变。
批次自适应不再只看最后一个 Codex turn,而是读取最近 3 个相同 (module,tier,family) 的
完整 effectiveCycleSeconds、记录数和失败状态。任一失败立即缩半;平均超过 420 秒减 4;
平均低于 90 秒且吞吐达到 0.25 record/s 增 8;低于 240 秒增 4。最终仍受 pure/geometry
32、普通 24、object 16、Part generic 12 的内存上限约束。
所有效率修改由 tools/test_scheduler_efficiency.py 固化。当前 10 项回归覆盖:Draft 标准
gate、Material 非标准 _object_module_batch_gate、拒绝不兼容 Mesh gate、mtime/hash 内容
变化、直接链接 archive 枚举、快/慢/失败三种批次调整、session token 预测,以及 TS compile
cache 命中时 Worker/native dispatch/resources release 仍真实执行。测试不运行全量功能矩阵,
但任何调度优化加载前必须先通过这组行为不变性门禁。
观察命令:
python3 tools/show_freecad_web_scheduler.py
python3 tools/analyze_scheduler_timings.py --cycles 20
状态页同时展示 batchFlow、activity、redundantWork、逐阶段真实事件 count 和
slowestAgentGaps。至少积累 5 个 timing-v5 完整周期后再与上述基线比较,避免把
单个复杂批次的波动误认为整体改进。
正确理解进度
大型项目通常同时存在两套进度,必须分开显示:
taskCards是工作流卡片的终态计数。聚合出口卡(例如最终 Y/gate 卡)在所有记录收口前保持in_progress,所以批次推进时这个数字可能不变。implementationBatches和 canonical API manifest 才反映批次移植进度。每个 focused gate 应更新记录 disposition、evidence 和已完成批次集合。
状态工具至少应同时展示 taskCards、implementationBatches、apiExposed、apiRemaining 和当前批次 ID。不要通过增加 task card 分子、提前关闭聚合卡或修改任务状态来制造进度。
必须实现的快路径
0. Feature 构建触发门
在 feature-first 模式中,文件变化本身不能触发 WASM 链接。先生成 module/capability registry,并将当前 batch selected IDs 与 implementationStatus=routed 求交;缺少任一记录时保存 implementation_incomplete 和 missingIds,直接进入下一实现回合。只有整批静态接线完整后执行一次目标构建。
桌面命令应拆入 FreeCADWeb<Module>DesktopCommandProbe,由模块 Worker 按需加载。结果从 {cmd,args} 和真实 BREP/Document 数据生成;禁止硬编码 fixture/result。构建 hash 只包含 build.ninja 中实际进入当前 target 的源文件,不包含同模块其它 probe。
2026-09-06 Part 基准:原 FreeCADWebPartBoxProbe 为约 70.5MB、50 个直接 archive,单次成功构建约 52-58 秒;拆出的 FreeCADWebPartDesktopCommandProbe 为约 17MB、26 个直接 archive,首次含 CMake 重生成 22.58 秒,稳定增量编译/链接 9.85 秒,无变更检查 0.06 秒。静态接线不完整时 pipeline 仅刷新两个 registry,0.136 秒返回且不构建。该数字是本机快照,不作为硬编码阈值;后续模块以相同相对指标评估。
1. 批次幂等
批次 manifest 必须包含 batchId 和选中记录 ID。checkpoint 保存 latestBatch.completed。当当前 batchId 已完成时,直接调用 planner 选择下一批;不得保留旧 manifest 再次运行相同 gate。
2. 上下文指纹
context manifest 的每个输入保存 bytes、mtime_ns 和 SHA-256。常规路径只比较大小和修改时间;只有变化或旧 manifest 缺少元数据时才读取文件并计算哈希。上下文未变化时输出 CONTEXT_PACKET_REUSED。
3. 失败账本快照
回合启动时读取一次失败账本,命令事件只更新内存对象;回合结束集中写入一次。重启恢复只导入尚未登记且包含 cycle.completed 的日志,并保存 hydratedLogs 索引。实现阶段默认冻结历史失败,只有直接阻断当前批次的错误才允许定点重放。
4. 卡片按需读取
不要每轮读取整个任务树。先通过文件名或紧凑索引定位活动卡、planned 卡和直接前置,再按需读取正文。全量卡片解析只允许出现在明确的最终审计命令中。
5. 外层拥有轮换
代理回合结束只写结果、证据和 phase=CONTINUE,然后返回。调度器下一回合统一执行 rotate、sync、prepare-next。代理回合禁止再次调用续接、planner、rotate 或全量 validator。
批次和回合自适应
默认批次大小只是起点。对每个 (module, tier, family) 维护最近 3 次周期的耗时和命令数:
if timeout or duration > 900s or commands > command_budget:
next_size = max(min_size, floor(current_size / 2))
elif duration < 300s and commands < 24 and no focused gate failure:
next_size = min(max_size, current_size + 4)
else:
next_size = current_size
当前实现中,prepare_full_parity_module_batch.py 在未传入 --size 时读取 scheduler 的上一回合状态并应用该规则;显式 --size 4/8/16/24/32 仍可用于恢复卡或受控演练。批次 manifest 会记录 targetSizeReason,方便审计 planner 为什么缩小或放大。
对象契约、序列化、复杂几何和依赖外部资源的批次应使用较小上限;纯函数和只读常量可以使用较大批次。单个批次仍必须共享一个 native adapter、一个 dispatcher、一个 Facade/Worker 分支和一个 evidence JSON。
回合命令预算
在提示词和 scheduler 中同时声明硬预算。当前高吞吐实现回合默认不超过 20 个工具命令,source/read 不超过 8 个;项目复杂度不同可通过配置调整。达到读取预算后必须进入实现;禁止重复读取同一文件、重复运行已通过 gate 或加载完整日志。超出预算时先落盘 checkpoint,让下一回合继续。
阶段计时命令:
python3 tools/analyze_scheduler_timings.py --cycles 20
实时状态中的 currentTurnPhaseTiming 和日志事件中的 schedulerPhase/schedulerDurationSeconds 可用于确认构建、资源 staging、focused gate、源码读取和 checkpoint 各自占比。
调度器外层准备链还会记录 preparationTiming.continue、compact、continueRetry 和 checkpointRepair,用于区分正常续接与异常恢复的成本。
验证分层
实现回合只运行:
- 当前批次源码/依赖接线。
- 最小 native fixture 和统一 dispatcher schema。
- 当前批次的 Facade/Worker focused gate。
validate_incremental_context.py。
完整 API、对象/格式矩阵、Chrome、性能、安全和发布 gate 必须在全部实现任务完成后集中执行,并由用户明确触发。这样可以避免每条 API 重复启动浏览器、重复编译或重复扫描全量清单。
本项目实测基线
本项目最近周期的可观测数据:
- 准备耗时约
0.14–0.37s,说明上下文/账本快路径已经生效。 - 短收尾周期会拉低总体中位数;最近主要实现批次(924–926)分别约为
761s、745s、606s,另有 1 个复杂周期达到1800s超时。 - 主要剩余耗时来自真实 FreeCAD/WASM fixture 编译和 focused gate,不是任务卡读取。
- 最近完成的 BIM 批次曾达到
24/24 exposed,统一 dispatcher、Facade/Worker、取消、reset 和资源释放均通过。
切换 timing-v2 后,1556 回合的阶段实测为 build≈381s、source/read≈63.9s、focused-gate≈1s;9 次命令失败主要来自系统冻结 Emscripten cache 和异常 fixture 重试。后续 timing-v3 使用项目内可写配置并修正 artifact 同步分类,避免把同步时间误计入源码读取。
因此下一阶段的收益排序是:批次自适应 > fixture/构建复用 > 减少 gate 进程启动 > 代理命令预算 > 继续微调上下文读取。准备快路径已实测低于 1 秒,继续压缩它的边际收益很小。不要用关闭验证或伪造结果换取吞吐。
步骤级计时优化
整回合耗时只能说明“慢”,不能说明慢在何处。run_freecad_web_scheduler.py 的 timing-v3 在每条 command event 上记录 schedulerPhase、开始时间和持续秒数,并生成受限的 docs/current/freecad-web-scheduler-timing.json。摘要只保留最近 50 个周期;show_freecad_web_scheduler.py 默认只分析最近 20 个周期,并输出:
prepare/checkpoint、context、batch-planning、source/read、build、artifact-manifest、resource-staging、focused-gate、checkpoint/rotation各阶段总耗时和占比。- 最近窗口最慢的命令,带 cycle、phase、duration 和截断后的 command 文本。
timedCommands、unmeasuredCommands、unaccountedSeconds和measuredCycles,区分 shell 命令时间与代理推理/等待时间。- 当前回合的
currentTurnElapsedSeconds及已测阶段累计值,长回合期间即可判断是否停在构建或代理处理。
摘要文件避免每次状态查看扫描数百个历史 JSONL;状态文件写入也按 250ms 节流,命令开始/结束、失败、阶段和回合收尾仍立即落盘。实时完整事件仍逐行写入 cycle log,不牺牲恢复证据。
优化判定顺序固定为:先看 unaccountedSeconds 是否异常,再看阶段占比,最后看最慢命令。source/read 高时收紧 manifest 读取;build 高时复用 RAM ccache、固定 -j8、使用可写 Emscripten cache 并避免失败重试;artifact-manifest 高时只同步变化的 runtime/WASM/data;focused-gate 高时扩大同 family 批次;prepare/checkpoint 高时修复账本/上下文重复读写。没有计时字段的旧周期只能作为历史吞吐参考,不能用于阶段优化结论。
构建阶段必须先集中源码修改,再执行一次正常构建;只有明确首错才允许一次重试。成功链接后不得因为重复读取、重复 gate 或相同源码 hash 再次重链;构建/重试完成后立即落盘结果并返回。当前 1556 回合构建占约 87.7%,因此构建去重的收益高于继续压缩不足 1 秒的准备阶段。
Artifact 子步骤缓存
web/tools/sync-assets.mjs 处理的 WASM/runtime/data 文件总量很大,单纯把
sync-assets 归为一个阶段会掩盖真正的复制和哈希成本。同步器现在把源文件
size+mtime+inode 签名与 SHA-256 保存在
docs/current/web-assets-sync-cache.json,并将最近一次子步骤耗时写入
docs/current/web-assets-sync-timing.json。只有签名变化、目标缺失或目标尺寸
不一致时才复制和重新哈希。实测首次建立缓存约 42.90s,相同输入的后续同步
约 0.04s(copied=0、hashComputed=0),manifest 仍每次写出且保留完整
artifact 哈希。状态命令会显示 copy、hash、manifest、命中数和缺失数,
因此后续优化应先确认是实际 artifact 变化,而不是重复同步造成的假耗时。
构建批量入口 tools/build_web_module_artifacts.py 同时为每个 target 记录
durationSeconds;判断构建瓶颈时使用单 target 最大值和总和,不把已完成的
no-op target 与真正的链接步骤混在一起。
服务端账户并发拒绝与普通断网必须分开统计。调度器只对明确的
Concurrency limit exceeded for account 做三次/180 秒的提前收口和指数退避;
普通网络断开不改变任务状态,也不作为本项目的性能优化目标。预算超限则在
当前命令结束后收口,保留 checkpoint,避免下一回合重复已完成步骤。
周期摘要会将 command event 之间、且没有活动 shell 命令的间隔归入
agent/reasoning。命令开始到结束的完整区间已经由 command duration 统计,
不会再重复归入事件等待;因此 unaccountedSeconds 只保留尚未观测到的尾部
时间。真正的源码读取、构建、artifact 和 focused gate 仍按各自命令单独统计。
优化时先确认 agent/reasoning 是否占主导,再决定是否缩小批次或减少工具调用。
若该阶段连续超过 FREECAD_AGENT_IDLE_LIMIT(默认 180 秒)且没有活动 shell 命令,
scheduler 会保存 checkpoint 并结束该回合,让下一回合继续;这避免单次代理空转
耗尽 1800 秒时间片,同时不打断正在运行的编译或 gate。
默认 scheduler 使用 model_reasoning_effort=low,因为最近窗口实测代理推理
已经与 WASM 构建并列为最大成本。复杂首错需要更深分析时才设置
FREECAD_SCHEDULER_REASONING_EFFORT=medium|high|xhigh;状态中的 reasoningEffort
用于确认当前策略,避免在没有证据时长期使用高推理档位。
对于已有 pass_with_boundaries 证据的收尾批次,scheduler 自动降到
model_reasoning_effort=low,避免重新分析已经完成的 fixture;只有未收口批次
才继续使用默认 medium。
批次已完成且 checkpoint/evidence 一致时,外层 scheduler 使用
scheduler/fast-path:只运行一次 continue_web_task_cycle.py 重新规划下一批,
不启动 Codex。该路径仍以 canonical evidence 和真实 gate 结果为前提;若续接
失败则回退到普通实现回合,不改变任务结论。
最新效率评估
当前窗口的判断口径分为“快速调度周期”和“真实实现回合”两类。最近连续完成的
CAM/Measure 收尾批次已经进入 scheduler/fast-path,单周期约 0.97s;它们不应
再启动完整 Codex 回合。最近仍需代理处理的实现回合中,代理推理约占 85.5%,
WASM 构建约占 11.3%,focused gate、准备和 artifact 合计低于 3%。因此下一
阶段的收益排序固定为:
- 继续扩大 evidence/checkpoint fast-path 覆盖,避免收尾批次进入代理。
- 构建前做 pthread/Qt/OCCT/外部库闭包预检,首错只允许一次定点修复,避免
100s+失败重链。 - 对仍需实现的批次使用低推理档位和最多 8 条检查命令,必要时才升级到
medium/high。 - 只在源 hash 变化时构建和同步;
fast-devartifact 不参与发布性能结论。 - 全部功能实现完成后,再统一切换
FREECAD_WEB_FAST_LINK=OFF做 release link 和全量性能验证。
tools/check_web_link_closure.py 现在作为 build/preflight 阶段计时。它只读取
目标 build.ninja 的 link rule,不扫描源码树;CAM 这类单线程 target 不会因库路径
中的 pthread 字样误报,真正启用了 QtConcurrent/Emscripten pthread 的 target
才要求 -pthread。对 OCCT -lTK* 依赖还会检查选定安装前缀中的归档是否存在,
因此类似 TKMeshVS 的悬空依赖会在链接前直接报告,而不是消耗一次完整 wasm-ld。
预检同时缓存 build.ninja 的静态 link rule,并按 inode+mtime+size 自动失效;
runtime/wasm 存在性和 OCCT 归档每次实时检查,避免旧缓存掩盖依赖变化。
若 focused gate 已生成 blocked_native_probe evidence(例如 Node pthread
初始化仍挂起),planner 会按 evidence 中的 apiId 将这些记录移出正常实现队列,
继续其它可执行模块;阻断状态和恢复证据保持不变,后续恢复阶段再定点重试。这样
同一外部/运行时阻断不会占满每个连续回合。
门禁复用还必须校验当前批次记录集合和 WASM/runtime artifact 的 SHA-256。只有
输入与产物均未变化时才复用既有 evidence;产物重建后自动重新探测。复用
blocked_native_probe 采用明确的 deferred 非零退出码,保持阻断语义且避免再次
等待完整运行时超时。
当 planner 返回 no_ready_batch 时,scheduler 现在只做一次轻量 planner 重试,
记录 scheduler/no-ready 后等待状态变化,不再启动没有实际批次的代理回合。这样
不会把空队列误记成实现失败,也避免代理重复读取同一份空 manifest。
fast-path 现在还检查 selected record 的逐条状态。顶层 pass_with_boundaries 中若有
not_implemented/planned,不会继续 fast-path,而是让 planner 依据 pending evidence
换到下一组记录,避免批次 ID 不变的快速循环。
planner 的 runtime evidence 索引写入 docs/current/module-batch-evidence-index.json,
使用文件 size+mtime+inode 做增量失效。索引只收录记录自身为 exposed、restricted、
nonportable、blocked 或 passed 的条目;父 evidence 的 pass_with_boundaries 不会
掩盖其中的 method_evidence_pending 或 source timeout,因此未实现 API 仍能进入
后续功能族批次。
同一 planner 进程同时复用 terminal/pending ID 集合,避免为一次选择重复扫描 runtime
JSON;只有显式开启 FREECAD_RETRY_EVIDENCED=1 才会重新调度已有 pending evidence。
runtime evidence 的记录既可能在顶层 records,也可能在 result.records;索引 v4
统一兼容两种结构,并把 passed_boundary 作为真实终态,避免边界批次重复进入代理。
启动代理前新增只读批次预检,验证 active task、module/tier/family、记录下限、
singletonFallback 和 context packet;不满足时先重规划/修复,避免把 manifest 或上下文
失配转化为一次无效 Codex 回合。
状态面板的效率统计也固定在最近 20 个周期(最多可通过
FREECAD_EFFICIENCY_WINDOW=50 扩大),不再按 scheduler lifetime 扫描数百个旧日志;
最近实测查询耗时约 0.3 秒。
build_web_module_artifacts.py 的 Ninja target 发现现在每个 build tree 只解析一次
build.ninja,并且默认按当前模块筛选 target;当前工作区实测发现 48 个配置目标约
0.65 秒,后续只会进入选定模块的构建。新增 --dry-run 可在不构建的情况下显示
当前批次将使用的 target 集合。
对于含多个辅助 probe 的模块,默认进一步选择主 probe(例如 Part 只选
FreeCADWebPartBoxProbe);其它 probe 通过 --target 或 --all 显式构建,避免
无关 target 的缺失依赖把当前批次拖入失败。
针对 Part 的 OCCT 2D/3D wrapper fixture,planner 将 generic-object 批次上限设为
12 条,避免 24 条同时进入 Python/WASM wrapper 时触发 memory access out of bounds;
这是吞吐与稳定性的边界控制,不改变单条 API 的真实 native 结果。
源码快速检索工具兼容 --path 与额外显式路径的混合写法,避免通配路径被 argparse
误判为非法参数;路径仍限制在工作区内,不会放宽检索范围。
同一代理回合中同一 shell 命令连续失败 3 次后,scheduler 会记录
codex_turn_repeated_failure_stopping 并结束本回合,让下一回合从 checkpoint 继续;
这避免了重复的 patch 校验或同一依赖错误长期占用代理时间,原始失败账本仍完整保留。
准备、上下文、源码读取和 gate 已经是亚秒到数秒级,不再作为主要优化目标。
Feature-first 回合的决策耗时治理
最近 20 个 feature-first 周期的分段计时显示,agent/post-read + agent/editing
约占 70% 以上,而 WASM build 约占 5%;shell 源码读取本身只有亚秒级。瓶颈不是磁盘,
而是每个 fresh 回合读完共享结构后再次推导修改位置。对应治理必须同时实施:
- implementation packet 除官方 Gui 源码片段外,直接嵌入 native dispatcher、Facade、 Worker 的有界可编辑片段;指纹未变时不再完整重读 support 文件。
- feature 回合只在相同
batchId内恢复会话,最多 3 轮;首轮 medium,恢复轮 low。 跨批次立即轮换,避免节省冷启动却引入旧假设。 - feature preflight 按自身 schema 校验
feature-workflow、Module:*ID 和 batch hash, 不再每轮进入旧 API manifest 的 repair 分支。 - 桌面命令 route 从 native 注册自动生成,区分
family-batch与direct-command; 跨工作台同名命令绑定 canonical owner。React 不保存逐命令默认参数表。 - 对共享一个 source family/dispatcher 的 feature command,默认 16 条集中实现后链接一次;
FREECAD_FEATURE_BATCH_SIZE可在 1-32 调整。运行时问题保持最多 4 条,避免扩大故障面。 - implementation packet 可用时,feature 回合默认最多 4 条定点读取;修改落盘后不再用
rg/git diff做尾部确认,直接交给确定性生成器和 pipeline。普通非 feature 回合仍保留 8 条上限。 - 相同模块和 source family 连续批次默认两批合并一次最终链接。第一批只在已有 artifact、
manifest 明确还有同族记录且不是 runtime issue 时写持久 pending 状态;第二批链接成功后
同时晋级两批回执。最后一批、首次构建和运行时修复不能延期。
同一模块若多个 desktop family 都不足批次上限,planner 还会将它们合成一个
desktop-command-mixed尾批,避免每个 1-7 条的小组分别支付一次最终链接。 - 对不断增长的单体 adapter,不要只提供首个命中的编辑片段。工作包应同时包含 handler、 registration、family dispatch 和 ABI entry 多个稳定锚点;同一文件单回合集中修改一次。 feature 回合第一次 patch conflict 即轮换,且该线程不写 session cache。
- Qt/Gui command 的 manifest 行号通常落在构造函数。外层准备器应从类名定位
activated(), 把实际Gui::cmdAppObjectArgs、选择和 Document 语义注入工作包,避免代理继续搜索整个 App/Gui 目录。 - ripgrep 的退出 1/空输出表示合法零命中,不是构建或目标别名失败。调度 wrapper 对显式文件 搜索归一为成功并忽略目录参数;启动迁移会自动关闭历史同类假失败。本轮首次归一化关闭了 23 条错误账本记录。
- 读取预算使用软/硬两级:工作包完整时目标 4 条;复杂首轮在尚未修改文件时最多 6 条。 已修改后超过软预算才立即收口,避免第 5 条必要读取触发 fresh 重建并重复消耗整个回合。
- Codex 常以
bash -lc执行命令,登录脚本可能覆盖父进程 PATH。scheduler 必须通过BASH_ENV再次前置 wrapper;wrapper 忽略目录参数、保留显式文件、限制单行 240 列, 防止 minified JSON 一次命中注入整份文件。 Workbench.cpp的MenuItem/ToolBarItem.setCommand()可能只是带子项的 UI 容器,不能 当作 native 命令。生成器通过后续*item << ...或add...(*item)自动识别;本轮识别 43 个容器,由 React navigation route 承载,避免对应代理回合和 WASM 链接。- implementation packet 已提供官方语义和编辑锚点后,feature 首轮与恢复轮都使用 low reasoning。复杂首错仍由外层 pipeline 返回结构化首错,不靠每个首轮提高全局推理档位。
- 支持整族 desktop ID 的 native 分支应使用可机读前缀判定,例如
if (apiId.startsWith("TechDraw:desktop:"))。生成器据此路由整族命令;transport alias 不进入 capability 分母。本轮因此消除了 6 个重复代理回合,并将 routed 从 428 提升到 547。 - 延期链接除了 pending 文件,还必须写
implementation_pending_build回执,否则 continuation 会保留原 batch 并在下一轮提前构建。第二批成功后将 pending 与当前回执一并晋级 ready。 - 失败构建输出保留头部和尾部。CMake 配置文本通常很长,只保留前 4 KiB 会丢失真正的
file:line: error;恢复代理不得在没有编译器诊断时猜头文件或 API。 - probe 发现使用规范化、大小写无关的模块前缀,避免
BIM/BimRuntimeProbe.cpp之类 命名差异产生空工作包。若文件没有 DesktopCommand 命名锚点,至少保留 family handler、 unified ABI 或有界文件尾,不能以 0 字节 editable packet 启动代理。 - Python workbench 的
FreeCADGui.addCommand(name, GroupClass())若对应类定义GetCommands(), 与 C++ menu/toolbar container 语义相同。用 AST 关联注册和类定义,生成 React navigation route; 全 UI-container 批次直接写回执且buildRequired=false。BIM 本轮因此一次关闭 12 条错误 native 任务,从 19 个无修改恢复回合转入下一模块。 fast_source_search应兼容代理常见的-n/--paths/--glob写法和直接执行;scheduler-bin 提供python到python3的兼容入口。输出必须在子进程返回后按全局--max-count截断, 因为 ripgrep 原生限制是逐文件计数。兼容迁移关闭了 36 条历史工具调用假失败。- 读取预算不能靠杀死尚未完成的代理 turn 实施。即使命令输出已写入 scheduler JSONL,强制
结束后恢复仍可能出现
Custom tool call output is missing。正确做法是在当前命令完成后写入 本轮专用 budget marker,让受控rg/fast_source_search返回“使用已有结果并编辑”,由同一 turn 自然完成。只有收到真实turn.completed才保存 thread ID;补丁冲突、流损坏和跨批次一律不续用。 - 同批续用轮不能再次注入完整工作包。首轮保留官方源码和 editable excerpts,续用轮只发送
batch/module/family/record IDs 与“基于上一轮输出立即编辑”的短指令。本轮实测提示由 28,072 B
降至 1,336 B(约减少 95.2%);收益必须继续用
sessionMode=resume、promptBytes、agent/initial-decision和agent/post-read的实际值确认。 - 状态页判断存活不能只看
/proc/<pid>是否存在,PID 可能已经失效或被复用。必须校验/proc/<pid>/cmdline含预期 scheduler/watchdog 脚本;进程不匹配且状态仍是 running 时明确显示observedState: stale,避免把停止的调度器误当成低效率运行。 apiId.startsWith("<Module>:desktop:")只能表示覆盖范围,不能证明每个 command 的源码语义 已实现。整族 route 必须带FC_WEB_AUDITED_DESKTOP_FAMILY("<Module>")审计标记;未审计的通用 fixture 不进入 routed 统计。这样可防止一个三角网格 fixture 冒充 Difference、Export、FillHole、 Decimating 等不同命令,并避免虚假进度触发无效链接。- implementation packet 要同时解析
<slug>-python.worker.ts与实际常见的<slug>.worker.ts。 Worker 已有 family dispatch 时只注入指纹和检查结果,不让代理为了确认文件名再执行rg --files、ls和整文件sed。首轮有完整 packet 时硬读取边界设为第 5 次;达到边界后协作式熔断, 不能再允许第 6/7 次重复确认产生累计 token 成本。 - feature 批次不能 all-or-nothing。用
FC_WEB_IMPLEMENTED_DESKTOP_COMMAND("name")显式标记 已执行官方源码语义的单个命令;pipeline 在ready > 0 && missing > 0时构建实际 runtime target、 写implementation_partial回执并只重排 missing IDs。部分批次不延期链接,防止未构建代码先被 规划器跳过。desktop build target 排序为DesktopCommandProbe > RuntimeProbe > 其它 family probe, 避免修改 RuntimeProbe 却错误链接另一个 GeometryFamilyProbe。 - marker 不能单独作为实现证据。生成器必须找到同名的命令专属
else if分支,并确认该分支 明确返回exposed;只在通用 fallback、交互摘要或nonportable/not_implemented分支中的 marker 一律忽略。planner 的 ready IDs 还必须取“历史实现回执”和“当前 capability 仍 routed”的交集, 这样错误标记被撤销后,旧回执不会永久跳过对应功能。 - 批量大小必须按“有效回执”而不是单轮耗时调整。默认 feature 批量保持 16;同一 manifest
超过 180 秒、最新 pipeline 仍为
implementation_incomplete、ready=0且批量大于 4 时, continuation 不再保留旧批次,planner 将同族记录缩成 4 条。产生部分/完整回执后下一批恢复默认 16,避免永久承担小批次开销。本轮实测把 19 个恢复周期仍为ready=0/16的 Mesh 批次重规划为ready=0/4,官方源码 excerpt 从约 12 KiB 降到 1.8 KiB,首轮 prompt 从约 36.8 KiB 降到 20.9 KiB。状态页必须显示batchSizing的 default、selected、reason 和 stallSeconds。 - 静态验收失败必须反馈具体原因,不能只返回 missing IDs。capability generator 输出
diagnostics.rejectedDesktopMarkers,pipeline 将当前批次命中的 marker、ID 和拒绝原因写入staticReadiness.rejectedMarkers,fresh/resume prompt 直接携带它。首分支if与后续else if都要被识别;本轮修复后 4 条缩批从ready=0/4变为ready=4/4,前三条完成 43.1 秒链接、 0.54 秒同步,第四条进入合并链接等待。状态页的 receipt records 只统计历史回执与当前 routed capability 的交集,同时保留 historical 数字供审计,避免错误旧回执冒充当前进度。 - 单体 runtime probe 超过默认 96 KiB 且仍在承载 desktop feature 批次时,implementation packet
输出
desktopAdapterRecommended=true和oversizedNativeSources。后续新增命令实现应迁到<Module>DesktopCommands.cpp/.h,runtime 仅保留共享调用,目标仍链接同一个模块 WASM。 这减少每批重编巨型翻译单元的成本;最终链接成本仍存在,继续由部分回执和两批合并链接控制。 - probe 文件名不能直接当模块名。对
FemGenericFamilyProbe、MeshPartRuntimeProbe、PartDesignRuntimeProbe等名称,应从 canonical workbench 模块集合做规范化最长前缀匹配。 命令分支验收同时兼容FC_WEB_DESKTOP_COMMAND("name")和name == QStringLiteral("name"),以及普通字符串/QStringLiteral两种 status 写法;同一命令有 对象类型映射和结果状态等多个分支时必须检查全部命中。本轮因此将 Fem 4 条从ready=0/4修正为ready=4/4,并避免继续消耗代理回合修已有功能。 - 拆出
<Module>DesktopCommands.cpp后,工作包与 route generator 必须真正以该文件为主: feature packet 省略其它大型 C++ probe 的 editable excerpts,但保留指纹;feature registry 在*DesktopCommands.cpp中同时扫描旧双参数注册宏和FC_WEB_IMPLEMENTED_DESKTOP_COMMAND。 命令专属分支兼容command/name变量,并遍历同名全部分支。Fem 实测 editable packet 从 9,176 B 降至 3,080 B(约 66%),增量 build 从 48.2 秒降至 31.3 秒(约 35%);4 条 post-filter 从ready=0/4变为4/4,并消除了对 142 KiB GenericFamilyProbe 的重复编辑读取。 - 同族批次默认 16,但
desktop-command-mixed涉及多个异构源码 family 时默认最多 8 条。 异构批次的官方语义和参数结构更分散,CAM 11 个 source family 填满 16 条时首轮初始决策升至 38.7 秒;限制为 8 可降低 prompt 和分派复杂度。连续两个 mixed 小批仍由同 target 的 deferred build 合并,只支付一次最终链接,因此不以增加链接次数换取代理时间。 - family Facade/Worker 发现不能依赖单一文件名和类型名。Facade 应匹配
<slug>*python*family*facade.ts,覆盖cam-python-family-batch-facade.ts;Worker 检查兼容PythonFamilyRequest与PythonFamilyBatchRequest。确认两者已统一调用fc_web_dispatch_json后仅保留指纹,不再注入 editable excerpt,避免每个新模块重复搜索已完成运输链。 - CAM 实测验证异构缩批:原 16 条 mixed 回合耗时 312 秒仍为
ready=0;重规划为 4 条同族后, Facade/Worker 发现修复使 editable packet 从 6,144 B 降到 4,096 B,最终 build 31.50 秒、 asset sync 2.93 秒并一次写入 4 条 ready 回执。编译首错Mod/Part/App/Feature.h不存在时, 应通过源码 include 先例定点解析为PartFeature.h,4.4 秒内修复后只重试一次,不重新启动代理分析。 - CAM 后续 mixed 实测:16 条时 prompt 约 25.2 KiB、初始决策 38.7 秒;8 条上限生效后 prompt 约 14.4 KiB(下降约 43%)、初始决策 7.5 秒(下降约 81%)。同时 family facade/worker 指纹识别使 editable packet 从 6,144 B 降至 4,096 B,只保留 native adapter。该数据说明 heterogeneous batch 应以模型上下文复杂度为主要约束,链接次数由 deferred build 单独合并。
- 无外层构建目标的 feature probe 也必须及时刷新静态能力投影。外层续接入口只比较
*Probe.cpp/*DesktopCommands.cpp的mtime_ns与feature-capability-registry.json;源码 发生变化时才运行一次轻量generate_feature_capability_registry.py,随后将当前批次中已经implementationStatus=routed的记录立即从 manifest 收缩。这样 ReverseEngineering 等没有 可用 WASM target 的模块不会因为缺少 pipeline receipt 而反复读取同一源码;未 routed 的记录 仍保留在下一批,结果不改变、证据不伪造。该刷新路径只读 stat + 受控 probe 扫描,不触发构建、 gate 或全量源码检索。 - 源码读取预算采用“软边界 + 无修改硬收口”:达到软预算只写 marker 并提示代理编辑;若
sourceReads >= hardLimit且本回合fileChanges == 0,scheduler 在下一事件终止该回合并从 checkpoint 新开低推理回合。这样保留已读结果又避免代理在无 editable 锚点时继续猜路径;一旦 已有文件修改,允许自然收口,不丢失实现补丁。 - implementation packet 的 native 发现必须覆盖实际 C++ 命名变体。除标准
*Probe.cpp外, 受控 probe 目录还可匹配模块前缀的*ProbeFixed.cpp与*InventoryApi.cpp;这类扩展只增加 packet 锚点和 build hint,不改变 target 所有权。若 packet 没有 editable native excerpt,代理 不得扫描仓库猜路径;优先修正 packet 发现规则,再继续实现。 - feature desktop packet 在存在 unified dispatcher 时只保留 dispatcher owner 的 C++ editable
excerpts;同模块其它 runtime/inventory/persistence probe 仍保留
supportFiles指纹但不再 注入 file-tail。这样代理上下文只包含实际可编辑入口,避免post-read因稳定文件重复解释而 膨胀;Surface 实测 editable excerpts 从约 11.9 KiB 降至 6.1 KiB,未改变 target、路由或构建 所有权。 - build hint 不能以“产物已存在”作为唯一可用条件。对于首次构建的目标,应从受控
build.ninja中确认 target rule,并推导tree/bin/runtime/wasm路径;只有当 target 未配置时 才返回空 build。否则新模块(本轮 MeshPart)会因尚无.js/.wasm被误判为outerPipelineAvailable=false,代理重复实现而无法进入首次增量链接。该修复不改变 target 或 链接闭包,只消除首次构建前的准备空转。 - deferred build 必须是可收口的事务,不能只依赖“下一批同 target”碰巧触发。planner 返回
no_ready_batch时,scheduler 先读取module-build-pending.json,按 target 做 link-closure preflight 和一次增量 build,全部成功后统一 sync 并写回原 batch receipt;失败 target 保留在 队列并设置有限退避。这样避免路由已经 100% 而最后一个 deferred batch 永久悬空,也避免 idle loop 每几秒重复同一个编译首错。该步骤只做目标构建和实现回执,不运行 runtime gate。 - 路由、构建、验证是三个独立状态。能力生成器识别 native marker 后只能把 route 标为
routed;目标.js/.wasm成功增量链接后才能写implementation_ready;运行时或 Full Parity 仍保持deferred,直到明确验证阶段。最终命令尤其要检查 C++ marker 是否为可编译的 no-op macro,并保证目标与其 pthread 依赖闭包使用一致的-pthread/-sUSE_PTHREADS参数。 - 能力 registry 的 native 所有权发现必须与 packet 保持同一受控文件集合。除
*Probe.cpp/*DesktopCommands.cpp外,还要扫描模块前缀匹配的*InventoryApi.cpp;否则 marker 虽已存在、packet 也能定位,静态路由仍会错误显示ready=0。本轮 Robot 修复后,4 条 命令由ready=0/4正确投影为 routed,随后 planner 自动切换到 Surface;该修复不改变运行时 结果,也不触发全量源码扫描。
判断收益时要比较同一批次的首轮与恢复轮:准备阶段应无 batchPreflightRepair,恢复轮
应显示 sessionMode=resume、reasoningEffort=low,并观察 agent/post-read 与
recoverySeconds 是否下降。只有这些实际计时下降才算优化生效,不能用路由数量变化代替。
首次组合实测(Sketcher 约束命令族):16 条功能的代理阶段为 90.0 秒,0 次源码读取、 2 个文件修改;增量 build/sync pipeline 为 37.4 秒,总计约 127.4 秒,即约 8.0 秒/功能。 优化前窗口平均 181.9 秒/回合且每批 8 条,约 22.7 秒/功能;因此当前端到端吞吐约提高 2.8 倍。该数字是历史样本,不作为硬编码阈值,后续仍以 timing JSON 的同族窗口滚动评估。
Mesh 部分提交实测(v9):优化前最近窗口平均 184.1 秒/回合;启用第 5 次读取协作熔断、
真实 turn.completed 会话续用、1,336 B continuation prompt、Worker 指纹复用和 partial receipt 后,
窗口平均降到 103.6 秒(约下降 43.7%),最近命令成功率从 92.19% 升到 100%。实际
FreeCADWebMeshRuntimeProbe 增量链接 16.5 秒、资源同步 0.52 秒;两轮部分回执先后晋级 2 条和
4 条真实命令,planner 自动从下一批移除,未实现命令继续排队。该结果说明主要收益来自减少
代理重复推导,同时部分回执避免为了凑齐整批而伪注册功能。
当前落地与后续改进
已经落地:单次 scheduler 续接、完成批次跳过、bytes+mtime_ns 上下文指纹、回合级失败账本快照、未登记周期日志索引、按需任务卡读取、RAM ccache、-j8 构建、增量验证和命令预算提示。
建议后续项目增加:
- 将每个批次的
duration、commands、timeout、gateResult写入小型性能账本,并让 planner 按上文公式自动调整下一批大小。 - 对同一 native probe 的多个 focused gate 使用单一 Node 进程或 worker pool,减少重复 loader 初始化。
- 将编译、资源 staging、WASM gate 分别计时;只有编译或 gate 超预算时才拆批,不因准备阶段微小波动改变批次。
- 把失败账本分成“当前批次阻断”和“历史待后置”两个集合,状态展示中禁止混用。
跨项目落地清单
- 定义唯一任务账本和唯一 checkpoint。
- 为批次、上下文和失败账本实现幂等指纹。
- 将 planner/rotate/sync 从代理回合移到外层 scheduler。
- 给每个周期写入 JSONL 事件和可读状态摘要。
- 为每个命令写入阶段、开始时间和持续秒数,并按阶段累计占比。
- 设置回合命令预算、批次超时和一次性重试规则。
- 使用受控源码搜索入口,禁止根目录全量扫描。
- 实现增量 gate 与最终全量 gate 的明确分界。
- 只在全部实现完成且收到明确指令后运行全量验证。
模块业务优先阶段(2026-09-07)
逐 API 推进会重复支付源码理解、Worker、链接和上下文成本,也会把内部符号数量误当成用户功能进度。后续统一以可直接使用的功能模块为实施单位,复用模块已有对象、Document、几何、属性、格式、Facade、Worker 和动态 WASM。模块内部直接调用 FreeCAD C++/Python 能力;API 清单只作源码导航,不再安排实现。
调度入口 prepare_implementation_business_batch.py 负责阶段切换:先补齐所有功能 route/build receipts,再按模块处理 37 组实际 import/export 用户工作流,最后合并唯一 React 主产品页面。不得调用 Python API 批次规划器,不得创建 API 专用 Worker/WASM/page。
实施阶段只运行依赖闭包预检、目标编译/链接、artifact sync 和 implementation receipt。历史 evidence 只用于定位,不重新扫描;canonical exposed/passed/native_handler 和已有 receipt 直接进入复用集合。focused gate、逐 API smoke、Chrome、Full Parity、性能与发布验证全部冻结,直到用户明确重新开启统一验证。
编译首错快车道
2026-09-07 的 20 周期窗口显示,优化前耗时主要是 agent/idle-stop=35.8%、agent/post-read=27.5%、agent/initial-decision=24.1%,合计 87.4%;真实增量编译通常仅 5-32 秒。根因是恢复提示遗漏编译首错、复用超过 25 万预测 token 的旧线程,以及 180 秒无事件等待。
当前恢复链固定为:pipeline 提取 file:line: error → 自动附带故障行附近源码和精确 CMake target 路径 → 同一 batch 的短会话复用(会话损坏时自动轮换)→ 30 秒专用无事件熔断 → 外层增量重建。失败 pipeline 在源码和批次未变化时不重复启动;一旦支持文件 mtime 变化才重新编译。MeshPart 实测恢复提示由约 31 KiB 降至 7.7 KiB,初始决策由最高 170 秒降至 3.8 秒,编辑 4.2 秒且无需源码读取;Inspection 同族错误恢复代理阶段约 14.5 秒。
所有 native target 在构建前统一执行链接闭包预检;pthread 闭包必须显式声明 -pthread 和 PTHREAD_POOL_SIZE。若 probe CMakeLists.txt 比 build tree 的 build.ninja 新,先执行一次 rebuild_cache,再读取闭包,防止旧构建图重复报告已修复错误。业务 batch ID、tier 和 record prefix 由 planner/preflight 共享合同,避免每个新格式模块先进入一次 checkpoint repair。
正常模块业务回合使用 90 秒无事件阈值;已有完整 file_change 后默认 20 秒(可用 FREECAD_POST_EDIT_FINALIZE_SECONDS 调整,最低 15 秒)无事件即自动结束代理并进入外层编译,不等待文字总结。代理尝试运行 tsc/test/gate/validate/Chrome 时调度器直接阻止;已有修改仍进入外层编译,避免历史验证错误把上下文带离当前模块。业务读取采用软预算 3、硬上限 8:修改前可读到上限,修改后超过软预算即禁止继续搜索。
BASH_ENV 必须把每个 bash -lc 归一到显式项目根,防止外层 workspace 使存在的源码被误报缺失。业务 packet 只注入承载 fc_web_dispatch_json 的 native owner、模块 IO facade、模块 Worker 和 FormatDispatcher;Python family facade、历史 gate 与非 owner probe 只保留或完全省略。Part 格式 owner 从错误的 PartIoProbe 修正为 Worker 实际加载的 PartBoxProbe,避免构建与运行时所有权分裂。
对代理异常退出或被熔断后的已落盘修改,调度器写 unbuilt-module-batch-edit.json。下一回合若同 batch 的 native/frontend 源码新于目标产物,先构建再决定是否启动代理;成功直接写 receipt,失败进入编译首错快车道。实测 Part 遗留修改先构建发现一处 C++ 签名错误,恢复代理仅用约 6.8 秒且零读取修复,随后真实链接 56.4 秒完成;Mesh 链接约 10.1 秒,Spreadsheet 约 13.8 秒。业务进度同期由 90.14% 提高到 95.85%。
provider 的 reconnect/error 只进入错误统计,不得刷新代理有效活动时钟,否则每条重连消息都会把 90 秒熔断重新计时。模块业务回合若已经完成源码读取但无修改而熔断,在没有活跃命令、patch conflict 或会话损坏时保存 thread;下一轮发送约 0.5-1 KiB 的直接编辑提示并复用已读上下文。Points 实测首轮读取后续接 prompt 为 530 B,恢复轮不再读取源码,完成修改后外层构建 16.2 秒成功。
第二轮调度优化(2026-09-07):编译首错回合只在当前 batch 的支持文件相对上次失败发生 mtime 变化时重跑 pipeline;否则复用已有诊断,直接进入修复会话。编译恢复和 feature 回合均限制为同一 batch 的短会话,避免跨批次上下文污染。实现回合对 npx --prefix <dir> tsc、npm --prefix <dir> run build 以及复合 git diff/status 形式执行前拦截,防止验证命令绕过策略并把既有错误写入失败账本。所有策略都有单元测试覆盖,调度器下次自然重启后生效,不改变当前正在运行的回合。
实现阶段的 tsc/test/gate/validate/Chrome 和代理自行 build 均由调度器硬阻止;若命令前已有文件修改,则把回合转为 post-edit finalize 并继续外层目标构建。每个 bash -lc 通过 FREECAD_SCHEDULER_ROOT 固定项目根,避免相对路径从外层 workspace 执行。格式 packet 的 target 必须与 Worker 实际加载的 unified dispatcher owner 一致,不能按 *IoProbe 文件名猜测。