feat(onboard/parity): 写入汇 WriteSink 与写入前计划 plan_onboarding,处置独立审查(P8c) #36

Merged
lanrtop merged 6 commits from feat/agent-client-parity-p8c into master 2026-10-03 09:59:15 +08:00
Owner

做了什么

P8c:一键接入的写入前计划(蓝图 agent-client-parity P8c 卡)。

  • lib.rs 新增写入汇 WriteSink(read / write / exists)与真实写入 DiskSink。接入包模板分发、蓝图规则同步、项目身份与兼容投影、Codex 托管块全部经写入汇读写,原入口改为 DiskSink 的薄包装,行为不变。
  • 新增 onboard/plan.rs:记录汇 PlanSink 只记录、不落盘,后续步骤能读到前面步骤待写的内容。write_project_files 是 plan 与 apply(P8d)共用的写入序列。plan_onboarding 输出:
    • 逐文件的 create / update / noop,带有上限的 diff;
    • 混合文件预警、身份冲突、手写同名条目、共享项目键、需人工处理等阻断项;
    • 本机预计动作:token、shim,以及 claude / codex / dsh 三端;
    • 单列的 codex_trust 与 next_actions;
    • digest。
  • mcp_inject::plan_onboarding 是生产入口。它在 P8e 加 MCP / Tauri 投影之前还没有调用方,输出也还不是外部契约。
  • 独立审查发现的处置(见下方「审查处置」)。

自验

  • 全量 cargo test:318 passed / 0 failed / 5 ignored;cargo check:0 warning。
  • onboard::plan 共 11 条测试,覆盖:
    • 空仓库全部为 create,目录快照(含 .git)前后字节一致;
    • 计划与随后 DiskSink 实际写入的文件集合、内容哈希一致;
    • apply 后连续两次计划全部 noop,digest 相同;
    • 本机动作变化、写入后内容变化、写入前内容变化,都会让 digest 变化;改 lefthook.yml 这类计划不会触碰的文件,digest 不变;
    • CRLF 检出判 noop;
    • 混合文件与身份冲突记为阻断项;
    • 手写 Codex 条目,加上本机 codex manual_kept,只记一条阻断项;
    • Codex 未信任只进 next_actions,不阻断,也不改变 digest;
    • 非 UTF-8 文件:已存在的跳过,全托管的记 failed;
    • 阻断项指向真正出错的文件,并且去重;
    • 阻断项映射表、PlanSink 覆盖读与撤销、diff 截断。
  • mcp_inject 新增 3 条:Codex 信任表解析(真实 TOML 键写法、不按祖先目录判定)、git 主仓库根解析(真实 worktree)、token 步区分未配置和 needs_repair。credential_store 新增 1 条:mcp_auth_token_state 与 get_or_create_mcp_auth_token 的分支一一对应。
  • 变异验证:先暂存,改完从暂存区还原。
    • 第一轮同时注入 6 处,恰好 7 条对应用例失败,其余 311 条通过。
    • 第二轮注入 2 处,3 条对应用例失败。

审查处置(独立只读审查 04a0069,结论 fix_then_merge)

# 级别 发现 处置
① 中 Codex 未信任记 needs_human,被并入 blocked,导致新项目一键接入全部卡住 已修:移出本机步骤,计划单列 codex_trust,未信任只进 next_actions,不阻断、不进 digest;映射抽成纯函数 machine_step_blocker 并补测试
② 中 dispatch_one 改用 read 判存在,已存在的非 UTF-8 文件从 skipped 变成 failed(生产路径回归) 已修:WriteSink::exists(),只判存在;全托管文件读不出仍记 failed;补 UTF-16 回归测试
③ 中 token 步把「未配置」和 needs_repair 合成 apply 已修:新增只读的 mcp_auth_token_state,needs_repair 记 needs_human(阻断)
④ 低 祖先目录信任未经验证;测试里的 TOML 非法,是假阳性 已修:去掉祖先规则,候选改为项目根 + git 主仓库根;键解析按 TOML 规则处理;改写测试
⑤ 低 写入序列维护两份,注释与实现不符 已修:抽出 write_project_identity_files_with,inject 与 plan 共用
⑥ 低 digest 语义说过头,也不绑定写入前内容 已修:update 带 before_sha256 并进 digest;模块文档与卡片写明准确语义(报备:卡片自查的验收表述已改为准确语义���断言只增不减)
⑦ 低 阻断项重复,target 指错文件 已修:新增 WriteFailure,指向真正出错的文件;按(target, reason)去重
⑧ 信息 onboard 本体反向依赖 commands / mcp_inject 不改:记入 known_limits(过渡态),P8d 起下沉
⑨ 信息 P8d / P8g 的 acceptance 缺单列项与本机步骤 本卡已单列 codex_trust;其余写进 P8d 卡「审查转入」,待统筹决定是否并入 acceptance

