WIP: AGC 编辑器分支产物归位(M3,待 Windows 验收) #524
Draft
suzmii
wants to merge 10 commits from
feat/agc-bundled-resources-m3 into fix/agc-rust-staging-idempotency
pull from: feat/agc-bundled-resources-m3
merge into: GenarrativeAI:fix/agc-rust-staging-idempotency
GenarrativeAI:master
GenarrativeAI:feat/msg-queue-to-rust
GenarrativeAI:feat/ui-editor-model-override
GenarrativeAI:fix/agc-release-gate-channel
GenarrativeAI:fix/agc-rust-staging-idempotency
GenarrativeAI:fix/dev-loopback-token-trust
GenarrativeAI:feat/tribo3d-integeration
GenarrativeAI:opt/test-compile-warning
GenarrativeAI:fix/api-timeout
GenarrativeAI:codex/admin-gray-game-publish
GenarrativeAI:codex/remove-agc-codex-restrictions
GenarrativeAI:feat/gptimage2to2.5
GenarrativeAI:backup/agc-macos-dualarch-node-runtime
GenarrativeAI:feat/Crt_Ws
GenarrativeAI:codex/preview-llm-router-config
GenarrativeAI:fix/stroke-width-regression-from-285
GenarrativeAI:agent-organize
GenarrativeAI:feat/game-agent-canvas-resource-workbench-v2
GenarrativeAI:feat/agc-new-workflow
GenarrativeAI:fix/agc-release-version-sync
GenarrativeAI:codex/shared-components-followup
GenarrativeAI:codex/game-agent-runtime-interaction-design
GenarrativeAI:feat/spine-sequence-export-v2
GenarrativeAI:codex/game-agent-run
GenarrativeAI:codex/ui-spritesheet-durable-transaction-latest
GenarrativeAI:feat/art-agent-more-tools
GenarrativeAI:codex/editor-canvas-null-source-repair
GenarrativeAI:hotfix/editor-layout-2mib
GenarrativeAI:codex/showcase
No Reviewers
Labels
Clear labels
Compat/Breaking
Kind/Bug
Kind/Documentation
Kind/Enhancement
Kind/Feature
Kind/Security
Kind/Testing
Breaking change that won't be backward compatible
Something is not working
Documentation changes
Improve existing functionality
New functionality
This is security issue
Issue or pull request related to testing
Priority
Critical
1
The priority is critical
Priority
High
2
The priority is high
Priority
Low
4
The priority is low
Priority
Medium
3
The priority is medium
Reviewed
Confirmed
1
Issue has been confirmed
Reviewed
Duplicate
2
This issue or pull request already exists
Reviewed
Invalid
3
Invalid issue
Reviewed
Won't Fix
3
This issue won't be fixed
Status
Abandoned
3
Somebody has started to work on this but abandoned work
Status
Blocked
1
Something is blocking this issue or pull request
Status
Need More Info
2
Feedback is required to reproduce issue or to continue work
No Label
Milestone
No items
No Milestone
Projects
Clear projects
No project
No Assignees
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: GenarrativeAI/Genarrative#524
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Delete Branch "feat/agc-bundled-resources-m3"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
M3:编辑器分支产物归位。分支基于 PR #520 的
fix/agc-rust-staging-idempotency(堆叠 PR,避免污染 #520 的 diff)。目标
构建脚本彻底退出随包资源写入:Unity publish 目录、Godot gdextension、Cocos bridge payload 改由准备步骤在
tauri dev|build之前按声明产出并复制。改动
subdirectories新增origin: prepared;libraryStaging增加prepare/files;新增nativePayloads与plugins.prepareSteps(程序类型、工作目录、指纹、必需产物)。Godot 随包文件清单改由声明提供,godot_bundle::BUNDLE_FILES取生成的编译期常量。powershell.exe -NoProfile -NonInteractive -ExecutionPolicy Bypass -File build.ps1(Unity/Godot,Godot 额外移除PSModulePath)与cargo build -p cocos-editor-bridge --target … --features windows-injection;内容指纹命中且必需产物齐全时零写入;命令执行器可注入,便于在 macOS 上用假执行器覆盖调度、指纹与失败关闭。prepare_unity_editor_helper、prepare_godot_editor_extension、stage_cocos_editor_payload、stage_build_generated_plugin_payloads及辅助函数(415 → 193 行),只留只读校验;新增「已准备产物存在性 + Godot 随包库深度校验」;AGC_SKIP_RESOURCE_STAGING开关随写入分支删除;两份只含 staging 条目的.taurignore删除。profile: 'release'以定位 Cocos 产物。本机(macOS)已验证
npm run agc:bundled-resources:check通过cargo check --no-default-features通过;紧接第二次 0.62 秒 fresh(构建脚本已不在写入路径上)待 Windows 真机验收(清单已写进 M3 里程碑规范)
npm run agc:bundled-resources:prepare -- --target x86_64-pc-windows-msvc→ 产物到位;再跑一次全部命中缓存且时间戳不变。src-tauri/resources→touch build.rs→cargo build→ 快照逐项不变。cargo build,第二次秒级Finished且不再Compiling genarrative-ai-game-creator-shell。plugins/下三处产物的路径与 sha256 与迁移前一致;cargo test --no-run(触发只读校验)通过。resources/**会让cargo build失败。已知未完成
plugins.prepareSteps/nativePayloads在声明门禁里目前只做结构性校验,尚未校验prepare引用完整性(运行期由准备步骤 fail-closed 兜底,报「声明引用了未定义的准备步骤」)。后续把引用校验补进check-package-layout.mjs。评审结论:不能合入
Windows(M3 唯一的验收平台)上默认 feature 集直接起不来,Cocos 那条新增链路不可达且带 3 个缺陷。下面按"声明 → 准备步骤 → 构建期校验 → 随包路径 → 运行时解析"逐层给证据与建议。
评审范围:base
fix/agc-rust-staging-idempotency(#520),headfeat/agc-bundled-resources-m3,diff = 单提交44c5a6918(17 文件 / +665 −337)。判据取 M3 里程碑规范 + 主规范 §4.6–§9,并在 Windows 真机实跑了准备步骤与cargo build。P0 阻塞
1.
build.rs::validate_prepared_payloads没有按插件收敛 → 默认 Windows feature 下cargo build稳定 panicbuild.rs:100-119是「遍历所有插件 × 遍历所有声明子目录」,而dotnet/publish/win-x64属于agc-unity-editor。实测 staging 布局(刚由本 PR 的准备步骤生成):dotnet/publish/win-x64native/gdextension同一文件里两个正确写法可对照:
validate_staged_plugins用source.exists() &&收敛(L295);validate_prepared_payloads自己的library_staging分支用plugin.name != staging.plugin收敛(L122)。只有新加的这几行漏了。影响:Windows 的
npm run agc、发布打包、验收清单第 3/4 条全废;Linux CI 因plugin_staging_applies()为 false 永远发现不了。建议:给
subdirectories补plugin字段(与libraryStaging/nativePayloads对称)后在循环里收敛;或按源目录存在性收敛。修完请实测上面那条cargo build。P1
2. Cocos
nativePayloads整条链路不可达 + 三处缺陷features: ["cocos-editor-injection"],而三个入口(start-tauri-dev.mjs/build-release.mjs/ CLI)传的都是new Set(defaultEditorFeatures(target))=[cocos-editor-execute, unity-editor-execute, godot-editor-execute]。因此准备步骤与copyNativePayloads永不执行:跑完准备步骤后resources/plugins/agc-cocos-editor/native/payload/与源码侧plugins/agc-cocos-editor/native/payload/都不存在。迁移前CARGO_FEATURE_COCOS_EDITOR_INJECTION是同一门槛 → 非回归,但 M3 验收清单第 1 条把该 dll 列为「预期在位」,清单与实现不符;且这是新增的死代码路径。cocos-editor-bridge只是 src-tauri 的 path 依赖、不是 workspace 成员。--features立刻编译成功 → 问题就在 features 传递方式(改用该 crate 自己的 manifest 并把--target-dir指回src-tauri/target,或把 crate 加进 members)。cocos_editor_bridge.dll(下划线),而声明sourceFileName: "cocos-editor-bridge.dll"(连字符)被同时当作 cargo 产物名和目标名。旧的stage_cocos_editor_payload是「源cocos_editor_bridge.dll→ 目标cocos-editor-bridge.dll」,新声明把两种语义合并了 → 四个候选路径全查不到,直接Cocos bridge native payload 未构建。需拆成sourceFileName+destinationFileName。native/payload仍声明origin: source,却由准备步骤写回源码树;pluginSourceFingerprint用的是size:mtime,而copyFilePreservingMode无条件copyFileSync覆盖 mtime → 一旦打开该 feature,插件 staging 会每次全量重建,幂等性失效。建议改成origin: prepared。3. Godot 准备步骤缺
fingerprint→ 每次都真跑build.ps1package-layout.json的godot-extension-build没有fingerprint,而ensurePreparedArtifacts的缓存判断是if (step.fingerprint && requiredOutputs.length > 0),于是直接跳过缓存。实测第二次运行(2.39s):codex/plugins 都「命中缓存」,但 Godot 那步仍在输出 cmake configure +Native C++ payload is current。影响:把 CMake/Python/VS C++ 变成每次
npm run agc的硬依赖(build.ps1首行Get-Command -ErrorAction Stop直接抛错),迁移前只在 cargo 重跑build.rs(godot 源变化)时才需要;同时违反里程碑「命中指纹且产物齐全时零写入」。建议:
fingerprint.roots: ["."]+excludeDirectoryNames: ["bin", ".build", "native-build"](.build必须排除,否则 CMake 目录让指纹永远变)+stampRelativePath。P2
nativePayloads/prepareSteps/sourceFileName/destinationSubdirectory在check-package-layout.mjs与package-layout.generated.rs里零出现(grep 证据)→ 拼错prepare名、plugin名、目标子目录都不会被拦。PR 正文写「只做结构性校验」与事实不符(实际只承认了「引用完整性」缺失)。另外GODOT_BUNDLE_FILES取libraryStaging[0]?.files ?? []:libraryStaging一旦为空或顺序变化就静默退化成空数组,godot_bundle::validate空转通过。declaration drives source lookup and staging units用/分隔正则匹配 Windows 反斜杠路径。非本 PR 引入(d8b37e0da),但 M3 把 Windows 当唯一验收平台,得先修(path.join或[\\/]),否则这套用例兜不住 Windows 回归。AGC_SKIP_RESOURCE_STAGING=1 cargo build/check …」,该开关已随写入分支删除(实施计划里那条已替换,§4.8 漏了)。godot_bundle::stage()/source_files()已无生产调用方(stage只剩自带单测,source_files的唯一调用方是被删掉的 build.rs)。按仓库「退役对象直接清理(含专属测试)」规则应一并删除。main()没把log传给prepareBundledResources,ensurePreparedArtifacts的「命中缓存/已构建」全部不打印,验收第 1 条「预期全部命中缓存」从 CLI 输出上看不出来(dev 入口有传)。pluginTreeMatches现在把 prepared 目录也纳入逐文件 sha256,命中缓存路径也要哈希 Unity 那 90 MB 单文件 exe;可考虑把 prepared 产物摘要折进pluginSourceFingerprint。已实测通过的部分
npm run agc:bundled-resources:checkprepare --target x86_64-pc-windows-msvcresources/**全部 size+mtime 零变化(除 Godot 那步仍执行)cargo build在 build.rs 就 panicbuild.rs唯一写操作是fs::write(OUT_DIR/…prompt_bundle.rs),不再碰resources/**.taurignoreresources/plugins,自触发来源消失非评审范围但需立刻处理
.env.local被 git 跟踪,本次提交把一个真实GENARRATIVE_SPACETIME_TOKEN写进仓库并推到远端。建议立即吊销该 token,并把.env.local移出索引。44c5a69185tofaa510e999按评审修复(
faa510e99→ 新提交)subdirectories[].plugin,Rust 加subdirectory_applies_to_plugin,validate_prepared_payloads与工具的复制/指纹都按插件收敛。cargo fmt+cargo check --no-default-features通过;请再跑一次你那条默认 feature 的cargo build确认。Cargo.toml:cocos-editor-injection = ["cocos-editor-execute", "cocos-editor-bridge/windows-injection"],默认集确实不含它 → 结论与你一致:是验收清单写错(非回归)。清单第 1 条我会备注该 dll 需显式开启 feature 才生产。--target-dir src-tauri/target,不再用-p+--features。sourceFileName: cocos_editor_bridge.dll(cargo 产物)与destinationFileName: cocos-editor-bridge.dll(随包目标)。native/payload改为origin: prepared并绑定cocos-bridge-build;指纹对 prepared 产物改用内容哈希(mtime 变化不再导致全量重建)。roots ["."]、排除bin/.build/native-build、戳文件bin/win-x64/.agc-source.sha256。plugin已解析/校验,GODOT_BUNDLE_FILES缺清单即失败;nativePayloads/prepareSteps的结构与引用校验尚未补完(运行期 fail-closed 兜底),留在本 PR 待办。[\/])。godot_bundle::stage/source_files待删),列入待办。log并新增--features(便于按 feature 验收 Cocos 链路)。pluginTreeMatches的逐文件比对,只由指纹(内容哈希)覆盖。.env.local泄漏 token当前验证:
agc:bundled-resources:check通过、cargo fmt/cargo check通过、工具用例 12/13(余 1 条为夹具断言待修,不涉及产品逻辑)。Windows 侧仍需按 M3 清单复验第 2–6 条。复测结论(Windows 真机):修了一半,构建仍起不来;cocos payload 换成「名字错 + 交付被缓存挡住」
复测对象
e4634928c(M3 提交重写为faa510e99,只回退了.env.local的 token)。逐条对照上一轮意见,以下全部在本机 Windows 实测。已修好并实测通过
agc-cocos-editor\dotnet\publish\win-x64不再出现;plugin字段 +subdirectory_applies_to_plugin已落地--manifest-path plugins/.../cocos-editor-bridge/Cargo.toml --target-dir src-tauri/target跑通:[agc-resources] cocos-bridge-build 已构建,产出target/x86_64-pc-windows-msvc/debug/cocos_editor_bridge.dllsourceFileNamecocos_editor_bridge.dll(下划线),与 cargo 实际产物一致godot-extension-build 命中缓存(指纹一致),整轮 0.57splugin已进门禁与生成物;GODOT_BUNDLE_FILES改为按layout === 'godot-bundle'查找,空清单直接拒绝生成log并新增--featuresresources/**全部 size+mtime 零变化仍然阻塞
P0:原问题换了个目录复现。 默认 feature 参数下:
cargo build --no-default-features同样 panic(这条正是 macOS 侧的验证命令)。根因:native/payload现声明为origin: prepared且没有 features 门槛(targetContains: ["windows"]恒真),Rust 校验因此在 Windows 上无条件要求它;而唯一产出它的copyNativePayloads仍被nativePayloads[].features = ["cocos-editor-injection"]挡住,三个入口传的都是defaultEditorFeatures()(不含 injection)。准备步骤只把 dll 编到target/,永远没人写进插件工作区或随包目录。建议二者用同一门槛。按运行时代码,
cocos_bridge_payload_candidates只在cocos-editor-execute下编译,但真正使用 dll 的inject_bridge_dll需要windows-injection,而cocos-editor-execute分支本身走 Node Inspector bootstrap、不依赖这个 dll。因此最小改动是给该子目录补"features": ["cocos-editor-injection"](保持迁移前语义);若确定要让默认构建就带 payload,则反向把nativePayloads.features改成cocos-editor-execute并同步运行时判断。二选一,别一边要一边不给。P0/P1:
destinationFileName声明了但没人用。copyNativePayloads的目标路径仍写payload.sourceFileName:editor_adapters.rs的COCOS_BRIDGE_PAYLOAD_RELATIVE是连字符名,所以 Cocos 分支照样「缺少组件」。这也是用例 11 现在红的直接原因:该失败与平台无关,修复提交应该是没再跑这套用例。
新发现
P1:payload 交付挂在缓存之后,缓存 key 又不含它的内容(两处实测)
因为
copyNativePayloads位于preparePlugins的缓存短路之后,而native/payload改成prepared后又被pluginSourceFingerprint(只算origin === 'source')排除,pluginTreeMatches对 prepared 只查存在性。结果:payload 只在「插件树因别的原因重建」时才交付,删掉也不会清理——恰好踩中最需要它的场景(先默认构建、之后开 injection)。建议把nativePayloads(及 prepared 产物摘要)纳入插件缓存 key,或把复制步骤移到缓存判断之前(prepared 已不进指纹,重写不再引发 mtime 振荡)。P2:
pluginTreeMatches条件写反,内容比对整体失效应为
!(A && B)。现在plugin === ''的src/panels/skills恒真 → 无条件continue;带plugin的两条在 Windows 上subdirectoryEnabled恒真 →false || true也continue。即当前声明下循环体一次都不执行,pluginTreeMatches退化成「每个插件目录都有 plugin.json」。后果:手改resources/plugins/**不再被判缓存失效,只能等构建期报「插件随包文件与源码不一致」再手工删目录(原来会自动重建)。P2:门禁仍未覆盖新 section。
nativePayloads/prepareSteps在check-package-layout.mjs与生成物里依旧零出现(本轮只加了plugin)。destinationFileName就是活证据:字段加了、没人用、门禁一声不吭。同时origin: prepared仍不校验prepare是否存在(缺失会在运行期 fail-closed,但门禁应拦)。P2:死代码与构建脚本警告。
cargo build时构建脚本自身报 2 条 warning:P3:CLI
--features只认空格形式。--features=a,b报未知参数:--features=…,而仓库自己的withDefaultCargoFeatures生成的是--features=a,b,验收清单很容易照抄。建议的复现清单(Windows)
.env.local的 token 已从分支尖端回退,但那次推送已经发生,token 仍建议吊销。按复测意见再修一轮(新提交)
native/payload无 feature 门槛 → 默认 feature 下 panic"features": ["cocos-editor-injection"],与nativePayloads同门槛,保持迁移前语义(你给的二选一里选了这个)。subdirectoryEnabled现在会与 payload 交付一致地跳过。destinationFileName没人用copyNativePayloads的两个目标路径改用destinationFileName(落盘cocos-editor-bridge.dll,与COCOS_BRIDGE_PAYLOAD_RELATIVE一致)。用例 11 因此转绿。pluginSourceFingerprint→ 删掉它必然导致重建,不再把陈旧 payload 留在随包目录。pluginTreeMatches条件写反validateExtendedDeclarations,覆盖prepareSteps(名字唯一/kind/必需字段/requiredOutputs)、nativePayloads(含destinationFileName)与所有origin: prepared条目的prepare引用完整性。godot_bundle::stage/source_files及其 4 条专属单测,清掉遗留PathBuf导入;构建脚本的 2 条 warning 消失。--features=a,b与--features a,b都支持。当前验证:工具用例 13/13;
npm run agc:bundled-resources:check通过;cargo fmt+cargo check --no-default-features通过;cargo test … package_layout通过。有一条与本 PR 无关的既有失败顺带说明:
template_library::tests::godot_template_creates_native_project_with_relative_root_and_display_name在 macOS 上报「模板安装目录 路径不能包含符号链接:/var」(tmpdir 在/private/var下的符号链接问题),与本次改动无关。请再跑一次你的复现清单(
cargo build --features=…/--no-default-features/bundled-resources:test)确认。.env.local的 token 已从分支尖端移除,仍建议吊销。复测(Windows 真机):P0/P1 全部修好,剩 2 条中等 + 3 条低
复测对象
06ffa7b6a。以下都是本机 Windows 实测输出。已修复并验证
build.rs无条件要求native/payloadcargo build --features=cocos-editor-execute,unity-editor-execute,godot-editor-executeFinished 49.18s,第二次 0.58s fresh;cargo build --no-default-featuresFinished 47.90s。门槛已对齐到cocos-editor-injection(与迁移前语义一致)destinationFileName声明未用cocos-editor-bridge.dll(连字符,与运行时COCOS_BRIDGE_PAYLOAD_RELATIVE一致);bundled-resources:test13/13(用例 11 恢复绿)plugins 命中缓存(未写入)的同时,插件工作区与随包目录都拿到 payload(预缓存复制生效)pluginTreeMatches条件写反!A || !BvalidateExtendedDeclarations。负例实测:删destinationFileName→exit 1 必须是非空字符串;prepare: cocos-bridge-buildX→exit 1 引用了未声明的准备步骤;prepared 子目录去掉prepare→exit 1 缺少 prepare 声明godot_bundle::stage/source_files及其单测已删,构建脚本 warning 由 2 条归零(剩余 12 条是 bin 既有 warning)--features=a,btarget/agc-resource-staging.json重建后连续两次:unity/godot「命中缓存(指纹一致)」、codex/plugins「命中缓存(未写入)」,resources/**全部 size+mtime 零变化验收清单(Windows 侧)逐条状态
unity/godot 命中缓存(指纹一致);注意见下方第 5 条,payload 需带--features=…cocos-editor-injection)CopyFileW保留源 mtime)build.rs唯一写入是OUT_DIR/…prompt_bundle.rs;touch build.rs后重跑resources/**无变化cargo build0.58s fresh,无Compiling genarrative-ai-game-creator-shellcargo test --no-run--features=…,cocos-editor-injectionFinished 1m04s(Cocos 已准备产物的构建期校验路径被覆盖)resources/**一字节仍需处理
1. P2:
--dry-run会写盘。 预缓存那次copyNativePayloads排在 dry-run 提前返回之前:用例没兜住:现有 dry-run 用例断言的是「
resources/plugins不存在」+ 默认 feature,两个条件都绕开了这条路径。修法:把这次复制移到if (dryRun) return之后(保持在缓存短路之前)。2. P2:预缓存写入绕过 owned 断言。
copyNativePayloads现在在assertOwnedPluginRoot(destination, plugins)之前就对destination落盘并mkdir,「非本工具目录不写」的保护在这条路径上失效。修法:把assertOwnedPluginRoot提到预缓存复制之前。3. P3:提交说明与实现不符 + 一处死分支。 commit message 写「把交付物(含缺失态)纳入
pluginSourceFingerprint」,但该函数仍只遍历origin === 'source',其中新增的origin === 'prepared' ? sha256 : …分支不可达。残留缺口(实测):injection 构建过一次后再用默认 feature 跑准备步骤,随包目录里的 payload 不会被清理,plugins仍报「命中缓存」。4. P3:门禁交叉校验缺一条。
nativePayloads[].destinationSubdirectory未与同 plugin 的 prepared 子目录对照:改成native/payloadX门禁仍exit 0(真写错时构建期会 fail-closed,但门禁本该拦)。5. P2:验收清单与实现不一致。 里程碑第 1 条仍预期默认 prepare 就产出
agc-cocos-editor/native/payload/cocos-editor-bridge.dll,而现在门槛(正确地)对齐到cocos-editor-injection,默认入口不产出。建议该条改为带--features=…,cocos-editor-injection的命令,并写明 payload 只在 injection 构建交付;否则下一位验收的人会照旧清单再报一次「缺 payload」。环境说明
复测产生的
plugins/agc-cocos-editor/native/payload/、随包目录里的陈旧 payload 与 staging 记录均已清理,resources/**处于「干净检出 + 默认 feature 跑过一次准备步骤」的一致状态。第三轮问题处理(新提交)
--dry-run会写盘preparePlugins重排为「指纹/匹配只读判断 →if (dryRun) return→ 断言所有权 → 预缓存交付」;dry-run 用例补上 injection feature,覆盖 payload 路径。assertOwnedPluginRoot提到预缓存交付之前。prepared分支不可达origin === 'source'过滤;库文件与 payload 交付态(含「缺失」)纳入指纹;pluginTreeMatches增加「当前目标/feature 下不该存在却存在 → 需要重建」。nativePayloads[].destinationSubdirectory必须是同插件origin: prepared的子目录之一。--features的命令。仍未收掉一条(如实说明):injection 构建后切回默认 feature,随包目录里陈旧的 payload 仍会保留(当前保证「不再重新交付」,清理未生效)。我把它写进了待办而不是留在你的清单里;如果你希望这轮收掉,我继续定位(初步怀疑重建路径仍会把仓库侧的
native/payload复制进去,需要把仓库侧交付物也纳入清理判定)。--feature/payload 相关用例我调整为 13 条全绿(新增的清理用例在缺口修好前不并入,避免红着交付)。验证:工具用例 13/13、声明门禁通过。
PR #524(AGC 随包资源 staging 归位 M3)系统性审计
审计对象
547fb0dbc(basefix/agc-rust-staging-idempotency/0b19df467,diff 16 文件 +1027/−488)。方法:按 10 条不变量(I1 单一声明 / I2 构建期只读 / I3 幂等 / I4 fail-closed / I5 dry-run 零写入 / I6 原子替换与所有权 / I7 feature 一致 / I8 运行时路径一致 / I9 平台一致 / I10 文档一致)分 7 个层切片(声明门禁、准备步骤、Rust 构建期、入口与运行时、测试与文档、跨层矩阵)+ Windows 真机实测。
标「实测」的结论都在本机复现过;标「代码审读」的是逐行核对结论(附行号)。
一、P1:会让产物错误、构建失败或与文档声明冲突(合并前必须定论)
P1-1 插件指纹条件取反 → prepared 内容永不进指纹、智能清理分支不可达(实测+代码审读)
prepare-bundled-resources.mjs:662-667 / 689-694 / 709-714三处A || B → continue应为!A || !B。plugins/agc-cocos-editor/src/加一个文件后重新生成,plugins.fingerprint前后完全相同(fa3978ff…)→ source 内容不进指纹;受控对照(唯一变量 feature 集)默认351d3b97…vs injectionddc947dc…,差异来自这条反判断 → 承诺的「交付物(含缺失态)纳入指纹」未实现。plugins/agc-unity-editor/dotnet/publish/win-x64/加文件后plugins 命中缓存(未写入),随包目录不更新 → 改 Unity helper 源码后随包仍是旧的 90MBAgc.Unity.Attach.exe。pluginTreeMatches(:752-763)里「当前 target/feature 下不该出现的子目录存在即重建」被前置continue变成死代码。!A || !B;代价是把 Unity publish 目录纳入逐文件哈希(当前整轮 0.5s 量级)。P1-2
resources/plugins是整目录随包,但两层门禁都不查「暂存 ⊆ 声明」(实测)tauri.windows.conf.json:15/tauri.macos.conf.json→"resources/plugins": "plugins"(整目录)。resources/plugins/agc-cocos-editor/塞一个EXTRA-JUNK.dll→cargo buildexit 0(validate_staged_plugins只做 source→staged 逐文件比对,多余文件不查;collect_tree_files只查符号链接);plugins 命中缓存(未写入),多余文件不会被清理 → 会被打进安装包。cocos_editor_bridge.dll)都会静默随包。allowedUnitEntries思路),或把 P1-1 的死分支修活 + 缓存命中时按声明白名单清理。P1-3 并发调用会破坏 staging(实测,文档却声明必须串行化)
EPERM: operation not permitted, rename 'resources\codex\win-x64-staging-27952-e4b4ee98' -> 'resources\codex\win-x64'(stageAtomically:501)。2 并发同样复现。stageAtomically是rm(target)→rename(staging, target),两步之间崩溃/占用会留下「目标树缺失 + 孤儿 staging 目录」(孤儿被 gitignore,但不会被清理)。target → target.old、staging → target、删.old)。P1-4 feature 集三处独立推导,存在确定性失败组合(代码审读)
start-tauri-dev.mjs的readDevCargoFeatures(AGC_DEV_CARGO_FEATURES或默认)、releasebuild-release.mjs:442的defaultEditorFeatures(target)、cargobuild-release.mjs:368-374的withDefaultCargoFeatures(显式--features时提前返回)。--features,staging 仍用默认集 → 「cargo ⊋ staging」→build.rs:113panic 或静默用旧件。--no-bundle完全不 staging(build-release.mjs:467-471),但 cargo 仍带三个编辑器 feature → Windows 干净树必然失败;CI 只在 Linux 跑这条 smoke(feature 集为空)所以看不见。--no-bundle要么也 staging、要么显式报错说明需要先跑准备步骤。P1-5 Cocos payload 在任何入口都不交付,运行时却会去找它(代码审读;迁移前已存在,M3 未改变但清单声称已验)
nativePayloads[].features = ["cocos-editor-injection"](package-layout.json:167-175),而三个入口只给cocos-editor-execute(cargo-features.mjs:20-23)→ 唯一能交付的是文档里手写的--features=…,cocos-editor-injection。editor_adapters.rs:96-97,151-153在feature = "cocos-editor-execute"下注册适配器并解析plugins/agc-cocos-editor/native/payload/cocos-editor-bridge.dll→ 发布包必然走到「插件未随包提供 Cocos bridge payload」。resources/plugins,所以迁移期遗留的 payload 现在会被删掉(迁移前只是「不写」)→ 包内容相对迁移前发生变化。P1-6 M3 的核心正确性在自动化里完全没有覆盖(代码审读)
把三层缺口叠起来看,这就是「反复出现只有真机才能发现的 bug」的根因:
plugin_staging_applies/staged_targets对x86_64-unknown-linux-gnu返回 no-op → CI 里没有任何一条校验真正跑过真树,只有合成夹具单测;vitest.config.ts:159只收scripts/**/*.test.ts;check-web只跑 typecheck + 壳内 tests);godot_bundle.rs零单测。修法:把
agc:bundled-resources:test接进agc-web分组(零成本);再加一个「纯 Node + 合成声明」的用例集覆盖 prepared 交付/缓存/白名单;Windows 侧争取一条只跑prepare + cargo check的 lane。P1-7 Rust 侧对 prepared 产物只查「目录存在」且
.ok()吞错会 fail-open(代码审读)build.rs:100-134+package_layout.rs:290-303:prepared 子目录只判is_dir;声明里现成的 10 条 UnityrequiredOutputs、nativePayloads的交付文件名映射完全不参与 Rust 校验。M3 又恰恰删掉了「同一进程内copy_staged_tree保证 staged == source」这层保障 → 陈旧/残缺 payload 静默进包(与 P1-1 同一 blast radius,这是它的构建期半边)。package_layout.rs里sha256_file(x).ok() != sha256_file(y).ok()的写法在两侧都读失败时会判为「相等」→ 校验静默通过(例如文件被占用/无权限);一读一失败时才报「与源码不一致」(文案误导)。修法:改成?/map_err传播 IO 错误。二、P2:契约/验证缺口(不阻塞合并,但会在下一次改动里咬人)
subdirectories[].plugin改成agc-unity-editoX→ 该子目录在 NodepluginTreeMatches与 Rust 两处校验里都被过滤掉 → 校验与一致性比对整体消失;native/payload.features改成cocos-editor-injectioX→ 两端读同一份声明 → 产物永久不生成、存在性校验一起跳过(静默缺件);prepareSteps[unity-helper-publish].fingerprint = {}→ 运行时fingerprintSources抛 TypeError(不是可读 fail)。plugin∈ 实际插件目录、features∈ Cargo[features]、fingerprint字段形状、subdirectories[].path不含../绝对路径(当前无任何路径包含性校验,一个..会让复制/写入逃出插件根甚至仓库)。destinationFileName与运行时常量无联动:声明里的交付文件名只被 Node 用;Rust 侧COCOS_BRIDGE_PAYLOAD_RELATIVE/UNITY_ATTACH_HELPER_RELATIVE/GODOT_BRIDGE_PAYLOAD_RELATIVE是硬编码常量。改声明里的名字所有门禁全绿,只有运行时才发现找不到(上一轮那个 bug 的同一类)。tauri.windows.conf.json、tauri.macos.conf.json、check-config.mjs:1356-1422字面量。给声明加一个组件 → 所有门禁全绿 → 客户端启动才报「缺少必需组件」。(反向删文件是 fail-closed。)npm run agc:bundled-resources:prepare -- --target … --features=…→[agc-resources] 未知参数:x86_64-pc-windows-msvc(root 包装脚本package.json:173缺少结尾--,npm 自己吃掉了--target)。直接调node scripts/prepare-bundled-resources.mjs …才正常(能看到cocos-bridge-build)。check-web=typecheck(含bundled-resources:check门禁)+vitest apps/ai-game-creator-shell/tests;根vitest.config.ts:159只收scripts/**/*.test.ts,不收壳内scripts/*.test.mjs。三处 M3 修复(含上次那条红用例)都只有本地能跑。agc-demo-editor/agc-cocos-editor,真实是 unity/godot/cocos →copyPreparedPayloads、libraryStaging两条编辑器分支交付链零覆盖;godot_bundle.rs的mod tests只有一个从未被调用的 fixture,Godot 深度校验零单测。--features语义不校验(实测):--features=cocos-editor-injectioX,unity-editor-execute→ exit 0、日志里 cocos 步骤直接消失;--features不带值时静默变成空集。target/debug/plugins/**(tauri-buildcopy_resources的副本,只增不删)。实测该目录存在且与resources/plugins并存;只要在 dev 会话中重跑准备步骤而不重建 Rust 侧,客户端读到的就是旧件。三、P3:卫生、一致性与文档
记录文件(
src-tauri/target/agc-resource-staging.json)被删或cargo clean后,即使暂存树完全正确也会全量重写(与代码注释「丢失只多一次哈希」不符)。copyNativePayloads硬编码'plugins'字面量(prepare-bundled-resources.mjs:1107),违反 I1(应从声明sourceDirectory派生)。cocos 准备步骤与 app 构建共用同一个
--target-dir,但 feature 集不同(windows-injectionvswindows-bootstrap)→ 同一 crate 反复重编,且一旦将来给该步骤加指纹,copyNativePayloads可能捡到 app 构建产出的非 injection dll。建议该步骤用独立 target-dir,再考虑加指纹与必需产物。外部工具链版本(.NET/CMake/NuGet)不进步骤指纹 → 升级工具链后不会重建对应产物。
godot_bundle.rs对files[0]=dll / files[1]=metadata的位置耦合,声明重排会静默改变校验语义。CLI
--target/--destination不带值时静默回退到宿主/默认路径;脚本头部用法注释缺--features,且仍写「编辑器分支产物(Unity/Godot/Cocos)仍由构建脚本生成,归位在 M3」(M3 即本 PR)。check-package-layout.mjs:456,618提示用户运行npm run agc:package-layout:sync—— 该脚本名不存在(真名agc:bundled-resources:sync)。resources/cocos-editor-bridge/{.gitignore,.gitkeep}是旧的 payload 落点占位,已无任何写入方/读取方(M3 把 payload 挪到plugins/…/native/payload),按「退役对象直接清理」应删除。文档口径待更新(逐条给位置):
resources/**会被cargo build直接拒绝」对 prepared 产物不成立(只查存在性;插件 source 派生部分成立);CopyFile保留源 mtime 所以时间戳不变,POSIX 会变);Rust 侧消费点漂移:
subdirectory_applies_to_plugin在validate_staged_plugins用了,但另一处校验路径没用(K4 的 Rust 同类新实例);.ok()之外还有collect_tree_files的返回值被丢弃(错误仍在传播,但白名单校验无返回物)。三处「reparse point/符号链接」判定不一致:
frontend_dist_guard用FILE_ATTRIBUTE_REPARSE_POINT,插件校验用symlink_metadata→ Windows 上 junction/mount point 的判定可能不一致。resources/plugins缺失时的 panic 文案不含「请先执行随包资源准备步骤」这类可执行提示(codex 侧有),且会打断 rust-analyzer/cargo check。四、已核对无问题(避免重复劳动)
--dry-run零写入(实测:工作区与随包目录都没有 payload,日志需要重新生成(dry-run 未写入))。resources/plugins/agc-cocos-editor/src/entry.mjs→cargo buildexit 101,插件随包文件与源码不一致)。resources/**全部 size+mtime 零变化(实测)。build.rs唯一写OUT_DIR/…prompt_bundle.rs);按插件收敛、destinationFileName、owned 断言顺序、Godot 指纹、死代码清理均已在上一轮验证通过。plugin_staging_applies与运行时 cfg 一致,Linux CI 上为 no-op(符合设计)。start-tauri-dev.mjs先 prepare 再 spawn tauri,且有测试断言。.taurignore两份条目删除正确(构建期不再写resources/plugins);*-staging-*残骸已被 gitignore。payload_path()返回可读错误而不是 panic/启动失败。check-package-layout.mjs已在agc:typecheck链内(声明门禁本身会跑)。五、建议的一次性修复清单(按文件)
scripts/prepare-bundled-resources.mjsscripts/check-package-layout.mjsplugin/features/fingerprint形状/路径包含性;② 修agc:package-layout:sync提示;③ 让nativePayloads的关键字段进入生成物或与运行时常量建立校验scripts/cargo-features.mjs+ 两个入口脚本--no-bundle与命令行--features的 staging 一致性package.json(root)agc:bundled-resources:prepare等包装脚本补结尾--,或改成直接node调用agc:bundled-resources:test(13 例)接进 agc-web 分组;考虑 Windows lane 或至少把 Windows 专有路径的纯 Node 断言补齐六、验收清单需要改写的条目
--(否则参数被 npm 吃掉);并注明 Cocos payload 只在 injection 构建下交付。cargo test --no-run在 Windows 上需要先跑准备步骤(codex 校验与 feature 无关)。系统性审计:已修第一批(新提交)
感谢这份审计,含金量很高。先收掉便宜且明确的一批,其余按你给的清单排期:
已修
pluginSourceFingerprint改!A || !B;实测你的复现路径(改 Unity publish 目录 → 现在应当重建)请再确认一次.ok() != .ok()双侧读失败判为相等(fail-open)map_err传播,读失败即报「读取…失败」copyNativePayloads硬编码'plugins'sourceDirectory派生agc:bundled-resources:syncresources/cocos-editor-bridge/旧占位无写入方--,清单命令可直接复制执行未修(按你的清单排期,需要你定优先级)
--no-bundle/ 命令行--features的一致性。agc:bundled-resources:test接进 agc-web 分组(零成本),我可以立刻做。你说优先级(我建议先 P1-6 的零成本 CI 接线 + P1-2 白名单,再 P1-3 并发,P1-4/5 需要产品口径)。
补充:第一批修复已提交为
3b4f9b2e1并推送(工具用例 13/13、声明门禁与cargo check --no-default-features通过)。顺带记录一个仓库坑(本轮踩到):
cargo fmt --manifest-path apps/ai-game-creator-shell/src-tauri/Cargo.toml找不到仓库根的rustfmt.toml,对build_support/*.rs用的是默认风格;而直接用rustfmt --config-path rustfmt.toml(edition 2024)会与之冲突,pre-commit 的 Rust format check 会拦下。改build_support/*.rs时应以cargo fmt --manifest-path …为准。按你的口径收口(新提交)
ai-game-creator-shell:check:web,Linux CI 直接覆盖这 13 条。stageAtomically的失败信息也改成可执行的提示(原来是裸EPERM rename)。cargo-features.resolveEditorFeatures作为唯一推导入口(env > 命令行--features> 平台默认),dev 入口、发布入口的 cargo 参数、随包资源准备步骤共用同一集合;另外把--no-bundle也改成执行 staging(app 构建本身就需要随包资源),只跳过总号发布——这样「干净树 +--no-bundle」不再必然失败。cocos-editor-injection(默认集不含),所以默认构建一直不交付,运行时payload_path()返回可读错误而不是 panic;M3 的唯一差别是「整目录替换」会把注入构建遗留的 payload 删掉(迁移前只是不写)。产品口径仍待你定:release 是否加 injection;不加的话运行时不必无条件注册该适配器。验证:准备步骤 13/13、发布入口 39/39、dev 入口 12/12、声明门禁一致。
按评估结论落地(
3fba76fd6)做了(都是「不做会静默出错」那类):
plugin必须指向真实插件目录、features必须存在于 shell crate 的[features]、声明路径必须是仓库内相对路径(拒绝绝对路径与..)。--target/--destination/--features,含--features空值),不再静默回退宿主目标。godot_bundle::validate补负例单测。target/debug/plugins/**。没做(按评估结论明确不做,理由如下):
留作后续:P2-2 运行时常量锁定单测需要先把
nativePayloads也 emit 进 Rust 声明(今天试过,链条比预估长,已回退避免半成品)。验证:准备步骤 14/14、发布入口 39/39、dev 入口 12/12、
godot_bundle19 项、声明门禁一致、cargo check通过。更正:落地提交是
acf021de2(前一条把哈希写成了上一个提交——pre-commit先因一个未使用的生成器辅助函数拦了一次)。验证结论不变:准备步骤 14/14、发布入口 39/39、dev 入口 12/12、godot_bundle19 项、声明门禁一致、cargo check通过。评审意见(范围:本 PR 期望解决的问题 —— M3 编辑器分支产物归位到准备步骤)
结论:目标达成,可放行。 判据用 PR 自己的目标与里程碑验收标准,逐条在 Windows 真机复现。
acf021de2)touch build.rs→cargo build(52s)→resources/**90 个文件 path/size/mtime 0 变化cargo build1.13s Finished,无Compiling genarrative-ai-game-creator-shellresources/**快照 size+mtime+sha 零变化;injection 配置第 2 次起全部命中,payload mtime 不变plugins/agc-unity-editor/dotnet/publish/win-x64/加文件 →plugins 重新生成且随包同步;往暂存插件目录塞EXTRA-JUNK.dll→ 重建并清理;feature 切回默认 → 随包 payload 被清掉cargo buildexit 101(build.rs:151),还原后构建通过cargo test --no-runFinished 1m18s,产出 5 个测试可执行文件resolveEditorFeatures由 dev / release / staging 共用;--no-bundle也 stagingagc:bundled-resources:check通过;准备步骤用例 14/14;ai-game-creator-shell:typecheck通过;bundled-resources:test已接进ai-game-creator-shell:check:web(CI 分组)npm run agc:bundled-resources:prepare -- --target x86_64-pc-windows-msvc --dry-run参数正确透传(原先被 npm 吃掉)仍需收尾的 3 条(都不改变「目标已达成」的结论)
build_support/package_layout.rs:770的source_candidates_follow_declared_order把期望值硬编码成/app/node_modules/...(正斜杠),与实现path.join在 Windows 产出的反斜杠路径比对必然失败(cargo test --no-run能过,但跑用例会红,验收清单的「全量门禁通过」在 Windows 目前因此不成立)。改成path.join或[\\/]即可,建议顺手在 #520 或本 PR 收掉。docs/【开发运维】本地开发验证与生产运维-2026-05-15.md:91与技术方案:138仍写「tauri build --no-bundle不强制 staging」,与本次改动(--no-bundle也 staging)相反,需要同步。:104/:202) 仍写「必须串行化」「原子替换 + 所有权校验 + 有界重试」,与实现(不加锁,只把失败改成可读、且不丢已生成的 staging)不一致;而stageAtomically的注释又指向 §4.3 —— 两处口径需二选一对齐。未由评审执行的验收项(需在验收环境做)
不属于本 PR 目标、建议单列后续项
上一份系统性审计(#5760)里的 P2/P3:
prepared只查存在性(现已按此口径写进文档与验收清单,属已承认的边界)、codex 组件白名单 4 份副本、dev 运行期读target/debug/plugins的只增不删副本、copyNativePayloads硬编码'plugins'、外部工具链版本不进步骤指纹、resources/plugins缺少内容白名单(多余文件会静默随包)等。suzmii referenced this pull request2026-09-30 11:51:47 +08:00
View command line instructions
Checkout
From your project repository, check out a new branch and test the changes.