补充 AGC 随包资源 staging 归位技术方案
- 新增 docs/technical/【技术方案】AGC随包资源staging归位-2026-09-26.md:记录根因证据(tauri-build 把随包资源登记成 build script 输入)、准备步骤合同、构建脚本退化边界、入口接线、验收判据、风险与里程碑拆分 - docs/README.md 挂入文档索引
This commit is contained in:
@@ -37,6 +37,7 @@
|
||||
|
||||
- [AI 游戏创作智能体 App 实施计划](./technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md):当前 DirectProject、受控语义工具、UI workflow、资源和运行时合同。
|
||||
- [AGC 后端框架整理与演进路线](./technical/【技术方案】AGC后端框架整理与演进路线-2026-09-18.md):共享 Runtime、本地执行宿主、云端控制面、领域/平台适配器及分阶段收口边界。
|
||||
- [AGC 随包资源 staging 归位](./technical/【技术方案】AGC随包资源staging归位-2026-09-26.md):随包资源改由准备步骤在 `tauri dev|build` 之前一次性生成、`build.rs` 退化为校验者;含缓存与原子性合同、入口接线、验收判据与里程碑拆分。
|
||||
- [AGC 异步操作可恢复闭环](./【技术方案】AGC异步操作可恢复闭环-2026-09-14.md):认证响应体、最近项目检查和首页自动创建的超时、逐项恢复与跨页防重合同。
|
||||
- [AGC 客户端稳定版生命周期大切换](./【技术方案】AGC客户端稳定版生命周期大切换-2026-09-14.md):统一 operation、认证/Runner、项目入口、本地恢复和 dev-stack 身份边界。
|
||||
- [策划会话 Runtime V2 接入与旧链路退役方案](./technical/【技术方案】策划会话RuntimeV2接入与旧链路退役-2026-09-03.md):历史方案,仅用于追溯 V2 的实现与退役过程,不作为当前实现依据。
|
||||
|
||||
@@ -0,0 +1,194 @@
|
||||
# AGC 随包资源 staging 归位技术方案
|
||||
|
||||
状态:待评审(方案草案,评审通过前不进入实现)
|
||||
日期:2026-09-26
|
||||
范围:AGC 客户端(`apps/ai-game-creator-shell`)随包资源的生成、校验与打包链路
|
||||
关联:`docs/【开发运维】本地开发验证与生产运维-2026-05-15.md`、`docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md`、`docs/project-memory/shared-memory/pitfalls.md`
|
||||
|
||||
## 1. 一句话目标
|
||||
|
||||
把「随包资源」从 build script 的**产物**改回它的**输入**:由准备步骤在 `tauri dev|build` 之前一次性 staging,`build.rs` 退化为校验者,构建期不再向 `src-tauri/resources/**` 写任何文件。
|
||||
|
||||
## 2. 目标与非目标
|
||||
|
||||
### 2.1 目标
|
||||
|
||||
1. `build.rs` 的输入集合里不再包含任何「本次构建会写」的文件,`cargo` 在源码不变时稳定 fresh。
|
||||
2. Tauri dev 的文件监听不再因为 staging 变更重启 `cargo run`(macOS 与 Windows 一致,不依赖 `.taurignore` 兜)。
|
||||
3. staging 与上游版本的绑定可验证:版本、平台、逐文件摘要由校验器 fail closed 检出,不会静默发旧二进制。
|
||||
4. 打包产物内容与当前口径一致(三份 tauri 配置的 `resources` 映射与包内资源门禁不变)。
|
||||
5. 删除症状层补丁(整目录重建的规避、`prune_staging`、`STAGED_*` 幂等判断、`.taurignore` 的 staging 条目)。
|
||||
|
||||
### 2.2 非目标
|
||||
|
||||
1. 不改发布渠道、版本号机制、签名与上传流程。
|
||||
2. 不改运行时资源解析顺序与完整性校验语义(`codex_cli.rs`、`plugin_host.rs`、`environment_check.rs`、`editor_adapters.rs` 保持行为)。
|
||||
3. 不统一 `server-rs` 与 `src-tauri` 两个 workspace,不改 target 布局。
|
||||
4. 不为 Linux 增加客户端产物(Linux 上五条 staging 全为 no-op,见 §3.6)。
|
||||
5. 不改 Windows/macOS 之外的平台支持面。
|
||||
|
||||
## 3. 现状与证据
|
||||
|
||||
### 3.1 build script 目前负责五条 staging
|
||||
|
||||
`apps/ai-game-creator-shell/src-tauri/build.rs` 的 `main()` 前段依次执行(`build.rs:225-229`):
|
||||
|
||||
| # | 步骤 | 动作类型 | 平台门槛 | 目标路径 |
|
||||
|---|---|---|---|---|
|
||||
| 1 | `stage_bundled_codex_cli` | 纯复制(内容+权限比对) | Windows / macOS | `resources/codex/**` |
|
||||
| 2 | `prepare_unity_editor_helper` | **外部工具链**(`powershell -File build.ps1`,需 .NET 10 SDK + VS C++ x64) | Windows + unity feature | `resources/plugins/agc-unity-editor/**` |
|
||||
| 3 | `prepare_godot_editor_extension` | **外部工具链**(`powershell -File build.ps1`,需 CMake ≥ 3.25 + VS17 2022 + Python 3) | Windows + godot feature | `resources/plugins/agc-godot-editor/native/gdextension/**` |
|
||||
| 4 | `stage_plugin_workspace` | 复制 + 清残留 | Windows / macOS | `resources/plugins/**` |
|
||||
| 5 | `stage_cocos_editor_payload` | 复制(同一 cargo 构建产出的 cdylib) | Windows | `resources/plugins/agc-cocos-editor/native/payload/**` |
|
||||
|
||||
(Prompt Bundle 生成代码写 `OUT_DIR`,属正确形态;Node 运行时已由 `scripts/stage-node-runtime.mjs` 在发布前一次性 staging,是本次改造的既有先例。)
|
||||
|
||||
### 3.2 这些写入为什么会让 build script 永远失效
|
||||
|
||||
`tauri-build` 会对 `bundle.resources` 里的**每个文件**发 `cargo:rerun-if-changed`,并把它拷进 target(`tauri-build-2.6.3/src/lib.rs:88-93`)。我们的三份 tauri 配置把 `resources/codex/**` 与 `resources/plugins` 列进了 `bundle.resources`(`tauri.windows.conf.json:7-15`、`tauri.macos.conf.json:8-22`;基线配置故意不含,见 `check-config.mjs:1377-1383`)。
|
||||
|
||||
于是「构建期写的文件」与「build script 声明的输入」是同一批:
|
||||
|
||||
```text
|
||||
staging 写 resources/** → tauri-build 把同一批文件登记成输入 → mtime 变化
|
||||
↑ ↓
|
||||
└────────────── cargo 判 build script stale,重跑脚本 ←──────┘
|
||||
```
|
||||
|
||||
判据是「输入文件 mtime 比 build script 的输出新」,与目录位置无关:把 staging 搬到 `target/` 也躲不开——登记的是配置里写的那些路径,只要构建期写它们,照样自触发。所以关键不是「产物放哪」,而是「**构建期有没有人写这些被登记的文件**」。
|
||||
|
||||
### 3.3 已发生的故障
|
||||
|
||||
| 症状 | 范围 | 观察证据 |
|
||||
|---|---|---|
|
||||
| 每次构建都重编主 crate(41–87 秒,从不变 fresh) | Windows + macOS(Linux 上五条 staging 全为 no-op,不受影响) | `CARGO_LOG=cargo::core::compiler::fingerprint=info cargo build --no-default-features` 打印 `stale: changed "resources/codex/mac-native/darwin-arm64/NOTICE.md"`,且 `.fingerprint/**/run-build-script-build-script-build.json` 的 `RerunIfChanged.paths` 含 staging 目标路径 |
|
||||
| `npm run agc` 反复 `Rebuilding application`,客户端起不来 | 仅 macOS | dev 日志一轮 28 次以上不收敛;原因是被重写的 `resources/codex/mac-native/**` 落在监听范围内且未被忽略 |
|
||||
| `remove_dir_all` 抛 `DirectoryNotEmpty`,整次启动中断 | 并发重建时 | `清理 macOS Codex staging 失败` |
|
||||
|
||||
### 3.4 三轮症状层修复都没有收口
|
||||
|
||||
| commit | 日期 | 修的是什么 | 结果 |
|
||||
|---|---|---|---|
|
||||
| `299877b19` | 2026-09-11 | 插件资源改成「内容变化才落盘」+ 引入 `.taurignore` | 只覆盖插件路径,且挡不住 cargo stale |
|
||||
| `710d6dddf` | 2026-09-23(PR #487) | Windows 上「权限一致就不写元数据」 | 只减少一类写入,未解决整目录重建 |
|
||||
| 本次未合入的 B 改动 | 2026-09-26 | 全部写入改幂等 + 按布局清残留 + 补 `.taurignore` | 实测可行(第二次 `cargo build` 0.25 秒 fresh),但规则靠人守:新增一条写 `resources/**` 的步骤漏改即回退(godot/cocos/plugin.json 正是三个漏点) |
|
||||
|
||||
结论:**只要 build script 继续写这些受跟踪路径,就必须持续为每一处写入维护「无改动不写」,成本随写入点增长**;这是结构问题,不是疏漏问题。
|
||||
|
||||
## 4. 方案设计
|
||||
|
||||
### 4.1 原则
|
||||
|
||||
build script 只写 `OUT_DIR`/`target`;随包资源是它的输入。凡需要「由本仓库生成、再随包分发」的内容,都由 `tauri dev|build` 之前的**准备步骤**生成,build script 只校验。
|
||||
|
||||
### 4.2 目标形态
|
||||
|
||||
```text
|
||||
准备步骤(新,唯一写入方) build.rs(退化为校验者)
|
||||
fetch/校验上游 → 生成到 staging 目录 只读 resources/** → 校验 manifest/hash/版本/平台
|
||||
→ 原子替换到 resources/** → 不写任何随包资源
|
||||
→ 写 manifest.json(schema 化)
|
||||
↑ ↑
|
||||
dev: start-tauri-dev 内、spawn tauri 之前 release: build-release/build-macos-ci 内
|
||||
```
|
||||
|
||||
### 4.3 准备步骤的合同(必须成立的行为)
|
||||
|
||||
1. **调用时机**:`tauri dev` / `tauri build` 之前完成;任何入口都不得依赖 build script 兜底生成。
|
||||
2. **缓存与幂等**:以「上游 lockfile `resolved` + `integrity` + 布局版本 + 目标三元」为 key;命中且 manifest 校验通过的 staging 目录不重写任何文件(避免把「产物」变成「每次构建都变」的新源头)。
|
||||
3. **原子性**:写入 staging 临时目录后 rename 替换;不得出现半成品目录(杜绝并发下的 `DirectoryNotEmpty`)。
|
||||
4. **所有权**:只允许替换由本工具创建并带 manifest 的目录;遇到非本工具目录、符号链接、越界路径必须 fail closed(沿用 `stage-node-runtime.mjs` 与 `prepare-macos-codex.mjs` 既有判定)。
|
||||
5. **清理语义**:准备步骤负责删除本轮布局不再产出的残留(否则旧组件会继续被打包),删除范围限于自己的 staging 目录,不得触碰受版本控制的文件(例如 `resources/codex/win-x64/NOTICE.md`)。
|
||||
6. **失败语义**:上游缺失、integrity 不匹配、目标平台不支持、外部工具链缺失 → 立即失败并给出可执行提示;不允许「跳过生成、继续打包」。
|
||||
7. **可观测**:输出一行汇总(命中缓存 / 重新生成 / 跳过原因),供本地与 CI 排障。
|
||||
8. **并发安全**:同一 staging 目录的并发调用必须串行化或幂等收敛(dev 与 IDE 各自触发时不能互相破坏)。
|
||||
|
||||
### 4.4 build.rs 退化后的职责
|
||||
|
||||
保留:
|
||||
|
||||
1. 读取 `TARGET` 并写 `cargo:rustc-env=AGC_BUILD_TARGET`(运行时定位随包目录依赖它)。
|
||||
2. 校验 `resources/**` 与清单一致:平台目录存在、manifest schema/平台/版本、逐文件 sha256、必需组件齐全(缺一即 fail closed)。
|
||||
3. 声明**真实输入**:上游源文件、布局表、受版本控制资源(含 macOS 声明文件)的 `rerun-if-changed`;不得声明任何由本脚本或准备步骤生成的文件。
|
||||
4. `tauri_build::build()` 与既有 Prompt Bundle、能力与配置校验。
|
||||
|
||||
删除:
|
||||
|
||||
1. 五条 staging 的写入逻辑、`prune_staging`、`STAGED_*` 幂等判断、针对大目录的整目录删除。
|
||||
2. 为绕开自触发而加的 `.taurignore` staging 条目与说明(准备步骤在监听启动前完成,不再需要)。
|
||||
|
||||
### 4.5 资源清单(谁生成、谁消费)
|
||||
|
||||
| 资源 | 生成方式 | 运行时消费方 | dev 是否需要 |
|
||||
|---|---|---|---|
|
||||
| `resources/codex/**` | 纯复制(上游平台包 `vendor/<triple>`) | `codex_cli.rs`(内置 sidecar 优先,其次 npm 目录、PATH) | macOS dev 只接受真 `.app` 的 `Contents/Resources`,非 `.app` 场景走 npm/PATH 回退;Windows dev 从 exe 同级读取 |
|
||||
| `resources/plugins/**` | 复制 + 三个编辑器分支的产物 | `plugin_host.rs`(`AGC_PLUGIN_WORKSPACE` → `resource_dir/plugins` → dev 回退仓库 `plugins/`)、`editor_adapters.rs` | dev 有仓库回退,但 cocos/unity/godot payload 仍以随包路径为准 |
|
||||
| `resources/plugins/agc-unity-editor/**`、`agc-godot-editor/native/gdextension/**` | 外部工具链(Windows 专属) | `editor_adapters.rs` 候选链 | 仅 Windows |
|
||||
| `resources/node-runtime/**` | 已有:`stage-node-runtime.mjs` | `environment_check.rs`(`agc-node-runtime.v1` 全量 sha256) | 发布与需要随包 Node 的 dev |
|
||||
| `design-agent`、`vendor/*` 许可 | 受版本控制 | `design_tools.rs` 等 | 无需 staging |
|
||||
|
||||
### 4.6 入口接线
|
||||
|
||||
| 入口 | 位置 | 现状 | 改造后 |
|
||||
|---|---|---|---|
|
||||
| AGC dev | `start-tauri-dev.mjs`(`runTauriDev` → 预检 → 前端 → `spawnCli`,准备点在前端就绪之后、`spawnCli` 之前) | 无准备步骤,依赖 build script | 在 `spawnCli` 之前调用准备步骤(命中缓存时秒退) |
|
||||
| Windows 发布 | `build-release.mjs`(`runTauriBuild`,现有 `stageRuntime(target)` 紧邻 spawn tauri) | 只有 Node 运行时走准备步骤 | 同一挂点串上全部 staging |
|
||||
| macOS 发布 | `build-macos-ci.mjs`(复用 `runTauriBuild`)→ `check-macos-bundle.mjs` | 同上 | 同上 |
|
||||
| 本机 Rust 门禁 | `ai-game-creator-shell:check:rust:shell`(Windows 上 `cargo test --no-run` 会跑 build script) | 依赖 build script 生成资源 | 校验器在该场景必须能只读通过;需要真实资源的用例沿用既有 fixture,不得依赖本机 staging 产物 |
|
||||
| `tauri build --no-bundle` | `build-release.mjs` 的 no-bundle 分支(当前跳过 `stageRuntime`) | 不生成 Node 运行时 | 保持「不打包就不强制 staging」的口径,但校验器要按「未打包」放行 |
|
||||
| CI(Linux) | `.gitea/workflows/project-ci.yml` 的 AGC 分组 | 五条 staging 全为 no-op | 不需要新增准备步骤;分片与 smoke 命令不变 |
|
||||
|
||||
### 4.7 与现有机制的关系
|
||||
|
||||
1. **已有先例**:`stage-node-runtime.mjs` 已实现 schema 常量、目标平台失败关闭、staging 目录 + rename 原子替换、拒绝覆盖非本工具目录;`prepare-macos-codex.mjs` 已实现 lockfile `integrity` 驱动的下载、缓存与原子替换。准备步骤应复用这两套范式而不是另起一套。
|
||||
2. **打包侧不变**:三份 tauri 配置的 `resources` 映射与 `check-config.mjs:1356-1424` 的逐字断言保持不变;`check-macos-bundle.mjs` 对包内 `coding-agent/mac-native`、`game-runtime/node`、`plugins/agc-cocos-editor` 的存在性、架构与 sha256 断言继续作为发布后门禁。
|
||||
3. **fail closed 已有兜底**:`tauri-build` 在资源缺失时以 `ResourcePathNotFound` 直接失败;校验器应比它更早、更明确地报错。
|
||||
|
||||
## 5. 兼容与迁移
|
||||
|
||||
1. **产物兼容**:包内路径、manifest schema(`genarrative-codex-sidecar.v2`、`agc-node-runtime.v1`、插件 `plugin.json`)不变,安装包内容逐项对得上;升级路径不需要用户侧动作。
|
||||
2. **过渡期**:改造期间 `resources/**` 仍由 build script 生成(当前主线状态),准备步骤上线后先并存(生成结果与校验一致),再删除 build script 的写入分支——删除与新增不得跨里程碑混在一起。
|
||||
3. **本机残留**:改造后 `.taurignore` 的 staging 条目、`prune_staging` 及相关注释一并删除;`resources/**` 仍保持 gitignored。
|
||||
4. **回滚**:准备步骤与校验器保持独立可关闭(例如校验器只读、不写),回滚只需恢复 build script 的写入分支,不涉及数据迁移。
|
||||
|
||||
## 6. 验收标准与证据
|
||||
|
||||
| 项 | 判据 |
|
||||
|---|---|
|
||||
| 构建新鲜度 | 源码不变时连续两次 `cargo build --no-default-features` 第二次为秒级 `Finished`;IDE 的 `cargo check --all-targets` 同样不重复构建 |
|
||||
| 无自触发 | `cargo:rerun-if-changed` 输出与 fingerprint 记录里不出现任何 `src-tauri/resources/**` 路径 |
|
||||
| dev 可用 | macOS/Windows `npm run agc` 在准备步骤后一次成功:`Rebuilding application` 为 0 次、`Running DevCommand` 为 1 次,客户端与 Runner 进程稳定存活 |
|
||||
| 包内容 | `check-macos-bundle.mjs` 全绿;Windows 安装包内 `coding-agent`、`plugins`、`game-runtime/node` 与改造前逐项一致 |
|
||||
| 失败关闭 | 上游缺失 / integrity 不匹配 / 清单缺组件 / 目标平台不支持 四类场景各自返回明确错误且不产出包 |
|
||||
| 幂等 | 准备步骤连续执行两次,staging 目录内容与 mtime 不变(不改动受跟踪输入) |
|
||||
| 门禁 | `check-config.mjs`、`check:encoding`、`check:doc-index`、`git diff --check`、AGC 相关 vitest 与 Rust 测试全绿 |
|
||||
|
||||
未验证项必须在交付记录中标注(例如 Windows 真机行为、真实上游包下载在受限网络下的表现)。
|
||||
|
||||
## 7. 风险与回滚
|
||||
|
||||
| 风险 | 影响 | 措施 |
|
||||
|---|---|---|
|
||||
| 忘记调用准备步骤(dev 或某个发布入口) | 资源缺失,打包或启动失败 | 校验器 fail closed + 入口测试断言「spawn tauri 前已调用准备步骤」 |
|
||||
| staging 缓存 key 不覆盖上游变化 | 静默发旧二进制 | key 含 lockfile `resolved`+`integrity`+布局版本+三元;manifest 校验作为第二道闸 |
|
||||
| 外部工具链步骤(unity/godot)搬出后顺序变化 | Windows 打包失败 | 准备步骤显式声明工具链前置检查;先在 Windows 上单独验证再合入 |
|
||||
| 并发调用(dev 与 IDE 同时触发) | staging 目录损坏 | 原子替换 + 所有权校验 + 有界重试 |
|
||||
| 迁移期两套生成并存 | 结果漂移 | 并存阶段以「准备步骤生成结果 == build script 生成结果」逐文件比对作为过渡判据 |
|
||||
|
||||
回滚点:准备步骤上线但校验器未启用前,任一步失败都可直接恢复 build script 写入分支,无需数据迁移。
|
||||
|
||||
## 8. 里程碑拆分(建议)
|
||||
|
||||
| 里程碑 | 交付 | 停止条件 |
|
||||
|---|---|---|
|
||||
| M1 校验器化 | `build.rs` 增加只读校验路径、准备步骤脚本骨架(codex + plugins 两条纯复制路径)、缓存与原子替换 | 校验器在既有 staging 产物上全绿,且不改变现有构建行为 |
|
||||
| M2 入口接线 | dev 与两个发布入口调用准备步骤;codex/plugins 的写入分支从 build.rs 移除;`cargo` 新鲜度与 dev 不再重建达标 | macOS 与 Windows 均达到 §6 的前三行判据 |
|
||||
| M3 外部工具链归位与清理 | unity/godot/cocos 的产物生成移出 build.rs;删除 `prune_staging`、`STAGED_*`、`.taurignore` staging 条目与相关注释;文档收口 | 包内容逐项对得上,门禁全绿 |
|
||||
|
||||
里程碑规范与单里程碑实现计划按 [`docs/【协作规范】规范驱动开发工作流-2026-09-12.md`](../【协作规范】规范驱动开发工作流-2026-09-12.md) 另立 `docs/project-memory/plans/` 下的临时文件;本方案是它们的主规范来源。
|
||||
|
||||
## 9. 未决问题
|
||||
|
||||
1. 准备步骤用 Rust bin(可复用 `codex_bundle.rs` 的布局表与校验)还是 Node 脚本(与 `stage-node-runtime.mjs` 同构)?倾向前者以复用布局表与 sha256 校验,避免第二份白名单。
|
||||
2. unity/godot 的外部工具链步骤是否值得搬出 build script(它们本身是构建动作,搬出后需要显式前置顺序)——需在 Windows 上确认收益与风险。
|
||||
3. `resources/**` 是否需要继续保留在 crate 内(`bundle.resources` 相对路径解析要求),还是改用生成式配置指向 `target/` 下的 staging:前者改动小、后者更彻底,需与 Tauri 的资源解析规则一起评估。
|
||||
Reference in New Issue
Block a user