dev-manager-tauri/docs/ai-context/project-context.yaml
lanrtop 97578b2261
All checks were successful
Push & PR Check / check (push) Successful in 1m27s
Push & PR Check / check (pull_request) Successful in 1m6s
feat(onboard/parity): 一键接入 MCP 与 Tauri 投影、Codex 信任计 pending_user 与仓库布局说明(P8e)
- MCP 新增 plan_onboarding(readOnly)与 onboard_project(幂等写操作):schema 各自单独成函数,
  dispatch 一行委托到 mcp_inject 生产入口(中间只做 JSON 类型解析);全局与项目路由都可见,
  项目路由强制本项目、跨项目返回 scope_violation;onboard_project required 含 expected_digest
  与 confirmed;共用可选选项 bypass_permissions / skip_targets;进 STRUCTURED_TOOLS 并给一行摘要
- Tauri 新增同名 async 命令(async fn + spawn_blocking),commands.ts 同步 OnboardOptions、
  OnboardingPlan、OnboardReport 类型与 planOnboarding / onboardProject 封装
- 统筹决定(P8d 审查 ⑩):L1 Codex 列仅因 codex_project_untrusted 未就绪的格不计入 done,
  completion 单列「Codex 信任」(pending_user),next_actions 保留 codex_trust;仓库子目录与
  worktree 补 next_action repo_layout 说明为何到不了 done;非 git 维持不能 done
- 契约同步:project-context.yaml、agent-button-system.md、AGENTS.md 架构速查;P8d 卡记统筹决定落地
- cargo test 413 passed / 0 failed / 6 ignored;pnpm typecheck、pnpm test 通过;三轮 10 处变异全部被杀

Feature-Confirmed: true
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 19:50:21 +09:00