没验什么

  • mcp_inject::plan_onboarding / plan_machine_steps 的组装依赖真实凭据存储和用户目录,没有单测,也没有在运行中的炼境上跑过(P8e 才有投影)。其中的映射函数都已单测。
  • Codex 信任的判定方式(精确目录 + git 主仓库根,worktree 继承主仓库的信任)沿用审查给出的描述,没有核对 Codex 源码,也没有用真实 Codex 实测。权威结论以 P4a 的 codex mcp get --json 为准。
  • 顶层点号键写法 projects.'x'.trust_level 不识别。路径比较不区分大小写,可能与 Codex 的精确字符串匹配有出入。
  • shim 步不可用时只记 unavailable、不阻断,与 token 步口径不同,没有修改。
  • bypassPermissions 默认值还没有单列(P8d)。
  • 只在 Windows 上跑过测试;WSL 项目没有覆盖。
  • 本 PR 不改 .blueprint/manifest.yaml,卡片仍为 🔵,合并后由统筹会话标 ✅。

🤖 Generated with Claude Code

## 做了什么 P8c:一键接入的写入前计划(蓝图 `agent-client-parity` P8c 卡)。 - `lib.rs` 新增写入汇 `WriteSink`(read / write / exists)与真实写入 `DiskSink`。接入包模板分发、蓝图规则同步、项目身份与兼容投影、Codex 托管块全部经写入汇读写,原入口改为 `DiskSink` 的薄包装,行为不变。 - 新增 `onboard/plan.rs`:记录汇 `PlanSink` 只记录、不落盘,后续步骤能读到前面步骤待写的内容。`write_project_files` 是 plan 与 apply(P8d)共用的写入序列。`plan_onboarding` 输出: - 逐文件的 create / update / noop,带有上限的 diff; - 混合文件预警、身份冲突、手写同名条目、共享项目键、需人工处理等阻断项; - 本机预计动作:token、shim,以及 claude / codex / dsh 三端; - 单列的 `codex_trust` 与 `next_actions`; - digest。 - `mcp_inject::plan_onboarding` 是生产入口。它在 P8e 加 MCP / Tauri 投影之前还没有调用方,输出也还不是外部契约。 - 独立审查发现的处置(见下方「审查处置」)。 ## 自验 - 全量 `cargo test`:**318 passed / 0 failed / 5 ignored**;`cargo check`:0 warning。 - `onboard::plan` 共 11 条测试,覆盖: - 空仓库全部为 create,目录快照(含 .git)前后字节一致; - 计划与随后 `DiskSink` 实际写入的文件集合、内容哈希一致; - apply 后连续两次计划全部 noop,digest 相同; - 本机动作变化、写入后内容变化、写入前内容变化,都会让 digest 变化;改 lefthook.yml 这类计划不会触碰的文件,digest 不变; - CRLF 检出判 noop; - 混合文件与身份冲突记为阻断项; - 手写 Codex 条目,加上本机 codex manual_kept,只记一条阻断项; - Codex 未信任只进 next_actions,不阻断,也不改变 digest; - 非 UTF-8 文件:已存在的跳过,全托管的记 failed; - 阻断项指向真正出错的文件,并且去重; - 阻断项映射表、PlanSink 覆盖读与撤销、diff 截断。 - `mcp_inject` 新增 3 条:Codex 信任表解析(真实 TOML 键写法、不按祖先目录判定)、git 主仓库根解析(真实 worktree)、token 步区分未配置和 needs_repair。`credential_store` 新增 1 条:`mcp_auth_token_state` 与 `get_or_create_mcp_auth_token` 的分支一一对应。 - 变异验证:先暂存,改完从暂存区还原。 - 第一轮同时注入 6 处,恰好 7 条对应用例失败,其余 311 条通过。 - 第二轮注入 2 处,3 条对应用例失败。 ## 审查处置(独立只读审查 04a0069,结论 fix_then_merge) | # | 级别 | 发现 | 处置 | |---|---|---|---| | ① | 中 | Codex 未信任记 needs_human,被并入 blocked,导致新项目一键接入全部卡住 | 已修:移出本机步骤,计划单列 `codex_trust`,未信任只进 `next_actions`,不阻断、不进 digest;映射抽成纯函数 `machine_step_blocker` 并补测试 | | ② | 中 | `dispatch_one` 改用 read 判存在,已存在的非 UTF-8 文件从 skipped 变成 failed(生产路径回归) | 已修:`WriteSink::exists()`,只判存在;全托管文件读不出仍记 failed;补 UTF-16 回归测试 | | ③ | 中 | token 步把「未配置」和 needs_repair 合成 apply | 已修:新增只读的 `mcp_auth_token_state`,needs_repair 记 needs_human(阻断) | | ④ | 低 | 祖先目录信任未经验证;测试里的 TOML 非法,是假阳性 | 已修:去掉祖先规则,候选改为项目根 + git 主仓库根;键解析按 TOML 规则处理;改写测试 | | ⑤ | 低 | 写入序列维护两份,注释与实现不符 | 已修:抽出 `write_project_identity_files_with`,inject 与 plan 共用 | | ⑥ | 低 | digest 语义说过头,也不绑定写入前内容 | 已修:update 带 `before_sha256` 并进 digest;模块文档与卡片写明准确语义(**报备**:卡片自查的验收表述已改为准确语义���断言只增不减) | | ⑦ | 低 | 阻断项重复,target 指错文件 | 已修:新增 `WriteFailure`,指向真正出错的文件;按(target, reason)去重 | | ⑧ | 信息 | onboard 本体反向依赖 commands / mcp_inject | 不改:记入 known_limits(过渡态),P8d 起下沉 | | ⑨ | 信息 | P8d / P8g 的 acceptance 缺单列项与本机步骤 | 本卡已单列 codex_trust;其余写进 P8d 卡「审查转入」,待统筹决定是否并入 acceptance | ## 没验什么 - `mcp_inject::plan_onboarding` / `plan_machine_steps` 的组装依赖真实凭据存储和用户目录,没有单测,也没有在运行中的炼境上跑过(P8e 才有投影)。其中的映射函数都已单测。 - Codex 信任的判定方式(精确目录 + git 主仓库根,worktree 继承主仓库的信任)沿用审查给出的描述,**没有核对 Codex 源码,也没有用真实 Codex 实测**。权威结论以 P4a 的 `codex mcp get --json` 为准。 - 顶层点号键写法 `projects.'x'.trust_level` 不识别。路径比较不区分大小写,可能与 Codex 的精确字符串匹配有出入。 - shim 步不可用时只记 unavailable、不阻断,与 token 步口径不同,没有修改。 - bypassPermissions 默认值还没有单列(P8d)。 - 只在 Windows 上跑过测试;WSL 项目没有覆盖。 - 本 PR 不改 `.blueprint/manifest.yaml`,卡片仍为 🔵,合并后由统筹会话标 ✅。 🤖 Generated with [Claude Code](https://claude.com/claude-code)
lanrtop added 6 commits 2026-10-03 09:55:37 +08:00
feat(onboard/parity): 写入汇 WriteSink 与写入前计划 plan_onboarding(P8c,待独立审查)
All checks were successful
Push & PR Check / check (push) Successful in 1m2s
ef2c7fd593
一键接入改写子项目受版本管理的文件,人在确认前必须看得到改什么;计划与 apply 走同一段
分发代码、只换写入汇,所以「看到的」就是「将写的」。

