aps-agent/docs/round-66-solver-isolation-w...

9.0 KiB
Raw Permalink Blame History

Round 66 工作计划:Windows 求解器进程隔离与 Fail-Closed

日期:2026-08-03

1. 背景与方向选择

  • Round 65 已完成并通过独立复审。完成矩阵下一优先级为 P0 可信试点基线。
  • 并行只读审计比较了三条候选:全量 P2/P3 证据封套、共享审批存储可靠性、Windows 求解器 fatal。
  • 本轮选择 Windows 求解器进程隔离,因为它可直接杀死后端主进程、绕过 Python 异常/审计,并曾出现“测试 passed 且 exit 0,但输出 Windows fatal”的假绿色。
  • 全量 P2/P3 证据封套与共享审批迁移 token/损坏源问题登记为后续 P0 轮次,不以本轮完成替代。

2. 已确认事实

  • 默认 PATH python 来自 Anaconda,用户 site OR-Tools 与 Conda pandas/pyarrow/DLL 混装可复现 0xc0000139 / WinError 127。
  • .venv 与 .sidecar-venv 的独立求解探针当前通过,但 .venv base 仍属于 Anaconda,不能作为发布运行时证明。
  • CP/HYBRID 目前在后端主进程调用 optimize_line_assignment();OR-Tools 时限不能隔离导入崩溃、C++ abort、native deadlock 或进程级 fatal。
  • 默认标准排产使用 HYBRID,rush/LNS/敏感性/鲁棒性等也通过引擎动态分派进入该边界。

3. 本轮目标

建立唯一的求解器进程边界:父进程构造不可变请求,子进程只执行 CP-SAT 优化并返回版本化 JSON;父进程验证退出码、fatal marker、协议、运行时身份和超时后,才允许在父进程物化世界状态。任何 native/runtime/timeout/protocol 失败都结构化失败关闭,不能杀死 8003,不能留下 PO/WO/可发布版本,HYBRID 不能伪装成 RULE 成功。

4. 范围

In scope

  • 新增 solver subprocess request/response 协议、fatal marker 检测、OS 级 timeout、进程树回收和运行时身份。
  • CP 与 HYBRID 的 optimize_line_assignment 改由子进程执行;健康路径保持原 ordered entries、gap、cumulative 和物化语义。
  • 子进程失败时父进程 world 深比较不变;CP/HYBRID 返回结构化 SOLVER_* 阻断,不做静默成功降级。
  • Sidecar 增加 --solver-child;普通 CPython 使用 -I -B -X faulthandler -m server.engines.solver_worker。
  • 强化 scripts/check_solver_runtime.py:内部 timeout、模块来源、base executable、user-site、版本和 fatal marker,即使 returncode=0 也拒绝。
  • 桌面 child env 清理 Conda PATH 段,继续设置 PYTHONNOUSERSITE=1。
  • 增加固定服务器运行时锁/身份说明,但不在本轮擅自重建用户 .venv。

Out of scope

  • 不修改 get_engine() 全局工厂。
  • 不把真实 Windows CI、干净离线机或签名冻结产物冒充本地完成;这些保留外部验收。
  • 不在本轮同时重构全部 P2/P3 evidence resolver、审批 outbox 或真实 MySQL 多主机。
  • 不提交、不推送、不合并、不发布。

5. 失败代码与产品语义

父进程统一识别:

  • SOLVER_RUNTIME_UNSAFE:运行时身份/模块来源不可信。
  • SOLVER_NATIVE_FATAL:stderr/stdout 命中 fatal marker,包括 returncode=0。
  • SOLVER_PROCESS_EXITED:非零退出且非特定 runtime 失败。
  • SOLVER_PROCESS_TIMEOUT:超过求解时限 + 启动宽限。
  • SOLVER_RESPONSE_INVALID:JSON 截断、缺成功标记、协议版本或 schema 不匹配。
  • SOLVER_VERSION_MISMATCH:父子协议或引擎版本不一致。

规则:

  1. 子进程失败前后 world 完全一致。
  2. 显式 CP 失败不降级 RULE。
  3. HYBRID native/runtime 失败默认阻断;若未来允许降级,必须是显式策略且不可发布。
  4. 只有验证通过的 ordered entries 才进入 materialize_schedule()。
  5. 失败必须可写算法失败审计或返回结构化阻断;父进程和 8003 必须继续存活。

6. 验收标准

  • 健康 .venv:CP/HYBRID subprocess 与现有 in-process 基准产生等价可行性、工序顺序、line assignment、gap/cumulative 元数据。
  • 故障注入:非零退出、returncode=0+fatal marker、超时、非法 JSON、缺 success marker、协议不匹配全部 fail-closed。
  • 故障前后 world 深比较一致,0 新 PO/WO/VL,不产生可发布版本。
  • 当前默认 Anaconda 探针稳定返回不安全/失败证据;.venv、.sidecar-venv 探针连续通过。
  • 冻结 Sidecar(若现有产物可用)--solver-child 冒烟通过;若产物需重打包,记录为构建后验证而不伪造。
  • 8003/5173 在模拟 child crash/timeout 后继续 HTTP 200。
  • focused、全量黄金、Node、Web build 通过;git diff --check 与 detect_changes(compare main) 完成。

