docs(blueprint/agent-client-parity): P8 一键接入设计定稿,拆为 P8-0、P8a–P8g 与 P4-0、P4a–P4e,A1 改写为三段(P8-0)
All checks were successful
Push & PR Check / check (push) Successful in 1m2s
All checks were successful
Push & PR Check / check (push) Successful in 1m2s
三角度方案(最小编排 / 收敛流水线 / 本体收口)并行设计 + 综合评审,设计文档入库 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>
This commit is contained in:
parent
44f7f667a6
commit
f8dc95c6ee
@ -511,9 +511,9 @@ modules:
|
||||
name: 三端客户端对等
|
||||
area: infra
|
||||
status: in_progress
|
||||
progress: 64
|
||||
progress: 35
|
||||
updated: 2026-10-03
|
||||
note: "P1/P2/P3/P3b/P7/P9/P10 完成:三端(Claude Code、Codex 含 Desktop、DSH 含桌面 GUI)在 Enterprise 经 %USERPROFILE%\\.lian-jing 稳定入口连项目专用地址,人终验通过。Claude 条目经 claude CLI 写入、以 claude mcp get 为准;DSH 由托管块全局行(headless)+ 会话级插件(GUI/web/acp 按会话工作目录)路由;接入写入幂等;接入包改写块外带未提交改动的托管块文件时给出混合文件预警;P7 实测 MCP 服务器说明仅 Claude/DSH 带入模型上下文、Codex 不带,引导提示保留在 skill。待做 P4 探针固化(依赖 architecture-evolution A1)、P5、P6、P8。"
|
||||
note: "P1/P2/P3/P3b/P7/P9/P10 完成:三端(Claude Code、Codex 含 Desktop、DSH 含桌面 GUI)在 Enterprise 经 %USERPROFILE%\\.lian-jing 稳定入口连项目专用地址,人终验通过。Claude 条目经 claude CLI 写入、以 claude mcp get 为准;DSH 由托管块全局行(headless)+ 会话级插件(GUI/web/acp 按会话工作目录)路由;接入写入幂等;接入包改写块外带未提交改动的托管块文件时给出混合文件预警;P7 实测 MCP 服务器说明仅 Claude/DSH 带入模型上下文、Codex 不带,引导提示保留在 skill。2026-10-03 P8 一键接入设计定稿(docs/tasks/2026-10-03-p8-onboarding-oneclick-design.md):A1 不前置;P4 分 L0 静态 / L1 零模型 / L3 无头会话,P8 只依赖 L1(P4a);拆为 P8-0、P8a–P8g 与 P4-0、P4a–P4e(共 13 张待做 S/M 卡),P8 进行中;进度按拆分后卡数重算。待做 P5、P6(基于 P8c 的 WriteSink)。"
|
||||
position: [3500, 950]
|
||||
|
||||
- id: architecture-evolution
|
||||
@ -521,8 +521,8 @@ modules:
|
||||
area: infra
|
||||
status: planned
|
||||
progress: 0
|
||||
updated: 2026-10-01
|
||||
note: "技术栈评估:Tauri v2 + Rust + React/TS 适合管家方针,不换栈。按收益排序的结构改进:A1 本体与投影分离(core crate + CLI,P4/P8 依赖)、A2 前后端类型契约生成、A3 配置文本模板化、A4 MCP 改用官方 rmcp SDK。"
|
||||
updated: 2026-10-03
|
||||
note: "技术栈评估:Tauri v2 + Rust + React/TS 适合管家方针,不换栈。按收益排序的结构改进:A1 本体与投影分离(2026-10-03 改写为三段——A1a 随 agent-client-parity P8b 落地的投影无关适应度测试、A1b 按需 CLI 薄客户端、A1c 触发式拆 core crate;P4/P8 不再依赖拆 crate)、A2 前后端类型契约生成、A3 配置文本模板化、A4 MCP 改用官方 rmcp SDK(P4b 轨迹环排在其后)。"
|
||||
position: [3750, 950]
|
||||
|
||||
edges:
|
||||
@ -863,7 +863,7 @@ edges:
|
||||
- from: project-dev-variables
|
||||
to: agent-client-parity
|
||||
type: dependency
|
||||
# P4 探针固化、P8 一键接入以本体与投影分离(A1 的 CLI 投影)为承载。
|
||||
# A1a(onboard/ 模块投影无关约束)随 P8b 落地;P4b 轨迹环挂在 A4(rmcp)合并后的边界上。
|
||||
- from: architecture-evolution
|
||||
to: agent-client-parity
|
||||
type: dependency
|
||||
|
||||
@ -111,12 +111,59 @@ skill 只是五个接入面之一,兼容层覆盖全部五面。
|
||||
根因更正(2026-10-02):首次判断「Codex Desktop(MSIX)看不见其安装后在 AppData 下新建的顶层目录」是错的——当时的对照目录本身也是在 Claude 桌面版的包环境里建的。真正原因:worktree 版炼境由 Claude 桌面版(MSIX,Claude_pzs8sxrjxfjjc)里的 agent 启动,继承了其包环境,写到 %LOCALAPPDATA%\lian-jing 的入口被 Windows 重定向进 %LOCALAPPDATA%\Packages\Claude_pzs8sxrjxfjjc\LocalCache\Local\,只有 Claude 的进程树看得到。对照:经 WMI 启动的非包进程在 AppData 下新建的顶层目录,Codex 包内可见;%USERPROFILE% 根不做这种重定向。入口迁至 %USERPROFILE% 因此仍是正解(与炼境在什么环境里启动无关);据错误前提加的「exe 在 AppData 下就复制副本」逻辑已按「退出」原则撤销(exe 总在真实路径上)。"
|
||||
- known_limits: "旧 %LOCALAPPDATA% 入口仅为迁移期兼容,各子项目需重注入(会改动其受跟踪的 .codex/config.toml,需在子项目提交)。炼境若在 MSIX 应用进程树里启动,它写的其他 AppData 内容(如安装目录外的缓存)同样只对该应用可见,凡给其他客户端读的文件都不应放在 AppData。"
|
||||
|
||||
### 📋 P4 — 三端对等探针固化为炼境能力
|
||||
### 📋 P4 — 三端对等探针固化为炼境能力(2026-10-03 拆为 P4-0、P4a–P4e)
|
||||
- status: todo
|
||||
- complexity: L(已拆,见下)
|
||||
- depends: P1, P2, P3
|
||||
- acceptance: "由子卡承担:L1 零模型检查见 P4a,L3 无头会话的 [MCP 全局] 与 [CLI] pnpm probe:clients 见 P4e。原口径「[CLI] 一条命令对指定项目依次跑三端无头探针,输出 A–F × 三端矩阵与耗时,断言无凭据泄露;接入包升级后重跑结果可比对」整体保留在 P4e。"
|
||||
- 设计:`docs/tasks/2026-10-03-p8-onboarding-oneclick-design.md` §6。分三层,便宜的先跑、前一层失败不跑后一层:L0 静态(verify_onboarding + clients 静态状态);L1 零模型(客户端自省 + 经真实 shim 的认证链重放,在项目路由上调 C–F 工具,约 5–10 秒,零额度);L3 无头会话(三端同一只读任务、同一断言,2–3 分钟、消耗额度,只在显式触发时跑,只开放在全局路由,须 confirmed=true)。每格标证据等级(static < client_introspection < protocol < session_event/server_trace),失败按环境 / 投影 / 适配器 / 模型归因。
|
||||
- 准入证据:本机 %TEMP% 下已有约 20 个手工探针目录(probe、probe2、lj-p7-probe、lj-p9-* 等),每次重写探针是实测的重复摩擦。退出条件:三端都原生支持标准、且连续多个版本矩阵不变时,降低 L3 频率或撤销。
|
||||
|
||||
### 📋 P4-0 — 探针前置实测(spike)
|
||||
- status: todo
|
||||
- complexity: S
|
||||
- files: .blueprint/modules/agent-client-parity.md, src-tauri/tests/fixtures/client_probe/
|
||||
- depends: P8-0
|
||||
- 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;任一假设不成立就回到设计,不硬编码绕过。"
|
||||
- 约束:零凭据;真实客户端单次可能超过 180 秒,只在主会话或后台任务里执行,不交给 Workflow 子 agent。
|
||||
|
||||
### 📋 P4a — L1 零模型三端检查
|
||||
- status: todo
|
||||
- complexity: M
|
||||
- files: scripts/ 或 src-tauri/src/commands/(实现时定), docs/ai-context/agent-button-system.md
|
||||
- depends: P1, P2, P3
|
||||
- acceptance: "agent_verify: [CLI] 一条命令对指定项目依次跑三端无头探针,输出 A–F × 三端矩阵与耗时,断言无凭据泄露;接入包升级后重跑结果可比对。"
|
||||
- files: src-tauri/src/onboard/probe_l1.rs, src-tauri/src/onboard/clients.rs, src-tauri/src/mcp_server.rs, src-tauri/src/mcp_tools.rs, src-tauri/src/commands/onboarding.rs, src/lib/commands.ts
|
||||
- depends: P4-0, P8b
|
||||
- acceptance: "agent_verify: [cargo test onboard::probe_l1] 进程内桩 MCP 服务 + 哑 shim:适配器取配置、按客户端方式执行 helper,再依次 initialize、tools/list、tools/call,C/D/E/F 均 ready 且每格带证据等级;shim 输出错误 token 记 auth_failed,shim 缺失记 shim_missing,端口不可达记 unreachable,客户端未安装记 not_installed(不计失败);期望工具集取自 tools_list_result_for_project,不写死数字;结果 JSON 与日志不含桩 token。mcp_server 识别 lian-jing-l1-probe 并排除出 has_initialized_project_client。[MCP 全局] probe_client_parity(enterprise, tier=l1) 10 秒内返回,三端适用格 ready 并附版本号;对 dev-manager-tauri 调用时 Claude 与 DSH 记 not_applicable(control_plane)。"
|
||||
|
||||
### 📋 P4b — MCP 服务端调用轨迹环
|
||||
- status: todo
|
||||
- complexity: S
|
||||
- files: src-tauri/src/mcp_observe.rs, src-tauri/src/mcp_server.rs
|
||||
- depends: architecture-evolution A4
|
||||
- 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 请求不进入轨迹;没有任何工具暴露原始轨迹。"
|
||||
- 排序(Q8):A4(rmcp)先合并,P4b 挂在 rmcp 的 dispatch/认证边界上;扩展 mcp_server.rs 已有的 INITIALIZED_PROJECT_CLIENTS 记录点,单一挂点。P8 不受影响。
|
||||
|
||||
### 📋 P4c — 探针判定本体(纯函数)
|
||||
- status: todo
|
||||
- complexity: M
|
||||
- files: src-tauri/src/onboard/probe/evaluate.rs, src-tauri/src/onboard/probe/adapters.rs, src-tauri/src/onboard/probe/redact.rs, src-tauri/src/envoy.rs
|
||||
- depends: P4-0
|
||||
- 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。L3 的 C–F 只认客观信号(Claude init 事件的 tools/skills、Codex --json 的 mcp_tool_call、DSH --json 事件、服务端轨迹的 clientInfo/认证结果/nonce),模型自报只记 model_fidelity。"
|
||||
|
||||
### 📋 P4d — 探针执行器 + 异步任务 + 持久化
|
||||
- status: todo
|
||||
- complexity: M
|
||||
- files: src-tauri/src/onboard/probe/runner.rs, src-tauri/src/onboard/probe/job.rs, src-tauri/src/db.rs
|
||||
- depends: P4a, P4b, P4c
|
||||
- acceptance: "agent_verify: [cargo test onboard::probe::runner] 假 ProcessRunner:超时时进程树全部退出(Windows Job Object)、该端记 timeout、其他端照常;未安装记 not_installed 且耗时 <1 秒;L1 判定未注册的一端零进程;子进程环境不含 CLAUDECODE 与 CLAUDE_CODE_*、含 MCP_CONNECTION_NONBLOCKING=false、stdin 为空、参数等于 P4-0 写死的组合;运行中再次 start 返回同一 run_id,启动时遗留的 running 记录改标 interrupted;同一指纹 7 天内命中缓存、force=true 时重跑;migrate() 新增 client_parity_runs 表且在旧库上重复执行幂等;探针前后 git status 不一致记 probe_side_effect、整次失败。"
|
||||
- 成本控制:串行 DSH → Claude → Codex、每端独立 nonce、全机单飞锁、不自动重试、默认用各客户端默认模型(设置里可为探针单独指定更便宜的模型——用户的决定,不是炼境选模型);产物脱敏后存 %USERPROFILE%\.lian-jing\probe-runs\<run_id>\,每项目保留最近 5 次,不放 AppData;泄露扫描在炼境进程内用真实 token 比对,token 不经过 CLI 或 agent。
|
||||
|
||||
### 📋 P4e — 探针投影 + CLI 薄客户端 + UI 按钮
|
||||
- status: todo
|
||||
- complexity: M
|
||||
- files: src-tauri/src/mcp_tools.rs, scripts/probe-clients.mjs, package.json, src/components/dashboard/OnboardPanel.tsx, src/lib/commands.ts, docs/ai-context/agent-button-system.md, docs/ai-context/project-context.yaml, AGENTS.md
|
||||
- depends: P4d, P8g
|
||||
- acceptance: "agent_verify: [MCP 全局] start_parity_probe confirmed=false 时拒绝且不起任何子进程,confirmed=true 时 1 秒内返回 run_id;轮询 enterprise 6 分钟内 done,三端 B–F 通过、G(无凭据泄露、探针前后 git status 一致、无非白名单写调用)通过,与 P1 基线一致;不做任何改动用 force 立即重跑,diff 为空;clients=[\"dsh\"] 时 60 秒内完成。[MCP 项目路由] 看不到 start_parity_probe,只有只读 get_client_parity。[CLI] pnpm probe:clients <id> --wait:全部适用格通过 exit 0,否则 exit 1,炼境不可达 exit 3。human_verify: 人看一次矩阵,确认证据等级和回退标注可读。"
|
||||
- CLI 形态(Q9):只做调用全局 MCP 的薄脚本,不新增协议面;Rust console lianjing.exe 与 skill 降级见 architecture-evolution A1b(按需)。
|
||||
|
||||
### 📋 P5 — 测试账户落地到被 Git 忽略文件
|
||||
- status: todo
|
||||
@ -127,7 +174,7 @@ skill 只是五个接入面之一,兼容层覆盖全部五面。
|
||||
- status: todo
|
||||
- complexity: M
|
||||
- files: src-tauri/src/commands/onboarding.rs, src-tauri/resources/(实现时定)
|
||||
- depends: P2
|
||||
- depends: P2, P8c(2026-10-03 Q3:P8c 先落地 WriteSink,P6 的分发基于它实现,避免两边同时改 onboarding.rs 分发逻辑)
|
||||
- acceptance: "agent_verify: [cargo test] 通用 skill(architect、splitter、code-review 等)由一处模板按版本投影到各客户端目录(Claude user/project skills、Codex/DSH 的 .agents/skills);[三端无头探针] B 项三端看到同一组 skill 且内容一致。"
|
||||
- 为什么:2026-09-30 探针显示同一批 skill 在三端各维护一份(Claude `~/.claude/skills`、Codex `source-command-*` 副本、DSH 手写 `.dsh/skills`),是实际的重复与漂移源。先把 skill 中的确定性步骤下沉为炼境 MCP 工具,让 skill 变薄,再做投影。
|
||||
- 不做:炼境不执行推理类 skill(无模型、会丢失会话代码上下文、越过「不替 agent 思考」边界);不以运行时拉取 skill 正文作为主路径(炼境离线即失效、脱离 Git 分支)。
|
||||
@ -148,13 +195,81 @@ skill 只是五个接入面之一,兼容层覆盖全部五面。
|
||||
- known_limits: "Claude 与 DSH 只验了无头模式,Claude 只用了 sonnet-4-6;Codex 未验 TUI 与桌面版(桌面版可能走 app-server 路径,结论可能不同),也未验 SSE 传输。探针用的是桩服务的中英混合短说明,未用炼境真实说明(中文 238 字、UTF-8 500 字节,远低于 DSH 默认上限 32768 字节)实测。Codex 后续版本若开始带入服务器说明,按「检测行为而非嗅探版本」重跑本探针(P4 固化后纳入矩阵),三端都支持时再迁移。现有服务器说明五条要点中,「先读状态」「canonical project_id」「开发变量先查后登记」在 skill 有对应条目;「其他写操作须用户明确确认」与「unavailable/unknown 不得当作成功」在 skill 与 AGENTS.md 中只有分场景表述(合并 PR、CI workflow、工具链安装;MCP 不可用时不伪造诊断),Codex 会话只能从这些条目与工具描述获知——是否补成通用条目留待下次改 skill 时一并处理(需升接入包版本),本卡不改。
|
||||
已补齐(2026-10-03,分支 feat/agent-client-parity-p7-skill-rules):两条已作为通用边界写进 lian-jing skill 模板开头(双投影到 .agents 与 .claude),两条规则的核心句与服务器说明逐字一致;只读查询、登记开发需求元数据与回传炼境明确列为无需确认的例外,与服务器说明的「其他写操作」及 skill「回传不必逐条征得同意」相容。服务器说明的例外是泛称「不含秘密的开发需求元数据」,skill 例外括注同时列出两个登记工具 register_project_test_account 与 register_project_dev_variable(审查修复:初版只列前者,会把开发变量登记误归入须确认的写操作);同批修正 skill 第 8 条——开发变量改用 register_project_dev_variable 登记名称与用途,测试账号仍用 register_project_test_account(初版两者都指向后者)。接入包 base-onboarding 1.7.0→1.8.0(修复在 1.8.0 发布前,未再升版本,只更新指纹);mcp_server 新增 server_instruction_universal_rules_are_in_skill,服务器说明与 skill 任一侧缺这两条即失败,skill 例外行缺任一登记工具也失败。AGENTS.md 托管块未改,仍只有分场景表述;存量项目需重建安装炼境后重跑 apply_onboarding_pack 才拿到新 skill。"
|
||||
|
||||
### 📋 P8 — 一键接入:创建/添加子项目即完成三端可用
|
||||
### 🔵 P8 — 一键接入:创建/添加子项目即完成三端可用
|
||||
- status: in_progress
|
||||
- complexity: L(2026-10-03 设计评审后拆为 P8-0、P8a–P8g,均为 S/M)
|
||||
- depends: P4a(原为 P4;P8 只依赖 L1 零模型检查,不等 L3)
|
||||
- acceptance: "agent_verify: [Tauri command / MCP] 对新登记项目 plan_onboarding → 用户确认 → onboard_project(expected_digest, confirmed=true),依次完成 canonical context、接入包分发(规则、skill 双投影、Codex 托管块)、本机三端注册(Claude local、DSH 白名单与会话插件、shim、token),并以 L1 零模型三端矩阵(每格带证据等级)验收,附最近一次 L3 结果及是否过期;重复执行收敛(全部 converged、文件 mtime 不变);接入包对项目文件的改动只报告 diff、不自动提交。human_verify: 在 Claude Code、Codex、DSH 桌面 GUI 各新开会话调 read_project_test_account(name=LJ_PROBE_NONEXISTENT),得「开发需求未登记」。"
|
||||
- 验收口径变更(2026-10-03,Q1,按自验报告纪律报备):原口径「以三端无头探针给出 A–F 兼容矩阵」改为「L1 零模型矩阵 + 可选 L3 结果及是否过期」。理由:L3 每次消耗三端模型额度、需 2–3 分钟且有随机性,不宜作为每次接入的门槛;L1 经真实 shim 重放认证链、在项目路由上实际调用 C–F 工具,给出协议级证据;「L1 全绿而真实会话失败」的风险用证据等级标注、客户端版本变化时提示跑 L3(P4e)兜底;GUI 形态归 human_verify。决策方式:用户以 /goal 授权按推荐方向代为推进,11 个决策点全部采纳推荐项、未逐项确认,用户可随时推翻。
|
||||
- 「完成」的判定:只按三端可用子集(身份、托管块版本、两份 skill、Codex 块、Claude local、DSH 白名单、lefthook、L1 适用格全部 ready)。verify_onboarding 的 origin 指向 Gitea、CI workflow、飞轮 commit 三项对新项目必然不通过,进入 next_actions,不算失败。
|
||||
- 设计:`docs/tasks/2026-10-03-p8-onboarding-oneclick-design.md`(三角度方案 + 综合评审、16 条已核实代码事实、风险 R1–R18、决策 Q1–Q11)。主方案为最小编排本体(同步、零模型的接入 + 可选异步探针,A1 不前置),嫁接 plan/digest 写前确认、经真实 shim 的认证链重放、「已接入」单一判定、客户端适配器注册表与 A1a 适应度测试。
|
||||
- 决策(Q2–Q11 均采纳推荐项):写入前预览走 plan → digest 确认 → apply;bypassPermissions 默认值保持现状但在 plan 与报告中单列、可按项目取消;Codex 信任列入 next_actions,炼境不写 ~/.codex 信任表;L3 只在显式触发时跑,建议在 canary(enterprise)项目、指纹变化时跑;做 register_project 的全局路由投影;A4 先于 P4b;CLI 只做 pnpm probe:clients 薄脚本;A1 改写为三段;探针默认用各客户端默认模型。
|
||||
- 非目标:不自动 commit/push;不自动建 Gitea 仓库、webhook、CI,不自动装工具链(只列 next_actions,Gitea 一项注明「工具默认建公开仓库」);不改客户端全局安全设置,不覆盖手写同名 MCP 配置;L3 不作接入门槛、不作 CI 闸门;不做离线 CLI 写入,不拆 core crate;P8 不改接入包模板;批量登记不自动接入。
|
||||
- 为什么:2026-10-01 Enterprise 验收时,接入散落在 inject_mcp、apply_onboarding_pack、verify_onboarding 与手工探针多个步骤。按「分发 vs 查询」:随仓库走的文件由炼境分发一次、按版本升级;与机器相关的客户端注册由炼境在本机维护、不进仓库;易变信息走 MCP 查询。默认三端都接入(成本小,保留随时换客户端的能力);能用标准的不做专门适配(Codex、DSH 原生读 AGENTS.md 与 .agents/skills,只有 Claude 需要 CLAUDE.md 与 .claude/skills 投影);炼境离线时规则与 skill 仍可用。
|
||||
|
||||
### ✅ P8-0 — 蓝图与依赖修正
|
||||
- status: done
|
||||
- complexity: S
|
||||
- files: .blueprint/modules/agent-client-parity.md, .blueprint/modules/architecture-evolution.md, .blueprint/manifest.yaml, docs/tasks/2026-10-03-p8-onboarding-oneclick-design.md
|
||||
- acceptance: "agent_verify: [YAML 解析] manifest.yaml 可解析;manifest 不再写「P4/P8 依赖 A1」。[读文件] P8 depends 改为 P4a,acceptance 按 Q1 改写并记录理由;P4 拆为 P4-0、P4a–P4e;A1 拆为 A1a/A1b/A1c,A1c 写明四个触发条件。"
|
||||
- 依赖顺序:P8-0 → P8a → P8b → P8c → P4a(另依赖 P4-0)→ P8d → P8e → P8g;P8f 可选、独立,P8e 端到端验收前完成。P8 关键路径不依赖 L3。
|
||||
- verification: "2026-10-03:manifest.yaml 经 PyYAML safe_load 解析通过(agent-client-parity progress 35、edges 95 条);manifest 全文「P4/P8 依赖」「依赖 architecture-evolution A1」「A1 的 CLI 投影」0 处;设计文档入库 docs/tasks/2026-10-03-p8-onboarding-oneclick-design.md,§9 十一项推荐项已勾选。"
|
||||
|
||||
### 📋 P8a — 「已接入」单一判定
|
||||
- status: todo
|
||||
- complexity: S
|
||||
- files: src-tauri/src/mcp_inject.rs, src-tauri/src/mcp_tools.rs
|
||||
- depends: P8-0
|
||||
- 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 后判为未注入。"
|
||||
- 起因:「已接入」现有三套判定——reinject_all_mcp 按组成员、DSH 白名单与根目录迁移按 token、list_all_projects 按 .claude/liangjing.json 是否存在;ensure_standalone_group 只在项目不在任何组时才注入,已在组内的项目重跑接入包不会修复本机注册。
|
||||
|
||||
### 📋 P8b — 客户端适配器注册表 + 结构化注入结果 + A1a 适应度测试
|
||||
- status: todo
|
||||
- complexity: M
|
||||
- files: src-tauri/src/mcp_inject.rs, src-tauri/src/commands/onboarding.rs, src-tauri/src/commands/projects.rs(实现时定)
|
||||
- depends: P4
|
||||
- acceptance: "agent_verify: [Tauri command / MCP] 对新登记项目执行一次接入 → 依次完成 canonical context、接入包分发(规则、skill 双投影、Codex 托管块)、本机三端注册(Claude local、DSH 白名单、shim、token),并以三端无头探针给出 A–F 兼容矩阵;重复执行幂等;接入包对项目文件的改动只报告 diff、不自动提交。"
|
||||
- 为什么:2026-10-01 Enterprise 验收时,接入散落在 inject_mcp、apply_onboarding_pack、verify_onboarding 与手工探针多个步骤。按「分发 vs 查询」:随仓库走的文件由炼境分发一次、按版本升级;与机器相关的客户端注册由炼境在本机维护、不进仓库;易变信息走 MCP 查询。默认三端都接入(成本小,保留随时换客户端的能力);能用标准的不做专门适配(Codex、DSH 原生读 AGENTS.md 与 .agents/skills,只有 Claude 需要 CLAUDE.md 与 .claude/skills 投影);炼境离线时规则与 skill 仍可用。
|
||||
- files: src-tauri/src/onboard/mod.rs, src-tauri/src/onboard/clients.rs, src-tauri/src/mcp_inject.rs, src-tauri/src/commands/onboarding.rs, src-tauri/src/mcp_tools.rs, src-tauri/src/lib.rs, src/lib/commands.ts
|
||||
- depends: P8a
|
||||
- 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 不变。"
|
||||
- 起因:inject_project_mcp 返回 Result<(),String>,sync_claude_local 返回 (),refresh_dsh_patch 失败只写日志——Claude 与 DSH 的成败没有结构化结果,直接编排会把失败报成成功(R4),所以 P8d 必须排在本卡之后。
|
||||
|
||||
### 📋 P8c — 写入前计划:WriteSink + plan_onboarding
|
||||
- status: todo
|
||||
- complexity: M
|
||||
- files: src-tauri/src/onboard/plan.rs, src-tauri/src/lib.rs, src-tauri/src/commands/onboarding.rs, src-tauri/src/commands/blueprint.rs, src-tauri/src/mcp_inject.rs
|
||||
- depends: P8b
|
||||
- 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 测试全部通过、行为不变。"
|
||||
- 说明:WriteSink 以 write_if_content_changed 作为真实写入汇;P10 的混合文件检测前移到写入之前;与 CI 的 plan/apply 同一模式(Q2)。
|
||||
|
||||
### 📋 P8d — onboard_project 编排本体
|
||||
- status: todo
|
||||
- complexity: M
|
||||
- files: src-tauri/src/onboard/apply.rs, src-tauri/src/onboard/report.rs, src-tauri/src/commands/onboarding.rs
|
||||
- depends: P8c, P4a
|
||||
- acceptance: "agent_verify: [cargo test onboard::apply] 临时 git 仓库、in-memory DB,假 CredentialStore、ClientAdapter、ProcessRunner:① plan 后 apply(expected_digest, confirmed=true) → status=done,HEAD 不变,repo_changes 与 git status --porcelain 一致且都未提交;② 立即再次 plan 全部 noop,再次 apply 全部 converged、mtime 不变;③ digest 不符或 confirmed=false → refused、零写入;④ Claude 适配器失败 → partial,Codex 和 DSH 照常完成,L1 的 Claude 列记 blocked(dep=claude_local),修复后重跑得 done;⑤ 未登记的 project_id、空路径或相对路径 → failed、零写入;⑥ 非 git 目录 → repo_changes 为 unavailable、status 不是 done;⑦ 新项目没有 origin、CI、飞轮数据仍可 done,三项进 next_actions,Gitea 一项注明「默认公开」;⑧ 同一项目并发 apply,第二个返回 busy。"
|
||||
- 说明:存量 apply_onboarding_pack 一并改为 #[tauri::command(async)](现为同步,内部可能起 claude CLI,会卡界面)。某步失败只把依赖它的步骤标 blocked,其他照常,整体记 partial;不做自动回滚——项目文件靠 git 撤销,本机配置由下次收敛修复,整体撤销走 remove_project_mcp。
|
||||
|
||||
### 📋 P8e — 投影收口与契约同步
|
||||
- status: todo
|
||||
- complexity: S
|
||||
- files: src-tauri/src/mcp_tools.rs, src-tauri/src/lib.rs, src/lib/commands.ts, docs/ai-context/project-context.yaml, docs/ai-context/agent-button-system.md, AGENTS.md
|
||||
- depends: P8d
|
||||
- acceptance: "agent_verify: [cargo test mcp_tools] 全局和项目路由的 tools/list 都含 plan_onboarding 与 onboard_project;项目路由上强制本项目,传跨项目参数返回 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)。"
|
||||
- 说明:两个工具的 schema 单独成函数(避开 json! 递归上限),dispatch 里只留一行委托,减少与 A4(rmcp)迁移的冲突面;AGENTS.md 架构速查补 src-tauri/src/onboard/。
|
||||
|
||||
### 📋 P8f — register_project 控制面投影
|
||||
- status: todo
|
||||
- complexity: S
|
||||
- files: src-tauri/src/commands/projects.rs, src-tauri/src/mcp_tools.rs
|
||||
- depends: P8-0
|
||||
- acceptance: "agent_verify: [cargo test] 空路径、相对路径、不存在的路径、重复登记都被拒绝,不建目录、不执行 git init。[MCP 全局] 对临时 git 仓库依次调用 register_project、plan_onboarding、onboard_project,结果为 done;项目路由的 tools/list 不含 register_project。"
|
||||
- 为什么(Q7):create_project 只写 DB、MCP 里也没有登记项目的工具,agent 复刻不了人「新建 → 接入」的操作路径(违反同源同边界)。只开放在全局路由,写操作须用户确认;抽出 create_project 本体,连接由调用方注入。
|
||||
|
||||
### 📋 P8g — UI 一键接入
|
||||
- status: todo
|
||||
- complexity: M
|
||||
- files: src/components/dashboard/OnboardPanel.tsx, src/components/dashboard/onboardView.ts, src/components/dashboard/onboardView.test.ts, src/components/dashboard/ProjectModal.tsx, src/components/dashboard/BatchAddModal.tsx, src/components/manage/NewProjectModal.tsx, src/components/dashboard/BlueprintModal.tsx, src/lib/commands.ts
|
||||
- depends: P8e
|
||||
- acceptance: "agent_verify: [pnpm typecheck && pnpm test] 视图纯函数:blocked 非空时确认按钮禁用并列出文件和原因;not_installed 和 not_applicable 显示为灰色说明、不显示为红色失败;bypassPermissions 和 Codex 信任单独列出;确认时携带 expected_digest。human_verify: ① 用临时 git 仓库新建项目,弹出接入预览;② 确认后 30 秒内看到分步结果、L1 矩阵和「待你提交的 N 个文件」;③ 另试一次在预览里取消,目录零改动;④ 在该目录新开 Claude Code、Codex、DSH 桌面 GUI 会话调 LJ_PROBE_NONEXISTENT,得「开发需求未登记」。"
|
||||
- 说明:新建 / 模板创建成功后弹出「接入预览」,create_project 本身保持无副作用;批量添加只提供批量入口、不自动接入;BlueprintModal 的「应用接入包」改为先 plan 再 apply。
|
||||
|
||||
### ✅ P9 — DSH 桌面 GUI 按工作区连项目地址(会话级插件)
|
||||
- status: done
|
||||
|
||||
@ -13,12 +13,35 @@ progress: 0
|
||||
|
||||
## 任务拆解
|
||||
|
||||
### 📋 A1 — 本体与投影分离:core crate + CLI
|
||||
### 📋 A1 — 本体与投影分离(2026-10-03 改写为三段:A1a / A1b / A1c)
|
||||
- status: todo
|
||||
- complexity: L(已拆,见下)
|
||||
- 原卡:core crate + CLI——「业务逻辑位于不依赖 Tauri 的 core crate;同一 exe 或独立 cli 在炼境界面未运行时可执行 onboard / probe / diagnostics」。原「为什么」:核心逻辑写在 Tauri 应用内,炼境不运行时 agent 与脚本无法调用;拆分后 Tauri 界面、MCP 服务、命令行成为同一本体的三个投影。
|
||||
- 改写理由(2026-10-03 agent-client-parity P8 设计评审,`docs/tasks/2026-10-03-p8-onboarding-oneclick-design.md` §5):
|
||||
1. P8 与 P4 的验收载体是 Tauri command 和 MCP,本来就跑在炼境进程里;探针测的是「客户端连上运行中的炼境 MCP」,炼境离线时无物可测——「界面不运行也能用的 CLI」对它们没有用例。
|
||||
2. 离线写入有实际危害:MCP_OP_LOCK、DSH_PATCH_LOCK 与凭据存储的锁只在进程内有效,~/.claude.json(经 claude CLI)、~/.dsh/cordis.patch.yml、Credential Manager、SQLite 会被两个进程竞争写;agent 从 MSIX 客户端里调用时 AppData 写入可能被重定向(P3 已实证)。
|
||||
3. 真正的耦合是全局 pool()(约 293 处、43 个文件)和进程内静态锁,拆 crate 解决不了;A1 是 L 级,会与 A4、P6 大面积冲突,把 P8 推迟数周。
|
||||
4. 复用主 exe 做 CLI 还要处理控制台挂接(release 为 windows_subsystem="windows")和 single-instance,那是拆 crate 时才需要解决的设计问题。
|
||||
结论:P8 需要的是 A1 的原则(本体不依赖投影),不是 crate 拆分。
|
||||
|
||||
### 📋 A1a — onboard/ 模块的投影无关约束(随 agent-client-parity P8b 落地)
|
||||
- status: todo
|
||||
- complexity: S
|
||||
- depends: agent-client-parity P8b
|
||||
- acceptance: "agent_verify: [cargo test] onboard_module_is_projection_free——src-tauri/src/onboard/ 下的源码不含 tauri::、db::pool(、#[tauri::command];连接、CredentialStore、ProcessRunner、ClientLocator 都由调用方注入,结果一律是 serde 结构体。"
|
||||
- 说明:由 P8b 承载实现,P8b ✅ 时本卡同步 ✅。存量代码不扩散旧写法,旧入口按绞杀者模式逐步改成薄包装;A1-ready 由测试机器校验,而不是靠约定。
|
||||
|
||||
### 📋 A1b — CLI 薄客户端(按需)
|
||||
- status: todo
|
||||
- complexity: M
|
||||
- acceptance: "agent_verify: [CLI] 由真实需求定义;探针场景已由 agent-client-parity P4e 的 pnpm probe:clients(调用全局 MCP 的薄脚本,不新增协议面)覆盖。"
|
||||
- 触发:「会话启动时 MCP 没连上、需要第二通道」被证明是常态时,再做 Rust console 版 lianjing.exe 与 skill 降级说明(需升接入包版本、全部子项目重新分发)。
|
||||
|
||||
### 📋 A1c — core crate 拆分(触发式,届时先 /architect)
|
||||
- status: todo
|
||||
- complexity: L(先 /architect 再拆)
|
||||
- files: src-tauri/Cargo.toml(改为 workspace)、新增 core 与 cli crate(实现时定)
|
||||
- acceptance: "agent_verify: [cargo test] 业务逻辑位于不依赖 Tauri 的 core crate;[CLI] 同一 exe 或独立 cli 在炼境界面未运行时可执行 onboard / probe / diagnostics 等命令并返回结构化结果。"
|
||||
- 为什么:核心逻辑写在 Tauri 应用内,炼境不运行时 agent 与脚本无法调用。业务函数已按「连接由调用方注入」编写,拆出成本可控。拆分后 Tauri 界面、MCP 服务、命令行成为同一本体的三个投影,契合「一个本体 + 多个投影」原则;agent-client-parity 的 P4 探针、P8 一键接入可直接做成命令。shim 已在用 exe 的命令行模式(--lianjing-mcp-header),只是尚未系统化。
|
||||
- acceptance: "agent_verify: [cargo test] 业务逻辑位于不依赖 Tauri 的 core crate;具体验收在触发后的 /architect 设计中定义。"
|
||||
- 触发条件(满足任一即启动):① 需要在没有 Tauri/WebView 的环境(例如 Linux CI)里跑本体;② 确实需要离线写入,并且跨进程锁和包上下文检测已经具备;③ cargo test 的编译时延成为主要的反馈延迟;④ 出现第三个独立的消费者。
|
||||
|
||||
### 📋 A2 — 前后端类型契约自动生成
|
||||
- status: todo
|
||||
|
||||
442
docs/tasks/2026-10-03-p8-onboarding-oneclick-design.md
Normal file
442
docs/tasks/2026-10-03-p8-onboarding-oneclick-design.md
Normal file
@ -0,0 +1,442 @@
|
||||
# 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 验收口径**
|
||||
- [x] [推荐] 改为「L1 零模型矩阵(带证据等级)+ 可选 L3 结果及是否过期」。P8 只依赖 P4a,可以先于 L3 交付。
|
||||
- [ ] 保持原口径「三端无头 A–F 矩阵」。P8 要等 P4 全部完成。
|
||||
|
||||
**Q2 写入前预览**
|
||||
- [x] [推荐] A:plan → digest 确认 → apply。通过 WriteSink 实现,与 CI 的 plan/apply 同一模式,并关闭 P10 遗留问题。
|
||||
- [ ] B:只做混合文件闸门 + 写后的 git diff 报告。少一张 M 卡,但写入前看不到具体改动。
|
||||
|
||||
**Q3 P6 与 P8c 的先后**
|
||||
- [x] [推荐] P8c 先落地 WriteSink,P6 基于它实现。
|
||||
- [ ] P6 先合并,P8c 再适配。
|
||||
- [ ] 并行开发(不推荐,onboarding.rs 必然冲突)。
|
||||
|
||||
**Q4 bypassPermissions 默认值**
|
||||
- [x] [推荐] 保持现状,但在 plan 和报告中单列,允许按项目取消勾选。
|
||||
- [ ] 新项目默认不写(这是行为变更,需要另写理由)。
|
||||
- [ ] 维持现在的静默写入。
|
||||
|
||||
**Q5 Codex 信任**(H-P8-1 成立时)
|
||||
- [x] [推荐] 列入 next_actions,由用户在 Codex 里自己信任。
|
||||
- [ ] 逐项目确认后,由炼境写 `~/.codex/config.toml` 的信任表(这是在改另一个工具的安全设置,不推荐)。
|
||||
|
||||
**Q6 L3 什么时候跑**
|
||||
- [x] [推荐] 只在显式触发时跑;建议在 canary 项目上、指纹变化时跑;新项目接入默认不勾选。
|
||||
- [ ] 首次接入默认勾选。
|
||||
- [ ] 另外加每周金丝雀定时探针(默认关闭)。
|
||||
|
||||
**Q7 register_project 的 MCP 投影**(让 agent 能复刻「新建 → 接入」)
|
||||
- [x] [推荐] 做。只开放在全局路由,写操作必须经用户确认。
|
||||
- [ ] 不做,新建项目只走 UI;agent 只能对已登记的项目验收。
|
||||
|
||||
**Q8 P4b 与 A4 的先后**
|
||||
- [x] [推荐] A4 先合并,P4b 挂在 rmcp 的 dispatch/认证边界上(P8 不受影响)。
|
||||
- [ ] P4b 先合并,A4 迁移时顺带处理挂点。
|
||||
|
||||
**Q9 CLI 形态**
|
||||
- [x] [推荐] P4e 只做 `pnpm probe:clients` 薄脚本(放在炼境仓库,调用全局 MCP)。
|
||||
- [ ] 现在就做 Rust console 版 `lianjing.exe` + skill 降级说明(要升接入包版本,全部子项目重新分发)。
|
||||
- [ ] 不做 CLI,P4 验收载体改为 MCP。
|
||||
|
||||
**Q10 A1 改写**
|
||||
- [x] [推荐] 拆成三段:A1a 随 P8 落地(适应度测试),A1b 按需做 CLI 薄客户端,A1c 触发式拆 crate。
|
||||
- [ ] 维持原 A1 卡,并把 P8 排在它之后。
|
||||
|
||||
**Q11 探针用的模型**
|
||||
- [x] [推荐] 用各客户端默认模型,设置里允许用户为探针单独指定更便宜的模型。
|
||||
- [ ] 固定用便宜模型。
|
||||
|
||||
## 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。工作区里的未提交改动没有动过,也没有判定归属。
|
||||
Loading…
Reference in New Issue
Block a user