- lib.rs:WriteSink trait(read/write,内容忽略行尾未变不写)与 DiskSink
- 接入包模板分发、蓝图规则同步、项目身份与 MCP 投影(context、settings 兼容条目、
  liangjing、Codex 托管块)全部改经写入汇;原入口改为 DiskSink 薄包装,行为不变
- onboard/plan.rs:PlanSink 只记录不落盘、后续步骤读到前面步骤的待写内容;
  write_project_files 为 plan 与 apply 共用的写入序列;plan_onboarding 给出逐文件
  create/update/noop、有上限的 diff、混合文件预警(P10 前移到写入前)、身份冲突、
  手写条目与共享项目键阻断项,并对计划算 digest
- mcp_inject:plan_onboarding 生产入口与本机侧预计动作(token、稳定入口、三端只读
  状态、Codex 信任),只读、不起子进程
- 新增测试:空仓库全 create 且目录快照字节不变、apply 后两次计划全 noop 且 digest
  相同、CRLF 判 noop、内容/本机状态变化 digest 变化、混合文件与身份冲突阻断、
  手写 Codex 条目与共享键阻断、记录汇覆盖与撤销、diff 截断、Codex 信任解析;
  cargo test 307 passed

Feature-Confirmed: true
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
自动合并,无冲突(mcp_inject.rs、commands/onboarding.rs、mcp_tools.rs 双方改动区域不重叠)。

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- plan_predicts_exactly_what_apply_writes:空仓库先 plan 再经 DiskSink apply,逐文件比对计划内容哈希与
  实际写入,并核对 git status 的文件集合与计划一致。P8d 的 digest 复核依赖这条不变式;
  变异 PlanSink::read 忽略待写内容时,原有 plan 端到端测试仍通过,只有本测试与记录汇单元测试失败。