7. 真实运行证据

  • 保留默认 Anaconda 失败探针、固定运行时通过探针和产品 API child-failure 存活证据。
  • 测试所有 APS_HOME、DB、world、日志输出重定向到临时目录;不写 server/data/**。
  • 记录当前 MOM world hash,运行前后必须一致。

8. 风险与外部阻断

  • GitNexus 对 get_engine 为 CRITICAL;本轮避免修改。
  • 动态引擎分派会低估 CpSatEngine.solve / HybridEngine.solve 的上游影响,因此按高风险验证。
  • 真正 Windows CI、干净 Win10/11、签名冻结产物、Defender 和客户机 DLL 环境需要外部基础设施。
  • .venv 重建属于运行环境变更,不在未获授权时执行。

9. Pre-target-merge 停止点

实现、验证和独立审计完成后,先报告:协议覆盖、失败注入、健康求解等价性、运行时来源、现场数据保护、共享 dirty 基线和残余外部环境。未经用户明确授权,不执行 commit/merge/push/publish。

10. 计划可行性审计

PLAN AUDIT: PASS

Blocking issues: none

Clarification needed: none(persistent goal 已授权继续推进最高优先级本地项;默认采用 fail-closed、不静默 RULE 降级)

Non-blocking improvements: 真实冻结产物 smoke 取决于现有 artifact;若需重新打包则只记录为外部/后续构建证据。

11. 执行结果与验收收口

状态:实现与真实运行门禁通过;最终全量黄金计数待主 agent 在新增源码 Sidecar -I 回归后复跑确认。

已完成

  • 子进程协议、request/response digest、fatal marker、OS timeout、进程树回收和运行时身份已落地,协议为 aps.solver-process.v1。
  • CP/HYBRID 仅在 child 执行优化,父进程负责校验与物化;fatal/timeout 时均 UNAVAILABLE、0 PO/WO、不可发布,且无 RULE 静默降级。
  • 复制 world.json 的 CP/HYBRID 均 OPTIMAL,1 PO / 10 WO / 10 operationSlots,runtimeSafe=true。
  • 复制 MOM 数据的 CP/HYBRID 均可完成求解,但现有业务主数据阻断为 0 PO/WO、1 slot/1 conflict;不宣称完成现场主数据。
  • fatal 注入后父进程与 8003 存活,业务世界原子;timeout 15.633 秒并回收 grandchild 进程树。
  • 默认 PATH Anaconda 探针稳定拒绝;.venv、.sidecar-venv 各连续 5 次通过。
  • 8003 PID 21576、5173 PID 9808 healthy;浏览器 title/root 正常且无 warning/error。
  • Node 60 passed、Web build passed、ruff/compile/node check/diff-check passed。

源码 Sidecar -I 补充发现

  • 真实源码 --solver-child 在 Python -I 下最初因源码 import root 被移除而失败。
  • 经 GitNexus LOW impact 门禁,仅 bootstrap 已解析的 trusted source root,并新增 test_source_solver_child_entry_runs_under_isolated_python。
  • 修复后实际源码 Sidecar child 为 CP OPTIMAL、1 PO / 10 WO / 10 slots、协议 v1、runtimeSafe=true。
  • 最新 focused 为 80 passed;最终全量黄金为 1029 passed / 2 warnings(300.23 秒)。

数据与发布边界

  • world.json hash:C6E7FF090719DB505AF5C3B0D8ADD376CBA7843D6E6982A68C1BA02C342052FA;MOM hash:6B64ADF6F29A43A518D89718610EE71FAD60E86D3F38CD5CFF26F1740CC53F7D;JSON 业务源前后不变。
  • server/data/**、.env 无 Git 状态;master.db 可能因 license last_seen 等运行元数据更新,不以文件 hash 证明业务不变。
  • 现有冻结 EXE 仅旧 --probe-child 通过;未重建,故新 --solver-child 的打包验收仍是构建后停止点。
  • 不提交、不推送、不合并、不发布。

后续 P0

全量 P2/P3 evidence 封套与审批迁移可靠性(token 摘要、损坏源 fail-closed、DB clock/TTL、CAS 并发)成为下一最高优先级本地项。

首轮独立审计修复

  • 首轮 AUDIT: FAIL 指出成功响应摘要未被父进程验证,且非空请求可被伪造成 OPTIMAL + 空 entries。

  • 父进程现强制重算 responseDigest,校验条目完整排列/业务字段、pipeline/status、可行解 objective/gap 与 operationSlots 覆盖;8 个新增负向用例全部通过。

  • 源码冻结路由现按 sys.frozen 自动执行自身 --solver-child;旧 EXE 未重建,仍为构建后验收边界。

  • 最终独立只读复审 AUDIT: PASS。

  • 复审补强:请求先落自动清理临时文件并作为 child stdin,避免 Windows 管道背压绕过 deadline;slot 越界/订单产品错绑、错误 pipeline/status 均失败关闭。