dev-manager-tauri/docs/tasks/2026-10-03-p8-onboarding-oneclick-design.md
lanrtop f8dc95c6ee
All checks were successful
Push & PR Check / check (push) Successful in 1m2s
docs(blueprint/agent-client-parity): P8 一键接入设计定稿,拆为 P8-0、P8a–P8g 与 P4-0、P4a–P4e,A1 改写为三段(P8-0)
三角度方案(最小编排 / 收敛流水线 / 本体收口)并行设计 + 综合评审,设计文档入库
docs/tasks/2026-10-03-p8-onboarding-oneclick-design.md(16 条已核实代码事实、风险
R1–R18、决策 Q1–Q11)。用户以 /goal 授权按推荐方向代为推进,11 项全部采纳推荐项。

- 主方案:同步、零模型的最小编排本体 + 可选异步探针;嫁接 plan/digest 写前确认、
  经真实 shim 的认证链重放、「已接入」单一判定、客户端适配器注册表
- A1 不前置:P8/P4 载体都在炼境进程内,离线写入会绕过进程内锁且有 MSIX 重定向
  风险,真正耦合是全局 pool() 而非 crate 边界;A1 改为 A1a(随 P8b 的适应度测试)、
  A1b(按需 CLI 薄客户端)、A1c(触发式拆 crate,四个触发条件)
- P4 分 L0 静态 / L1 零模型 / L3 无头会话;P8 只依赖 L1(P4a)
- 验收口径变更(Q1,报备):P8 由「三端无头 A–F 矩阵」改为「L1 零模型矩阵 + 可选
  L3 结果及是否过期」,理由写在 P8 卡
- P6 依赖 P8c(Q3);P4b 排在 A4 之后(Q8);manifest 清除「P4/P8 依赖 A1」

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 07:35:53 +09:00

42 KiB
Raw Blame History

P8 一键接入 与 P4 三端探针:设计决策(2026-10-03)

决策状态:2026-10-03 由三角度方案(最小编排 / 收敛流水线 / 本体收口)并行设计加综合评审产出。用户以 /goal 授权「按推荐方向代为推进」,§9 的 11 个问题全部采纳推荐项(已勾选),用户可随时推翻。其中 Q1 属于修改验收口径,理由见 §9 Q1 与 agent-client-parity P8 卡。 蓝图落点:agent-client-parity(P8-0、P8a–P8g、P4-0、P4a–P4e)、architecture-evolution(A1a/A1b/A1c)。下文「本会话」指设计评审会话(只读调研,未运行客户端)。

这次是只读调研,没有改任何文件,也没有提交。本会话 lian-jing MCP 连接失败(ECONNREFUSED),所以没调 get_diagnostics。下文结论都来自读代码、读蓝图和读本机探针素材,凡是没实测过的地方都标了「待实测」。

0. 评审结论

  • 主方案选方案一(最小编排本体:先做同步、零模型的接入,探针做成可选的异步任务,A1 不前置)。它离现有代码最近,能最快交付,而且不改接入包模板,所以不会触发全部子项目重新分发。
  • 嫁接方案二:写入前的 plan + digest 确认;用真实稳定入口重放认证链,做不调模型的检查;判定以客观证据为准,对失败做归因;把 bypassPermissions 默认值和 Codex 信任单列为人确认点;先做 spike 再写代码;断言探针无副作用。
  • 嫁接方案三:统一「已接入」的判定;客户端适配器注册表;架构适应度测试(让 A1-ready 由测试校验);矩阵每格标证据等级;L3 只在 canary 项目、指纹变化时跑,并按指纹缓存;A1 拆成三段。
  • 不采纳:
    • 方案二的 onboarding_runs 表和心跳锁。收敛式设计不需要运行状态,v1 用 runtime_logs 就够。
    • 方案二「首次接入默认跑 L3」。
    • 方案三「每天对全部项目跑 L1」,因为每个项目都要起一次 claude CLI,噪声大。
    • 方案三「Rust console CLI + skill 降级 + 接入包升版」,范围膨胀,要求全部子项目重新分发。
    • 方案一的「只靠 --allowedTools 收窄 Claude」,因为项目 settings 默认是 bypassPermissions,见风险 R8。

1. 已核实的代码事实(三份方案的关键论断逐条核过,另有补充)

