Merge remote-tracking branch 'origin/master' into feat/typed-tool-error
Project CI / AI game creator shell Rust crates (pull_request) Successful in 1m44s
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 2m19s
Project CI / Backend tests (pull_request) Successful in 3m55s
Project CI / Frontend tests (pull_request) Successful in 2m16s
Project CI / Native shell tests (pull_request) Successful in 6m55s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Successful in 10m2s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Successful in 10m28s
Project CI / Repository checks (pull_request) Successful in 2m57s
Project CI / AI game creator shell web tests (pull_request) Successful in 2m18s

# Conflicts:
#	apps/ai-game-creator-shell/src-tauri/src/agent/direct_tools_mcp.rs
#	docs/project-memory/shared-memory/decision-log.md
This commit is contained in:
2026-10-01 16:04:30 +08:00
135 changed files with 13720 additions and 6078 deletions
@@ -0,0 +1,225 @@
# 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. 删除症状层补丁(整目录重建的规避、为绕开自触发而加的 `.taurignore` staging 条目)。`prune_staging`、`STAGED_*` 一类命名只出现在未合入的症状层补丁里,主线从未有过,不需要清理。
### 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 目录的并发调用未加锁,会以可读错误失败并保留已生成的 staging 目录;需要并发时由调用方外部串行化。
9. **实际构建目标**:资源准备与 Cargo 必须使用同一目标和 feature 集。正式发布使用发布目标;无显式 `--target` 的 `--no-bundle` 使用宿主平台,Windows/macOS 仍须准备资源,Linux 编译 smoke 不准备桌面平台专属资源。feature 从最终 Tauri/Cargo 参数解析,不能被开发环境变量单独覆盖;dev 启动器转交的应用参数不参与构建参数解析。
### 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 的写入逻辑(M2 删除了 codex 与插件工作区的写入分支,仍是构建期产物的三处留在 M3)、针对大目录的整目录重建。
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 |
| `resources/claude-agent/**` | 纯复制(应用 `agent-sidecar/src/index.mjs` + 上游 `@anthropic-ai/claude-agent-sdk`、`@anthropic-ai/claude-agent-sdk-<platform>`) | `claude_code_cli.rs`(`exe_dir/claude-agent/index.mjs`、macOS `.app` 的 `Contents/Resources/claude-agent`) | Windows / macOS dev:准备步骤按声明 staging |
| `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 运行时 | 改为:`--no-bundle` 也执行 staging(app 构建本身需要随包资源),只跳过总号发布;校验器在资源缺失时仍然 fail closed |
| 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` 直接失败;校验器应比它更早、更明确地报错。
### 4.8 单一声明与实现形态(M1 定案)
准备步骤与构建期校验共用一份人工声明:`apps/ai-game-creator-shell/src-tauri/build_support/package-layout.json`。
| 侧 | 读取方式 | 用途 |
| --- | --- | --- |
| Node 准备步骤(`scripts/prepare-bundled-resources.mjs`) | 直接读声明 JSON | 组件白名单、Codex 上游候选路径、插件随包子目录与跳过规则、平台与 feature 门槛、缓存 key 组成、manifest 序列化 |
| Rust 构建期校验与运行期布局 | 读声明生成的 `build_support/package-layout.generated.rs`(编译期常量) | 只读校验既有产物、运行期定位随包组件(`codex_bundle.rs` 接口不变) |
| 声明门禁 | `scripts/check-package-layout.mjs`(`npm run agc:bundled-resources:check`,已进 `agc:typecheck` 链) | 校验生成物与声明一致,并检查声明自身不变量:目标唯一、`executable` 属于白名单、每个目标恰有一条第三方声明来源、`codex.version` 与应用锁定的 `@openai/codex` 一致 |
形态选择**混合**:生成归 Node(复用 `stage-node-runtime.mjs` / `prepare-macos-codex.mjs` 的下载、`integrity`、临时目录 + rename 原子替换范式),声明与校验归 Rust(复用 `codex_bundle.rs` / `godot_bundle.rs` 的布局与摘要校验,运行期模块不改公开接口)。理由:Rust 侧没有下载与 lockfile 解析能力(`[build-dependencies]` 无 HTTP 客户端),Node 侧没有 staging 能力;任选单一语言都要迁移另一侧既有资产。Rust 侧刻意不解析 JSON:声明经生成器变成编译期常量,避免运行期解析与生命周期妥协,也让 `&'static` 布局表与现有调用点保持不变。
构建脚本自 M3 起只做只读校验(写入分支与 `AGC_SKIP_RESOURCE_STAGING` 开关一并删除),`cargo build/check` 本身就是对既有产物的校验。
### 4.9 M2/M3 实况:构建期写入边界
「谁写随包资源」按产物来源分界,声明里的 `origin` 字段表达同一口径:
| 来源 | 例子 | 谁写 | 时机 |
| --- | --- | --- | --- |
| `source`:声明 + 仓库源码即可生成 | `resources/codex/**`、插件工作区的 `src`/`panels`/`skills`/`native/payload` | 准备步骤(Node) | `tauri dev` / `tauri build` 之前 |
| `prepared` / `libraryStaging` / `nativePayloads`:需要外部工具链或同一次 cargo 构建 | Unity `dotnet/publish/win-x64`、Godot `native/gdextension`、Cocos `native/payload` | 准备步骤:先按声明运行 `powershell.exe -File build.ps1` 或 `cargo build -p … --target …`,再复制产物 | 同上 |
M2 之后构建脚本只做只读校验;M3 之后它也不再生成任何随包资源(连编辑器分支产物一并交给准备步骤),并在校验阶段确认源码派生内容逐文件一致、已准备产物在位、Godot 随包库通过既有深度校验。因此源码不变时 `cargo` 稳定 fresh(macOS 实测连续三次 `cargo build --no-default-features` 为 0.69 / 0.22 / 0.22 秒),`resources/plugins` 也不再需要在 `.taurignore` 里忽略(两份 staging 条目已删除)。
准备步骤的 Windows 侧命令执行(powershell / cargo)只在本机无法验证,验收清单见 M3 里程碑规范。
**2026-09-30(合并 master)**:新增随包组件 Claude Agent SDK sidecar(cc 执行模式)沿用同一口径——声明 `claudeAgent` section(上游 SDK 包与平台原生运行时包的目标表、复制跳过规则、锁定版本),staging 由准备步骤整目录原子替换,`build.rs` 只读校验(入口与仓库源码一致、SDK 与原生运行时版本等于声明、白名单外文件)。它的 `resources/claude-agent` 映射放在 `tauri.windows.conf.json` 与 `tauri.macos.conf.json`,**不得**回到基线 `tauri.conf.json`:基线同时服务 Linux(CI 只在那里编译壳 crate 且不装 npm 依赖),`tauri-build` 会因资源缺失直接失败。
## 5. 兼容与迁移
1. **产物兼容**:包内路径、manifest schema(`genarrative-codex-sidecar.v2`、`agc-node-runtime.v1`、插件 `plugin.json`)不变,安装包内容逐项对得上;升级路径不需要用户侧动作。
2. **过渡期(已完成)**:M1 让准备步骤与构建脚本产物并存并逐文件比对一致,M2 移除了构建脚本里 codex 与插件工作区的写入分支;剩余在构建期写入的三处(Unity publish 目录、Godot gdextension、Cocos payload)随 M3 归位。删除与新增不跨里程碑混在一起。
3. **本机残留**:M2 已删除 codex 与插件工作区的写入逻辑与整目录重建;`.taurignore` 的 staging 条目及其原因说明保留到 M3——Windows 上仍有三处构建期写入落在 `resources/plugins/**`,去掉忽略会重新引入监听自触发。`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 上单独验证再合入 |
| 并发调用同一 staging 目录 | 失败可读、不丢 staging | 不加锁也不支持并发:替换失败给出可执行提示并保留 staging 目录,需要并发时外部串行化 |
| 迁移期两套生成并存 | 结果漂移 | 并存阶段以「准备步骤生成结果 == build script 生成结果」逐文件比对作为过渡判据 |
回滚点:准备步骤上线但校验器未启用前,任一步失败都可直接恢复 build script 写入分支,无需数据迁移。
## 8. 里程碑拆分(建议)
| 里程碑 | 交付 | 停止条件 |
|---|---|---|
| M1 校验器化 | 单一声明 + 生成门禁、`build.rs` 只读校验路径、准备步骤脚本(codex + plugins 两条纯复制路径)、缓存与原子替换 | 校验器在既有 staging 产物上全绿且不改变现有构建行为(写入分支与 `AGC_SKIP_RESOURCE_STAGING` 开关在 M3 一并删除) |
| M2 入口接线 | dev 与两个发布入口调用准备步骤;codex/plugins 的写入分支从 build.rs 移除;`cargo` 新鲜度与 dev 不再重建达标 | 已交付(2026-09-27):macOS 侧 §6 前三行达标(连续 `cargo build` 0.69 / 0.22 / 0.22 秒 fresh;`tauri dev` 全程 `Rebuilding application` 0 次、`Running DevCommand` 1 次);Windows 新鲜度待 M3 归位三处构建期产物后复验 |
| M3 外部工具链归位与清理 | unity/godot/cocos 的产物生成移出 build.rs;删除 `.taurignore` 的 staging 条目与相关注释;文档收口 | 已实现(2026-09-27):三处产物改由准备步骤按声明运行 powershell/cargo 后复制,build.rs 只剩只读校验,两份 `.taurignore` 已删除;已通过第五轮 Windows 真机评审:构建脚本不再写资源(90 个文件 0 变化)、第二次 `cargo build` 1.13s fresh、三分支产物齐备、幂等与 fail-closed 通过;仍待验收的是「安装包内产物路径/sha256 比对」与「三个编辑器分支客户端加载」 |
里程碑规范与单里程碑实现计划按 [`docs/【协作规范】规范驱动开发工作流-2026-09-12.md`](../【协作规范】规范驱动开发工作流-2026-09-12.md) 另立 `docs/project-memory/plans/` 下的临时文件;本方案是它们的主规范来源。
## 9. 未决问题
1. ~~准备步骤用 Rust bin 还是 Node 脚本?~~ **已定(2026-09-27):混合**——生成归 Node、声明与校验归 Rust,布局与白名单收敛为单一声明文件。机制、门禁与验证入口见 §4.8。
2. unity/godot 的外部工具链步骤是否值得搬出 build script(它们本身是构建动作,搬出后需要显式前置顺序)——需在 Windows 上确认收益与风险。
3. `resources/**` 是否需要继续保留在 crate 内(`bundle.resources` 相对路径解析要求),还是改用生成式配置指向 `target/` 下的 staging:前者改动小、后者更彻底,需与 Tauri 的资源解析规则一起评估。
@@ -91,6 +91,10 @@ UI 编辑器的“分析参考图”步骤、Rust 命令 `suggest_ui_design_sema
| 保持不变的边界 | 工作区根本身是链接、候选子目录是链接、二层及更深目录不递归,这三条既有边界不动 | `import_tests::ignores_symbolic_link_child_candidate_without_writing_agent_metadata`、`import_tests::ignores_windows_reparse_child_candidate_without_writing_agent_metadata`、`import_tests::ignores_godot_projects_below_the_first_child_level` |
| 内置插件行 | 设置→扩展 的内置插件行不再渲染手动「启动 / 停止」按钮;启动由项目切换时的前端自动启动承担,停止走该行启用开关(禁用即停止并断开编辑器连接);导入扩展行的启动按钮保留 | `apps/ai-game-creator-shell/src/features/runtime-config/RuntimeConfigDialog.tsx`、`tests/pluginHost.test.ts` |
## 2026-09-30 扩展设置隐藏插件进程诊断文案
设置→扩展的内置插件与导入插件卡片不再渲染宿主返回的 `lastError`(包括“插件进程已退出”)。插件启用状态、运行状态、重载与启用/禁用操作保持现有行为;错误仍由宿主保留用于状态判定和操作反馈。
## 2026-09-20 DirectProject 七项效率闭环(补齐合同)
本节补齐并覆盖下节中仅靠 Skill 要求预检、收尾、批读和原生命令预算的部分。完整目标仍为:自动预检、宿主验收与收尾、分层验证、统一执行/返修预算、稳定测试基线、请求耗时与批量读取、所有工具并行。已有代码及测试不等于全部目标已完成;按下表逐项验收。
@@ -740,6 +744,8 @@ Prompt 静态门禁必须断言上述 Bundle section 当前定义的权威语义
Agent 可见的系统指令、工具与参数说明、恢复指引和上下文模板统一由外置提示词文件维护。AGC 沿用 `prompts/runtime/manifest.json`:已有 composition/section 保持原有组合关系,独立调用的文本按职责登记在 `textCatalogs`,目录为 `prompts/runtime/texts/`,每份 JSON 是稳定文本键到正文的映射。构建期校验目录、文件、重复键和空正文,并生成可供 `format!` 使用的编译期文本宏;变量填充沿用 Rust 格式语法。Runtime 状态、用户内容、schema 类型与枚举、权限和校验继续由代码生成。服务端 Agent 的独立 crate 使用各自 `prompts/` 中的编译期文本文件。迁移以当前组装结果和工具 schema 等价为验收依据,源码门禁检查各提示词入口的内联正文与外置引用。
`tests/prompt_source_boundaries.rs` 的入口清单随生产函数迁移同步更新。UI 设计文档代码上下文在入队冻结时由 `src/agent/direct_codex_user_item/wire.rs::ui_design_code_context_for` 生成,门禁检查该函数继续使用外置的 `projectContext.uiDesign.codeContext` 与 `projectContext.uiDesign.generationErrorContext`;旧 `render_ui_design_code_context` 已随冻结与纯投影拆分删除,不保留兼容入口。
2026-07-12 起,通用开发能力的 Runtime V1.1 增量以 [`【技术方案】AI游戏创作Agent Runtime V1.1-2026-07-12.md`](<./【技术方案】AI游戏创作Agent Runtime V1.1-2026-07-12.md>) 为编码级事实源。它补充仓库启动上下文、同一发布二进制独立 Runner、受限本地预览浏览器验证、动态隔离子 Agent 和真实 Provider 全链路验收;本文件中“进程内 tokio task”“首轮不预加载项目内容”和“不创建动态执行实例”的旧口径由 V1.1 明确替代,未涉及能力继续沿用本文件。
同一文档的“V1.2 对标 Codex CLI 增量”继续作为受控命令与推理档位的事实源。对一次性 `command.exec` 而言,只接受 Runtime 白名单内的固定 `program` 和逐项 `args` argv,默认 `confirm`,可执行文件解析为项目外绝对路径且子进程只使用安全 PATH;不解析 shell 字符串,不提供管道、重定向、PTY 或后台进程。这里对 PTY 和后台进程的排除仅适用于 `command.exec`,不能用来否定 V1.10 的独立持久进程工具,也不能把 `command.exec` 自身改成长驻入口。`command.exec` 的 action、stdout / stderr、退出码、超时与源码指纹结果统一进入现有 `action / observation`、project revision、verification gate 和 `needs-reconciliation` 链路;只有明确验证型命令且退出码、源码指纹、命令日志、manifest 与 Agent DB 审计全通过才签发 passed gate,Git / rg / cargo metadata / 普通 npm run 只作诊断。首版只请求终止受控进程组,安全等级与 `project.verify` 相同,不宣称已具备完整 OS sandbox 或 detached-process 隔离。
@@ -1781,6 +1787,9 @@ Direct 回合的所有权属于进程内项目身份锁,不属于当前页面
- 清单补充可选 `projectName` 与 `pendingFiles`:名称来自本地 manifest;`pendingFiles` 是本轮失败、延后、并发变动与非策略排除的跳过文件数量。`0` 表示扫描范围已同步;大文件等被跳过不能标成完整。旧清单字段缺失表示完整性未知,维持可读取兼容,不反写旧清单。
- 项目名称或完整性发生变化时,即使文件内容没有差异也要提交新清单;本机索引记录上次已提交的这两个字段。实际客户端下一次正常同步可补齐历史清单元数据;后台只读访问不迁移旧清单。缺失 `pendingFiles` 不能默认成 0,临时跳过原因消失后允许无文件上传的 `partial → ready` 转换。
- 后台增加“项目工程”入口,仅 owner 及拥有 `project-snapshots` 页签权限的管理员可访问。`GET /admin/api/project-snapshots?cursor=&limit=20` 读取私有 OSS 清单并返回 `{items,nextCursor}`;单页最多 100 个,游标由服务端校验,目录与清单读取有界。条目为 `{userId,projectId,projectName,syncRevision,syncedAtMs,fileCount,totalBytes,status}`,状态为 `ready / partial / unverified`,名称缺失时显示 projectId。
- 后台“按同步时间排序”是主动读取当前渠道全量项目的临时展示操作:从首游标开始,以每次 100 条沿 `nextCursor` 顺序读取,只有全部读取成功后才切换为 `syncedAtMs` 降序、`userId` 字符串升序、`projectId` 字符串升序;不能只排序当前远端页。成功后回到第一页,20/50/100 条分页及上一页/下一页复用浏览器中的完整列表,不再请求远端;“刷新”重新读取全量并回到第一页,“恢复默认顺序”在远端目录第一页读取成功后统一切换列表、排序模式和分页游标;读取失败保留原排序集合、当前页码和本地分页,支持重试。切换渠道、令牌或离开页面销毁临时结果并取消在途读取,不跨会话持久化。
- 全量读取展示已取得项目数并允许取消;单次最多 200 个远端页、10,000 个唯一项目、60 秒,重复游标或超限明确报错。读取失败、鉴权失败、超时或取消保留原来的列表和分页,不能发布部分数据作为排序完成的结果。并发同步期间重复项目按用户/项目身份合并,保留同步时间较新的一份,相同时间取后读到的清单;该列表是遍历期间读到的数据集合,不承诺 OSS 全局事务快照,后续同步由管理员刷新读取。仍由原接口实施渠道和页签鉴权,不新增 OSS 索引、数据库表、API 参数或客户端迁移。
- 排序验收覆盖远端多页与空中间页、时间相同的身份排序、本地翻页/页容量、刷新与恢复默认、失败/取消/超时/限额/重复游标、并发重复身份,以及渠道/令牌切换和卸载后的旧响应隔离;证据入口为 `apps/admin-web/src/pages/AdminProjectSnapshotsPage.test.tsx`、后台类型检查和编码/文档索引检查。
- `GET /admin/api/project-snapshots/{userId}/{projectId}/download` 只读取该用户/项目的固定清单与其引用对象,返回 `application/zip` 附件。ZIP 中路径直接使用原始相对路径,不包含 userId、摘要目录或 OSS 前缀;名称使用经过安全处理的项目名和 revision。下载固定本次读到的清单,远端并发回收导致对象缺失则整体失败,不能静默遗漏。
- `partial` 快照下载返回 409;`unverified` 历史快照可导出已同步文件,列表明确显示“完整性未知”,动作称“下载已存文件”。`ready` 才显示“下载完整工程”。ZIP 构建核验每一文件的长度与 fnv1a64 摘要,拒绝穿越、绝对路径、重复/大小写冲突路径、非法项目身份;缺失或损坏整体失败,不返回成功的残缺 ZIP。
- ZIP 使用服务端临时文件并限制并发,不将 2 GiB 工程整体驻留内存;成功、失败、客户端取消均清理临时文件。单文件、总量、文件数沿用上传上限,超限明确拒绝。零字节工程文件可以上传和导出。OSS 凭据与签名不下发浏览器,列表失败保留错误而非伪造空列表。