dev-manager-tauri/.blueprint/modules/project-dev-variables.md
lanrtop 0b0bc18ce4 docs(resource/dev-variables): record Codex approval blocker
Three isolated real-client attempts were denied before dispatch by approval policy=never. Keep V7 blocked pending an explicitly approved pilot.

Rules-Applied: R03,R10,R13
2026-09-26 16:57:58 +09:00

113 lines
11 KiB
Markdown
Raw 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.

---
id: project-dev-variables
name: 项目开发变量补给
status: in_progress
progress: 86
---
## 设计决策:人填写、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 typecheck` exit 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: blocked
- complexity: M
- files: docs/ai-context/project-context.yaml, docs/ai-context/agent-button-system.md, .blueprint/modules/project-dev-variables.md
- depends: V6
- acceptance: "agent_verify: [真实 Codex CLI + 合成数据] 跨会话登记→填写→读取→撤销后再读失败,项目目录无新 .env 或 token;[CLI] cargo test、pnpm typecheck、git diff --check 均 exit 0。human_verify: 不使用真实生产账号、不触发真实 PR/部署/数据库写入,确认日常开发摩擦是否下降。"
- blocked_reason: 真实 Codex CLI 三次隔离合成试点均在调用 `register_project_dev_variable` 时被客户端审批策略拒绝(`MCP tool call requires approval, but approval policy is never`),服务端未收到登记请求;未尝试绕过审批。需用户决定是否允许一次受控、可审批的真实客户端试点。
- pending: 真实 Codex CLI 调新工具完成整条业务链、正式打包版、桌面 UI 人眼便利性终验;V1 的真实 Codex 认证初始化与 V6 的真实 scoped HTTP 业务链已分别验证,不把组合证据冒充本卡端到端完成。