# 事实 位置
F1 inject_project_mcp 返回 Result<(),String>。sync_claude_local 返回 (),refresh_dsh_patch 失败只写日志。Claude 和 DSH 两端的成败没有结构化结果 mcp_inject.rs:591, 1053, 1254
F2 ensure_standalone_group 只在项目还不在任何组里时才注入。已在组内的项目重跑接入包,不会修复本机注册 groups.rs:147-198
F3 「已接入」有三套判定:reinject_all_mcp 按组成员(project_group_map),DSH 白名单和根目录迁移按 token,list_all_projects 按 .claude/liangjing.json 是否存在 mcp_inject.rs:1417, 806, 1476;mcp_tools.rs:2058
F4 apply_onboarding_pack 是同步的 #[tauri::command],内部直接调用 buff,再到建组、注入,可能起 claude CLI(单次超时 20 秒),会卡界面 onboarding.rs:471
F5 create_project、batch_create_projects、create_project_from_template 只写 DB,不会触发接入。MCP 里也没有登记项目的工具,所以 agent 复刻不了「新建 → 接入」这条人的操作路径 projects.rs:182
F6 MCP 路径下 verify_onboarding 的运行态(server_observed_runtime_status)把 reachable/handshake 写死为 Ok,工具名取的是全局清单。UI 路径的 collect_runtime_evidence 会真做一次 loopback 握手,但不带认证、也不经过 shim,覆盖不到 F 项 settings.rs:561-584, 469-558
F7 verify_onboarding 包含「origin 指向 Gitea」「CI workflow 存在」「飞轮 commit 数据流入」三项,新项目必然不通过。所以一键接入的「完成」不能定义为 verify 全过 onboarding.rs:1093-1151
F8 注入用 or_insert 往项目 .claude/settings.json(受跟踪文件)写 permissions.defaultMode=bypassPermissions。一键接入会让这个默认值扩散到每个新项目 mcp_inject.rs:1281
F9 setup_gitea_repo 的 private 默认是 false,即创建公开仓库 mcp_tools.rs:388
F10 mcp_server.rs 已有 INITIALIZED_PROJECT_CLIENTS,记录项目路由的 initialize,并排除 lian-jing-readiness。这是服务端调用轨迹的雏形 mcp_server.rs:36-66
F11 run_claude_cli 超时只杀直接子进程,不杀进程树 mcp_inject.rs:505
F12 release 主 exe 是 windows_subsystem="windows",--lianjing-mcp-header 只能经管道使用 main.rs:2
F13 内部用到 pool() 的地方约 293 处,分布在 43 个文件。真正的耦合在全局连接池和进程内静态锁(MCP_OP_LOCK、DSH_PATCH_LOCK),拆 crate 解决不了 grep 统计
F14 P1 中 Codex 用的是 workspace-write + approval never。P7 中 -s read-only 下的 MCP 调用被 approval 拦截。DSH 经 app.asar 内的 cli.js 加 ELECTRON_RUN_AS_NODE 启动,Codex 在带哈希的 bin 目录里 %TEMP%\probe2、lj-p7-probe
F15 %TEMP% 下已有约 20 个手工探针目录(probe、probe2、probe3、lj-p7-probe、lj-p9-* 等),说明「每次重写探针」这种重复摩擦确实存在 本机
F16 inject_mcp 和 apply_onboarding_pack 的 schema 里有 project_id 参数,因此在项目路由上可见(子项目 agent 能对本项目调用) mcp_tools.rs:445

2. 三方案比较

维度 方案一 最小编排 方案二 收敛流水线 + 验证金字塔 方案三 本体收口 + A1 三段
管家准入 好:只组合现有本体,不改模板 好,但 L3 首次默认跑,额度消耗偏多 中:Rust CLI 加 skill 降级要升接入包版本,全部子项目重新分发
先本体后投影 好:onboard_project 本体,加 Tauri 和 MCP 两个投影 好:commands/onboard.rs 本体 最好:onboard/ 模块加适应度测试,由机器校验
写前确认 只有混合文件闸门 plan、diff、digest plan、diff、digest
P8 的验收证据 静态检查加运行态,但 MCP 路径的运行态是写死的(F6),偏弱 L1 自握手经过真实 shim,强 L1 配置回放加协议握手,强
「已接入」不一致 只列为风险 只列为风险 立卡修复
成本 最低,6 张核心卡 最高,10 张卡加运行记录表 中高,12 张卡加 console 二进制
与在途工作冲突 低 P8b 与 P6 冲突 P8-b1 与 P6 冲突,A1b 牵涉打包
明显缺陷 --allowedTools 收窄不成立 运行记录、心跳过度设计 每天全量 L1 有噪声;CLI 范围膨胀

3. 目标与非目标

目标

  1. 新建或添加项目后,一次确认就完成以下各步,并用零模型的 L1 矩阵验收:canonical context,接入包(规则、skill 双投影、Codex 托管块、蓝图规则),本机三端注册(token、shim、Claude local、DSH 白名单和会话插件)。
  2. 收敛式幂等:重跑等于修复;稳态下零写入、文件 mtime 不变。
  3. 每一步都返回结构化状态;unavailable 和 unknown 不当作成功报出。
  4. 项目文件改动只报告、不提交;写入前可以预览(见 Q2)。
  5. P4:三端兼容性表可以自动重跑。L3 按需触发、结果可比对、带指纹。
  6. 一个本体、两个投影(Tauri async command 和 MCP),同源同边界。

非目标

  • 不自动 commit 或 push。不自动建 Gitea 仓库、webhook、CI,不自动装工具链,这些只列进 next_actions。
  • 不修改客户端的全局安全设置(Codex 信任表、Claude 全局 permissions),不覆盖手写的同名 MCP 配置。
  • L3 不作为接入成功的门槛,也不作为 CI 闸门。不替 agent 判定完成,不替 agent 选模型。
  • 不做离线 CLI 写入,不拆 core crate。
  • P8 不改接入包模板。
  • GUI 形态(DSH 桌面、Codex Desktop、Claude 桌面)不在自动验证范围,归 human_verify。
  • 批量登记不自动接入。

4. 一键接入流程

入口分三种:

  • UI 新建或模板创建项目成功后,弹出「接入预览」。create_project 本身保持没有副作用。
  • 已有项目点「一键接入」。
  • agent 在用户确认目标项目后,调用 plan_onboarding(project_id)。

0【自动·只读,约 1 秒】预检。 任一项不过就 failed,零写入。

  • 项目已登记;根目录是存在的绝对路径;记录是否为 git 仓库以及 HEAD。
  • .lian-jing/context.json 已有不同的 project_id 时,标 blocked(identity_conflict)。
  • 控制面项目(mcp_scope=global):Claude local 跳过,DSH 写停止哨兵。
  • WSL 项目:Claude local 和 DSH 显式标 unsupported,不静默跳过。
  • 探测客户端:claude 加 git-bash、codex(ClientLocator,支持设置里覆盖路径)、~/.dsh;记录各端版本。
  • 检查 MCP /health 可达。
  • 检测炼境是否运行在 MSIX 包上下文中。

1【自动·只读,约 3 秒】生成计划(Q2 选 A 时)。

  • 项目文件逐项给出动作(create/update/append/noop/skip)、有上限的 diff,以及混合文件预警(P10 检测前移到写入之前)。
  • 本机变更清单:token、shim、Claude local(只读快路径判定,计划阶段只允许 claude mcp get)、DSH 托管块和插件、产品组和 buff、.git/hooks。
  • 单列两项:
    • bypassPermissions 默认值(F8);
    • Codex 是否已信任该目录(取决于 P4-0 的 H-P8-1)。
  • 生成 digest,覆盖项目文件新内容的哈希和本机动作清单。

