- V12:新增人用 Tauri command reveal_project_dev_variable(不进 MCP), 点「修改」预填原账号与密码,密码默认遮罩可切换显示 - V13:lian-jing skill 新增第 8 步——需要测试账号先 list/read, 没有则登记并请用户在 UI 填写;禁止对话索要明文;base-onboarding 升至 1.5.0 - 契约同步 project-context.yaml;materialize 提案顺延为 V14–V16 Feature-Confirmed: true Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
21 KiB
21 KiB
| id | name | status | progress |
|---|---|---|---|
| project-dev-variables | 项目开发变量补给 | in_progress | 91 |
设计决策:人填写、Agent 按项目取值(2026-09-26)
背景与目标
开发 Agent 在子项目验收时会反复需要本机专属配置,例如非生产测试账号、沙箱 API Key 或测试端点。用户希望在炼境的项目 UI 填写一次,此后 Agent 能稳定发现并读取,避免把值留在子项目 .env 中。首轮只解决 Agent 获取开发变量;子项目进程的 process.env 自动注入、CI、生产凭证及部署能力不在范围内。
本模块是已完成的 project-resource-broker 的明确例外:R1–R11 保持 Gitea/API Key/webhook 常态不泄露、按操作代办;只有用户明确放入“Agent 可读开发变量”的值,才允许经专门工具返回明文。不能将旧的 get_secret 禁令改写成通用任意取密接口。
方案选择
| 方案 | 收益 | 代价与结论 |
|---|---|---|
继续各项目 .env |
与现有脚本直接兼容 | 需要重复配置,仍有误提交与 Agent 访问不稳定的问题;保留为回退路径 |
| 炼境保存后由无认证 scoped MCP 返回值 | 最省实现步骤 | scoped URL 仅隔离误操作,不认证调用者;任何能访问本机端口的进程可能取值,否决 |
| 项目级开发变量 + 认证的 scoped MCP 单项读取 | 人填一次、Agent 跨会话复用、仓库无值 | Agent 会看到明文;需要验证 Codex 的认证头与本机凭据供应链,选定 |
信任与数据边界
- 仅支持登记在炼境的项目、明确标为开发用途且允许 Agent 读取的标量值。用户在对应项目 UI 填写、修改、撤销;Agent 可登记名称/用途和读取状态,但不能通过 MCP 设置值。非生产、低权限、可重置是使用规则,不能靠变量名称自动判断生产性质。
- SQLite 只保存
project_id、规范化变量名、用途、状态所需元数据和随机credential_ref;值保存在现有 Windows Credential Manager adapter。普通列表、readiness、日志、错误与文档不含值。 - 唯一明文出口是
read_project_dev_variable(project_id, name);必须同时验证真实项目绑定、scoped MCP 连接的项目 ID、客户端认证以及记录已由人配置。全局/mcp、未认证请求和跨项目请求必须拒绝。明文进入 Agent 工具结果与会话,因此本方案不声称对 Agent 保密。 - MCP 认证材料不得写入项目仓库、URL 或普通日志。Codex
http_headers_helper已用两次独立真实 CLI 会话验证:从 Windows Credential Manager 的随机合成项取得 bearer,真实 initialize 请求均带正确Authorization;失败不得降级成无认证接口。正式打包路径尚未验证。 - 同一 Windows 用户的恶意进程仍可能读取用户可访问的配置或受保护存储;认证只阻断无凭据的本机 HTTP 调用,不提供跨进程强隔离。其他客户端 adapter 未验证前不宣称兼容。
系统边界与预计文件
- 数据:
src-tauri/src/db.rs增加兼容 migration;新业务模块src-tauri/src/commands/dev_variables.rs注入数据库连接和CredentialStore,实现登记、保存、读取、撤销的单点边界。 - 认证:
src-tauri/src/mcp_server.rs/mcp_tools.rs在取值调用前验证项目绑定及 bearer;src-tauri/src/credential_store.rs存储随机认证材料;src-tauri/src/main.rs与mcp_inject.rs评估最小本机 header helper 和配置注入。旧 MCP 工具继续按原契约可用。 - 人的投影:
src-tauri/src/lib.rs注册 Tauri commands,src/lib/commands.ts同步类型与封装;在src/components/dashboard/ProjectCard.tsx就近入口接入项目级面板,值仅通过显式填写表单提交。 - Agent 投影:
src-tauri/src/mcp_tools.rs暴露登记需求、查询非秘密状态、单项取值;不提供批量导出、任意命令、HTTP/SSH/SQL 或部署代理。 - 契约同步:
docs/ai-context/project-context.yaml与docs/ai-context/agent-button-system.md说明例外与信任模型;保留旧设计历史,不将项目通用 API Key 改为 Agent 可读。
验证路径
[Codex CLI + 合成 MCP]用http_headers_helper提供合成 bearer 后初始化 → 服务端观测认证头;无 header/错误 header → 敏感工具拒绝;未通过则停止实现取值接口。[cargo test]用 in-memory SQLite + 模拟 CredentialStore 登记、保存、重启式重建后读取、撤销 → 状态正确,旧值不可再取,列表与错误不含合成值。[真实 scoped MCP]Agent 在p1登记E2E_PASSWORD,人通过同一本体保存合成值,Agent 单项读取 → 仅p1的已认证调用得到该值;p2、全局路由和无认证调用失败。human_verify:炼境项目 UI 的待填写、已配置、修改和撤销路径可用,默认不显示值;使用者判断是否比重复配置.env省事。
风险与阶段闸门
- 认证材料供应:helper 与客户端跨会话读取已用随机合成项验证,正式打包版尚未验证;若后续失败必须回到设计,不把 token 写进受 Git 跟踪的
.codex/config.toml。 - 明文工具结果:用户选择开发便利,Agent 可能在会话中持有值;先只用合成数据验证,不读取或迁移真实账号。生产和高权限凭证仍用原有代办/CI/运行时机制。
- 不替代运行时
.env:Agent 读取值不自动设置子进程环境变量;需要时另立任务,不在本迭代暗加export_env。 - 依赖 PR #22:从已提交的 R11 基线独立分支开发,先核验 PR #22 状态;不得把新改动混入已有 PR 或原
master脏工作区。
任务拆解
✅ V1 — 认证头 helper 真实客户端可行性
- status: done
- complexity: M
- files: src-tauri/src/main.rs, src-tauri/src/lib.rs, src-tauri/src/credential_store.rs, src-tauri/src/mcp_inject.rs
- depends: project-resource-broker
- acceptance: "agent_verify: [Codex CLI + 合成 MCP] 通过本机 helper 从模拟受保护存储提供 bearer → initialize 请求含正确 Authorization,重启客户端后仍可读取;helper 缺失/失败时连接或取值 fail closed,项目配置文件不含 token。未通过即停止 V2–V7。"
- evidence:
cargo test --locked codex_client_reads_windows_helper_header_across_sessions -- --ignored双会话通过;真实 exe helper 的读取/缺失 fail closed 测试通过;config 测试断言无 token。
✅ V2 — 敏感 MCP 调用认证与项目绑定
- status: done
- complexity: M
- files: src-tauri/src/mcp_server.rs, src-tauri/src/mcp_tools.rs, src-tauri/src/credential_store.rs
- depends: V1
- acceptance: "[cargo test] 合成 bearer 下 p1 的敏感 tools/call 认证门槛通过;无 header、错误 header、p2 和全局 /mcp 均在 dispatch 前拒绝,现有非敏感 scoped 工具保持可调用;cargo test 全绿。真实取值工具的 HTTP 端到端验收归 V6。"
- evidence:
sensitive_read_requires_matching_project_bearer_on_both_transports通过;完整cargo test --locked通过。
✅ V3 — 开发变量元数据与受保护存储本体
- status: done
- complexity: M
- files: src-tauri/src/db.rs, src-tauri/src/commands/dev_variables.rs, src-tauri/src/credential_store.rs
- depends: V2
- acceptance: "[cargo test] in-memory DB + 模拟 CredentialStore 完成登记、保存、覆盖、读取、撤销;模拟写入或删除失败时旧值/引用可恢复,列表序列化和错误不含合成值;旧 schema 经 migrate 后可用。"
- evidence:
dev_variables::tests与dev_variable_migration_adds_metadata_table_to_existing_database通过;数据库重开后仍可读取合成值,删除失败保留旧引用。
✅ V4 — Tauri 人用命令与前端契约
- status: done
- complexity: M
- files: src-tauri/src/commands/dev_variables.rs, src-tauri/src/lib.rs, src/lib/commands.ts
- depends: V3
- acceptance: "[Tauri command/cargo test] 以项目 ID 查询待填写/已配置、保存合成值、撤销 → 状态随真实存储变化;常态返回类型无 value,pnpm typecheck exit 0。"
- evidence:
tauri_commands_use_same_state_and_never_return_value_in_list通过,mise exec -- pnpm typecheckexit 0。
✅ V5 — 项目 UI 填写与撤销投影
- status: done
- complexity: M
- files: src/components/dashboard/ProjectCard.tsx, src/components/dashboard/ProjectDevVariablesPanel.tsx, src/lib/commands.ts
- depends: V4
- acceptance: "agent_verify: [Tauri command/cargo test] 填写、覆盖、撤销同一本体状态正确,pnpm typecheck exit 0。human_verify: 在对应项目可见用途与待填写状态,填写/修改/撤销顺手,常态不显示值。"
- human_verify: pending(未启动隔离分支的真实桌面 UI;交人终验时判断交互摩擦)
- evidence: Tauri command 真实函数路径测试与 TypeScript 类型检查通过;填写值从 React Query mutation 缓存中移除。
✅ V6 — Agent 登记、状态和单项读取工具
- status: done
- complexity: M
- files: src-tauri/src/mcp_tools.rs, src-tauri/src/commands/dev_variables.rs, src-tauri/src/mcp_server.rs
- depends: V4, V5
- acceptance: "[真实 scoped MCP] p1 Agent 登记 E2E_PASSWORD → UI 待填写;人保存合成值后已认证 Agent 单项读取成功;未认证、p2 和全局路由拒绝;tools/list 无批量导出或任意代理。"
- evidence:
scoped_dev_variable_http_pilot_denies_missing_wrong_cross_and_global_auth真实本机 HTTP 测试通过;撤销后读取失败,工具清单无批量出口。
✅ V7 — 契约、合成试点与改动边界验收
- status: done
- complexity: M
- files: docs/ai-context/project-context.yaml, docs/ai-context/agent-button-system.md, .blueprint/modules/project-dev-variables.md, src-tauri/src/mcp_server.rs
- depends: V6
- acceptance: "agent_verify: [真实 Codex CLI + 合成数据] 跨会话登记→填写→读取→撤销后再读失败,项目目录无新 .env 或 token;[CLI] cargo test、pnpm typecheck、git diff --check 均 exit 0。human_verify: 不使用真实生产账号、不触发真实 PR/部署/数据库写入,确认日常开发摩擦是否下降。"
- resolved_blocker: 原阻碍为非交互
exec默认 approval=never 拒绝写工具。2026-09-30 用户授权单次--approve-for-me(仅会话 A 登记这一写调用),沿用 R11 先例;未放宽全局策略,读工具无需审批。 - verification: "2026-09-30:新增 ignored 宿主
mcp_server::tests::isolated_dev_variables_client_pilot(内存 DB、随机合成账户、临时 scoped MCP,认证走真实dev-manager-tauri.exe --lianjing-mcp-headerhelper 取 Windows 凭据中的项目 bearer)。自检:helper exit 0;无认证 read_project_test_account → HTTP 403。真实 Codex CLI 0.155.0-alpha.9.2(gpt-5.6-terra)三个独立会话:A 登记 PILOT_LOGIN(environment=local)→ 宿主代人填写;B 新会话 list → configured=true、read → username=pilot-user-12abb974,与宿主生成值一致;宿主代人撤销后 C 新会话 read → 失败「开发变量尚未由用户填写」。finish 后宿主断言:状态全部未配置、目录无.env*、任何文件不含 MCP token 与测试密码、MCP 认证材料已删除、临时目录已清理,test ok。回归:cargo test --locked 227 passed / 5 ignored,pnpm typecheck exit 0,git diff --check 通过。" - human_verify: pending(真实桌面 UI 填写/撤销与日常开发摩擦是否下降,需人终验)
- unverified: "正式打包版 helper 路径;Claude/DeepSeek 等非 Codex 客户端的 bearer helper 取值;B 会话中模型把 list/read 各重复调用 3 次(结果一致,属模型行为,未影响断言);宿主强制终止时 Drop 不保证执行,可能残留合成 Windows 凭据。"
设计决策:测试账户一次填写与跨会话复用(2026-09-28)
背景
单个开发变量虽已能登记、填写和读取,但测试登录通常同时需要账号与密码。让 Agent 分别登记两个变量、让用户分别填写,仍有重复操作和半配置状态;本迭代把常见测试账户做成一个有语义的需求卡,不扩展到生产凭证或任意表单生成器。
方案选择
| 方案 | 取舍 |
|---|---|
| 两个独立变量由 UI 猜测配对 | 无迁移,但没有可靠的同一需求身份,保存时可能只写成功一个字段;不选 |
在现有开发变量记录上增加 test_account 类型,账号/密码作为一份受保护值原子保存 |
复用现有项目边界、Windows Credential Manager 与撤销路径,增加少量非秘密元数据;选定 |
| 新建通用表单/凭证库 | 能支持任意字段,却扩大明文出口与维护面;本轮不做 |
系统边界与受影响文件
src-tauri/src/db.rs:旧表兼容迁移,新增类型、目标系统和环境元数据;旧记录保持scalar。src-tauri/src/commands/dev_variables.rs:需求登记幂等;账号/密码按一份结构化值写入同一受保护引用,读写和撤销沿用项目校验;普通标量 API 不读取或覆盖测试账户记录。src-tauri/src/mcp_tools.rs、mcp_server.rs:项目级登记、状态发现、专用读取;读取遵守现有 bearer 门槛,未认证、跨项目和全局路由拒绝,不增加通用get_secret。src-tauri/src/lib.rs、src/lib/commands.ts、src/components/dashboard/ProjectDevVariablesPanel.tsx:一张卡一次填写账号与密码;常态列表仅展示目标系统、环境、用途和配置状态,不回显值。docs/ai-context/project-context.yaml、docs/ai-context/agent-button-system.md:同步外部契约与例外边界。
验证路径
[cargo test]旧 schema 迁移后旧标量仍可读;重复登记同一测试账户只得一张卡,语义冲突拒绝;一次保存后账号与密码同时可读,撤销后同时不可读,列表与错误不含合成值。[真实 scoped MCP]合成项目登记→Tauri 本体填写→已认证读取→跨项目、无认证及全局路由拒绝;工具目录没有批量导出或任意代理。human_verify:用户在项目面板一次填写账号与密码,后续会话无需重复填写;仅在人主动修改时出现输入框。
风险与闸门
- Agent 读取时会得到明文;本能力仅用于用户明确放入的非生产、低权限、可重置测试账户,不承诺对 Agent 保密。
- 真实 Codex 非交互
exec对写工具的客户端限制属于 V7 接入验证,不以错误标注只读或放宽全局策略绕过;业务层合成验收可以独立先做,真实客户端全链路未通过前不宣称日常可用。 - 不自动把值注入子进程环境、不写
.env、不接管生产账号、SSH、SQL、HTTP 或部署。
拆解建议
按类型与受保护存储 → scoped Agent 投影 → 人用单表单投影 → 合成链路与文档四卡实施;既有 V7 仍单独保持 blocked,直到真实客户端验证通过。
任务拆解:测试账户预设
✅ V8 — 测试账户类型与原子受保护存储
- status: done
- complexity: M
- files: src-tauri/src/db.rs, src-tauri/src/commands/dev_variables.rs
- depends: V6
- acceptance: "agent_verify: [cargo test] 旧 schema migrate 后旧 scalar 正常;register_test_account(project,name,service,environment,purpose) 重复调用只保留一条且语义冲突拒绝;save_test_account 一次写入合成账号/密码,read_test_account 同时返回,revoke 后同时不可读;列表及错误不含值,写入失败不留下半配置。"
- evidence:
cargo test --locked test_account4 项通过;旧表迁移幂等、登记幂等、类型隔离、一次写入/撤销和初次/覆盖失败恢复均由合成数据断言。
✅ V9 — Agent 测试账户需求与项目级读取
- status: done
- complexity: M
- files: src-tauri/src/mcp_tools.rs, src-tauri/src/mcp_server.rs
- depends: V8
- acceptance: "agent_verify: [真实 scoped MCP/cargo test] p1 登记一张合成测试账户需求、查询无秘密状态、已认证读取账号与密码;未认证、p2、全局 /mcp 拒绝读取,普通 scalar API 不能读取测试账户;tools/list 不含批量导出或任意代理。"
- evidence:
sensitive_read_requires_matching_project_bearer_on_both_transports与扩展后的scoped_dev_variable_http_pilot_denies_missing_wrong_cross_and_global_auth通过;新明文工具先接入认证门槛再暴露,真实本机 HTTP 拒绝未认证/错误认证/p2/全局调用。
✅ V10 — 一张卡填写账号与密码
- status: done
- complexity: M
- files: src-tauri/src/lib.rs, src/lib/commands.ts, src/components/dashboard/ProjectDevVariablesPanel.tsx
- depends: V8
- acceptance: "agent_verify: [Tauri command/cargo test + pnpm typecheck] UI 一次提交调用 save_test_account,状态从待填写到已配置,修改与撤销共用本体;常态 DTO、React Query 缓存与日志不含账号或密码,typecheck exit 0。human_verify: 项目面板一张卡一次填写两个字段,默认不回显值。"
- human_verify: pending(隔离分支真实桌面 UI 尚未由用户终验)
- evidence:
tauri_test_account_command_uses_same_state_without_listing_values通过,mise exec -- pnpm typecheckexit 0;UI 通过独立调用提交两字段,列表 DTO 只有非秘密元数据和配置状态。
✅ V11 — 合成复用验收与契约同步
- status: done
- complexity: M
- files: src-tauri/src/mcp_server.rs, docs/ai-context/project-context.yaml, docs/ai-context/agent-button-system.md
- depends: V9, V10
- acceptance: "agent_verify: [真实 scoped MCP/cargo test] 合成项目登记→同一本体填写→再次查询已配置→读取→撤销后读取失败,并确认目录没有新增 .env 或明文配置;cargo test、pnpm typecheck、git diff --check exit 0。human_verify: 真实桌面 UI 与真实 Agent 客户端跨会话便利性另报未验证,不冒充 V7 完成。"
- human_verify: pending(真实桌面 UI 和真实 Agent 客户端跨会话复用尚未终验;V7 保持 blocked)
- evidence: 真实本机 scoped MCP HTTP 合成链路逐步断言登记、待填写、受保护存储填写、复查已配置、项目 bearer 读取、撤销后拒绝;数据库重开复用测试通过。
cargo test --locked227 passed/4 ignored,mise exec -- pnpm typecheck与git diff --checkexit 0;未新增.env或明文配置文件。
任务拆解:UI 终验反馈与 agent 可发现性(2026-09-30)
来源:用户对 V7 的 human_verify 否决理由——「修改时空白,应像修改配置一样呈现旧值」「撤销无确认框」(后者为 bug,已由
f74fe11修复并加回归测试);以及 agent 侧可发现性缺口:接入包 skill 未提示「需要测试账号时先查炼境」,agent 会继续向人索要。
✅ V12 — 修改时显式查看原值(人用投影)
- status: done
- complexity: M
- files: src-tauri/src/commands/dev_variables.rs, src-tauri/src/lib.rs, src/lib/commands.ts, src/components/dashboard/ProjectDevVariablesPanel.tsx, docs/ai-context/project-context.yaml
- depends: V10
- acceptance: "agent_verify: [cargo test] reveal(已配置测试账户) → 账号与密码原值;reveal(标量) → 原值;未配置 → 错误;MCP tools/list 不含任何 reveal 工具。human_verify: 点「修改」→ 账号预填可见、密码预填但默认遮罩,点「显示」可见;保存后生效。"
- verification: "2026-09-30:cargo test reveal_returns_original_value_by_kind_only_for_humans 覆盖测试账户/标量取回、未配置与撤销后报错、MCP tools/list 不含 reveal;全量 cargo test --locked 228 passed / 5 ignored,pnpm typecheck exit 0,vitest 8 passed。"
- human_verify: pending(第二次 UI 终验)
- decision: 值仅经本机 Tauri command 在人显式点「修改」时取回,沿用 API Key R2 的显式 reveal 模式;不进 MCP,agent 取值仍只走已认证的 read_project_test_account。密码默认遮罩防截图/旁观泄露。
✅ V13 — 接入包 skill 提示 agent 先查炼境测试账户
- status: done
- complexity: S
- files: src-tauri/resources/onboarding/.agents/skills/lian-jing/SKILL.md, src-tauri/src/commands/onboarding.rs
- depends: V9
- acceptance: "agent_verify: [cargo test onboarding] base-onboarding 版本升级后旧版本项目报告 version_mismatch、新模板含测试账户指引。human_verify: 合并并重建后对 Enterprise apply_onboarding_pack,agent 需要登录时先查炼境而非向人索要。"
- verification: "2026-09-30:SKILL.md 新增第 8 步(先 list,已配置单项读取,否则登记并请用户在 UI 填写;禁止对话索要明文、禁止登记生产、禁止写入 Git 跟踪文件、认证被拒如实报告);BASE_ONBOARDING_PACK_VERSION 与模板 capability-packs.json 同步升至 1.5.0(仅升常量时 2 个 onboarding 测试失败,已同步修正);cargo test --locked 全绿。"
- human_verify: pending(分发到 Enterprise 后观察 agent 行为)