Compare commits

..

18 Commits

Author SHA1 Message Date
suzmii acf021de2a 按评估结论落地:门禁交叉校验、CLI 缺值校验、编辑器分支覆盖与文档
Project CI / AI game creator shell Rust crates (pull_request) Successful in 1m19s
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 1m51s
Project CI / Native shell tests (pull_request) Failing after 1m10s
Project CI / Frontend tests (pull_request) Failing after 43s
Project CI / Backend tests (pull_request) Successful in 3m26s
Project CI / Repository checks (pull_request) Successful in 1m51s
Project CI / AI game creator shell web tests (pull_request) Failing after 1m29s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Successful in 8m33s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Successful in 8m34s
- 门禁新增交叉校验:plugin 必须指向真实插件目录、features 必须存在于 shell crate 的 [features]、所有声明路径必须是仓库内相对路径(拒绝绝对路径与 ..)
- 准备步骤 CLI:--target/--destination/--features 缺值直接失败(不再静默回退宿主目标),--features 空值也拒绝
- 用例:fixture 补齐 agc-unity-editor/agc-godot-editor/agc-cocos-editor,新增「编辑器分支产物按 prepared 与 library staging 交付」用例;godot_bundle 补 validate 负例单测
- 运维文档:dev 构建下客户端读 target/debug/plugins/**,改资源要重跑准备步骤
- 未做(已按评估结论记录):P2-2 运行时常量锁定需要先把 nativePayloads 也 emit 进 Rust 声明,留作后续
- 验证:准备步骤 14/14、发布入口 39/39、dev 入口 12/12、godot_bundle 19 通过、声明门禁一致、cargo check 通过
2026-09-28 11:36:34 +08:00
suzmii 3fba76fd6e 按审计结论收口 M3:CI 接线、并发口径、feature 单一推导与文档对齐
Project CI / AI game creator shell Rust crates (pull_request) Successful in 1m22s
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 1m52s
Project CI / Native shell tests (pull_request) Failing after 1m12s
Project CI / Frontend tests (pull_request) Failing after 44s
Project CI / Backend tests (pull_request) Successful in 3m28s
Project CI / Repository checks (pull_request) Successful in 1m50s
Project CI / AI game creator shell web tests (pull_request) Successful in 1m20s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Successful in 7m31s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Successful in 8m14s
- CI:把 bundled-resources:test(13 条,跨平台、注入假执行器)接进 ai-game-creator-shell:check:web,Linux CI 即可覆盖准备步骤逻辑
- 并发:按「无实际场景不做并发支持」定论——不加锁,但替换失败改为可读错误并保留已生成的 staging;技术方案 §4.3 与风险表同步改写
- feature:新增 cargo-features.resolveEditorFeatures 作为唯一推导入口(env > 命令行 --features > 平台默认),dev 与发布入口的 cargo 参数、随包资源准备步骤共用同一集合;--no-bundle 也执行 staging(app 构建本身需要随包资源),只跳过总号发布
- 文档:pitfalls 的校验收口限定到 source 派生内容;里程碑验收清单按实况修正(命令 --、prepared 只查存在性、Windows 侧已复跑)
- 验证:准备步骤 13/13、发布入口 39/39、dev 入口 12/12、声明门禁一致
2026-09-28 10:54:29 +08:00
suzmii 3b4f9b2e10 按系统性审计修复 M3:指纹反判断、守卫死代码、fail-open 与卫生项
Project CI / AI game creator shell Rust crates (pull_request) Successful in 1m23s
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 1m55s
Project CI / Native shell tests (pull_request) Failing after 1m26s
Project CI / Frontend tests (pull_request) Successful in 1m36s
Project CI / Backend tests (pull_request) Successful in 3m37s
Project CI / AI game creator shell web tests (pull_request) Failing after 34s
Project CI / Repository checks (pull_request) Successful in 1m49s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Successful in 7m37s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Successful in 8m19s
- P1-1:pluginSourceFingerprint 的判断取反(prepared 内容与交付态真正进入指纹);pluginTreeMatches 的「当前目标/feature 下不该存在却存在 → 重建」从死代码修活
- P1-7:package_layout 的 `sha256_file(x).ok() != ...` 改为 map_err 传播,两侧读失败不再被判为「相等」
- P3-2:copyNativePayloads 的 'plugins' 字面量改由声明 sourceDirectory 派生
- P3-5:payload 交付内容一致时跳过写入(命中缓存保持零写入、时间戳稳定)
- P3-7:门禁提示里的脚本名改为 agc:bundled-resources:sync
- P3-8:删除已无写入方的 resources/cocos-editor-bridge 占位目录
- P2-4:root 包装脚本补结尾 `--`,文档里的 prepare 命令可直接复制执行
- 验证:工具用例全绿、声明门禁通过
2026-09-28 10:20:09 +08:00
suzmii 547fb0dbce 按第三轮复测修复 M3:dry-run 零写入、所有权先行、指纹与门禁补口
Project CI / AI game creator shell Rust crates (pull_request) Successful in 1m23s
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 2m1s
Project CI / Backend tests (pull_request) Successful in 3m46s
Project CI / Frontend tests (pull_request) Successful in 1m53s
Project CI / Native shell tests (pull_request) Successful in 5m46s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Successful in 8m51s
Project CI / Repository checks (pull_request) Successful in 1m57s
Project CI / AI game creator shell web tests (pull_request) Successful in 1m26s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Successful in 9m39s
- P2:preparePlugins 重排——dry-run 只做只读判断即返回,预缓存交付不再排在 dry-run 之前;所有权断言提到任何写入之前
- P3:pluginSourceFingerprint 放开 origin=source 过滤(prepared 内容哈希分支可达),库文件与 payload 交付态纳入指纹,pluginTreeMatches 增加「不该存在却存在 → 需要重建」判定
- P3:门禁交叉校验 nativePayloads[].destinationSubdirectory 必须是同插件 origin=prepared 的子目录之一
- P2:里程碑验收清单注明 Cocos payload 只在 injection 构建交付并给出对应命令;dry-run 用例补 injection feature 覆盖
- 夹具补齐 agc-cocos-editor 插件(所有权检查要求目标插件真实存在)
- 验证:工具用例 13/13、声明门禁通过
- 已知缺口:injection 构建后切回默认 feature,随包目录里陈旧 payload 的清理尚未生效(已记在 PR)
2026-09-28 01:03:40 +08:00
suzmii 06ffa7b6ac 按复测修复 M3:payload 门槛与交付、缓存键、条件写反与门禁覆盖
Project CI / AI game creator shell Rust crates (pull_request) Successful in 1m41s
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 2m8s
Project CI / Backend tests (pull_request) Successful in 3m50s
Project CI / Frontend tests (pull_request) Successful in 1m58s
Project CI / Native shell tests (pull_request) Successful in 5m55s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Successful in 9m12s
Project CI / Repository checks (pull_request) Successful in 1m51s
Project CI / AI game creator shell web tests (pull_request) Successful in 1m24s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Successful in 9m51s
- P0:native/payload 补 features=["cocos-editor-injection"](与 nativePayloads 同门槛,保持迁移前语义),Windows 默认 feature 下不再无条件要求该目录
- P0:copyNativePayloads 真正改用 destinationFileName(此前声明了却仍写 sourceFileName,落盘成 cocos_editor_bridge.dll 而运行时找 cocos-editor-bridge.dll)
- P1:payload 交付移到插件缓存短路之前(先默认构建、之后开 injection 不再整条跳过),并把交付物(含缺失态)纳入 pluginSourceFingerprint,删掉它必须导致重建
- P2:修正 pluginTreeMatches 里被写反的条件(原为 || 短路,导致内容比对整体失效)
- P2:门禁新增 validateExtendedDeclarations,覆盖 prepareSteps/nativePayloads 的字段与引用完整性(含 destinationFileName)
- P2:删除 godot_bundle 的 stage/source_files 及其单测(构建脚本不再有调用方),清掉遗留的 PathBuf 导入
- P3:CLI 支持 --features=a,b 形式
- 验证:工具用例 13/13;声明门禁通过;cargo fmt/cargo check 通过
2026-09-28 00:50:48 +08:00
suzmii e4634928c6 按评审修复 M3:校验按插件收敛、Cocos 链路与 Godot 指纹、门禁与文档收口
Project CI / AI game creator shell Rust crates (pull_request) Failing after 1m26s
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 2m7s
Project CI / Backend tests (pull_request) Successful in 3m46s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Failing after 6m9s
Project CI / Frontend tests (pull_request) Successful in 1m52s
Project CI / Native shell tests (pull_request) Successful in 6m4s
Project CI / Repository checks (pull_request) Successful in 2m18s
Project CI / AI game creator shell web tests (pull_request) Successful in 1m40s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Failing after 9m42s
- P0:validate_prepared_payloads 遍历所有插件 × 所有子目录 → 与 libraryStaging 分支对称地按插件收敛(声明新增 subdirectories[].plugin,Rust 加 subdirectory_applies_to_plugin,工具复制与指纹同样按插件收敛)
- P1 Cocos:cargo 准备步骤改用该 crate 自身 manifest 并把 --target-dir 指回 src-tauri/target(原 -p + --features 被 cargo 以「cannot specify features for packages outside of workspace」拒绝);nativePayloads 拆出 sourceFileName(下划线)与 destinationFileName(连字符);native/payload 改为 origin=prepared 并绑定 cocos-bridge-build
- P1 Godot:补 fingerprint(roots ["."],排除 bin/.build/native-build,戳文件 bin/win-x64/.agc-source.sha256),不再每次真跑 build.ps1
- P2:pluginSourceFingerprint 对 prepared 产物改用内容哈希(避免无条件覆盖 mtime 导致每次全量重建)、库文件纳入指纹;pluginTreeMatches 对 prepared 只查存在性(内容由指纹覆盖,顺带缓解 90MB exe 的哈希开销);GODOT_BUNDLE_FILES 缺清单即失败,不再静默退化为空数组;CLI 传 log 并新增 --features;用例正则兼容 Windows 路径分隔符;主规范 §4.8 残留的 AGC_SKIP_RESOURCE_STAGING 已清
- 安全:撤出误提交进 .env.local 的真实 token(还原模板并 force-push 分支尖端)
- 验证:npm run agc:bundled-resources:check 通过;cargo fmt + cargo check --no-default-features 通过(含新 plugin 字段与按插件收敛的校验);工具用例 12/13(余 1 条为夹具断言待修)
2026-09-28 00:23:26 +08:00
suzmii faa510e999 AGC 编辑器分支产物归位:构建脚本彻底退出写入
Project CI / AI game creator shell Rust crates (pull_request) Successful in 1m29s
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 2m1s
Project CI / Backend tests (pull_request) Successful in 3m52s
Project CI / Native shell tests (pull_request) Has been cancelled
Project CI / Frontend tests (pull_request) Has been cancelled
Project CI / Repository checks (pull_request) Has been cancelled
Project CI / AI game creator shell web tests (pull_request) Has been cancelled
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Has been cancelled
- 声明扩展:subdirectories 新增 origin=prepared;libraryStaging 增加 prepare/files;新增 nativePayloads 与 plugins.prepareSteps(程序类型、工作目录、指纹、必需产物)
- Godot 随包文件清单改由声明提供,godot_bundle::BUNDLE_FILES 从生成的编译期常量取值,不再各写一份
- 准备步骤按声明运行 powershell.exe -File build.ps1(Unity/Godot,Godot 移除 PSModulePath)与 cargo build -p cocos-editor-bridge --target … --features windows-injection,内容指纹命中且必需产物齐全时零写入,命令执行器可注入以便在 macOS 上覆盖调度与失败关闭
- build.rs 删除 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 产物
- 文档:技术方案 §4.9/§8、M3 里程碑(含 Windows 验收清单)、运维文档、决策日志与排障经验同步
- 验证:准备步骤 13 条用例通过(三类准备步骤调度、指纹跳过、缺产物失败关闭、幂等);npm run agc:bundled-resources:check 通过;cargo check --no-default-features 通过且第二次 0.62 秒 fresh
- 未验证:Windows 真机(powershell/cargo 路径、产物归位、包内容一致性与客户端加载),按 M3 里程碑验收清单在 Windows 上确认
2026-09-28 00:17:49 +08:00
suzmii 0b19df467e 补充 M3 的 Windows 侧验收清单
Project CI / AI game creator shell Rust crates (pull_request) Successful in 1m32s
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 1m45s
Project CI / Backend tests (pull_request) Failing after 16s
Project CI / Frontend tests (pull_request) Successful in 2m10s
Project CI / Repository checks (pull_request) Failing after 19s
Project CI / AI game creator shell web tests (pull_request) Successful in 1m27s
Project CI / Native shell tests (pull_request) Successful in 5m56s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Successful in 9m34s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Successful in 10m19s
- M3 里程碑规范新增「验证步骤(Windows 侧执行清单)」:准备步骤单跑与幂等、构建期不再写随包资源、构建新鲜度、打包一致性、客户端启动与三类边界负例
- 明确记录方式:命令与输出贴回里程碑或 PR,未通过项回到主规范 §9
2026-09-27 23:01:20 +08:00
suzmii 96b44d8660 补充上游元数据漂移的拒绝用例
Project CI / AI game creator shell Rust crates (pull_request) Successful in 1m23s
Project CI / Backend tests (pull_request) Failing after 13s
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 1m56s
Project CI / Frontend tests (pull_request) Successful in 2m11s
Project CI / Repository checks (pull_request) Failing after 17s
Project CI / AI game creator shell web tests (pull_request) Failing after 1m28s
Project CI / Native shell tests (pull_request) Successful in 5m31s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Successful in 8m31s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Successful in 8m41s
- 准备步骤用例新增一条:上游 codex-package.json 的 version/layoutVersion/entrypoint/resourcesDir 任一与声明不一致时必须拒绝且不落产物(共 10 条)
- 同步决策日志与 M2 里程碑规范里的用例计数
2026-09-27 19:01:00 +08:00
suzmii 33308a93e8 校正 M2 交付后的文档口径
Project CI / AI game creator shell Rust crates (pull_request) Successful in 1m21s
Project CI / Backend tests (pull_request) Failing after 14s
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 2m7s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Has been cancelled
Project CI / Repository checks (pull_request) Has been cancelled
Project CI / AI game creator shell web tests (pull_request) Has been cancelled
Project CI / Native shell tests (pull_request) Has been cancelled
Project CI / Frontend tests (pull_request) Has been cancelled
- 技术方案修正症状层补丁清单:prune_staging/STAGED_* 只存在于未合入的补丁,主线从未有过
- 技术方案 §5.2/§5.3 记录过渡期已完成、.taurignore staging 条目保留到 M3 的原因,§8 M2 行标注已交付与 Windows 待复验
- M2 里程碑规范按证据勾选 macOS 侧可达的验收项,未验证项保持未勾选
- M1 实施计划标注 codex_package_metadata.rs 已在 M2 退役
2026-09-27 18:57:54 +08:00
suzmii f6dad1950f AGC 随包资源准备步骤接入 dev 与发布入口,构建脚本退出写入
Project CI / AI game creator shell Rust smoke (pull_request) Has been cancelled
Project CI / Backend tests (pull_request) Has been cancelled
Project CI / AI game creator shell Rust crates (pull_request) Has been cancelled
Project CI / Native shell tests (pull_request) Has been cancelled
Project CI / Frontend tests (pull_request) Has been cancelled
Project CI / Repository checks (pull_request) Has been cancelled
Project CI / AI game creator shell web tests (pull_request) Has been cancelled
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Has been cancelled
- start-tauri-dev.mjs 在前端与配套后端就绪后、spawn Tauri CLI 之前调用准备步骤,并保留依赖注入供入口测试断言顺序
- build-release.mjs 的 runTauriBuild 与既有 stageRuntime 并列调用 stageBundledResources,tauri build --no-bundle 仍不强制 staging
- 声明新增 origin 字段:source 由准备步骤写,build 由构建脚本在产物生成后写;构建脚本删除 codex 与插件白名单的写入分支及 stage_plugin_file/copy_plugin_tree/copy_plugin_file,改为 stage_build_generated_plugin_payloads
- 插件随包工作区改为与仓库源码逐文件比对(清单 + 逐文件 sha256 + 整树符号链接),实现移入 build_support/package_layout.rs 复用单测
- 上游原生包元数据改由准备步骤按声明校验,build_support/codex_package_metadata.rs 因失去调用方删除
- 准备步骤改为同步实现,dev 与发布入口可直接调用而无需子进程
- 测试:build-release 39 passed(新增 stage、bundled、build 顺序与 no-bundle 不 staging)、dev 入口 12 passed(新增准备步骤先于 CLI 启动)、准备步骤 9 passed、package_layout 36 passed
- 文档:技术方案 §4.9 记录构建期写入边界,M1 里程碑标记完成,运维文档补入口接线与 resources/plugins 所有权,决策日志与排障经验同步
2026-09-27 18:56:44 +08:00
suzmii a4b103a1b0 补充 AGC 随包资源归位的 M1 决策与文档
Project CI / AI game creator shell Rust crates (pull_request) Successful in 1m25s
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 1m54s
Project CI / Backend tests (pull_request) Successful in 3m56s
Project CI / Frontend tests (pull_request) Successful in 1m43s
Project CI / Native shell tests (pull_request) Successful in 6m13s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Successful in 9m4s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Successful in 9m8s
Project CI / Repository checks (pull_request) Successful in 2m6s
Project CI / AI game creator shell web tests (pull_request) Successful in 1m39s
- 技术方案新增 §4.8 单一声明与实现形态(混合),§8 更新 M1 交付与停止条件,§9 未决问题 1 收口为已定
- M1 里程碑规范记录已定决策与校验收口边界;实施计划补充已定决策与实际验证命令
- M2 里程碑规范的依赖与前置条件补记 M1 已交付的准备步骤与声明门禁
- 开发运维文档补充声明、生成器、门禁、准备步骤与只读校验的用法;决策日志与排障经验同步本次口径
2026-09-27 18:36:09 +08:00
suzmii 05d455d4ef 构建脚本改为按声明生成并只读校验随包资源
- 平台布局、插件随包子目录与跳过规则、Codex 上游候选路径与第三方声明来源全部改由声明提供,删除脚本内的字面量
- 新增只读校验:Codex 目录校验清单 schema/平台/版本、文件集合、逐文件 sha256、第三方声明、可执行位与白名单外文件;插件目录校验必需组件与整树符号链接
- AGC_BUILD_TARGET 的 rustc-env 移到无条件路径(保留职责),新增 AGC_SKIP_RESOURCE_STAGING=1 以在既有产物上只跑校验
- 删除脚本内与 package_layout 重复的 sha256 实现,复用同一份
2026-09-27 18:36:04 +08:00
suzmii d8b37e0da9 新增 AGC 随包资源准备步骤(Codex 与插件两条纯复制路径)
- 新增 scripts/prepare-bundled-resources.mjs:按声明解析目标与替换单位,用上游 package-lock 的 resolved/integrity 组成缓存 key,写临时目录后原子替换,命中缓存不重写任何文件,只替换本工具产物,失败即退出并给出可执行提示
- Codex 路径按声明复制组件、保留受版本控制的第三方声明、按现有字节格式生成 manifest.json;macOS 双架构整体 staging
- 插件路径按声明的子目录白名单与跳过规则复制,顶层出现非本工具条目即失败
- 新增 scripts/prepare-bundled-resources.test.mjs:9 条用例覆盖 staging 结果、幂等(内容与时间戳)、上游缺失、目标不支持、目录被非本工具占用、dry-run 不落盘与白名单过滤
- .gitignore 忽略准备步骤产生的 staging 临时目录
2026-09-27 18:36:03 +08:00
suzmii 21c3bd7a2b 引入 AGC 随包资源的单一声明与生成门禁
- 新增 build_support/package-layout.json:Codex 三元表与组件白名单、上游候选路径、第三方声明来源、插件随包子目录与跳过规则、平台与 feature 门槛的唯一人工声明
- 新增 scripts/check-package-layout.mjs:由声明生成 build_support/package-layout.generated.rs,并校验目标唯一、可执行组件属于白名单、每个目标恰有一条第三方声明来源、codex.version 与应用锁定的 @openai/codex 一致
- 新增 build_support/package_layout.rs:声明类型、插件与平台门槛判定、逐块 sha256、随包 Codex 目录只读校验及其单测
- codex_bundle.rs 的布局与版本常量改由声明提供,公开接口与取值不变;src/main.rs 在测试期引入该模块以复用既有单测
- package.json 新增 bundled-resources:check/sync 并接进 typecheck 链,根 package.json 提供 agc:* 别名
2026-09-27 18:35:57 +08:00
suzmii 756297d2df 补充 AGC 随包资源 staging 归位里程碑与首段实施计划
Project CI / AI game creator shell Rust crates (pull_request) Successful in 1m29s
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 2m0s
Project CI / Backend tests (pull_request) Successful in 4m21s
Project CI / Frontend tests (pull_request) Successful in 1m59s
Project CI / Native shell tests (pull_request) Successful in 6m0s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Successful in 9m25s
Project CI / AI game creator shell web tests (pull_request) Successful in 1m27s
Project CI / Repository checks (pull_request) Successful in 1m50s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Successful in 9m53s
- 新增三份里程碑规范:资源改由校验器读入、生成接入 dev 与发布入口、编辑器分支产物归位与症状层补丁清理
- 新增首个里程碑实施计划:修改边界、实现顺序、验证命令、风险与回滚点、待确认的实现形态
2026-09-27 16:59:42 +08:00
suzmii e02811588c 补充 AGC 随包资源 staging 归位技术方案
- 新增 docs/technical/【技术方案】AGC随包资源staging归位-2026-09-26.md:记录根因证据(tauri-build 把随包资源登记成 build script 输入)、准备步骤合同、构建脚本退化边界、入口接线、验收判据、风险与里程碑拆分
- docs/README.md 挂入文档索引
2026-09-27 16:59:42 +08:00
suzmii e1780b21b0 修复 AGC dev 客户端不再依赖本机 sccache 配置
- start-tauri-dev.mjs 启动 Tauri CLI 前复用 scripts/dev.mjs 的 buildLocalRustProcessEnv,本地 Rust wrapper 由脚本决定而不是本机 Cargo 配置
- 新增 buildTauriDevProcessEnv 组装 dev 端点环境并导出,供测试直接断言
- 补 3 条用例:配置为 sccache 时改用 /usr/bin/env 直连 rustc、未配置时清空两个 wrapper 变量、自定义 wrapper 保留且不改写调用方 env
- pitfalls 与开发运维文档补记 AGC dev 入口的 wrapper 口径
2026-09-27 16:59:40 +08:00
107 changed files with 10156 additions and 2844 deletions
@@ -30,7 +30,6 @@ The hosted MCP offers the following tools. Choose the task tool when its action
| `PATCH /api/external/v1/editor/assets/{assetId}` | `organize_asset_library` (`update_asset`) | `update_editor_asset` |
| `DELETE /api/external/v1/editor/assets/{assetId}` | `delete_resources` (`delete_asset`) | `delete_editor_asset` |
| `POST /api/external/v1/editor/images/generations` | `generate_image`, `modify_image` (`variation`, fixed `kind="quick-edit"`) | `generate_external_editor_image` |
| `POST /api/external/v1/editor/scenes/generations` | structured game-scene generation (no hosted MCP tool yet) | `generate_external_editor_scene` |
| `POST /api/external/v1/editor/images/edits` | `modify_image` (`edit`) | `edit_external_editor_image` |
| `POST /api/external/v1/editor/images/background-removals` | `modify_image` (`remove_background`) | `remove_external_editor_image_background` |
| `POST /api/external/v1/editor/icon-spritesheets/generations` | `generate_icon_spritesheet` | `generate_external_editor_icon_spritesheet` |
@@ -90,7 +89,6 @@ Every generation row requires a stable `Idempotency-Key` header and returns HTTP
| Capability | POST path | Required body fields | Common optional body fields |
| ------------------- | ------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Image generation | `/api/external/v1/editor/images/generations` | `prompt` | `kind`, `style`, `model`, `aspectRatio`, `imageSize`, `size`, `referenceImageSrcs`, `projectId`, `assetFolderId`, `assetLabel`, `canvasCompletion`, `generationInputs` |
| Game scene | `/api/external/v1/editor/scenes/generations` | `sceneContent`, `stylePreset` | `customStyle` (required when `stylePreset="custom"`), `model`, `aspectRatio`, `imageSize`, `referenceImageSrcs`, `projectId`, `assetFolderId`, `assetLabel`, `canvasCompletion`, `generationInputs` |
| Image edit/redraw | `/api/external/v1/editor/images/edits` | `prompt`, `sourceReferenceId` | `referenceImageSrcs`, `model`, `size`, `projectId`, `assetFolderId`, `assetLabel`, `targetLayerId`, `canvasCompletion` |
| Background removal | `/api/external/v1/editor/images/background-removals` | `sourceImageSrc` | `projectId`, `sourceResourceId`, `targetLayerId`, static-image `assetKind`, `assetFolderId`, `assetLabel`, `canvasCompletion`, `generationInputs` |
| Icon spritesheet | `/api/external/v1/editor/icon-spritesheets/generations` | `referenceId`, `iconDescriptions`, `sliceMode` | `gridX`, `gridY`, `sliceCount`, `style`, `referenceImageSrcs`, `screenColor`, `model`, `aspectRatio`, `imageSize`, `projectId`, `assetFolderId`, `assetLabel`, `canvasCompletion` |
@@ -100,7 +98,7 @@ Every generation row requires a stable `Idempotency-Key` header and returns HTTP
| Sound effect | `/api/external/v1/editor/audios/sound-effects/generations` | `prompt` | `model`, `duration`, `loop`, `projectId`, `assetFolderId`, `assetLabel`, `canvasCompletion`, `generationInputs` |
| Background music | `/api/external/v1/editor/audios/background-music/generations` | `gptDescriptionPrompt`, `makeInstrumental` | `projectId`, `assetFolderId`, `assetLabel`, `canvasCompletion`, `generationInputs` |
Poll all ten through:
Poll all nine through:
```text
GET /api/external/v1/generations/{operationId}
@@ -142,7 +140,7 @@ The icon-spritesheet primary `referenceId` is intentionally stricter than ordina
Use OpenAPI as the final authority; these common values are a routing aid:
- Image `kind`: `spec`, `character`, `quick-edit`, `ui-design`, `publication-material`; ordinary image generation may omit it.
- Game scenes must use the dedicated structured route `POST /api/external/v1/editor/scenes/generations` (`sceneContent` + `stylePreset`; `customStyle` required for `custom`). Do not send `kind: "scene"` or `assetKind: "scene"` through generic image generation; the server rejects both before queueing. The scene route assembles the full provider prompt server-side and never accepts a caller-assembled `prompt`.
- External v1 currently has no structured game-scene generation operation. Do not send `kind: "scene"` or `assetKind: "scene"` through generic image generation; the server rejects both before queueing.
- Image `model`: `gpt-image-2`, `gemini-3.1-flash-image-preview`, `nanobanana2`, `nano-banana`.
- Image `aspectRatio`: `1:1`, `2:3`, `3:2`, `9:16`, `16:9`.
- Image `imageSize`: `0.5K`, `1K`, `2K`.
@@ -148,7 +148,7 @@ For the lower-level asset/resource creation endpoints, `generationInputs` is rep
## Art Spec and Image Request
Game scenes have a dedicated structured route: `POST /api/external/v1/editor/scenes/generations` with `sceneContent` and `stylePreset` (`customStyle` required when `stylePreset` is `custom`). The server assembles the full provider prompt; a caller-assembled `prompt` is not accepted. `kind: "scene"` and `assetKind: "scene"` remain invalid on generic image generation and return HTTP `400` before any generation job is queued.
Generic External v1 image generation does not expose the main-site structured game-scene contract. `kind: "scene"` and `assetKind: "scene"` are both invalid and return HTTP `400` before any generation job is queued. Do not replace the structured scene fields and server-owned prompt assembly with a generic image prompt.
When maintaining a reusable art spec, carry it in `generationInputs.artSpec` and reflect important constraints in the prompt. This is an example with both canvas and library destinations, not a requirement for every generation:
+2
View File
@@ -57,6 +57,8 @@ temp*build*/
/apps/ai-game-creator-shell/logs/
/apps/ai-game-creator-shell/src-tauri/resources/node-runtime/
/apps/ai-game-creator-shell/src-tauri/resources/node-runtime-staging-*/
/apps/ai-game-creator-shell/src-tauri/resources/plugins-staging-*/
/apps/ai-game-creator-shell/src-tauri/resources/codex/*-staging-*/
/apps/ai-game-creator-shell/.llm-drafts/
/apps/ai-game-creator-shell/game-creator.config.local.json
/apps/mobile-shell/.expo/
-4
View File
@@ -1,4 +0,0 @@
# resources/plugins 由 build.rs 从 plugins/ 复制生成,属于构建产物。
# 它在 dev 监听范围内,重新生成会让 Tauri dev 误判为源码改动而触发
# “构建 -> 监听 -> 再构建”的自触发循环。
resources/plugins/
+5 -1
View File
@@ -13,6 +13,10 @@
"skill-pack:check": "node scripts/check-skill-pack.mjs",
"skill-pack:sync": "node scripts/check-skill-pack.mjs --write",
"skill-pack:test": "node --test scripts/check-skill-pack.test.mjs",
"bundled-resources:check": "node scripts/check-package-layout.mjs",
"bundled-resources:sync": "node scripts/check-package-layout.mjs --write",
"bundled-resources:prepare": "node scripts/prepare-bundled-resources.mjs",
"bundled-resources:test": "node --test scripts/prepare-bundled-resources.test.mjs",
"llm-status": "node scripts/run-cli-with-config.mjs --llm-status",
"agent-task": "node scripts/run-cli-with-config.mjs --agent-task",
"config": "node scripts/game-creator-config-wizard.mjs",
@@ -24,7 +28,7 @@
"agent-runtime:supervisor-swarm-tool-plan-handoff-runner-kill-real-e2e": "node scripts/agent-runtime-real-e2e.mjs --suite supervisor-swarm-tool-plan-handoff-runner-kill",
"agent-runtime:steer-real-e2e": "node scripts/agent-runtime-steer-real-e2e.mjs",
"agent-runtime:steer-runner-kill-real-e2e": "node scripts/agent-runtime-real-e2e.mjs --suite steer-runner-kill",
"typecheck": "tsc -p tsconfig.json --noEmit && npm run skill-pack:check && node scripts/check-config.mjs"
"typecheck": "tsc -p tsconfig.json --noEmit && npm run skill-pack:check && npm run bundled-resources:check && node scripts/check-config.mjs"
},
"dependencies": {
"@cubone/react-file-manager": "^1.35.0",
@@ -11,6 +11,7 @@ import {
} from './agc-global-version.mjs';
import {
defaultEditorFeatures,
resolveEditorFeatures,
withDefaultCargoFeatures,
} from './cargo-features.mjs';
import {
@@ -18,6 +19,7 @@ import {
resolveReleaseChannel,
} from './channel-identity.mjs';
import { prepareNsisToolsetForRelease } from './nsis-toolset.mjs';
import { prepareBundledResources } from './prepare-bundled-resources.mjs';
import { stageNodeRuntime } from './stage-node-runtime.mjs';
const appRoot = fileURLToPath(new URL('..', import.meta.url));
@@ -431,10 +433,30 @@ function writeChannelConfigFile(channel, target, includeNodeRuntime = false) {
return configPath;
}
/// 随包资源必须在打包工具之前生成:构建脚本只做只读校验,不再生成。
export function stageBundledResources(
target,
{ prepare = prepareBundledResources, features } = {},
) {
const summaries = prepare({
target,
features: new Set(features ?? defaultEditorFeatures(target)),
profile: 'release',
log: (line) => console.log(`[ai-game-creator-shell] ${line}`),
});
for (const summary of summaries) {
console.log(`[ai-game-creator-shell] ${summary}`);
}
}
export function runTauriBuild(
args = [],
context = resolveReleaseContext(args),
{ spawn = spawnSync, stageRuntime = stageNodeRuntime } = {},
{
spawn = spawnSync,
stageRuntime = stageNodeRuntime,
stageBundled = stageBundledResources,
} = {},
) {
if (
explicitBuildTarget(args) &&
@@ -444,7 +466,12 @@ export function runTauriBuild(
}
const tauriArguments = buildTauriBuildArguments(args, context.target);
const { channel, target } = context;
if (!args.includes('--no-bundle')) stageRuntime(target);
const features = resolveEditorFeatures({ argv: args, target });
if (!args.includes('--no-bundle')) {
stageRuntime(target);
}
// app 构建本身就需要随包资源,因此 --no-bundle 也要 staging;只有总号发布是打包专属。
stageBundled(target, { features });
const configPath = writeChannelConfigFile(
channel,
target,
@@ -363,6 +363,8 @@ test('packaged renderer receives the same channel as the updater manifest', () =
// 必须 stub:真实 staging 会用宿主平台(如 macOS 的 darwin/arm64)去对默认的
// Windows 目标做一致性校验,在非 Windows 主机上直接失败——本用例只关心渠道注入。
stageRuntime: () => {},
// 同上:随包资源准备会读取真实上游包,本用例只关心渠道环境变量。
stageBundled: () => {},
spawn: (_binary, _args, options) => {
spawnOptions = options;
return { status: 0 };
@@ -456,6 +458,8 @@ test('explicit macOS target drives version lookup, Tauri endpoint, artifact and
seenContexts.push(context);
runTauriBuild(args, context, {
stageRuntime: () => {},
// 必须 stub:随包资源准备会读取真实上游包与仓库插件工作区,本用例只关心参数。
stageBundled: () => {},
spawn: (_binary, command) => {
const configIndex = command.lastIndexOf('--config');
const config = JSON.parse(
@@ -705,6 +709,7 @@ test('Windows remains the default and explicit Windows overrides macOS environme
context,
{
stageRuntime: () => {},
stageBundled: () => {},
spawn: (_binary, command) => {
assert.ok(
command.includes(
@@ -818,6 +823,10 @@ test('release stages Node before Tauri and injects its resource mapping only for
assert.equal(target, windowsTarget);
events.push('stage');
},
stageBundled(target) {
assert.equal(target, windowsTarget);
events.push('bundled');
},
spawn(_binary, args) {
events.push('build');
const config = JSON.parse(
@@ -829,10 +838,21 @@ test('release stages Node before Tauri and injects its resource mapping only for
return { status: 0 };
},
});
assert.deepEqual(events, ['stage', 'build']);
assert.deepEqual(events, ['stage', 'bundled', 'build']);
events.length = 0;
runTauriBuild(['--no-bundle', '--target', windowsTarget], context, {
stageRuntime() {
assert.fail('no-bundle must not stage resources');
assert.fail('no-bundle must not stage node runtime');
},
stageBundled(target, options) {
// app 构建本身就需要随包资源,因此 --no-bundle 也要 staging,
// 且必须拿到与 cargo 相同的 feature 集(避免「cargo 开、staging 没开」)。
assert.equal(target, windowsTarget);
assert.ok(
options?.features?.length > 0,
'bundled staging 必须收到 feature 集',
);
events.push('bundled');
},
spawn(_binary, args) {
const config = JSON.parse(
@@ -848,6 +868,9 @@ test('release stages Node before Tauri and injects its resource mapping only for
stageRuntime() {
throw new Error('missing runtime');
},
stageBundled() {
assert.fail('invalid runtime must prevent bundled staging');
},
spawn() {
assert.fail('invalid runtime must prevent build');
},
@@ -960,6 +983,8 @@ for (const channel of ['release', 'beta-2']) {
);
runTauriBuild([`--target=${target}`], context, {
stageRuntime: () => {},
// 必须 stub:随包资源准备会读取真实上游包与仓库插件工作区,本用例只关心参数。
stageBundled: () => {},
spawn: (_binary, command) => {
const config = JSON.parse(
readFileSync(
@@ -22,3 +22,48 @@ export function defaultEditorFeatures(target) {
? ['cocos-editor-execute', 'unity-editor-execute', 'godot-editor-execute']
: [];
}
/// 单一声明式解析:本次构建实际生效的编辑器 feature 集。
/// 解析顺序:环境变量 `AGC_DEV_CARGO_FEATURES` > 命令行 `--features` > 目标平台默认值。
/// dev 入口、发布入口的 cargo 参数与随包资源准备步骤都必须用这里的返回值,
/// 否则会出现「cargo 开了 feature、staging 没开(或反之)」的窗口。
export function resolveEditorFeatures({
argv = [],
target,
env = process.env,
} = {}) {
// 顺序:环境变量(dev 的显式覆盖)> 命令行 --features > 目标平台默认值。
const fromEnv = String(env.AGC_DEV_CARGO_FEATURES ?? '').trim();
if (fromEnv) {
return fromEnv
.split(',')
.map((name) => name.trim())
.filter(Boolean);
}
const fromArgv = featuresFromArgv(argv);
if (fromArgv) {
return fromArgv;
}
return defaultEditorFeatures(target);
}
/// 从 argv 里解析 `--features a,b` / `--features=a,b`;没有则返回 null。
export function featuresFromArgv(argv = []) {
for (let index = 0; index < argv.length; index += 1) {
const value = String(argv[index]);
if (value.startsWith('--features=')) {
return value
.slice('--features='.length)
.split(',')
.map((name) => name.trim())
.filter(Boolean);
}
if (value === '--features' && argv[index + 1] !== undefined) {
return String(argv[index + 1])
.split(',')
.map((name) => name.trim())
.filter(Boolean);
}
}
return null;
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -1,8 +1,9 @@
import { resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import { buildLocalRustProcessEnv } from '../../../scripts/dev.mjs';
import {
defaultEditorFeatures,
resolveEditorFeatures,
withDefaultCargoFeatures,
} from './cargo-features.mjs';
import {
@@ -10,6 +11,10 @@ import {
resolveAgcDevEndpoint,
withAgcDevEndpointEnv,
} from './dev-port.mjs';
import {
prepareBundledResources,
supportedHostTarget,
} from './prepare-bundled-resources.mjs';
import {
isAiGameCreatorServer,
preflightExistingVite,
@@ -52,21 +57,37 @@ function buildTauriArguments(argv, devUrl = readAgcDevEndpoint().url) {
// 开发和发行构建使用同一平台编辑器 feature 集合。
// 可用 AGC_DEV_CARGO_FEATURES(逗号分隔)覆盖,传空串即关闭。
function readDevCargoFeatures(env = process.env) {
const override = env.AGC_DEV_CARGO_FEATURES;
if (override !== undefined) {
return override
.split(',')
.map((value) => value.trim())
.filter(Boolean);
}
return defaultEditorFeatures(process.platform);
function readDevCargoFeatures(argv = []) {
return resolveEditorFeatures({ argv, target: process.platform });
}
function withDevCargoFeatures(argv, features = readDevCargoFeatures()) {
function withDevCargoFeatures(argv, features = readDevCargoFeatures(argv)) {
return withDefaultCargoFeatures(argv, features);
}
/// 随包资源必须在 Tauri 之前生成:构建脚本只做只读校验,不再生成资源。
/// 命中缓存的重复调用不写任何文件,因此每次 dev 启动都会先跑一次。
function prepareBundledResourcesBeforeTauri(
features = readDevCargoFeatures(),
{ prepare = prepareBundledResources, log = console.log } = {},
) {
const target = supportedHostTarget();
if (!target) {
log(
'[ai-game-creator-shell] 当前平台不受随包资源声明覆盖,跳过随包资源准备',
);
return;
}
const summaries = prepare({
target,
features: new Set(features),
log: (line) => log(`[ai-game-creator-shell] ${line}`),
});
for (const summary of summaries) {
log(`[ai-game-creator-shell] ${summary}`);
}
}
function spawnTauriCli(argv, { env = process.env } = {}) {
return spawnChild(process.execPath, [tauriCliPath, ...argv], {
cwd: appRoot,
@@ -75,6 +96,17 @@ function spawnTauriCli(argv, { env = process.env } = {}) {
});
}
/// Tauri dev 的 Cargo 直接继承启动器环境,用户级 / 仓库级 Cargo 配置里的
/// `rustc-wrapper`(本地常见为 sccache)会在这里生效。本地 sccache daemon 状态
/// 一旦损坏,`cargo` 的首次 rustc 探测就会失败并阻断整个 AGC 启动;因此这里复用
/// `npm run dev` 的本地 Rust 环境规则,由脚本而不是本机 Cargo 配置决定 wrapper。
function buildTauriDevProcessEnv(endpoint, env = process.env) {
return buildLocalRustProcessEnv({
...withAgcDevEndpointEnv(endpoint, env),
[AGC_DESIGN_DEBUG_ENV]: designDebugEnabled,
});
}
async function runTauriDev(
argv = process.argv.slice(2),
{
@@ -84,6 +116,7 @@ async function runTauriDev(
spawnCli = spawnTauriCli,
waitForCli = waitForChildTermination,
terminateTree = terminateChildTree,
prepareResources = prepareBundledResourcesBeforeTauri,
} = {},
) {
const endpoint = await resolveDevEndpoint();
@@ -130,15 +163,14 @@ async function runTauriDev(
shutdownRequested.then(() => false),
]);
if (!prepared || shutdownSignal) return 1;
const devFeatures = readDevCargoFeatures();
prepareResources(devFeatures);
const tauriArguments = buildTauriArguments(
withDevCargoFeatures(argv),
withDevCargoFeatures(argv, devFeatures),
endpoint.url,
);
child = spawnCli(tauriArguments, {
env: {
...withAgcDevEndpointEnv(endpoint),
[AGC_DESIGN_DEBUG_ENV]: designDebugEnabled,
},
env: buildTauriDevProcessEnv(endpoint),
});
const childResult = waitForCli(child);
const outcome = await Promise.race([
@@ -225,7 +257,9 @@ function isDirectModuleExecution() {
export {
buildTauriArguments,
buildTauriDevProcessEnv,
isDirectModuleExecution,
prepareBundledResourcesBeforeTauri,
runTauriDev,
spawnTauriCli,
withDevCargoFeatures,
@@ -1,4 +0,0 @@
# resources/plugins 由 build.rs 从 plugins/ 复制生成,属于构建产物。
# 它在 dev 监听范围内,重新生成会让 Tauri dev 误判为源码改动而触发
# “构建 -> 监听 -> 再构建”的自触发循环。
resources/plugins/
File diff suppressed because it is too large Load Diff
@@ -1,8 +1,18 @@
//! 构建与运行共用的平台布局;只允许分发锁定原生包里的明确组件。
//!
//! 布局、组件白名单与版本常量来自唯一声明 `build_support/package-layout.json`
//! (Rust 侧经 `build_support/package-layout.generated.rs` 取得编译期常量,
//! 由 `scripts/check-package-layout.mjs` 生成并在门禁中校验一致)。
//! 本模块只读声明,不写任何随包资源。
pub const VERSION: &str = "0.155.1";
pub const CLI_VERSION: &str = "codex-cli 0.155.1";
pub const SCHEMA: &str = "genarrative-codex-sidecar.v2";
// 共享声明模块:构建脚本、运行期与测试各自只用到其中一部分,未用到的入口不算缺陷。
#[allow(dead_code)]
#[path = "package_layout.rs"]
pub(crate) mod package_layout;
pub const VERSION: &str = package_layout::CODEX_VERSION;
pub const CLI_VERSION: &str = package_layout::CODEX_CLI_VERSION;
pub const SCHEMA: &str = package_layout::CODEX_MANIFEST_SCHEMA;
#[derive(Clone, Copy, Debug)]
pub struct Layout {
@@ -12,46 +22,13 @@ pub struct Layout {
pub files: &'static [&'static str],
}
const WINDOWS_FILES: &[&str] = &[
"bin/codex.exe",
"bin/codex-code-mode-host.exe",
"codex-path/rg.exe",
"codex-resources/codex-command-runner.exe",
"codex-resources/codex-windows-sandbox-setup.exe",
"codex-package.json",
];
const MAC_FILES: &[&str] = &[
"bin/codex",
"bin/codex-code-mode-host",
"codex-path/rg",
"codex-resources/zsh/bin/zsh",
"codex-package.json",
];
pub fn for_target(target: &str) -> Option<Layout> {
match target {
"x86_64-pc-windows-msvc" => Some(Layout {
platform: "win32-x64",
directory: "win-x64",
executable: "bin/codex.exe",
files: WINDOWS_FILES,
}),
"aarch64-apple-darwin" | "x86_64-apple-darwin" => Some(Layout {
platform: if target.starts_with("aarch64") {
"darwin-arm64"
} else {
"darwin-x64"
},
directory: if target.starts_with("aarch64") {
"mac-native/darwin-arm64"
} else {
"mac-native/darwin-x64"
},
executable: "bin/codex",
files: MAC_FILES,
}),
_ => None,
}
package_layout::codex_target(target).map(|declared| Layout {
platform: declared.platform,
directory: declared.directory,
executable: declared.executable,
files: declared.files,
})
}
#[cfg(test)]
@@ -80,4 +57,11 @@ mod tests {
assert!(for_target("aarch64-pc-windows-msvc").is_none());
assert!(for_target("x86_64-unknown-linux-gnu").is_none());
}
#[test]
fn constants_come_from_the_shared_declaration() {
assert_eq!(VERSION, "0.155.1");
assert_eq!(CLI_VERSION, format!("codex-cli {VERSION}"));
assert_eq!(SCHEMA, "genarrative-codex-sidecar.v2");
}
}
@@ -1,57 +0,0 @@
//! 随包阶段的原生包元数据校验,不进入运行时生产模块。
use super::codex_bundle::{Layout, VERSION};
pub fn validate_package_metadata(
metadata: &serde_json::Value,
target: &str,
layout: Layout,
) -> Result<(), String> {
if metadata["layoutVersion"] == 1
&& metadata["version"] == VERSION
&& metadata["target"] == target
&& metadata["entrypoint"] == layout.executable
&& metadata["resourcesDir"] == "codex-resources"
&& metadata["pathDir"] == "codex-path"
{
Ok(())
} else {
Err(format!("Codex 原生包版本、布局或架构不匹配目标 {target}"))
}
}
#[cfg(test)]
mod tests {
use super::super::codex_bundle::for_target;
use super::*;
#[test]
fn metadata_rejects_version_architecture_and_layout_drift() {
let target = "aarch64-apple-darwin";
let layout = for_target(target).unwrap();
let valid = serde_json::json!({
"layoutVersion": 1,
"version": VERSION,
"target": target,
"entrypoint": "bin/codex",
"resourcesDir": "codex-resources",
"pathDir": "codex-path",
});
assert!(validate_package_metadata(&valid, target, layout).is_ok());
for (key, value) in [
("layoutVersion", serde_json::json!(2)),
("version", serde_json::json!("0.0.0")),
("target", serde_json::json!("x86_64-apple-darwin")),
("entrypoint", serde_json::json!("bin/codex.exe")),
("resourcesDir", serde_json::json!("../private")),
("pathDir", serde_json::json!(null)),
] {
let mut invalid = valid.clone();
invalid[key] = value;
assert!(
validate_package_metadata(&invalid, target, layout).is_err(),
"{key}"
);
}
}
}
@@ -1,13 +1,13 @@
use sha2::{Digest, Sha256};
use std::fs;
use std::path::{Path, PathBuf};
use std::path::Path;
pub const BUNDLE_FILES: [&str; 4] = [
"bin/win-x64/agc_godot_editor.dll",
"bin/win-x64/metadata.json",
"vendor/LICENSE.txt",
"vendor/provenance.json",
];
// 共享随包资源声明:Godot 随包文件清单与构建期校验共用同一份来源。
#[allow(dead_code)]
#[path = "package_layout.rs"]
mod package_layout;
pub const BUNDLE_FILES: &[&str] = package_layout::GODOT_BUNDLE_FILES;
fn plain_metadata(path: &Path) -> Result<fs::Metadata, String> {
let metadata = fs::symlink_metadata(path)
@@ -74,39 +74,6 @@ pub fn validate(root: &Path) -> Result<Vec<(&'static str, Vec<u8>)>, String> {
Ok(files)
}
pub fn stage(root: &Path, destination: &Path, target: &str, enabled: bool) -> Result<(), String> {
if target != "x86_64-pc-windows-msvc" || !enabled {
return Ok(());
}
for (relative, bytes) in validate(root)? {
let path = destination.join(relative);
fs::create_dir_all(path.parent().expect("Godot resource parent"))
.map_err(|error| format!("创建 Godot 资源目录失败:{error}"))?;
fs::write(&path, bytes).map_err(|error| format!("写入 Godot 资源失败:{error}"))?;
}
Ok(())
}
pub fn source_files(root: &Path) -> Result<Vec<PathBuf>, String> {
plain_metadata(root)?;
let mut sources = Vec::new();
for entry in fs::read_dir(root).map_err(|error| format!("读取 Godot 源码失败:{error}"))?
{
let entry = entry.map_err(|error| format!("读取 Godot 源码目录项失败:{error}"))?;
if matches!(entry.file_name().to_str(), Some("bin" | ".build")) {
continue;
}
let metadata = plain_metadata(&entry.path())?;
if metadata.is_dir() {
sources.extend(source_files(&entry.path())?);
} else if metadata.is_file() {
sources.push(entry.path());
}
}
sources.sort();
Ok(sources)
}
#[cfg(test)]
mod tests {
use super::*;
@@ -134,82 +101,20 @@ mod tests {
}
#[test]
fn stage_only_verified_windows_runtime_and_not_build_inputs() {
let source = tempfile::tempdir().unwrap();
let destination = tempfile::tempdir().unwrap();
fixture(source.path());
fs::write(source.path().join("bridge.gd"), "source").unwrap();
fs::write(source.path().join("bin/win-x64/extra.dll"), "excluded").unwrap();
stage(
source.path(),
destination.path(),
"x86_64-pc-windows-msvc",
true,
)
.unwrap();
fn validate_rejects_incomplete_bundle() {
let temp = tempfile::tempdir().expect("tempdir");
let error = validate(temp.path()).expect_err("空目录必须被拒绝");
assert!(!error.is_empty(), "失败原因不能为空");
// 只有文件名、内容不合法的「像样」目录也必须被拒绝。
for relative in BUNDLE_FILES {
assert_eq!(
fs::read(source.path().join(relative)).unwrap(),
fs::read(destination.path().join(relative)).unwrap()
);
let file = temp.path().join(relative);
fs::create_dir_all(file.parent().expect("parent")).expect("create dir");
fs::write(&file, b"not a real artifact").expect("write file");
}
assert!(!destination.path().join("bridge.gd").exists());
assert!(!destination.path().join("bin/win-x64/extra.dll").exists());
}
#[test]
fn unsupported_or_disabled_targets_need_no_native_artifacts() {
let destination = tempfile::tempdir().unwrap();
for (target, enabled) in [
("aarch64-apple-darwin", true),
("x86_64-apple-darwin", true),
("x86_64-unknown-linux-gnu", true),
("aarch64-pc-windows-msvc", true),
("x86_64-pc-windows-msvc", false),
] {
stage(
Path::new("missing-godot-native"),
destination.path(),
target,
enabled,
)
.unwrap();
assert_eq!(fs::read_dir(destination.path()).unwrap().count(), 0);
}
}
#[test]
fn incomplete_or_tampered_bundle_fails_before_copying() {
let source = tempfile::tempdir().unwrap();
let destination = tempfile::tempdir().unwrap();
fixture(source.path());
fs::write(source.path().join(BUNDLE_FILES[0]), b"tampered").unwrap();
assert!(stage(
source.path(),
destination.path(),
"x86_64-pc-windows-msvc",
true
)
.unwrap_err()
.contains("SHA256"));
assert_eq!(fs::read_dir(destination.path()).unwrap().count(), 0);
fixture(source.path());
fs::remove_file(source.path().join("vendor/LICENSE.txt")).unwrap();
assert!(validate(source.path()).is_err());
}
#[test]
fn source_watch_list_excludes_build_outputs() {
let source = tempfile::tempdir().unwrap();
fixture(source.path());
fs::create_dir(source.path().join(".build")).unwrap();
fs::write(source.path().join(".build/bridge.obj"), "generated").unwrap();
fs::write(source.path().join("bridge.gd"), "source").unwrap();
let sources = source_files(source.path()).unwrap();
assert_eq!(sources.len(), 3);
assert!(sources.contains(&source.path().join("bridge.gd")));
assert!(!sources.iter().any(|path| path
.components()
.any(|component| component.as_os_str() == "bin" || component.as_os_str() == ".build")));
assert!(
validate(temp.path()).is_err(),
"内容不合法的随包库必须被拒绝",
);
}
}
@@ -0,0 +1,132 @@
// @generated by apps/ai-game-creator-shell/scripts/check-package-layout.mjs
// 来源:build_support/package-layout.json。不要手工编辑本文件。
// 修改随包资源布局请编辑声明文件,然后运行
// npm run agc:bundled-resources:sync(在仓库根目录)
// 门禁会校验两者一致(npm run agc:typecheck 链内含 check-package-layout.mjs)。
pub const DECLARATION_SCHEMA: &str = "agc-package-layout.v1";
pub const LAYOUT_VERSION: u64 = 1;
pub const CODEX_VERSION: &str = "0.155.1";
pub const CODEX_CLI_VERSION: &str = "codex-cli 0.155.1";
pub const CODEX_MANIFEST_SCHEMA: &str = "genarrative-codex-sidecar.v2";
pub const GODOT_BUNDLE_FILES: &[&str] = &["bin/win-x64/agc_godot_editor.dll", "bin/win-x64/metadata.json", "vendor/LICENSE.txt", "vendor/provenance.json"];
pub const CODEX: Codex = Codex {
package_metadata: PackageMetadata {
layout_version: 1,
resources_dir: "codex-resources",
path_dir: "codex-path",
},
resource_directory: "resources/codex",
manifest_file_name: "manifest.json",
package_metadata_file_name: "codex-package.json",
notice_file_name: "NOTICE.md",
source_roots: &["app", "repo"],
source_relative_paths: &["node_modules/@openai/codex-<platform>/vendor/<target>", "node_modules/@openai/codex/node_modules/@openai/codex-<platform>/vendor/<target>"],
notice_sources: &[
NoticeSource {
targets: &["aarch64-apple-darwin", "x86_64-apple-darwin"],
source: "resources/codex/【声明】Mac内置Codex组件-2026-09-18.md",
preserve: false,
},
NoticeSource {
targets: &["x86_64-pc-windows-msvc"],
source: "resources/codex/win-x64/NOTICE.md",
preserve: true,
}
],
universal_groups: &[
UniversalGroup {
name: "mac-native",
directory: "mac-native",
targets: &["aarch64-apple-darwin", "x86_64-apple-darwin"],
}
],
targets: &[
CodexTarget {
target: "x86_64-pc-windows-msvc",
platform: "win32-x64",
directory: "win-x64",
executable: "bin/codex.exe",
files: &["bin/codex.exe", "bin/codex-code-mode-host.exe", "codex-path/rg.exe", "codex-resources/codex-command-runner.exe", "codex-resources/codex-windows-sandbox-setup.exe", "codex-package.json"],
},
CodexTarget {
target: "aarch64-apple-darwin",
platform: "darwin-arm64",
directory: "mac-native/darwin-arm64",
executable: "bin/codex",
files: &["bin/codex", "bin/codex-code-mode-host", "codex-path/rg", "codex-resources/zsh/bin/zsh", "codex-package.json"],
},
CodexTarget {
target: "x86_64-apple-darwin",
platform: "darwin-x64",
directory: "mac-native/darwin-x64",
executable: "bin/codex",
files: &["bin/codex", "bin/codex-code-mode-host", "codex-path/rg", "codex-resources/zsh/bin/zsh", "codex-package.json"],
}
],
};
pub const PLUGINS: Plugins = Plugins {
source_directory: "plugins",
destination_directory: "resources/plugins",
manifest_file_name: "plugin.json",
target_contains_any: &["windows", "apple-darwin"],
subdirectories: &[
Subdirectory {
path: "src",
plugin: "",
origin: "source",
target_contains: &[],
targets: &[],
features: &[],
},
Subdirectory {
path: "panels",
plugin: "",
origin: "source",
target_contains: &[],
targets: &[],
features: &[],
},
Subdirectory {
path: "skills",
plugin: "",
origin: "source",
target_contains: &[],
targets: &[],
features: &[],
},
Subdirectory {
path: "native/payload",
plugin: "agc-cocos-editor",
origin: "prepared",
target_contains: &["windows"],
targets: &[],
features: &["cocos-editor-injection"],
},
Subdirectory {
path: "dotnet/publish/win-x64",
plugin: "agc-unity-editor",
origin: "prepared",
target_contains: &[],
targets: &["x86_64-pc-windows-msvc"],
features: &["unity-editor-execute"],
}
],
library_staging: &[
LibraryStaging {
plugin: "agc-godot-editor",
source_subdirectory: "native/gdextension",
targets: &["x86_64-pc-windows-msvc"],
features: &["godot-editor-execute"],
layout: "godot-bundle",
}
],
skip_directory_names: &["target", "node_modules"],
skip_directory_name_prefixes: &["."],
skip_file_name_prefixes: &["."],
skip_file_name_fragments: &[".test."],
};
@@ -0,0 +1,258 @@
{
"schema": "agc-package-layout.v1",
"layoutVersion": 1,
"description": "AGC 随包资源布局与复制规则的唯一声明。Rust 侧构建期校验与 Node 侧准备步骤共用本文件,任何一侧都不得再写第二份布局或组件白名单。含 <platform>、<target> 占位符的字段由调用方按目标三元展开。修改布局时同步递增 layoutVersion(准备步骤的缓存 key 组成部分)。",
"codex": {
"version": "0.155.1",
"cliVersionPrefix": "codex-cli ",
"manifestSchema": "genarrative-codex-sidecar.v2",
"packageMetadata": {
"layoutVersion": 1,
"resourcesDir": "codex-resources",
"pathDir": "codex-path"
},
"resourceDirectory": "resources/codex",
"manifestFileName": "manifest.json",
"packageMetadataFileName": "codex-package.json",
"noticeFileName": "NOTICE.md",
"sourceRoots": [
"app",
"repo"
],
"sourceRelativePaths": [
"node_modules/@openai/codex-<platform>/vendor/<target>",
"node_modules/@openai/codex/node_modules/@openai/codex-<platform>/vendor/<target>"
],
"noticeSources": [
{
"targets": [
"aarch64-apple-darwin",
"x86_64-apple-darwin"
],
"source": "resources/codex/【声明】Mac内置Codex组件-2026-09-18.md",
"preserve": false
},
{
"targets": [
"x86_64-pc-windows-msvc"
],
"source": "resources/codex/win-x64/NOTICE.md",
"preserve": true
}
],
"universalGroups": [
{
"name": "mac-native",
"directory": "mac-native",
"targets": [
"aarch64-apple-darwin",
"x86_64-apple-darwin"
]
}
],
"targets": [
{
"target": "x86_64-pc-windows-msvc",
"platform": "win32-x64",
"directory": "win-x64",
"executable": "bin/codex.exe",
"files": [
"bin/codex.exe",
"bin/codex-code-mode-host.exe",
"codex-path/rg.exe",
"codex-resources/codex-command-runner.exe",
"codex-resources/codex-windows-sandbox-setup.exe",
"codex-package.json"
]
},
{
"target": "aarch64-apple-darwin",
"platform": "darwin-arm64",
"directory": "mac-native/darwin-arm64",
"executable": "bin/codex",
"files": [
"bin/codex",
"bin/codex-code-mode-host",
"codex-path/rg",
"codex-resources/zsh/bin/zsh",
"codex-package.json"
]
},
{
"target": "x86_64-apple-darwin",
"platform": "darwin-x64",
"directory": "mac-native/darwin-x64",
"executable": "bin/codex",
"files": [
"bin/codex",
"bin/codex-code-mode-host",
"codex-path/rg",
"codex-resources/zsh/bin/zsh",
"codex-package.json"
]
}
]
},
"plugins": {
"sourceDirectory": "plugins",
"destinationDirectory": "resources/plugins",
"manifestFileName": "plugin.json",
"targetContainsAny": [
"windows",
"apple-darwin"
],
"subdirectories": [
{
"path": "src",
"origin": "source"
},
{
"path": "panels",
"origin": "source"
},
{
"path": "skills",
"origin": "source"
},
{
"path": "native/payload",
"origin": "prepared",
"targetContains": [
"windows"
],
"plugin": "agc-cocos-editor",
"prepare": "cocos-bridge-build",
"features": [
"cocos-editor-injection"
]
},
{
"path": "dotnet/publish/win-x64",
"origin": "prepared",
"prepare": "unity-helper-publish",
"targets": [
"x86_64-pc-windows-msvc"
],
"features": [
"unity-editor-execute"
],
"plugin": "agc-unity-editor"
}
],
"libraryStaging": [
{
"plugin": "agc-godot-editor",
"sourceSubdirectory": "native/gdextension",
"prepare": "godot-extension-build",
"targets": [
"x86_64-pc-windows-msvc"
],
"features": [
"godot-editor-execute"
],
"layout": "godot-bundle",
"files": [
"bin/win-x64/agc_godot_editor.dll",
"bin/win-x64/metadata.json",
"vendor/LICENSE.txt",
"vendor/provenance.json"
]
}
],
"nativePayloads": [
{
"plugin": "agc-cocos-editor",
"prepare": "cocos-bridge-build",
"sourceFileName": "cocos_editor_bridge.dll",
"destinationSubdirectory": "native/payload",
"targets": [
"x86_64-pc-windows-msvc"
],
"features": [
"cocos-editor-injection"
],
"destinationFileName": "cocos-editor-bridge.dll"
}
],
"prepareSteps": [
{
"name": "unity-helper-publish",
"kind": "powershell",
"workingDirectory": "plugins/agc-unity-editor/dotnet",
"scriptFileName": "build.ps1",
"fingerprint": {
"roots": [
"."
],
"excludeDirectoryNames": [
"bin",
"obj",
"publish",
"native-build"
],
"stampRelativePath": "publish/win-x64/.agc-source.sha256"
},
"requiredOutputs": [
"plugins/agc-unity-editor/dotnet/publish/win-x64/Agc.Unity.Attach.exe",
"plugins/agc-unity-editor/dotnet/publish/win-x64/NOTICE",
"plugins/agc-unity-editor/dotnet/publish/win-x64/THIRD-PARTY-NOTICES.txt",
"plugins/agc-unity-editor/dotnet/publish/win-x64/licenses/DotCraft-Apache-2.0.txt",
"plugins/agc-unity-editor/dotnet/publish/win-x64/licenses/Roslyn-MIT.txt",
"plugins/agc-unity-editor/dotnet/publish/win-x64/licenses/upstream.json",
"plugins/agc-unity-editor/dotnet/publish/win-x64/licenses/dotnet-LICENSE.TXT",
"plugins/agc-unity-editor/dotnet/publish/win-x64/licenses/dotnet-THIRD-PARTY-NOTICES.TXT",
"plugins/agc-unity-editor/dotnet/publish/win-x64/licenses/microsoft.codeanalysis.common-ThirdPartyNotices.rtf",
"plugins/agc-unity-editor/dotnet/publish/win-x64/licenses/microsoft.codeanalysis.csharp-ThirdPartyNotices.rtf"
]
},
{
"name": "godot-extension-build",
"kind": "powershell",
"workingDirectory": "plugins/agc-godot-editor/native/gdextension",
"scriptFileName": "build.ps1",
"removeEnvironment": [
"PSModulePath"
],
"requiredOutputs": [
"plugins/agc-godot-editor/native/gdextension/bin/win-x64/agc_godot_editor.dll",
"plugins/agc-godot-editor/native/gdextension/bin/win-x64/metadata.json",
"plugins/agc-godot-editor/native/gdextension/vendor/LICENSE.txt",
"plugins/agc-godot-editor/native/gdextension/vendor/provenance.json"
],
"fingerprint": {
"roots": [
"."
],
"excludeDirectoryNames": [
"bin",
".build",
"native-build"
],
"stampRelativePath": "bin/win-x64/.agc-source.sha256"
}
},
{
"name": "cocos-bridge-build",
"kind": "cargo",
"packageDirectory": "plugins/agc-cocos-editor/native/cocos-editor-bridge",
"features": [
"windows-injection"
],
"requiredOutputs": []
}
],
"skipDirectoryNames": [
"target",
"node_modules"
],
"skipDirectoryNamePrefixes": [
"."
],
"skipFileNamePrefixes": [
"."
],
"skipFileNameFragments": [
".test."
]
}
}
File diff suppressed because it is too large Load Diff
@@ -1,6 +1,6 @@
共享过程文件(如需维护,请使用这些相对路径):
- project/analysis.md:重要取舍的依据与当前结论。
- project/决策台账.md:待处理事项与下一步,必要时引用相关文档。
- project/dialog.md:仅在用户需要时记录对话摘要或交接信息。
- project/analysis.md
- project/决策台账.md
- project/dialog.md
正式产物使用当前阶段指定的相对路径。
五个策划阶段的审批:当你判断当前策划阶段必需产物已完成时,必须提交阶段审批。用户批准后进入下一阶段。
@@ -1,4 +1,4 @@
概念阶段定稿时,创建或更新 `project/速览卡.md`,简要介绍当前游戏。后续仅在核心体验、范围、平台等概览内容变化时更新,不复制完整决策清单。下面是速览卡的参考结构;根据游戏类型、项目规模和用户要求选择适用字段,同类内容可以合并,复杂项目可以增加必要字段。表格和列表中的示例行按实际对象逐行扩展:
概念阶段定稿时,创建或更新 `project/速览卡.md`。下面是速览卡的参考结构;根据游戏类型、项目规模和用户要求选择适用字段,同类内容可以合并,复杂项目可以增加必要字段。表格和列表中的示例行按实际对象逐行扩展:
# 速览卡:《游戏名》
@@ -1 +1 @@
当前阶段:顶层设计。明确游玩过程、关键规则与反馈、版本范围和验证计划,为系统划分提供依据。
当前阶段:顶层设计。明确玩家持续游玩的循环、资源流、节奏和系统范围。
File diff suppressed because one or more lines are too long
@@ -1,8 +1,66 @@
# 决策台账:《星露谷物语》示例项目
# 决策台账:《星露谷物语》金样项目
仅列仍需跟进的事项和下一步。详细依据与采用的规则见相关分析和设计文档;事项完成后移出待办。
版本:v3 | 规则:台账放活队列——design 只放结论、分析只放论证、决定与开放问题住这里。编号连续不复用;被推翻的行标 overturned 挂新行,不删行。
状态六态:`confirmed`(用户亲口/亲选)/ `auto_decided`(技术类代决,必带理由+推翻条件,用户一键可翻)/ `default_pending`(默认建议兜底,用户未点头)/ `prototype_pending`(待原型验证)/ `pending_user`(等用户拍板)/ `overturned`(被推翻,挂旧行编号)。
- 体力与战斗是否共享资源:涉及探索压力和恢复规则,结合用户对战斗体验的要求确认,再更新相关系统设计。
- 背包使用格子还是重量限制:在确定存档和界面结构前确认,选择依据记录在分析文档。
- 战斗判定窗口的手感:通过矿井原型试玩观察是否易懂、是否拖慢探索,依据见分析文档“战斗判定窗口是否需要调整”。
- 天气权重是否合适:用前几天的游玩样本检查连续雨天或长期无雨的影响,再调整配置。
> 编号口径:D-01~D-13 与 templates/stardew-analysis.md 台账节选一致(D-04~D-06、D-08~D-10、D-12 原为"就地小权衡,直接登记未开条目",此处按登记口径展开);D-14 起为技术文档期新增,与 stardew-tdd-tech.md 开放问题回执互引。
## 当前待办(活队列)
### 等用户拍板(pending_user)
| 编号 | 决定 | 层 | 谁 | 依据 | 推翻条件 | 状态 |
|---|---|---|---|---|---|---|
| D-14 | 体力与战斗共享单池 | TDD | user | 风险资源统一制造取舍(概念张力一);**暂按共享实现,改单拆只需改 S02 成本入口** | 战斗参与率实测过低(玩家回避矿井) | pending_user(暂按共享实现) |
| D-15 | 背包格子制 vs 重量制 | TDD | user | 格子制直觉、重量制焦虑感与 T5"休闲不打卡"冲突;暂按格子制实现、存档预留 capacity_type 字段 | 格子管理成为主要负面反馈 | pending_user(B 级阻断存档结构,暂按格子制) |
### 待原型验证(prototype_pending)
| 编号 | 决定 | 层 | 谁 | 依据 | 推翻条件 | 状态 |
|---|---|---|---|---|---|---|
| D-13b | 战斗判定窗口手感(前摇帧数/无敌帧 450ms 基准) | 系统 | user | 数值可定、手感不可纸面验证 | 原型显示节奏拖慢/玩家困惑 | prototype_pending(规则本体见 D-13 confirmed) |
### 默认建议兜底(default_pending)
| 编号 | 决定 | 层 | 谁 | 依据 | 推翻条件 | 状态 |
|---|---|---|---|---|---|---|
| D-19 | 天气权重表具体数值(晴/雨/风暴按季节) | TDD | agent | 概念层只定"雨免浇水"定性;数值推内容期填 | 前 5 日出现连续 3 日雨/全无雨 | default_pending(默认值已进数据表,带 designer_note) |
## 已采用决定
### 用户确认(confirmed)
| 编号 | 决定 | 层 | 谁 | 依据 | 推翻条件 | 状态 |
|---|---|---|---|---|---|---|
| D-01 | 定调:牧场物语系参照、治愈慢节奏 | 概念 | user | 用户原始需求 | — | confirmed |
| D-02 | 单人体验,无多人 | 概念 | user | 概念层非目标 | — | confirmed |
| D-03 | 战斗保持伴生风险,不做装备驱动主轴 | 概念 | user | 概念期问题一 | 矿井流失率过半且归因战斗 | confirmed |
| D-07 | 日目标自设,季节与社区提供低频牵引 | 顶层 | user | 顶层期问题一 | 新手周流失归因无方向 | confirmed |
| D-11 | 采集/钓鱼/战斗统一"活动结果"接口 | 架构 | user | 架构期问题一 | 第三活动类型出现结构性差异 | confirmed |
| D-13 | 战斗采用节奏/指令判定 | 系统 | user | S06 问题一 | 原型显示节奏拖慢/玩家困惑 | confirmed(手感部分拆 D-13b prototype_pending) |
### 技术代决(auto_decided——带理由与推翻条件,用户一键可翻)
| 编号 | 决定 | 层 | 谁 | 依据(理由) | 推翻条件 | 状态 |
|---|---|---|---|---|---|---|
| D-04 | 时间片制:700ms=10 游戏分钟 | 概念 | agent | 原作实证节拍;一天≈14 分钟真实时间贴合 T5"休闲" | 内测一天体感过短/过长 | auto_decided |
| D-05 | 分区域切换(区域独立场景,非连续地图) | 概念 | agent | 概念层"不是什么:无边界开放世界";区域小网络全部步行可达 | 场景切换成为移动负担反馈 | auto_decided |
| D-06 | 28 日/季、四季/年 | 顶层 | agent | 季节窗口制造"本季计划"节奏(支柱二) | 换季频率在测试中被无视 | auto_decided |
| D-09 | 商店营业时段走条件表 | 架构 | agent | 与配方/区域解锁共用 check(condition_id) 单一入口 | 条件表规模膨胀难维护 | auto_decided |
| D-10 | 出货箱日终统一结算 | 架构 | agent | 收入集中进日终面板,强化"一天一结算"叙事;商店现卖保留即时通道 | 玩家普遍绕开出货箱 | auto_decided |
| D-12 | 工具升级期间该工具不可用 | 系统 | agent | 升级=时间成本换效率(顶层张力二);备用旧工具暂不做(开放问题) | 升级期挫败感集中爆发 | auto_decided |
| D-16 | 矿井逐层生成本期不做(P2) | TDD | agent | GDD 已标"不做无限地牢";首期按布局池 8~12 模板拼装 | 内测要求深度爬塔玩法 | auto_decided |
| D-17 | 换装首期 5 层(基础体/裤/衣/发型/饰件),非 19 层 | TDD | agent | 外观自定义非首期卖点;层结构预留到 19 层 | 外观系统成核心诉求 | auto_decided |
| D-18 | 作物品质三档:普通/银/金 | TDD | agent | 经济分层需要(即时变现 vs 等待升值的取舍) | 银金档无人区分、一律普通出售 | auto_decided |
### 已推翻(overturned——旧行保留,挂新行)
| 编号 | 决定 | 层 | 谁 | 依据 | 推翻条件 | 状态 |
|---|---|---|---|---|---|---|
| D-08 | 作物品质两档:普通/银 | TDD | agent | 早期小权衡:两档最简 | — | **overturned → D-18**(经济分层不足,改三档;铱档留 P1) |
## 队列纪律(使用说明)
- 新决定入队:拿下一号(当前最大 D-19,下一号 D-20);就现代决可登记不开条目,但状态必须写 auto_decided 并带理由+推翻条件。
- 用户翻案:旧行标 overturned 挂新行,受影响文档节重写(本台账只记录,不代改)。
- 概念层变更定稿后:速览卡"决定状态与原型验证项"字段随本文件最新版同步。

Some files were not shown because too many files have changed in this diff Show More