【人确认 C1】应用前确认。

  • UI:点「确认接入」。agent:必须先把计划给用户看,得到明确同意后,再带 expected_digest 加 confirmed=true 调用。
  • 有 blocked 项(混合文件、身份冲突、手写同名条目、共享 Claude 项目键)时,确认按钮禁用,需要人逐项选「跳过 / 中止」。
    • 混合文件只能「先在子项目提交或丢弃块外改动,再重跑」,禁止字符串手术。

2【自动·写】应用。

  • 先重算计划;digest 不符就 refused,零写入。
  • 按依赖图执行,每步状态为 converged / applied / skipped / not_applicable / blocked(dep=…) / failed / needs_human:
    1. 身份:context.json 和 liangjing.json 兼容投影。
    2. 项目文件:模板、蓝图规则、Codex 托管块、settings.json 兼容条目,都经 WriteSink 写入,内容不变不写。
    3. lefthook install;缺可执行文件时记 needs_human。
    4. 产品组和 buff,作为兼容步骤保留。
    5. token,然后 shim,然后各客户端适配器:Claude local(经 claude CLI)、DSH 托管块和会话插件。手写同名配置记 manual_kept,不覆盖。
  • 某步失败时,只把依赖它的步骤标 blocked,其他照常执行,整体记 partial。不做自动回滚:项目文件靠 git 撤销,本机配置由下次收敛修复,整体撤销走现有的 remove_project_mcp。

3【自动·只读,约 5 到 10 秒】验收。

  • L0:verify_onboarding 静态检查,加上新的 clients 字段。
  • L1:零模型三端矩阵,见 §6。
  • 「完成(done)」只按三端可用子集判定:身份、托管块版本、两份 skill、Codex 块、Claude local、DSH 白名单、lefthook、L1 适用格全部 ready。origin、CI、飞轮三项进入 next_actions,不算失败(F7)。

4【自动·只读】报告。

  • 内容:分步状态,L1 矩阵(带证据等级),git status --porcelain 和 diff --stat 给出的待提交文件,断言 HEAD 没变,附一条建议的 commit message。
  • 提示哪些已打开的会话需要重开。
  • 给出最近一次 L3 结果,以及它是否已过期。

【人确认 C2,可选】L3 无头探针。 会消耗三端模型额度,约 2 到 3 分钟,默认不勾选。确认后以异步任务运行。

【人确认 C3】提交。 由人,或者经用户同意的子项目 agent,按子项目的 git 工作流提交。炼境不 add、不 commit。

【人确认 C4】next_actions。 机器只列出,不执行:

  • Gitea 建仓:要提醒「工具默认建公开仓库」,再配 webhook。
  • CI:plan_cicd_workflow 后由人确认,再 apply_cicd_workflow。
  • 工具链:由人确认后调用 prepare_toolchain(confirmed=true)。
  • lefthook 可执行文件。
  • 在 Codex 里信任该目录(如果 H-P8-1 成立)。
  • 托管块变化后重启 DSH 桌面 GUI。
  • Claude 交互模式首次打开目录时的 trust 确认。

【human_verify】 在 Claude Code、Codex、DSH 桌面 GUI 各新开一个会话,调用 read_project_test_account(name=LJ_PROBE_NONEXISTENT),返回「开发需求未登记」即为通过。

目标耗时(估算,未实测):plan 不超过 3 秒;首次 apply 不超过 30 秒(包含 claude CLI 写入);收敛后重跑不超过 10 秒(L1 里包含 claude mcp get 和 codex mcp get)。

5. A1 是否前置:结论是不前置,按触发条件后置

理由

  1. P8 和 P4 的验收载体是 Tauri command 和 MCP,本来就跑在炼境进程里。探针测的是「客户端连上运行中的炼境 MCP」,炼境离线时没有东西可测。所以「界面不运行也能用的 CLI」对它们没有用例。
  2. 离线写入有实际危害:
    • MCP_OP_LOCK、DSH_PATCH_LOCK 和 credential store 的锁只在进程内有效,~/.claude.json(经 claude CLI)、~/.dsh/cordis.patch.yml、Credential Manager、SQLite 会被两个进程竞争写;
    • agent 从 MSIX 客户端里调用时,AppData 写入可能被重定向(P3 已实证)。
  3. 真正的耦合是全局 pool()(F13)和进程内锁,拆 crate 解决不了。A1 是 L 级,会和进行中的 A4、P6 大面积冲突,把 P8 推迟数周。
  4. 复用主 exe 做 CLI 还要处理控制台挂接和 single-instance(F12),那是 A1 自己要解决的设计问题。