48 lines
22 KiB
YAML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 项目契约路径映射
# 由炼境根据技术栈扫描生成,技术栈变更后重新运行接入包生成
# 供 AGENTS.md 元规则 T1 查询:「哪个文件是这个项目的外部契约?」
contracts:
- label: API 接口(Axum)
paths:
- "src-tauri/src/mcp_server.rs"
doc: "MCP 仅监听 127.0.0.1;新项目使用 /mcp/projects/<project_id> scoped route,连接 scope 拒绝跨项目参数和全局管理写工具。scoped route 只服务已登记项目,未知 project_id 返回 404(数据库不可用同样拒绝)。各客户端经 %USERPROFILE%/.lian-jing/bin/lianjing-mcp-header.cmd 稳定入口取认证头(炼境每次启动指向当前 exe;入口不放 AppData,因炼境若在 MSIX 应用的进程树里启动(如由 Claude 桌面版里的 agent 启动),AppData 下新建的目录会被重定向到该应用的私有目录,其他客户端看不到,用户目录根不受此影响;旧 %LOCALAPPDATA% 入口在迁移期同步保留,verify_onboarding 报 Legacy 引导重注入):Codex 写项目 .codex/config.toml,Claude 写 local 条目(优先于 user 级全局;一律经 claude CLI、cwd=项目目录写入,项目键推导、文件锁与配置路径交给 Claude 自身,不直接编辑 ~/.claude.json;项目根或其祖先键下已有一致的托管条目则不起 CLI,否则以项目目录下 claude mcp get 的实际生效条目为准:本项目托管条目刷新/移除,他项目托管条目与手写条目不碰,无法确认现状一律不写;项目目录已不存在时只记录残留不写入),DSH 写 ~/.dsh/cordis.patch.yml 托管块(内嵌炼境登记的「项目根目录→project_id」白名单,仅 headless profile 由 !!js 从启动目录向上精确匹配根目录才选项目地址并取认证,web/acp/tui/桌面版一个进程可服务多个工作区,全局行一律走全局以防跨项目带认证;这些模式由同一托管块引用的会话级插件(随炼境打包,写到 %USERPROFILE%/.lian-jing/dsh/,文件名带版本号)在每个会话创建时按会话工作目录匹配白名单,命中才在该会话作用域挂项目地址并经稳定入口取认证,其他会话不受影响;任意目录里的 context.json 不作授权依据;未命中回退全局;项目增删与端口变更时重写)。Claude local 条目与 DSH 块只替换炼境托管的条目,手写同名配置不覆盖;移除项目时一并清理。旧 /mcp 只作兼容迁移。read_project_dev_variable 与 read_project_test_account 是两种有界开发值读取:仅 scoped 路由、匹配项目的本机 bearer 认证后单项读取;认证失败在 dispatch 前拒绝。路由变更需同步 Agent 按钮体系文档。"
- label: MCP 工具接口
paths:
- "src-tauri/src/mcp_tools.rs"
- "src-tauri/src/commands/delivery_status.rs"
- "src-tauri/src/commands/toolchain.rs"
- "src-tauri/src/commands/cicd.rs"
- "src-tauri/src/commands/gitea.rs"
- "src-tauri/src/commands/dev_variables.rs"
- "src-tauri/src/envoy.rs"
- "src-tauri/src/onboard/probe_l1.rs"
- "src-tauri/src/onboard/plan.rs"
- "src-tauri/src/onboard/report.rs"
- "src-tauri/src/commands/projects.rs"
doc: "工具 schema、dispatch 与调用说明必须同步;使者回传三件套:report_envoy_return 为项目级(三端在 scoped 地址可用;kind/confidence/suggestion/client 为枚举,scope 为 this_project|all_projects|project_type:<类型> 格式校验,content 压成单行且限长;无凭据字段,服务端对私钥头与常见令牌前缀做兜底拒绝,其余靠描述约束——内容会双写进子项目 .blueprint/notes-archive.jsonl 随项目进 Git;同项目同内容未处置时幂等返回原编号),resolve_envoy_return 与 list_envoy_returns 仅控制面(全局地址),子项目 agent 只回传不处置;list 默认只列未处置并报告已处置计数,include_resolved=true 附列回执,超出展示上限须明说不得静默截断,回传格式首行前缀【炼境使者回传】保持兼容(B4 沉默项目统计按前缀匹配);get_project_resource_status 为项目 scoped 只读状态,仅返回 Gitea PR/webhook 的状态与来源,不返回凭证、引用、URL;旧凭证未迁移时返回 pending_migration,不提前宣称 configured。MCP 不提供 reveal_api_key、get_secret 或任意 HTTP/SSH/SQL/部署代理。明文例外只限 read_project_dev_variable 的单项标量与 read_project_test_account 的单张非生产测试账户;均须匹配 scoped 项目及本机认证。register_project_test_account 只写非秘密需求元数据;列表不返回值,禁止批量导出。production delivery profile 由项目维护,炼境只读 Gitea 部署 context 与公开版本探针,双证据相符才能报告 deployed。get_delivery_status 的 PR/CI/review 默认按项目主工作区当前分支与本地 HEAD 查询;可选 pr_number(优先)或 branch 时改由 Gitea 解析 head 分支与 head SHA(指定分支无开放 PR 时取远端分支 head),CI 结论绑定该 SHA,PR 不存在返回 not_found;顶层 target 标明 mode(local_branch/pull_request/branch)、pr_number、branch、head_sha 与来源,git 字段始终反映主工作区;显式给出的非法 pr_number/branch 直接报错,不得静默回退主工作区。外部来源不可用时返回显式降级状态,不得伪造成功。verify_onboarding 必须同时返回静态接入 checks 与 configured/reachable/handshake/tools_ready/reload_required 运行态证据,配置存在不得替代运行可用。CI workflow 使用 plan_cicd_workflow 只读预览,用户确认相同 digest 后方可 apply_cicd_workflow 写入;该接口不接收凭证值且不触发 CI 或部署。unregister_project 与 UI 删除项目共用同一本体(projects::delete_project_registration):持接入写锁先清理 MCP 配置、再在同一事务内解除 Gitea 仓库绑定并删登记(与一键接入串行,P8d 审查 ①),释放写锁后尽力删除 Gitea 侧 webhook 与本机签名凭证;Gitea 轮询跳过所属项目已不存在的遗留绑定,且每个仓库写入前复核绑定仍在、项目仍登记,不向已删除或已解绑的项目 ID 写 commit 与 PR 事件;每个遗留绑定每次运行只记一次日志,日志写明清理路径(对该 ID 调 unregister_project)。register_project(P8f)只开放在全局路由(is_project_scoped_tool 显式排除,项目路由 tools/list 不含):与 UI create_project 共用登记本体 projects::register_project_records(连接注入),只写登记、不建目录、不执行 git init、不接入;拒绝空路径、相对路径、不存在的目录、已存在的 ID,以及同一目录的重复登记。唯一例外:UI 入口不选目录时沿用既有的概念占位登记(RootRequirement::ExistingDirOrPlaceholder,不校验目录、不查重复),MCP 入口不接受占位;是否改为显式占位概念待用户决定。同目录判定:已有登记按 platform 只取有效根目录(Windows 取 win_path、WSL 取 wsl_path,Linux 路径按 wsl.localhost 的 UNC 换算;切换平台残留的另一列不参与),只算字面键 projects::registration_path_key(与 DSH 白名单 mcp_inject::normalize_dsh_root 共用实现:去 Windows 长路径前缀、正反斜杠统一、合并重复分隔符、去尾部分隔符、不区分大小写);新目录在写事务外另算 canonical 键(canonicalize 去长路径前缀,消掉 8.3 短名与 .. 段),两键任一命中即拒绝,错误信息给出已有 project_id。Windows 目录落库为 canonical 规范形式(真实大小写、反斜杠,junction/符号链接/subst 解析到真实目标),WSL 路径原样落库。MCP 入参只收 Windows 绝对路径 path 与可选 name,project_id 由炼境生成(UUID),与落库的规范目录一并以 structuredContent 返回,归入当前空间;目录里遗留别的 project_id 的 .lian-jing/context.json 时回执带 warning(只读提示,不沿用旧 ID);描述要求调用前向用户列出目标并确认。scan_unregistered_projects 的已登记判定与父目录去重改用同一字面键。批量添加对同一目录跳过(计入 skipped);挂载仓库(mount_repo)在任何写入前做同一检查,profile 与 workspace 同事务写入,被拒绝不留孤儿 profile;模板新建在建目录前复用同一检查。probe_client_parity(P4a)只开放在全局路由、readOnly,tier 目前只有 l1:L1 零模型三端检查,Claude 用 claude mcp get 与坏模型名 init 自省、Codex 用 codex mcp get --json、DSH 读托管块白名单与会话插件哈希,再按各客户端方式执行稳定 helper 在其配置的项目地址上重放 initialize、tools/list(与 tools_list_result_for_project 比对)、get_diagnostics、list_project_dev_variables 与测试账户敏感读取(LJ_PROBE_<nonce>);返回 L1Report(A–F × 三端,每格 state/cause/evidence/detail,证据等级 static < client_introspection < protocol;not_installed、not_applicable 不计失败;控制面项目 Claude 与 DSH 整列 not_applicable),认证头只在炼境进程内使用、不进结果与日志;探针 initialize 自报 clientInfo.name=lian-jing-l1-probe,服务端与 lian-jing-readiness 一样不计入 has_initialized_project_client;检查期间本项目路由上的全部 initialize(含探针的 claude mcp get 健康检查与零模型 init,均以 claude-code 身份连接)也不计入,宁可多提示一次重启。协议重放只证明服务端视角:客户端自己未连上时 D–F 记 blocked,客户端看不到某工具(Claude init 工具名、Codex enabled_tools/disabled_tools)时对应格记 tools_mismatch;配置里的 helper 只有逐字等于炼境托管写法(当前或旧 %LOCALAPPDATA% 稳定入口 + 本项目 digest)才执行,否则记 unknown(manual_config);零模型 init 带 --settings {\"disableAllHooks\":true},只挂 type/url/headersHelper。一键接入两个工具(P8e)plan_onboarding(readOnly、幂等)与 onboard_project(非只读、非破坏、幂等:重复执行收敛)在全局与项目路由都开放:项目路由把 project_id 绑定为本项目(schema 改 const、可省略),跨项目参数返回 scope_violation;schema 各自单独成函数,dispatch 只一行委托 mcp_inject::plan_onboarding / mcp_inject::onboard_project(与同名 Tauri command 同一本体,投影不复制判定)。plan_onboarding 的 required 为 project_id,onboard_project 的 required 为 project_id、expected_digest、confirmed;两者共用可选选项 bypass_permissions(布尔,默认 true)与 skip_targets(字符串数组,取 plan 的 blocked[].target,只有 manual_entry / shared_key 可跳过,其余列进来照旧阻断),计划与应用必须同一组选项,否则 digest 不符被拒;选项、expected_digest、confirmed 类型不对在任何 IO 之前报错,缺省 confirmed 视为 false。描述约束 agent:先把计划(要改的文件、blocked、bypass_permissions、next_actions)给用户看,用户明确确认后才带同一 digest 与 confirmed=true 调 onboard_project;Codex 需用户在 Codex 里信任目录(炼境不写信任表);Gitea 建仓工具默认建公开仓库,私有须显式 private=true。两者进 STRUCTURED_TOOLS:structuredContent 为 OnboardingPlan / OnboardReport 原样结构(与 commands.ts 同名类型一致),content 为一行摘要(计划:文件动作计数、本机七步、阻断与跳过、bypassPermissions、Codex 信任、digest 与下一步;报告:本体 summary、待提交接入文件数与 next_actions 名);refused / busy / failed / partial 以报告 status 返回,不作工具错误。list_all_projects 与 scan_unregistered_projects 的引导改为 plan_onboarding → 用户确认 → onboard_project(inject_mcp 只做本机 MCP 注入)。"
- label: 被管理项目身份上下文
paths:
- "src-tauri/src/mcp_inject.rs"
- "src-tauri/src/commands/onboarding.rs"
- ".lian-jing/context.json"
- ".lian-jing/capability-packs.json"
- ".claude/liangjing.json"
doc: "`.lian-jing/context.json` 是 project_id 的工具无关单一事实来源;`.claude/liangjing.json` 仅为兼容投影;`.lian-jing/capability-packs.json` 是炼境分发基线的版本描述。lian-jing skill 以同一模板双投影:`.agents/skills/lian-jing/`(Codex、DSH 读取)与 `.claude/skills/lian-jing/`(Claude Code 不读 .agents/skills),两份内容必须一致。context.json 可选字段 mcp_scope=global 声明控制面项目(如炼境自身):Claude 不写 local 条目、DSH 不入白名单,保留全局地址与跨项目工具。迁移必须保留未知字段;验证必须返回显式状态与 upgrade_hint;升级仅覆盖炼境托管文件,绝不覆盖项目定制或手工 MCP 配置,且不得以客户端全局配置或项目 UI 身份替代项目根目录上下文。apply_onboarding_pack 的模板结果 TemplateResult 可带 warning(混合文件预警):写入前 AGENTS.md、docs/ai-context/git-workflow.md 或 CLAUDE.md 在 git 中已有托管块(ONBOARDING_MANAGED / BLUEPRINT_MANAGED)之外的未提交改动(未跟踪文件带块外内容也算;比较只忽略 CRLF/LF 与文末托管块连同其前空白——即炼境追加托管块带来的差异,块外空行增删与行尾空格均算改动),且本次确实改写了该文件时给出;只提示不阻断、块外内容不动,非 git 仓库或 git 不可用时静默。CLAUDE.md 只由蓝图规则同步改写,仅在预警时补一条结果。模板分发之后的步骤(lefthook 激活、buff)报错时,已改写的混合文件名并入错误信息返回。「已接入」只有一个判定:本机受保护存储里有项目 MCP token(mcp_inject::is_project_injected);DSH 白名单、根目录迁移、批量重注入(产品组成员 + 有 token 的独立项目)与 list_all_projects 同源,.claude/liangjing.json 不作判据。接入经客户端适配器注册表(onboard::clients::registry:Claude、Codex、DSH)逐端收敛并返回结构化结果 ClientOutcome(converged/applied/refreshed/removed/manual_kept/other_project/not_installed/not_applicable{control_plane|wsl}/deferred/missing/unavailable/needs_human/failed);inject_mcp 回执逐端列出,只有 Codex 托管块写入失败算整体失败;项目 .codex/config.toml 已手写同名 server 记 manual_kept,不再整体报错;verify_onboarding 附加 clients 字段(三端只读状态,不计入 checks、不影响 pass)。src-tauri/src/onboard/ 是一键接入本体模块,源码不得依赖 Tauri、不得直接取全局连接池(适应度测试 onboard_module_is_projection_free)。一键接入编排本体 onboard::apply(P8d;生产入口 mcp_inject::plan_onboarding 与 mcp_inject::onboard_project,P8e 的 MCP 工具与 Tauri command 同名投影都只一行委托它们):计划含项目根目录 root(将要写入的目录)、逐文件动作与 diff、本机七步 machine_changes(token、shim、claude、codex、dsh、product_group_buff、git_hooks)、blocked、skipped、bypass_permissions(本次会向 .claude/settings.json 新写入 permissions.defaultMode=bypassPermissions 时为 true)、codex_trust、next_actions 与 digest(绑定 root、文件、本机步骤、阻断项与跳过项);选项 bypass_permissions=false 即按项目取消,settings.json 缺 permissions 时改写显式 defaultMode=default 记住选择(之后的重注入同样只在缺省时写);选项 skip_targets 是人对阻断项逐项选「跳过」的 target:只有 manual_entry(手写同名条目)与 shared_key(共享项目键)可跳过,移入 skipped、对应一端 apply 时记 skipped(manual_kept|other_project)、不调用该端适配器、文件原样保留,身份冲突、混合文件与 needs_human 列进来也照旧阻断;跳过不等于该端可用,done 仍按三端可用子集判定。onboard_project(expected_digest, confirmed) 依次:同一项目已在执行 → busy;预检(未登记、空路径、相对路径、根目录不存在)→ failed;未确认 → refused;接入写锁内重新解析登记(等锁期间登记被删除 → failed;根目录或端口变了 → refused 并附当前计划),再重算计划,digest 不符或有阻断项 → refused 并附当前计划;以上均零写入。之后在写锁内按依赖执行 identity、project_files(与计划同一写入序列)、product_group_buff(只写 DB)、token → shim → 三端适配器(token 或 shim 未完成则 blocked(dep=…);Codex 配置即项目 .codex/config.toml,由 project_files 写入,codex 步只复核,本次写过记 applied),释放写锁后执行 git_hooks(lefthook install,非 git 仓库或仓库子目录 not_applicable,缺可执行文件 needs_human,本地可执行文件存在却起不来 failed 并附启动错误);每步状态 converged/applied/skipped/not_applicable/blocked/failed/needs_human;某步失败只阻断依赖它的步骤,整体 partial,不自动回滚。随后跑 L1:客户端步骤未完成的一端整列 blocked(cell.cause=dependency、cell.dep=步骤名)、不起进程。报告 status=done/partial/failed/refused/busy,done 只按三端可用子集判定(全部步骤 converged/applied/skipped/not_applicable,且 completion 中不带 pending_user 的项全部 ready:verify_onboarding 中除 origin、CI、飞轮、工具链、delivery profile 外的检查全部 ready,三端只读判定已收敛,L1 适用格全部就绪,git 仓库可报告改动且 HEAD 未变)。completion 每项为 {item, source(l0|clients|l1|repo), ready, detail, pending_user};统筹决定(P8d 审查 ⑩,P8e 实现):L1 的 Codex 列里仅因 cause=codex_project_untrusted 未就绪的格只等用户在 Codex 里信任目录(炼境不写 Codex 信任表),不计入「L1 适用格」,completion 单列一项「Codex 信任」(source=l1、ready=false、pending_user=true,不计入 done),next_actions 保留一条 codex_trust(计划已列则不重复,计划预判已信任而 L1 仍未信任时补一条);Codex 列其余原因(含 blocked)照常计入;L1Report.all_applicable_ready 保持 L1 自身的原始判定。非 git 目录 repo_changes 为 unavailable,到不了 done;项目根是仓库子目录或 .git 为文件(worktree、子模块)时 verify 的「lefthook hook 挂载」只认项目根 .git/hooks、不会就绪,到不了 done,next_actions 补一条 repo_layout 说明原因(外层仓库根或实际 hooks 目录)与怎样才能 done;written_files 为本次实际写入的项目文件;repo_changes 为 git status --porcelain --untracked-files=all -- . 与 git diff --stat -- .(限定在项目根内;非 git 为 unavailable),并分出 onboard_files(本次写入的,加上接入写入序列管理且仍未跟踪的,路径相对项目根)与 other_changes(用户原有改动等);建议提交信息与 next_actions 的 commit 一项(git add -- <onboard_files>)只针对接入文件,炼境不 add、不 commit;next_actions 含 commit、Gitea 建仓(注明工具默认建公开仓库)、CI、飞轮、工具链、lefthook、Codex 信任、仓库布局(repo_layout)、重启 DSH GUI、Claude 首次 trust 与重开已打开的会话(不据 reload_required 推断已重载)。一键接入执行了写入时另补一条 category=mcp_inject 的事件(token、shim 与三端步骤都完成记 info,否则 warn),get_diagnostics 的注入状态与设置页注入告警据此更新。删除登记(UI delete_project 与 unregister_project)在同一把接入写锁内先清理 MCP 配置、再删登记的数据库部分;端口变更的批量重注入在写锁内取目标快照。"
- label: Tauri command 接口(前后端契约)
paths:
- "src-tauri/src/commands/**/*.rs"
- "src/lib/commands.ts"
doc: "新增 command 时前后端必须同步。Gitea/API Key 普通 DTO 不含原始凭证;API Key 仅本机 UI 显式 reveal,get_project_resource_status 仅返回非秘密状态;项目开发需求列表 DTO 仅含名称、类型、目标系统、环境、用途、configured 与时间。本机 UI 对 test_account 一次提交账号/密码到同一受保护引用,允许修改或撤销;列表常态不含值,仅人显式点「修改」时经 reveal_project_dev_variable 取回原值(密码默认遮罩),该 command 不进 MCP;旧 scalar 保持兼容。旧 SQLite 明文列按 write→verify→事务切换引用并清除的顺序兼容迁移,失败需可恢复,不能降级明文。Agent readiness 必须保留 configured/reachable/handshake/tools_ready/reload_required 分层;Windows autostart 默认关闭,只允许正式安装版,debug/target 构建必须拒绝启用。probe_client_parity(async,阻塞线程池执行)与 MCP 同名工具共用 mcp_inject::probe_client_parity 本体,返回同一 L1Report(commands.ts 的 ClientParityL1Report);L1 格子可带 dep(只在一键接入编排里出现:cause=dependency,dep 为未完成的接入步骤名)。apply_onboarding_pack 为 #[tauri::command(async)](P8d:buff 建组会注入 MCP、可能起 claude CLI,lefthook install 为子进程且带 60 秒超时;在 Tauri 异步运行时的任务里执行,不占界面线程,但会占住一个运行时 worker,同 buff.rs、groups.rs 的写法),入参与返回不变。一键接入的 Tauri 投影(P8e)plan_onboarding(projectId, options?) → OnboardingPlan 与 onboard_project(projectId, expectedDigest, confirmed, options?) → OnboardReport 为 async fn + tokio::task::spawn_blocking(与 probe_client_parity 一致;不用 #[tauri::command(async)] 包同步函数,以免占住 MCP 服务端所在运行时的 worker 30 秒以上),与 MCP 同名工具一样只委托 mcp_inject 生产入口;options 可省略(OnboardOptions 默认:bypass_permissions=true、skip_targets 为空),计划与应用须传同一组;plan 的错误(未登记、路径无效等)以 Err 返回,onboard_project 的 refused / busy / failed / partial 一律以报告 status 返回,Err 只表示线程异常。commands.ts 的 OnboardOptions、OnboardingPlan、OnboardReport(含 completion[].pending_user、repo_changes.onboard_files / other_changes、written_files、plan.root / skipped)与 Rust 序列化结构一一对应。"
architecture_docs:
- "AGENTS.md"
- "docs/ai-context/agent-button-system.md"
agent_docs:
- "AGENTS.md"
- "docs/ai-context/project-brief.md"