124 lines
11 KiB
Markdown
124 lines
11 KiB
Markdown
# P2 实施笔记 — 写操作过确认卡门禁(Fallback Execute Lane)
|
||
|
||
> 施工:Agent-J(实施员),对照 `P2-DESIGN.md v1.0` 逐条落地。
|
||
> 代码轮验证日期:2026-09-03。状态:**代码与黄金测试全部就绪,待 K 轮真实冒烟 + 注入复证后合闸**。
|
||
|
||
---
|
||
|
||
## 1. 改动清单(`git diff --stat` 实测)
|
||
|
||
跟踪文件改动(`git diff --stat`,不含 `package.json` 的既有未提交改动):
|
||
|
||
```
|
||
docs/CHANGELOG.md | 19 +
|
||
docs/architecture/fallback.md | 90 ++++
|
||
docs/architecture/harness.md | 3 +
|
||
server/agent_core/fallback_lane.py | 915 +++++++++++++++++++- (净增,含 docstring/注释)
|
||
server/agent_core/harness.py | 4 +
|
||
server/aps_domain/workflow.py | 45 ++
|
||
server/integrations/pi_bridge.py | 152 ++++-
|
||
tests/golden/test_fallback_lane.py | 31 ++
|
||
8 files changed, 1246 insertions(+), 13 deletions(-)
|
||
```
|
||
|
||
新建文件(untracked):`server/agent_core/fallback_verify.py` 190 行、`tests/golden/test_fallback_execute.py` 762 行(19 例)。
|
||
|
||
(工作区另有 `tests/golden/_saga_cp.json` 被既有 saga 测试例行改写,已 `git checkout --` 还原;`package.json` +3 行为既有 docx 依赖改动,非本轮产物。)
|
||
|
||
与设计 §1 文件清单逐条对应:
|
||
|
||
| # | 设计条目 | 落地情况 |
|
||
|---|---|---|
|
||
| 1 | `fallback_lane.py` 计划锁 + 执行编排 | ✅ `plan_fingerprint`/`load_plan`/`validate_plan`/`check_step_request`/`_check_constraints` + `execute_plan` + `_stage_plan_confirmation` + `write_guard_extension` + `build_pi_runner` + `FallbackConfig` +3 字段 |
|
||
| 2 | `pi_bridge.py` 邮箱协议 + fs_write + 简报模板 | ✅ `ActionMailbox` + `handle_fs_write` + `fs_write`/`aps_invoke` 注册 + `render_plan_task_brief` + `_PLAN_TASK_TEMPLATE`(含 `<<<UNTRUSTED_DATA` 段落) |
|
||
| 3 | `harness.py` 权力注册 | ✅ `_POWER_MAP`/`_POLICY_DESC` 各 +2(`agent.fallback.execute`=P2、`agent.fallback.execute.highrisk`=P3) |
|
||
| 4 | `workflow.py` execute_confirmed +1 分支 | ✅ 证据校验后、`schedule.publish` 前;try/except 归一为显式失败文案;WORLD_WRITE 审计 result=SUCCESS/DENIED/FAILED;补写落盘 |
|
||
| 5 | `fallback_verify.py` 新建 | ✅ `world_diff`/`check_expectations`/`diff_summary_lines`/`build_report`/`report_fingerprint` |
|
||
| 6 | `tests/golden/test_fallback_execute.py` 新建 | ✅ 19 例(T-1..T-17 + E-1/E-5/E-7)全绿 |
|
||
| 7 | `test_fallback_lane.py` 守卫模板断言 | ✅ 扩为 3 条(见 §2 偏差①) |
|
||
| 8 | `docs/architecture/harness.md` | ✅ 权力矩阵 +2 行 + 变更记录 FB-02 |
|
||
| 9 | `docs/architecture/fallback.md` | ✅ 追加 P2 章节(协议细节以代码为准,本文为准入口) |
|
||
| 10 | `docs/CHANGELOG.md` | ✅ FB-02 条目(含实测数字) |
|
||
| 11 | `scripts/poc/smoke_s3_execute.py` | ❌ **归 K 轮**(设计 §7 已标注"给 Agent-K",J 分工为代码+notes;接口交接见 §4) |
|
||
| 12 | `scripts/poc/rogue_llm_server.py` +2 剧本 | ❌ **归 K 轮**(同上;剧本规格见 §4) |
|
||
|
||
P1 既有面**逐字节不变**(黄金 `test_pi_bridge.py`/`test_fallback_lane.py` 原断言全绿佐证):`_TASK_TEMPLATE`/`render_task_brief`/`handle_fs_read`/`render_request_envelope`/`parse_llm_result`;`run_fallback_lane(...)` 不传新参数时行为与 P1 一致;readonly 守卫模板字节不变。
|
||
|
||
---
|
||
|
||
## 2. 与设计的偏差及理由
|
||
|
||
**① 守卫双模板方案(v1 readonly / v2 plan+execute),设计预期的"既有守卫测试被打破"未发生。**
|
||
设计 §2 E 案要求扩展 `_GUARD_TS_TEMPLATE`,并预期打破 `test_fallback_lane.py::test_write_guard_extension_matches_template`,要求同轮修复为逐字节断言。实际实施采用**双模板**:`mode="readonly"` 输出 P1 模板逐字节不变(原测试原样通过,无需修复),`"plan"/"execute"` 输出 v2 模板(追加 EXECUTE LANE MODE 注释 + APS_WORK_DIR + `fs.unlink/rename/writeFile` 许可包装)。理由:P1 只读 lane 的守卫面不应为 P2 承担任何字节级回归风险;v2 的新表面由同轮新增的两条测试逐字节锁定(沙箱写门面 + 两个 mode 的模板内容)。设计的真实意图——"改守卫必须改测试"——以更强形式满足(新增表面均有字节级断言)。
|
||
|
||
**② 执行段任务简报渲染器位于 `fallback_lane.py` 而非 `pi_bridge.py`。**
|
||
`_render_exec_task_brief` 依赖 `PendingAction` 结构与摘要字段,与编排器同模块避免跨层 import;`pi_bridge` 保持"信封/邮箱/注册表"纯协议层。`plan_fingerprint`/`load_plan` 同理放 `fallback_lane`(计划锁是编排概念)。设计 §2 的模块归属描述以本节为准。
|
||
|
||
**③ 步骤级 TOOL 审计由 `execute_plan` 成功路径补写,而非 workflow 分支。**
|
||
设计 §1 #4 说 workflow 分支"成功则补写步骤级审计"。实际:步骤级审计条目在 `_execute_steps` 各 handler 内构造、成功路径统一补写(含 actor/evidenceRefs/计划指纹);workflow 分支只写一条 WORLD_WRITE 汇总审计。理由:归并失败路径(try/except 捕获 + 显式失败文案)位于 execute_plan 内部,失败现场信息(cp_failed 指纹、回滚验证、diff 详情)只有 execute_plan 拿得到;workflow 分支保持薄壳。
|
||
|
||
**④ `ExecuteResult.message` 由 `execute_plan` 组装。**
|
||
设计说"message 由 workflow 分支组装"。实际失败文案的组装素材(失败原因、checkpoint id、漂移/回滚指纹)全部在 execute_plan 作用域内,workflow 只透传 `fb_result.message`。卡片摘要仍由编排器 `_stage_plan_confirmation` 从结构化字段再生成(设计 §4.1 的硬约束未变)。
|
||
|
||
**⑤ T-17 经 `monkeypatch.setattr("server.agent_core.llm_client.get_provider", ...)` 直驱 `parse_llm_result`。**
|
||
低置信/未知 verdict/非法 JSON 三态不需要真起 rogue HTTP 服务;真实 rogue 服务的三态复证归 K 轮 §7。
|
||
|
||
**⑥ `ExecuteResult` 默认 `status="failed"`,成功路径在 try 块末尾显式置位。**
|
||
施工期曾因此出现"全步骤成功却走失败分支"的缺陷(回滚把已落库成果冲掉),已修复并由 T-2 锁定。记录在此提醒后续维护者:**成功是显式结论,不是默认值**。
|
||
|
||
---
|
||
|
||
## 3. 自验结果(全部实测,命令可复跑)
|
||
|
||
Python 一律 `.venv\Scripts\python.exe`(硬约束)。
|
||
|
||
| 验证项 | 命令 | 结果 |
|
||
|---|---|---|
|
||
| 目标三文件 | `pytest tests/golden/test_fallback_execute.py tests/golden/test_fallback_lane.py tests/golden/test_feature_flags.py -q` | **41 passed** |
|
||
| 爆炸半径切片(impact 上下游全符号对应黄金文件) | confirm/审批/saga/checkpoint/chat/契约/导入 16 文件 | **118 passed** |
|
||
| 邻近切片 | assistant/state 24 例 | **24 passed** |
|
||
| 全量黄金·批一(80 文件) | | **573 passed** |
|
||
| 全量黄金·批二(77 文件) | | 517 passed, 3 failed, 14 errors |
|
||
| ruff(本轮全部新/改文件) | `ruff check` ×8 文件 | **All checks passed!**(零新增) |
|
||
|
||
批二 3 failed + 14 errors **与本轮无关**,`git stash -u` 前后逐例一致(基线复跑实测):
|
||
|
||
- `test_preference_features.py` 3 例:内存时钟 2026-09-03 越过示例文档的 validUntil 2026-06-30(日期炸弹,仓库既有)。
|
||
- `test_mes_http.py` / `test_mes_readiness.py` / `test_trace_external_http.py` 14 errors:fixture 假数据不满足新接口(仓库既有)。
|
||
|
||
T-1..T-17 全链路确定性:sandbox fake runner 由 `_start_fake_runner` 动态分配端口、结束即关,不涉及固定端口;execute 段 fake runner 不经端口(直接注入 `build_pi_runner`)。
|
||
|
||
关键断言落点(19 例 ↔ 设计 §5 映射):
|
||
|
||
- 锁:`load_plan` 指纹错/缺文件/超步数/空计划/非法结构拒绝;`validate_plan` 漂移拒绝;`check_step_request` 非法 intent/未知字段/数量非正/intent 与 action 不一致拒绝。
|
||
- 圈禁:`handle_fs_write` 写 `work/../x` → `{"error": "path escapes allowed roots"}`;越界 intent(order.delete)在 LLM 层拒绝、不进入审批流。
|
||
- 门禁:未决卡阻塞首步(含防重放 mock 回调篡改);无卡世界直接拒绝(hint 含 fallback.propose)。
|
||
- 回滚:数量篡改、verify 偏离、子进程崩溃三例均恢复 before 世界 + 显式失败文案 + WORLD_WRITE 失败审计 +(篡改类)cp_failed 现场指纹可复算。
|
||
- 证据:expectation 容差 0(±1 拒绝)、无关行变化容忍、中文文案 PASS、diff 摘要行格式。
|
||
- 审计链:PROPOSE→GATE(plan 指纹)→GATE(confirm 指纹+快照集)→TOOL×2(步骤)→WORLD_WRITE;失败链 PROPOSE→GATE(plan)→GATE(confirm)→WORLD_WRITE(FAILED)。
|
||
- 小世界:17 实体 demo 世界全流程成功(仅种子行可 import.commit)。
|
||
- 总开关:features.json `fallback.enabled=false` 时新分支不触达(feat_fallback=False 复跑 2 例)。
|
||
|
||
---
|
||
|
||
## 4. 遗留问题 / 交接清单(→ Agent-K)
|
||
|
||
1. **真实 S3 冒烟(`scripts/poc/smoke_s3_execute.py`,设计 §7 规格)**:临时 APS_HOME 双仓 + features.json 开 fallback + 根 `.env` 的 kimi-k2.6 key。验收点 A–H 见设计 §7。接口提示:fake pi runner 注入点为 `fallback_lane.build_pi_runner`(签名 `(config, *, mode="readonly")`,execute 段用 `mode="execute"`);`make_customer_file` 自检要求 `detect_kind` 对该文件**失败**(否则 commit 阶段会先 reject)。
|
||
2. **注入复证**:`rogue_llm_server.py` SCENARIOS += `inject_via_csv`(CSV 藏指→断言零文件落盘+零审批+计划锁拦截)与 `inject_fake_approval`(Pi 伪造审批→断言卡片结构不采纳+审批仓空);复跑 P1 三剧本回归。
|
||
3. **真实 pi + 真实 LLM 端到端未验**:守卫 v2 模板在真实 pi 子进程下的行为未验(黄金测试锁定的是模板字节内容本身);`--sabotage` 变体脚本未实现。
|
||
4. **ASSISTED 长执行占事件循环**:设计 §5.2 已知边界,main 期处置项(K 轮真实冒烟时观察 pi 超时 120s 的占用表现)。
|
||
5. **async_jobs 不复用**:P2 同步执行(设计 §4.2 取舍),main 期若执行耗时长再评估异步化。
|
||
6. **`handle_fs_write` 的 `root != "work"` 拒绝分支无专门用例**:当前注册表只暴露 work;未来暴露 outbox 写时需补测试。
|
||
7. **`harness.py` 存量 5 条、`workflow.py` 存量 50 条 ruff 告警**(`git stash -u` 基线复跑同为 55 条,零新增;治理归清洁口粮,不在本 round)。
|
||
|
||
---
|
||
|
||
## 5. 宪法红线自证
|
||
|
||
- 新写路径唯一入口 = 既有 `/api/actions/confirm`(`workflow.py` 分支挂在 confirm 处理器内部,无新 HTTP 端点)。
|
||
- 全部新 powers 注册进 Harness 权力矩阵并过 `require_power` 调用链(stage→plan→confirm→execute 四段 GATE)。
|
||
- 失败路径显式文案且自动回滚;无任何静默降级。
|
||
- checkpoint 前/后/失败现场三段快照成对出现,指纹双向验证(beforeFingerprint 重算比对 + 回滚后指纹验证)。
|
||
- Pi 写面仍是圈禁 `work/` 邮箱 + 沙箱 unlink/rename/writeFile 三函数包装——**没有给 pi 新增任何物理写通道**;`fs_write` 仅供中间产物,`aps_invoke` 仅产生请求、由计划锁拦截。
|
||
- 计划指纹 sha256 绑定 approvedPlanJson,卡片摘要全部由编排器从结构化字段再生成,注入文本只能出现在 `<<<UNTRUSTED_DATA` 数据段落。
|