docs(blueprint/agent-client-parity): P8f 记实现、规范化规则与验证,链路实测待 P8e 合并后由统筹会话完成(P8f)
All checks were successful
Push & PR Check / check (push) Successful in 1m8s
All checks were successful
Push & PR Check / check (push) Successful in 1m8s
卡片保持 🔵:acceptance 的 [MCP 全局] register_project → plan_onboarding → onboard_project
依赖 P8e 才有的两个 MCP 工具。另记行为变化(UI 添加项目改为拒绝空路径、相对或不存在的路径、
ID 冲突)、其他登记入口的取舍、没验什么、known_limits 与有意偏差。
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
0851aa9783
commit
bd4ebaf096
@ -287,10 +287,19 @@ skill 只是五个接入面之一,兼容层覆盖全部五面。
|
|||||||
### 🔵 P8f — register_project 控制面投影
|
### 🔵 P8f — register_project 控制面投影
|
||||||
- status: in_progress
|
- status: in_progress
|
||||||
- complexity: S
|
- complexity: S
|
||||||
- files: src-tauri/src/commands/projects.rs, src-tauri/src/mcp_tools.rs
|
- files: src-tauri/src/commands/projects.rs, src-tauri/src/mcp_tools.rs, src-tauri/src/commands/templates.rs(实施扩展), src-tauri/src/commands/git_repos.rs(实施扩展), src/components/dashboard/BatchAddModal.tsx(实施扩展), docs/ai-context/project-context.yaml(T1)
|
||||||
- depends: P8-0
|
- depends: P8-0
|
||||||
- acceptance: "agent_verify: [cargo test] 空路径、相对路径、不存在的路径、重复登记都被拒绝,不建目录、不执行 git init。[MCP 全局] 对临时 git 仓库依次调用 register_project、plan_onboarding、onboard_project,结果为 done;项目路由的 tools/list 不含 register_project。"
|
- 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 本体,连接由调用方注入。
|
- 为什么(Q7):create_project 只写 DB、MCP 里也没有登记项目的工具,agent 复刻不了人「新建 → 接入」的操作路径(违反同源同边界)。只开放在全局路由,写操作须用户确认;抽出 create_project 本体,连接由调用方注入。
|
||||||
|
- 实现(2026-10-03,分支 feat/agent-client-parity-p8f):projects.rs 抽出登记本体 `register_project_records(conn, input)`(连接注入),UI `create_project` 只做取连接的薄包装,MCP `register_project` 经 `register_project_at(conn, path, name)` 换算入参后调同一本体——边界只写一处。本体只写数据库:不建目录、不执行 git init、不接入;依次拒绝空 ID、空路径、相对路径、不存在的目录、空名称、同一目录已登记、已存在的 ID;重复检查与 profile+workspace 插入放在同一 IMMEDIATE 事务(沿用 envoy 先查后插的做法),并发登记同一目录时后到者等前者提交后再查。workspace 插入由 `INSERT OR IGNORE` 改为 `INSERT`,ID 冲突不再静默返回成功。MCP `register_project`(schema 单独成函数,插在 scan_unregistered_projects 之后):入参 path(Windows 绝对路径,必填)与可选 name(缺省取目录名);project_id 由炼境生成 UUID(与 UI 选目录新建一致),归入当前空间(UI 首页按当前空间列项目);返回 JSON 并列入 STRUCTURED_TOOLS,structuredContent 带 project_id、name、path、space_id,摘要写明「只写登记、尚未接入,接入先 plan_onboarding 预览、经用户确认再 onboard_project」。is_project_scoped_tool 显式排除 register_project(与 unregister_project、setup_gitea_repo 并列),项目路由 tools/list 不含它,项目路由调用返回 scope_violation;annotations 为非只读、非破坏、非幂等。描述写明调用前须向用户列出目标目录与名称并取得确认。
|
||||||
|
- 重复路径规范化(`projects::registration_path_key`,纯字面):去首尾空白 → `/` 统一为 `\` → 去 `\\?\` 长路径前缀(`\\?\UNC\x` 还原为 `\\x`)→ 合并重复分隔符(保留 UNC 开头的 `\\`)→ 去尾部分隔符 → 小写。比较对象是每条已有登记的 win_path 与 wsl_path 两列:Windows 路径原样;WSL 的 Linux 路径按 `\\wsl.localhost\<settings.wsl_distro>\…` 换算(与 mcp_inject::project_root_with_conn 同口径),wsl_path 已是 Windows/UNC 形式的原样使用。命中即拒绝,错误写明「该目录已登记为项目 project_id=<已有 ID>(<登记路径>)……请直接使用该 project_id」。
|
||||||
|
- 其他登记入口(UI 取舍):① 模板新建(create_project_from_template)要先建目录、再跑初始化命令、最后入库,过不了本体「目录须已存在」,所以保持原流程不变,只在建目录前复用 `ensure_path_not_registered`(目录已删但登记仍在时,不再新建出第二条同目录登记);② 批量添加(batch_create_projects)保持「跳过不报错」语义,同一目录已登记(含同批前项)计入 skipped,提示文案改为「ID 已存在或目录已登记」,不加存在性与绝对路径校验(扫描结果必为已存在目录);③ 挂载仓库(mount_repo)给出路径时同样拒绝同一目录。
|
||||||
|
- 行为变化(报备):UI「添加项目」此前允许不选目录、允许相对或不存在的路径、ID 冲突时静默成功;现在与 MCP 共用本体,这些情况都报错(空路径登记曾让接入链路把文件写进炼境进程 cwd,见 mcp_inject::validate_registered_path 的 2026-07-15 回归)。WSL 平台以 wsl_path 为准校验,Linux 路径的存在性经 `\\wsl.localhost` 判定,settings.wsl_distro 与实际发行版不符时会被拒绝(此时 project_root 同样会解析错,提前暴露)。
|
||||||
|
- verification: "2026-10-03:全量 cargo test 335 passed / 0 failed / 5 ignored(master 为 325,新增 10 条),编译 0 warning;pnpm typecheck 通过。projects 新增 7 条:registration_path_key 归并大小写、斜杠方向、尾部分隔符、重复分隔符、长路径前缀,UNC 保留双分隔符,不同目录不归并;空串、空白、相对路径(含 cwd 下真实存在的 src)、盘符相对路径、不存在的目录全部拒绝,零行写入、无孤儿 profile、不存在的目录未被创建;同一临时目录以大写、小写、正斜杠、尾部分隔符等 5 种写法及改走 WSL 平台再登记均被拒绝,错误含 project_id=first,表内仍 1 行,目录内没有 .git;WSL Linux 路径的登记能按 UNC 写法查到;ID 冲突报错且原登记不被改写;register_project_at 生成 UUID、名称取目录名、归入当前空间、存去掉首尾空白的路径,同目录再登记的错误含已有 project_id;批量添加跳过已登记目录与同批重复目录。mcp_tools 新增 3 条:全局 tools/list 含 register_project(required=[path]、非只读非破坏、描述含用户确认、不建目录、git init、project_id),项目路由 tools/list 不含,schema 加上 project_id 后 is_project_scoped_tool 仍为 false;项目路由调用 register_project 返回 scope_violation;成功回执 structuredContent 带 project_id、摘要写明尚未接入。变异验证(先暂存,改源码,跑 projects:: 与 mcp_tools::,再从暂存区还原)9 处全部被杀:去掉重复目录检查(改 IMMEDIATE 事务前后各验一次)、比较键不小写、不去尾部分隔符、不统一斜杠方向、去掉目录存在校验、去掉绝对路径校验、批量添加不跳过同目录、is_project_scoped_tool 不排除 register_project。"
|
||||||
|
- 没验什么:① acceptance 的 [MCP 全局] 链路 register_project → plan_onboarding → onboard_project 依赖 P8e 才有的两个 MCP 工具,本卡只实现与测试 register_project;**链路实测待 P8e 合并后由统筹会话完成**(要在运行中的炼境上登记一个临时 git 仓库,属写运行中 DB,按授权规则处理),卡片保持 🔵。② 没在运行中的炼境上调过 register_project(未重编译安装,且会写运行中 DB)。③ IMMEDIATE 事务防并发重复只有结构保证,没有并发测试。④ 模板新建与挂载仓库的重复目录检查没有单测(Tauri command 直接取连接池,模板路径还要建目录、跑命令),由共用的 find_registration_by_path 测试间接覆盖。⑤ UI 添加项目的报错只看了代码路径(ProjectModal 以 toast 显示错误),没有人工点过。⑥ 本卡为 S 级,未做独立审查。
|
||||||
|
- known_limits: "① 规范化只做字面:`..`、符号链接、junction、8.3 短名与长名不归一;WSL 路径也按不区分大小写比较(Linux 上仅大小写不同的两个目录会被误判为同一目录,极少见)。② update_project 把路径改成一个已登记目录不拦(不在本卡范围);存量的同目录重复登记(如待用户处理的 beast-mode / 940dfbb4)不受影响,仍按统筹文档 §3 由用户执行数据合并。③ 批量添加的 wsl_convert 模式把 E:\x 存成 /mnt/e/x,与同目录的 Windows 登记比较键不同,查不出这类跨写法重复。④ scan_unregistered_projects 判定已登记仍是区分大小写的字面比较,可能把只差大小写的已登记目录列成未登记;对它调 register_project 会被本卡的重复检查拒绝并给出已有 project_id。"
|
||||||
|
- 有意偏差:① 未加 confirmed 参数——任务要求是「描述写明写操作须用户确认目标」;登记只写炼境 DB、可经 unregister_project 撤销,按 unregister_project 的先例只在描述里约束。② MCP 不收 project_id,统一由炼境生成 UUID,避免 agent 自拟 ID 与已有命名冲突。③ 重复目录检查扩到批量添加、模板新建、挂载仓库三个入口(files 标「实施扩展」):PR #34 审查 L1/L2 的根因是同一目录多条登记,只堵 create_project 和 MCP 两个入口治不了根。
|
||||||
|
- T1:新 MCP 工具 register_project 已同步 docs/ai-context/project-context.yaml「MCP 工具接口」段(paths 补 projects.rs,doc 补路由、本体、规范化与入参语义);Tauri command 签名未变,src/lib/commands.ts 无需改动。
|
||||||
|
|
||||||
### 📋 P8g — UI 一键接入
|
### 📋 P8g — UI 一键接入
|
||||||
- status: todo
|
- status: todo
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user