- mcp_inject::plan_onboarding 注释由「不起子进程」改为「不起客户端 CLI,混合文件预警会起只读 git」,
  与 has_uncommitted_outside_managed 的实际行为一致。

cargo test 311 passed / 0 failed / 5 ignored。

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
docs(blueprint/agent-client-parity): P8c 标 🔵,补实现、验证、自查与有意偏差(P8c)
All checks were successful
Push & PR Check / check (push) Successful in 1m3s
04a0069363
- 实现:WriteSink/DiskSink、三处分发改经写入汇、onboard/plan.rs 记录汇与计划/digest、
  mcp_inject 本机侧预计动作;合并 master(PR #34)自动合并无冲突。
- 验证:合并后 cargo test 310 passed,补测试后 311 passed / 0 failed / 5 ignored;变异记录。
- 自查:acceptance 逐条对照;有意偏差为计划阶段完全不起 claude CLI(比「假 CLI 只收到 mcp get」更严)、
  文件动作只有 create/update/noop、DiskSink 读失败报错不覆盖。
- 自查发现留待独立审查:非 UTF-8 已存在文件由 skipped 变 failed;bypassPermissions 未在计划单列;
  Codex 信任解析不识别基本字符串转义段头。T1:plan_onboarding 尚无投影,project-context.yaml 由 P8e 补。

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Codex 未信任不再并入 blocked:计划新增 codex_trust 与 next_actions 字段,未信任或读不出
  Codex 配置只进 next_actions,不阻断、不进 digest;「本机步骤 → 阻断项」抽成纯函数 machine_step_blocker。
- WriteSink 新增 exists():dispatch_one 只判存在,已存在的非 UTF-8 文件(UTF-16 lefthook.yml 等)
  恢复为 skipped(修复 apply_onboarding_pack 回归);全托管文件读不出仍记 failed。
- token 步改用只读 mcp_auth_token_state 区分 Present / Missing / NeedsRepair,needs_repair 记 needs_human,
  与 get_or_create_mcp_auth_token 的分支一一对应;is_project_injected 仍只作「已接入」判据。
- Codex 信任去掉未验证的祖先目录规则,候选为项目根 + git 主仓库根(worktree 继承主仓库信任);
  键解析按 TOML 规则处理字面量键、基本字符串转义、[projects] 内联表与点号键;删掉非法 TOML 的假阳性用例。
- 写入序列只留一份:抽出 write_project_identity_files_with,inject 与 plan 共用;删除 write_project_mcp_files_with。
- digest 绑定 update 文件的写入前哈希(before_sha256),模块文档写明准确语义。
- 新增 WriteFailure,阻断项指向真正出错的文件;本机 codex 步指向 .codex/config.toml,blocked 按(target, reason)去重。

cargo test 318 passed / 0 failed / 5 ignored(新增 7 条),cargo check 0 warning;两轮变异验证均被对应用例捕获。

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
docs(blueprint/agent-client-parity): P8c 补审查处置与 known_limits,P8d 记审查转入项(P8c)
All checks were successful
Push & PR Check / check (push) Successful in 1m3s
Push & PR Check / check (pull_request) Successful in 1m3s
7ef924127a
- review:独立审查 9 条发现逐条处置(7 条已修并补测试,2 条信息项转入 known_limits / P8d)。
- 自查条目「任一目标文件变化 digest 就变 ✅」改为准确语义(只绑定计划会写入或核对的文件,
  update 另绑定写入前哈希);按纪律报备:改的是验收表述,断言只增不减。
- known_limits:onboard 反向依赖投影层(过渡态)、Codex 信任静态预判的边界、shim unavailable 不阻断、
  bypassPermissions 未单列、plan_machine_steps 组装未单测。
- P8d 卡新增「审查转入」:bypass_permissions 单列、machine_changes 补产品组与 buff、.git/hooks,待统筹决定是否并入 acceptance。

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
lanrtop merged commit 04db0983e6 into master 2026-10-03 09:59:15 +08:00
Sign in to join this conversation.
No reviewers
No Label
No Milestone
No project
No Assignees
1 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: lanrtop/dev-manager-tauri#36
No description provided.