Skip to content

Decision:项目蓝图 Workdir 事实源 ​

状态:implemented 类型:feature Owner:backend/package/yuxi/services/project_blueprint_service.py 日期:2026-09-24 关联 Feature:项目蓝图 Workdir 事实源

问题 ​

参谋板需要一条把模糊意图收敛为可演进方案的通道。项目蓝图覆盖产品规划、设计、工程与交互方案,需要随项目发展持续修订,并由项目数字员工起草、由人审阅编辑。上游 Yuxi 拥有 Project 与 Workdir 文件 capability,但没有“项目级方案文档”的事实源。

把蓝图正文放进数据库会与 Workdir 形成两份内容:Agent 通过文件工具写入文件,页面或接口写入数据库,双方都自称事实源并可能静默漂移。把蓝图直接放进 Workdir 根目录,会与用户已有目录冲突;文档名携带路径分隔符或 .. 时,写入还可能越出 Workdir 边界。

决策 ​

在 Project Workdir 的固定目录 .yuanlei/blueprint/ 承载蓝图文档,新增用例与 HTTP 端点,不新增数据库表。

  • 文档是固定目录内的单层小写 Markdown 文件名,形如 [a-z0-9][a-z0-9._-]*.md,长度不超过 120;名称在进入文件系统前归一,..、路径分隔符、非小写与非常规名一律 422。
  • 蓝图正文的事实 Owner 是文件。读取每次回读文件;写入以原子替换落盘。数据库没有蓝图行,直接改库不影响蓝图内容。
  • 写入按需幂等创建 .yuanlei 与 blueprint;任一层被普通文件或符号链接占用时以结构化 409 fail-closed。
  • 用例在 backend/package/yuxi/services/project_blueprint_service.py;路由在 backend/server/routers/project_blueprint_router.py,暴露 GET /api/projects/{id}/blueprint、GET/PUT /api/projects/{id}/blueprint/{name}。
  • 鉴权复用 Project 归属查询:仅当前用户 active、selectable 的 Project 可读可写,其他情况 404。
  • Agent 侧在 build_prompt_with_context 的文件系统提示中声明 .yuanlei/blueprint/ 为蓝图固定目录,项目数字员工用既有文件工具起草,人通过 HTTP 端点编辑。
  • 决策的 proposed → implemented 生命周期继续由治理域 governance_decisions 拥有;蓝图记录方案内容,决策记录结论与理由,两者的关联通过议题/决策标题与正文引用表达,不在蓝图文件上复制决策状态。

替代方案 ​

  • 蓝图正文进数据库:拒绝。会产生第二份内容与静默漂移,且数据库无法原生表达可 diff 的演进。
  • 蓝图放在 Workdir 根目录的 blueprint/:拒绝。linked 模式下可能命中用户已有同名目录,读取到无关文件。
  • 为蓝图增加数据库 revision 与 hash 对账:拒绝作为本期范围。蓝图正文本身可进 Git,revision 对账留给确实需要并发写控制的场景;引入会新增第二状态 Owner。
  • 为 Agent 新增专用蓝图读写工具:拒绝作为本期范围。项目数字员工已有受 Workdir 约束的文件工具,提示中声明固定目录为零新增抽象的等价路径。
  • 由前端直接读写文件系统:拒绝。前端守卫不是授权边界,读写必须经后端 Project 归属校验。

后果 ​

  • 蓝图与 Workdir 同生命周期,可进 Git、可 diff、可被 Agent 与人共同编辑;不存在数据库正文副本。
  • 蓝图目录是元垒在文件系统上的命名空间约定,与元垒持久化结构进独立域的原则一致,降低与用户内容冲突的风险。
  • 本记录不承诺蓝图的版本对账、并发冲突解决与前端展示入口;这些能力需要时另行决策。
  • 上游同步时,本用例与端点为元垒新增面,不随上游覆盖删除;build_prompt_with_context 的提示行是元垒对上游文件的最小差异,冲突时按业务语义保留。

验证 ​

验收主张失败面语义 Owner直接证据 / 命令负向案例当前结果
蓝图正文事实 Owner 是 Workdir 文件数据库与文件两份内容project_blueprint_service.py + Workspacetest/integration/services/test_project_blueprint_service.py::test_blueprint_round_trip_is_filesystem_owned直接改写磁盘文件后读取回读新内容Passed
文档名越界被拒写到 Workdir 之外validate_blueprint_nametest/unit/services/test_project_blueprint_service.py../escape.md、sub/escape.md、大写与非 .md 名 422 且不落盘Passed
目录被非目录占用 fail-closed静默覆盖或未报告失败_ensure_blueprint_directorytest/integration/services/test_project_blueprint_service.py::test_blueprint_conflicts_when_directory_is_file.yuanlei 为普通文件时 409Passed
仅所属用户可见项目可读写越权读改他人蓝图_require_projecttest/integration/services/test_project_blueprint_service.py::test_blueprint_requires_owned_selectable_project其他用户、隐式与已删除 Project 404Passed
HTTP 契约与非法输入处理非法 payload 或名称进入用例project_blueprint_router.pytest/unit/routers/test_project_blueprint_router.py未知字段 422,ValueError 映射 422,缺失 404Passed
项目数字员工获知蓝图固定目录Agent 无固定起草位置build_prompt_with_contexttest/unit/agents/test_chatbot_prompt.py缺少 Workdir 路径时显式失败Passed

本项目基于 MIT License 开源,欢迎使用和贡献。