P8 需要的是 A1 的原则,不是 crate 拆分。 建议把 A1 改写为三段:

  • A1a(随 P8 落地):新代码统一放进 src-tauri/src/onboard/。约束如下:
    • 不 import tauri::,不调用 db::pool(,不写 #[tauri::command];
    • 连接、CredentialStore、ProcessRunner、ClientLocator 都由调用方注入;
    • 结果一律是 serde 结构体。 这些约束由 cargo 架构适应度测试 onboard_module_is_projection_free 机器校验。存量代码不扩散旧写法,旧入口按绞杀者模式逐步改成薄包装。
  • A1b(CLI 薄客户端,按需):P4e 只做 scripts/probe-clients.mjs,作为全局 /mcp 的 MCP 客户端,不新增协议面。Rust console 二进制 lianjing.exe 和 skill 降级等真实需求出现再做,比如「会话启动时 MCP 没连上,需要第二通道」被证明是常态。
  • A1c(core crate 拆分,触发式,L 级,届时先走 /architect):满足以下任一条就启动:
    1. 需要在没有 Tauri/WebView 的环境(例如 Linux CI)里跑本体;
    2. 确实需要离线写入,并且跨进程锁和包上下文检测已经具备;
    3. cargo test 的编译时延成为主要的反馈延迟;
    4. 出现第三个独立的消费者。

需要同步修正的蓝图位置:manifest 第 516 行「P4 依赖 A1」、第 525 行「P4/P8 依赖」、第 866 行注释,以及 A1「为什么」段里「P4/P8 可直接做成命令」的表述。这些改动归卡 P8-0。

6. P4 探针:定位与成本控制

定位。 P4 是三端兼容性表(Can I Use)的自动重跑器,也是 P8 的验收判官。分三层,越便宜的越先跑,前一层失败就不跑后一层:

层 内容 成本 何时运行
L0 静态 verify_onboarding,加上 clients 静态状态:读 ~/.claude.json 快路径、解析 DSH 块 毫秒级 每次 verify
L1 零模型 ① 客户端自省:Claude 在项目目录执行 claude mcp get lian-jing(Local 条目、本项目地址、Connected 状态);Codex 执行 codex mcp get lian-jing --json(待 H-P8-1 确认,不可用时退回解析配置);DSH 解析白名单并校验插件哈希。② 认证链重放:按各客户端的方式执行稳定 shim(Claude/Codex 经 cmd /C,DSH 直接执行),拿到认证头后在项目路由上依次调用 initialize、tools/list(与 tools_list_result_for_project 自省值比对,不写死数字)、get_diagnostics(D)、list_project_dev_variables(E)、read_project_test_account(LJ_PROBE_<nonce>)(F)。A 和 B 用静态证据 约 5–10 秒,零额度 每次接入,以及显式调用
L3 无头会话 三端同一只读任务、同一断言,判定以客户端事件流和服务端轨迹为准 2–3 分钟,消耗额度 只在显式触发时运行;建议在 canary(enterprise-system)项目上、指纹变化时跑

矩阵。 A–F 乘三端,另加全局项 G:无凭据泄露,且探针前后 git status 一致、没有非白名单的写调用。

证据等级。 每格标注证据等级,从弱到强为 static < client_introspection < protocol < session_event/server_trace;self_report 只作辅证,避免把 L1 的绿当成真实会话的绿。

格子状态。 取值为 pass / fail / unknown / not_installed / not_applicable / blocked / timeout。

按项目画像给期望值。

  • 控制面项目:E、F 为 not_applicable,C 期望全局工具集。
  • WSL 项目:DSH 为 unsupported。

L3 判定规则(来自方案二)。

  • C–F 只认客观信号:
    • Claude:init 事件里的 tools 和 skills;
    • Codex:--json 输出中的 mcp_tool_call 事件;
    • DSH:--json 事件;
    • 服务端:轨迹中的 clientInfo、认证结果,以及 LJ_PROBE nonce。
  • A:模型在不调用工具的前提下逐字摘录一句,摘录必须是规则文件磁盘文本的子串。
  • 模型自报的工具数和条目数只记作 model_fidelity,不影响格子结论。

失败归因按三分定位,从环境到投影到本体:

  • 环境:client_not_installed、client_auth_or_quota(记 unknown,不记 fail)、timeout、env_package_context。
  • 投影:route_global、not_connected、codex_project_untrusted、rules_not_loaded、skill_not_visible、client_policy_blocked。
  • 适配器:adapter_drift,即客户端输出格式变了,需要升级炼境适配器。
  • 模型:model_noncompliant。

成本控制

  1. 从不自动运行。MCP 调用必须带 confirmed=true(与 prepare_toolchain 同一模式),描述里写明会消耗额度、需要 2–3 分钟。只在全局路由开放;项目路由只提供只读的 get_client_parity。
  2. clients 参数可以只选部分端(例如只跑 DSH,约 15 秒)。L1 已判定未注册的一端直接跳过 L3。
  3. 串行执行,顺序 DSH → Claude → Codex,快的先跑,便于早失败,也便于按时间窗归属。每端用独立的 nonce。
  4. 超时(P4-0 实测后校准):DSH 60 秒、Claude 210 秒(基线出现过 138 秒)、Codex 120 秒。超时后用 Windows Job Object 杀整个进程树(F11 证明只杀子进程不够)。
  5. 全机单飞锁:重复调用 start 返回同一个 run_id,这也防止探针会话里的 agent 递归调用。
  6. 缓存:缓存键由客户端版本、接入包版本、项目工具目录 digest、注入配置哈希、路由类型、probe_spec_version 组成。7 天内命中直接复用,force=true 时重跑。
  7. 不自动重试。
  8. 模型:默认用各客户端自己的默认模型;用户可以在设置里为探针单独指定更便宜的模型(这是用户的决定,不是炼境选模型)。
  9. 子进程卫生:
    • 清掉 CLAUDECODE 和 CLAUDE_CODE_* 环境变量,stdin 置空,设 MCP_CONNECTION_NONBLOCKING=false 和 CLAUDE_CODE_GIT_BASH_PATH;
    • Claude 的收窄参数组合和 Codex 的沙箱/审批组合,都由 P4-0 实测后写死;
    • 炼境运行在 MSIX 包上下文中时,报告标注「结果可能不代表真实客户端环境」。
  10. 产物:脱敏后存到 %USERPROFILE%\.lian-jing\probe-runs\<run_id>\,每个项目保留最近 5 次,不放 AppData。泄露扫描在炼境进程内用真实 token 比对,token 不经过 CLI 或 agent。
  11. 已知开销:Codex 单次约 3.06 万 token(P1 实测)。三端合计估计 4–8 万 token(估算,未实测)。

准入与退出。 五条准入都满足;F15 的约 20 个临时目录就是「消除重复」的证据。退出条件:三端都原生支持标准、且连续多个版本矩阵不变时,降低 L3 频率或撤销。

7. 任务卡清单(全部为 S/M)

依赖顺序

P8-0 ──> P8a ──> P8b ──> P8c ──┐
  └────> P4-0 ─────────> P4a ──┴─> P8d ──> P8e ──> P8g
                         (P4a 也依赖 P8b)          ^
P8f(可选,独立;P8e 端到端验收前完成)─────────┘
A4 合并 ──> P4b ─┐
P4-0 ──> P4c ────┼─> P4d ──> P4e(含 UI 探针按钮)
P4a ─────────────┘
P6 与 P8c 的先后见 Q3

P8 的关键路径不依赖 L3:P8-0 → P8a → P8b → P8c → P4a → P8d → P8e → P8g。

卡片

P8-0 蓝图与依赖修正(S)

  • files:.blueprint/modules/agent-client-parity.md,.blueprint/modules/architecture-evolution.md,.blueprint/manifest.yaml
  • acceptance:
    • agent_verify:[YAML 解析] manifest.yaml 可解析;第 516、525 行的 note 和第 866 行注释不再写「P4/P8 依赖 A1」。[读文件] P8 的 depends 改为 P4a;acceptance 按 Q1 改写,并记录改口径的理由;P4 拆为 P4-0、P4a–P4e;A1 拆为 A1a/A1b/A1c,A1c 写明四个触发条件。
    • human_verify:用户确认 Q1、Q2、Q10。
  • 注:工作区里这两个模块文件、notes-archive、usage.json 有未提交改动(P6/P8/A4 的状态改为 in_progress),动手前先确认归属。

P4-0 探针前置实测(S,spike,零凭据,只能在主会话或后台任务里执行,不交给 Workflow 子 agent)

  • files:.blueprint/modules/agent-client-parity.md(假设实测结果节),src-tauri/tests/fixtures/client_probe/(脱敏夹具)
  • acceptance:agent_verify:[主会话 bash] 逐项给出结论并附原始日志路径。
    • H-P8-1:在新建的未信任目录里,Codex 是否加载项目 .codex/config.toml;codex mcp get lian-jing --json 能否反映项目配置和信任状态。
    • H-P8-2:项目 settings 为 bypassPermissions 时,哪组参数(--permission-mode / --tools / --allowedTools / --disallowedTools)能把可调用工具收窄到 lian-jing 只读三件加 Skill,同时 MCP 仍可调用。
    • H-P8-3:Codex 在 -s read-only 下 MCP 调用是否被拦截;如果被拦截,记录可用的最窄参数组合。
    • H-P8-4:claude -p 读到 system/init 后立即终止,是否零模型调用。可行的话,Claude 的 B、C 两项并入 L1。
    • H-P8-5:Codex MCP 工具调用的默认超时。
    • H-P8-6:DSH headless 入口的稳定定位方法,以及 --json 事件字段。
    • 每端一份夹具(事件流、stderr、版本、参数);grep 夹具不含 Bearer 和项目 token。任一假设不成立就回到设计,不硬编码绕过。

P8a 「已接入」单一判定(S)

  • files:src-tauri/src/mcp_inject.rs,src-tauri/src/mcp_tools.rs(list_all_projects)
  • acceptance:agent_verify:
    • [cargo test mcp_inject] 用 in-memory DB 加假 CredentialStore:有 token、不在任何组的项目进入 reinject_all_mcp 的目标集合;组成员仍在;token 已删的项目不在(锁内复查语义不变)。新函数的连接由调用方传入。
    • [cargo test mcp_tools] list_all_projects 的「已注入」与 DSH 白名单、根目录迁移调用同一个函数:删除 .claude/liangjing.json 不改变判定,删除 token 后判为未注入。

P8b 客户端适配器注册表 + 结构化注入结果 + A1a 适应度测试(M)

  • files:src-tauri/src/onboard/mod.rs(新增),src-tauri/src/onboard/clients.rs(新增,ClientAdapter trait 和 claude/codex/dsh 注册表),src-tauri/src/mcp_inject.rs(sync_claude_local 和 ensure_dsh_patch 改为返回结果枚举,保留日志,公开函数签名不变),src-tauri/src/commands/onboarding.rs(verify 增加 clients 附加字段),src-tauri/src/mcp_tools.rs(inject_mcp 文案改为按实际结果),src-tauri/src/lib.rs,src/lib/commands.ts
  • acceptance:agent_verify:
    • [cargo test onboard::clients] 注册表恰好列出三端,每端声明 detect、static_status、apply。sync_claude_local 的每条分支映射到一种 ClientOutcome(converged / applied / refreshed / manual_kept / other_project / unavailable{reason} / skipped{control_plane|wsl})。DSH 未安装记 not_installed;块外有手写条目记 manual_kept;缺 claude CLI 时 claude 记 unavailable,其余照常。原有 log_event 的类别和文案不变。
    • [cargo test] onboard_module_is_projection_free:src/onboard/ 下的源码不含 tauri::、db::pool(、#[tauri::command]。
    • [cargo test onboarding] 新增的 clients 字段不改变 checks 计数和 VerifyState;控制面和 WSL 记 not_applicable,不拉低 pass。
    • [MCP] 对 enterprise 连续两次 inject_mcp:第二次三端都是 converged,耗时不到 1 秒(未起 claude CLI),四个项目文件的 mtime 不变。

P8c 写入前计划:WriteSink + plan_onboarding(M,Q2 选 A 时做;选 B 时改为「混合文件闸门 needs_confirmation」,S)

  • files:src-tauri/src/onboard/plan.rs(新增),src-tauri/src/lib.rs(WriteSink trait,以 write_if_content_changed 作为真实写入汇),src-tauri/src/commands/onboarding.rs(dispatch_* 改经写入汇;MixedFileGuard 前移),src-tauri/src/commands/blueprint.rs(sync_blueprint_rules 改经写入汇),src-tauri/src/mcp_inject.rs(context、Codex 块、settings、liangjing.json 改经写入汇)
  • acceptance:agent_verify:
    • [cargo test onboard::plan] 在临时 git 仓库里:
      • 空仓库做 plan,全部是 create,目录快照前后字节一致;
      • 已接入的仓库连续两次 plan,全部 noop、digest 相同;任一目标文件变化,digest 就变;
      • 块外有未提交改动时,该文件带预警,且 plan.blocked 非空;
      • 内容相同但行尾为 CRLF,判 noop;超长 diff 截断并标注;
      • context.json 是别的 project_id 时,记 blocked(identity_conflict);
      • machine_changes 列出各本机步骤的预计动作,计划阶段假 claude CLI 只收到 mcp get。
    • [cargo test] 原有的 onboarding、blueprint、mcp_inject 测试全部通过,行为不变。

P4a L1 零模型三端检查(M)

  • files:src-tauri/src/onboard/probe_l1.rs(新增),src-tauri/src/onboard/clients.rs(各适配器的 probe_l1),src-tauri/src/mcp_server.rs(识别 lian-jing-l1-probe 并排除出 has_initialized_project_client),src-tauri/src/mcp_tools.rs(probe_client_parity,tier=l1),src-tauri/src/commands/onboarding.rs(async command),src/lib/commands.ts
  • acceptance:agent_verify:
    • [cargo test onboard::probe_l1] 用进程内桩 MCP 服务加哑 shim:适配器取配置,按客户端方式执行 helper,再依次 initialize、tools/list、tools/call,C/D/E/F 均为 ready,每格带证据等级。shim 输出错误 token 时 F 记 auth_failed;shim 缺失记 shim_missing;端口不可达记 unreachable;客户端未安装记 not_installed,不计失败。期望工具集取自 tools_list_result_for_project。结果 JSON 和日志里不含桩 token。
    • [MCP 全局] probe_client_parity(enterprise, tier=l1) 在 10 秒内返回,三端适用格 ready,并附版本号;对 dev-manager-tauri 调用时,Claude 和 DSH 记 not_applicable(control_plane)。

P8d onboard_project 编排本体(M)

  • files:src-tauri/src/onboard/apply.rs、report.rs(新增),src-tauri/src/commands/onboarding.rs(存量的 apply_onboarding_pack 一并改为 #[tauri::command(async)])
  • acceptance:agent_verify:[cargo test onboard::apply] 用临时 git 仓库、in-memory DB,以及假的 CredentialStore、ClientAdapter、ProcessRunner:
    1. plan 后 apply(expected_digest, confirmed=true):status 为 done,HEAD 不变,repo_changes 与 git status --porcelain 一致且都未提交。
    2. 立即再次 plan:全部 noop;再次 apply:全部 converged,mtime 不变。
    3. digest 不符或 confirmed=false:refused,零写入。
    4. Claude 适配器失败:partial,Codex 和 DSH 照常完成,L1 的 Claude 列记 blocked(dep=claude_local);修复后重跑得到 done。
    5. 未登记的 project_id、空路径或相对路径:failed,零写入。
    6. 非 git 目录:repo_changes 为 unavailable,status 不是 done。
    7. 新项目没有 origin、CI、飞轮数据:仍可以是 done,这三项进入 next_actions;Gitea 一项注明「默认公开」。
    8. 同一项目并发 apply:第二个返回 busy。

P8e 投影收口与契约同步(S)

  • files:src-tauri/src/mcp_tools.rs(plan_onboarding、onboard_project 的 schema,单独成函数以避开 json! 的递归上限;dispatch 里只留一行委托),src-tauri/src/lib.rs,src/lib/commands.ts,docs/ai-context/project-context.yaml,docs/ai-context/agent-button-system.md,AGENTS.md(架构速查补 src-tauri/src/onboard/)
  • acceptance:agent_verify:
    • [cargo test mcp_tools] 全局和项目路由的 tools/list 都包含这两个工具;在项目路由上强制本项目,传跨项目参数返回 scope_violation;onboard_project 的 required 包含 expected_digest 和 confirmed;annotations 中 plan 为 readOnly、apply 为 idempotent。
    • [pnpm typecheck] 通过。
    • [MCP 全局] 对 enterprise 做 plan,全部 noop;做 apply,结果 done 且各步都是 converged。
    • [读文件] project-context.yaml 已补上 plan、digest 和状态语义(T1)。

P8f register_project 控制面投影(S,可选,见 Q7)

  • files:src-tauri/src/commands/projects.rs(抽出 create_project 的本体,连接由调用方注入),src-tauri/src/mcp_tools.rs(只开放在全局路由,并在 is_project_scoped_tool 中排除)
  • acceptance:agent_verify:
    • [cargo test] 空路径、相对路径、不存在的路径、重复登记都被拒绝,不建目录,不执行 git init。
    • [MCP 全局] 对临时 git 仓库依次调用 register_project、plan_onboarding、onboard_project,结果为 done;项目路由的 tools/list 不含 register_project。

P8g UI 一键接入(M)

  • files:src/components/dashboard/OnboardPanel.tsx(新增),src/components/dashboard/onboardView.ts 及其 .test.ts(新增,报告到视图的纯函数),ProjectModal.tsx,BatchAddModal.tsx(只提供批量入口,不自动接入),src/components/manage/NewProjectModal.tsx,BlueprintModal.tsx(「应用接入包」改为先 plan 再 apply),src/lib/commands.ts
  • acceptance:
    • agent_verify:[pnpm typecheck && pnpm test] 视图纯函数:blocked 非空时确认按钮禁用,并列出文件和原因;not_installed 和 not_applicable 显示为灰色说明,不显示为红色失败;bypassPermissions 和 Codex 信任单独列出;确认时携带 expected_digest。
    • human_verify:
      1. 用一个临时 git 仓库新建项目,确认弹出预览。
      2. 确认后 30 秒内看到分步结果、L1 矩阵和「待你提交的 N 个文件」。
      3. 另试一次在预览里取消,确认目录零改动。
      4. 在该目录新开 Claude Code、Codex、DSH 桌面 GUI 会话,调用 LJ_PROBE_NONEXISTENT,确认得到「开发需求未登记」。

P4b MCP 服务端调用轨迹环(S,在 A4 合并后做,见 Q8)

  • files:src-tauri/src/mcp_observe.rs(新增),src-tauri/src/mcp_server.rs(单一挂点,扩展 F10 中已有的记录点)
  • acceptance:agent_verify:[cargo test mcp_observe] 在项目路由上依次 initialize(codex-mcp-client)、tools/list、调用 read_project_test_account(name=LJ_PROBE_A1B2C3D4),分别用无认证、错误认证、正确认证:
    • 依次记录 auth=none/invalid/valid、route、method、tool、截断后的 clientInfo/UA,以及 nonce;
    • Authorization 的值、bearer、其他参数值(例如 SECRET_X)都不进入记录;
    • 容量上限按 FIFO 淘汰;
    • readiness 和 l1-probe 的请求不进入轨迹;
    • 没有任何工具暴露原始轨迹。

P4c 探针判定本体(M,纯函数)

  • files:src-tauri/src/onboard/probe/{evaluate,adapters,redact}.rs(新增),src-tauri/src/envoy.rs(把敏感模式抽成共享函数)
  • acceptance:agent_verify:[cargo test onboard::probe]
    • P4-0 的真实夹具解析出的矩阵与期望一致。
    • 合成夹具逐一命中对应归因:route_global、auth_header_missing、token_mismatch、adapter_drift(判 unknown)、model_noncompliant、client_auth_or_quota(判 unknown)、rules_not_loaded,以及 credential_leak(落盘文本已脱敏)。
    • 控制面画像下 E、F 为 not_applicable。
    • compare 只列出变化的格子;probe_spec_version 不同时标记为不可比。
    • 判定函数没有任何 IO。

P4d 探针执行器 + 异步任务 + 持久化(M)

  • files:src-tauri/src/onboard/probe/runner.rs(ProcessRunner 和 ClientLocator trait、Job Object、环境清理、包上下文检测),src-tauri/src/onboard/probe/job.rs,src-tauri/src/db.rs(migrate() 新增 client_parity_runs 表)
  • acceptance:agent_verify:[cargo test onboard::probe::runner] 用假 ProcessRunner:
    • 超时:进程树全部退出,该端记 timeout,其他端照常。
    • 未安装:记 not_installed,耗时不到 1 秒。
    • L1 判定未注册的一端:零进程。
    • 子进程环境不含 CLAUDECODE 和 CLAUDE_CODE_*,含 MCP_CONNECTION_NONBLOCKING=false,stdin 为空;参数等于 P4-0 写死的组合。
    • 运行中再次 start:返回同一个 run_id;启动时遗留的 running 记录改标 interrupted。
    • 同一指纹 7 天内命中缓存;force=true 时重跑。
    • migrate() 在旧库上重复执行是幂等的。
    • 探针前后 git status 不一致时,记 probe_side_effect,整次失败。

P4e 探针投影 + CLI 薄客户端 + UI 按钮(M)

  • files:src-tauri/src/mcp_tools.rs(start_parity_probe 和 get_parity_probe 只在全局路由;get_client_parity 在项目路由只读),scripts/probe-clients.mjs,package.json(probe:clients),src/components/dashboard/OnboardPanel.tsx(探针按钮和矩阵),src/lib/commands.ts,docs/ai-context/agent-button-system.md,docs/ai-context/project-context.yaml,AGENTS.md(「炼境自己的 Agent 按钮清单」加一行)
  • acceptance:
    • agent_verify:
      • [MCP 全局] confirmed=false 时拒绝,且不起任何子进程;confirmed=true 时 1 秒内返回 run_id。
      • [MCP 全局] 轮询 enterprise,6 分钟内 done,三端 B–F 通过、G 通过,与 P1 基线一致;不做任何改动,用 force 立即重跑,diff 为空。
      • [MCP 全局] clients=["dsh"] 时 60 秒内完成。
      • [MCP 项目路由] 看不到 start_parity_probe。
      • [CLI] pnpm probe:clients <id> --wait:全部适用格通过时 exit 0,否则 exit 1;炼境不可达时 exit 3。
    • human_verify:人看一次矩阵,确认证据等级和回退标注可读。

8. 风险

# 风险 应对
R1 与在途工作冲突。P6 要改 onboarding.rs 的分发逻辑,和 P8c 改同一处;A4(rmcp)要改 mcp_server.rs 和 mcp_tools.rs,和 P4b、P8e、P4e 冲突 按 Q3、Q8 排序。新工具在 dispatch 里只留一行委托。开工前查 origin 和 Codex worktree 分支(目前没有 P6 或 A4 的代码分支)
R2 Codex 可能不加载未信任目录的项目配置,结果是「配置写了但不生效」 先由 P4-0 实证;成立的话列为 next_actions,炼境不写 ~/.codex 的信任表(Q5)
R3 bypassPermissions 默认值随一键接入扩散到每个新项目(F8) plan 中单列,默认值交给 Q4 决定
R4 P8b 之前直接编排会掩盖失败,把 Claude 或 DSH 的失败报成成功(F1) P8d 必须排在 P8b 之后
R5 隐式副作用链拆平后行为会变:已在组内的项目也会刷新本机注册,失败会显式暴露 有意为之,在 PR 中声明;独立产品组作为兼容步骤保留
R6 L1 只是配置回放,可能 L1 全绿而真实会话失败 每格标证据等级;版本变化时提示跑 L3;GUI 形态归 human_verify
R7 L3 的额度消耗和随机性;模型自报不可靠 C–F 只认客观证据;不自动重试;不作为 CI 闸门;额度或登录问题记 unknown
R8 项目 settings 默认 bypassPermissions,--allowedTools 在这种模式下大概率不构成限制(待 H-P8-2 实测确认),探针会话可能调用写工具 P4-0 写死收窄参数组合;外加副作用断言和服务端轨迹兜底
R9 Codex 在 read-only 下 MCP 调用被拦截,回退到 workspace-write 后模型可能写文件 比对 git status 前后,检查非白名单调用,违反即整次失败
R10 环境污染:MSIX 包上下文、继承 CLAUDECODE、GUI 进程的 PATH 不全 检测并在报告中标注;客户端路径允许在设置里覆盖
R11 杀进程树不彻底(F11),.cmd 包装下的 node 进程会成为孤儿 使用 Job Object
R12 MCP 单次调用超时:apply 最坏情况下 claude CLI 要跑 4 次,每次最多 20 秒,可能超过 Codex 的默认工具超时 主要调用方是 async 的 Tauri command 和控制面 Claude 会话;等 H-P8-5 实测后再决定是否把 apply 也改成异步任务
R13 L3 并发归属:用户同时开着的会话可能混进时间窗 F 项用 nonce 区分;C–E 靠串行、时间窗和 clientInfo;记为已知限制
R14 轨迹环越界,存进了参数值或请求头 只存元数据,用测试钉死
R15 依赖客户端私有实现(DSH 的 app.asar、Codex 的哈希 bin 目录,本机已有两个) 通过 ClientLocator 和设置覆盖定位;格式变化归因为 adapter_drift;适配器升级需要重建炼境
R16 T1 契约变更:新 MCP 工具、新 Tauri command、OnboardingVerification.clients、client_parity_runs 表 同步 commands.ts、project-context.yaml,并在 migrate() 中补 migration
R17 真实客户端验收超过 180 秒,会被 Workflow 子 agent 判为卡死 P4-0、P4e、P8g 的端到端验收只在主会话或后台任务里跑
R18 新项目的 verify 必然不全过(F7) 「完成」只按三端可用子集判定

9. 需要用户决定的问题([推荐] 为推荐项)

Q1 P8 验收口径

  • [推荐] 改为「L1 零模型矩阵(带证据等级)+ 可选 L3 结果及是否过期」。P8 只依赖 P4a,可以先于 L3 交付。
  • 保持原口径「三端无头 A–F 矩阵」。P8 要等 P4 全部完成。

Q2 写入前预览

  • [推荐] A:plan → digest 确认 → apply。通过 WriteSink 实现,与 CI 的 plan/apply 同一模式,并关闭 P10 遗留问题。
  • B:只做混合文件闸门 + 写后的 git diff 报告。少一张 M 卡,但写入前看不到具体改动。

Q3 P6 与 P8c 的先后

  • [推荐] P8c 先落地 WriteSink,P6 基于它实现。
  • P6 先合并,P8c 再适配。
  • 并行开发(不推荐,onboarding.rs 必然冲突)。

Q4 bypassPermissions 默认值

  • [推荐] 保持现状,但在 plan 和报告中单列,允许按项目取消勾选。
  • 新项目默认不写(这是行为变更,需要另写理由)。
  • 维持现在的静默写入。

Q5 Codex 信任(H-P8-1 成立时)

  • [推荐] 列入 next_actions,由用户在 Codex 里自己信任。
  • 逐项目确认后,由炼境写 ~/.codex/config.toml 的信任表(这是在改另一个工具的安全设置,不推荐)。

Q6 L3 什么时候跑

  • [推荐] 只在显式触发时跑;建议在 canary 项目上、指纹变化时跑;新项目接入默认不勾选。
  • 首次接入默认勾选。
  • 另外加每周金丝雀定时探针(默认关闭)。

Q7 register_project 的 MCP 投影(让 agent 能复刻「新建 → 接入」)

  • [推荐] 做。只开放在全局路由,写操作必须经用户确认。
  • 不做,新建项目只走 UI;agent 只能对已登记的项目验收。

Q8 P4b 与 A4 的先后

  • [推荐] A4 先合并,P4b 挂在 rmcp 的 dispatch/认证边界上(P8 不受影响)。
  • P4b 先合并,A4 迁移时顺带处理挂点。

Q9 CLI 形态

  • [推荐] P4e 只做 pnpm probe:clients 薄脚本(放在炼境仓库,调用全局 MCP)。
  • 现在就做 Rust console 版 lianjing.exe + skill 降级说明(要升接入包版本,全部子项目重新分发)。
  • 不做 CLI,P4 验收载体改为 MCP。

Q10 A1 改写

  • [推荐] 拆成三段:A1a 随 P8 落地(适应度测试),A1b 按需做 CLI 薄客户端,A1c 触发式拆 crate。
  • 维持原 A1 卡,并把 P8 排在它之后。

Q11 探针用的模型

  • [推荐] 用各客户端默认模型,设置里允许用户为探针单独指定更便宜的模型。
  • 固定用便宜模型。

10. 没验什么

  • lian-jing MCP 在本会话中没有连上,所以没有用 get_diagnostics 或 verify_onboarding 核对 enterprise 当前的运行态基线。实施 P8-0 之前,应该在炼境在线时复核一次。
  • 本次没有运行任何客户端。以下都只是依据蓝图记录或推断,没有实测:
    • H-P8-1 至 H-P8-6;
    • claude mcp get 的 Connected 状态是否意味着 helper 已被执行(依据 P1/H2 的记录推断);
    • bypassPermissions 下 --allowedTools 不构成限制(依据对 Claude Code 权限模型的理解,需要实测);
    • 各项耗时目标和三端合计 token 估算。
  • 所有代码结论都来自 master(10434d4)的只读阅读,没有跑 cargo test。工作区里的未提交改动没有动过,也没有判定归属。