修复 #599:首页建项不再等待 AI 项目命名,改为后台条件改名 #606

Open
suzmii wants to merge 12 commits from fix/home-project-naming-async into master
Member

关联 issue

Closes #599(Kind/Enhancement、Priority/Low)

改了什么

1. 建项不再等 AI 命名

  • 首页「开启创作 / 进入立项策划」的提交链路是「Web 环境预检 → 立即用宿主兜底名创建工作区 → 进入项目 → 首轮创作」;create_automatic_local_game_project 固定传 name: null(宿主兜底名 GameAgent 项目 <8 位短 id> / 策划项目 <8 位短 id>),不再等待 suggest_automatic_project_name 返回。命名慢、超时、失败或返回非法内容都不影响建项、附件导入与首轮创作。
  • 命名请求在建项前并行打出;AI 返回合法名称后走 rename_local_game_project_if_unchanged(projectPath, expectedProjectId, expectedName, name):只有「项目 ID 相同」且「当前名称仍是本次创建的兜底名」才落盘改名,用户已手动改名 / 项目 ID 不符 / 名称未变一律跳过;空 expectedProjectId / expectedName / 空名 / 控制字符 / 超长失败关闭。与既有 rename_local_game_project 同口径:簿记写入,不推进项目 revision。
  • 「做方案」不发起自动命名;手动选目录建项行为不变。

2. 自动命名是「生成任务」体系里的一条后台任务(不是页面里的 promise)

  • 任务类型:账本记录新增 taskType(asset-generation / project-naming,缺省 asset-generation),kind 改为可选(命名任务为 null);schema 升 agc-asset-generation-task.v2,读取同时接受 v1 与 v2(v1 记录按素材任务读回)。
  • 可见位置:项目工作台右上角那条既有「生成任务」侧栏——与素材生成共用同一份账本、同一个变更事件(game-creator-asset-generation-task-changed)与同一套卡片样式;展示名固定「AI 项目命名」,不渲染缩略图 / 提示词 / 派发与「定位到素材」,结论只在终态显示。
  • 状态流转:enqueue_local_project_naming_task(排队中)→ update_local_project_naming_task(命名中)→ 已完成(已应用 AI 名称 / 建议名与现状一致 / 用户已手动改名,已跳过 AI 命名)或 失败(无可用名称、自动改名失败,均保留兜底名)。命名任务由命名链路推进,因此 enqueue 登记 live、终态写盘后摘除,否则任何一次账本读取都会按「上次运行中断」把它误收口为失败(中断残留仍按命名口径收口为失败)。
  • 账本只是展示旁路:入队/推进失败只写诊断日志,绝不影响建项、命名与首轮创作;写 UI 投影(项目上下文、最近项目行重检)仍等「进项目落定」并过壳的生命周期守卫。
  • 未改共享契约(packages/shared/**)、server-rs/**、任何 SpacetimeDB schema 或 HTTP 路由。

测试

  • tests/homeProjectNamingAsync.test.tsx(12 例,promise 闸门控时序,不用 sleep):命名请求永不返回仍进工作区、AI 结果先于进项目落定仍不被兜底名覆盖、手动改名不被覆盖且任务按「已完成 + 已跳过」收口、空/非法响应 → 任务 failed 且保留兜底名、条件改名失败不影响已建工作区、做方案不发起命名、切到别的工作区不被劫持、卸载/pagehide 后不写上下文;并断言账本推进序列 入队 → running → 终态。
  • tests/projectNamingGenerationTaskRow.test.tsx(3 例):固定展示名、进行中不渲染阶段文案与缩略图/定位动作、终态才显示结论、失败徽章与原因、在途计数。
  • tests/appSurface/home.suite.ts:建项后「生成任务」里出现这条「AI 项目命名」(进行中可见),账本变更事件 → 一次受控快照读取 → 终态结论出现在同一行;窗口标题与最近项目行同步 AI 名。
  • Rust:asset_generation_task 22 例(含 naming_task_stays_in_flight_across_ledger_reads_until_terminal、legacy_v1_ledger_reads_back_with_the_default_asset_generation_task_type、enqueueing_the_same_naming_task_id_twice_keeps_a_single_record、interrupted_naming_task_is_settled_as_failed_with_naming_wording)、conditional_project_rename_tests 6 例。
  • 反向证伪(均实测):删 await entrySettled → 顺序用例红;去掉上下文写入的项目路径守卫 → 劫持用例红;去掉生命周期守卫 → 卸载/隐藏两条用例红;去掉命名任务的 live 登记 → 在途记录被误收口为失败(Rust 用例红)。

验证(实跑)

npx vitest run apps/ai-game-creator-shell/tests/homeProjectNamingAsync.test.tsx        # 12 passed
npx vitest run apps/ai-game-creator-shell/tests/projectNamingGenerationTaskRow.test.tsx # 3 passed
npx vitest run apps/ai-game-creator-shell/tests/appSurface.test.ts                      # 200 passed / 9 skipped
npm run test -- apps/ai-game-creator-shell/tests                                        # 1934 passed / 17 skipped
cargo test --locked --features=cocos-editor-execute,unity-editor-execute,godot-editor-execute --bin genarrative-ai-game-creator-shell asset_generation_task                 # 27 passed
cargo test --locked --features=cocos-editor-execute,unity-editor-execute,godot-editor-execute --bin genarrative-ai-game-creator-shell conditional_project_rename_tests     # 7 passed
cargo fmt --all --manifest-path apps/ai-game-creator-shell/src-tauri/Cargo.toml -- --check  # 通过
npm run agc:typecheck                                                                  # 通过(含 check:tests:types 与 check-config)
npm run check:encoding                                                                 # 通过(5234 个文件)
npm run check:spacetime-schema                                                         # SpacetimeDB schema guard passed for 91 table(s)
git diff --check                                                                       # 干净

独立评审修复(0 P0/P1,4 P2 + 1 P3 全部处理)

  • P2-2 命名行可能永久停在「命名中」:事件订阅与快照读原本是两条独立 effect(先读后订阅),落在读的 IPC 往返里的终态事件会丢。改为先订阅再读快照(与素材队列「先订阅后派发」同口径),并在「生成任务」面板打开时补读一次兜底。新增两条 appSurface 用例(recovers the naming row when the terminal event lands before the subscription is ready、re-reads the naming ledger when the generation tasks panel opens),并做了「改前红」验证:把实现退回「先读后订阅 / 面板打开不补读」,两条即红。
  • P2-3 update_local_project_naming_task 门禁不对称 + 终态可被覆盖:补 enforce_project_permission_policy(root, "project.rename")(与 enqueue 同口径)、校验记录 project_id 与该项目一致、终态只接受同状态幂等重放(completed → failed 一律 Err 且不写盘)。新增 3 条 Rust 用例(权限拒绝、跨项目记录拒绝、终态不同状态拒绝 + 同状态重放通过)。
  • P2-4 失败关闭与 schema 分支缺测试:补超长项目名 → Err 且 manifest/revision 不变、不支持 schemaVersion → Err、缺 schemaVersion 按 v1 接受,共 3 条 Rust 用例。
  • P3-1 legacy 兼容用例钉死文案常量:改为断言兼容性质(v1 记录读回后字段原样保留),不再引用 phase 常量。

兼容性边界(单向,已知并记录)

  • 兼容是单向的:新构建读旧账本 OK;旧构建读新账本会失败。 命名记录写 "kind": null,而旧构建的 AssetGenerationTaskRecord.kind 必填、read_ledger 也不校验 schema,解析含命名记录的账本会整份失败 → 「生成任务」面板报「生成任务列表读取失败」,同一批读里在途的素材任务会被按「上次运行中断」收口为失败。触发条件:同一项目先被新构建写过命名任务,之后又被旧构建(降级 / 新旧混跑)打开。
  • 潜在的低成本正向兼容方向(未实施,需先确认):命名记录也写一个合法 kind(例如 unknown),只靠 taskType 区分——旧构建会把它渲染成一张名为「AI 项目命名」的素材任务卡,但账本能解析、不会误伤在途素材任务。涉及是否允许命名记录借用素材 kind 词表,留给用户定。

合并与剩余风险

  • 本分支已连续合并:origin/master tip ccd271572(merge eeac2c759)、origin/fix/ci-master-red tip 8b11dc4e7(merge 459fe5b43,PR #609 修掉共享红)、origin/master tip 33552d2ed(merge 3dcfbb1d1,仅 decision-log.md / pitfalls.md 两处文档冲突,两边条目全部保留)与 origin/master tip a2f250364(merge 160b7e6cf,零冲突),全部用 merge、未 rebase。
  • 合并 master 后按其新增的测试类型门禁(check:tests:types)把直接赋值 window.__TAURI__ 迁到 installTauriRuntime / resetTauriRuntime;命名任务的账本推进命令名写成字面量,满足 check-config.mjs 的静态枚举门禁。
  • 首轮 CI 的 Repository checks 红在 lint:eslint(simple-import-sort/imports:view/project-development/index.tsx 的 type 导入与值导入混排),已用 eslint --fix 归位并实跑 npm run lint:eslint(0 problems);该修复见提交 cd07b7375。
  • 未覆盖:真机(真实 Tauri 客户端)建项 smoke 未跑——CDP 9222 只在 WebView2 创建前生效,需要重启在跑的 AGC 客户端;该权限已交给另一 executor。
## 关联 issue Closes #599(Kind/Enhancement、Priority/Low) ## 改了什么 **1. 建项不再等 AI 命名** - 首页「开启创作 / 进入立项策划」的提交链路是「Web 环境预检 → 立即用宿主兜底名创建工作区 → 进入项目 → 首轮创作」;`create_automatic_local_game_project` 固定传 `name: null`(宿主兜底名 `GameAgent 项目 <8 位短 id>` / `策划项目 <8 位短 id>`),不再等待 `suggest_automatic_project_name` 返回。命名慢、超时、失败或返回非法内容都不影响建项、附件导入与首轮创作。 - 命名请求在建项前并行打出;AI 返回合法名称后走 `rename_local_game_project_if_unchanged(projectPath, expectedProjectId, expectedName, name)`:只有「项目 ID 相同」且「当前名称仍是本次创建的兜底名」才落盘改名,用户已手动改名 / 项目 ID 不符 / 名称未变一律跳过;空 `expectedProjectId` / `expectedName` / 空名 / 控制字符 / 超长失败关闭。与既有 `rename_local_game_project` 同口径:簿记写入,**不推进项目 revision**。 - 「做方案」不发起自动命名;手动选目录建项行为不变。 **2. 自动命名是「生成任务」体系里的一条后台任务(不是页面里的 promise)** - **任务类型**:账本记录新增 `taskType`(`asset-generation` / `project-naming`,缺省 `asset-generation`),`kind` 改为可选(命名任务为 `null`);schema 升 `agc-asset-generation-task.v2`,**读取同时接受 v1 与 v2**(v1 记录按素材任务读回)。 - **可见位置**:项目工作台右上角那条既有「生成任务」侧栏——与素材生成共用同一份账本、同一个变更事件(`game-creator-asset-generation-task-changed`)与同一套卡片样式;展示名固定「AI 项目命名」,不渲染缩略图 / 提示词 / 派发与「定位到素材」,结论只在终态显示。 - **状态流转**:`enqueue_local_project_naming_task`(排队中)→ `update_local_project_naming_task`(命名中)→ 已完成(已应用 AI 名称 / 建议名与现状一致 / **用户已手动改名,已跳过 AI 命名**)或 失败(无可用名称、自动改名失败,均保留兜底名)。命名任务由命名链路推进,因此 enqueue 登记 live、终态写盘后摘除,否则任何一次账本读取都会按「上次运行中断」把它误收口为失败(中断残留仍按命名口径收口为失败)。 - **账本只是展示旁路**:入队/推进失败只写诊断日志,绝不影响建项、命名与首轮创作;写 UI 投影(项目上下文、最近项目行重检)仍等「进项目落定」并过壳的生命周期守卫。 - 未改共享契约(`packages/shared/**`)、`server-rs/**`、任何 SpacetimeDB schema 或 HTTP 路由。 ## 测试 - `tests/homeProjectNamingAsync.test.tsx`(12 例,promise 闸门控时序,不用 sleep):命名请求永不返回仍进工作区、AI 结果先于进项目落定仍不被兜底名覆盖、手动改名不被覆盖且任务按「已完成 + 已跳过」收口、空/非法响应 → 任务 failed 且保留兜底名、条件改名失败不影响已建工作区、做方案不发起命名、切到别的工作区不被劫持、卸载/`pagehide` 后不写上下文;并断言账本推进序列 `入队 → running → 终态`。 - `tests/projectNamingGenerationTaskRow.test.tsx`(3 例):固定展示名、进行中不渲染阶段文案与缩略图/定位动作、终态才显示结论、失败徽章与原因、在途计数。 - `tests/appSurface/home.suite.ts`:建项后「生成任务」里出现这条「AI 项目命名」(进行中可见),账本变更事件 → 一次受控快照读取 → 终态结论出现在同一行;窗口标题与最近项目行同步 AI 名。 - Rust:`asset_generation_task` 22 例(含 `naming_task_stays_in_flight_across_ledger_reads_until_terminal`、`legacy_v1_ledger_reads_back_with_the_default_asset_generation_task_type`、`enqueueing_the_same_naming_task_id_twice_keeps_a_single_record`、`interrupted_naming_task_is_settled_as_failed_with_naming_wording`)、`conditional_project_rename_tests` 6 例。 - 反向证伪(均实测):删 `await entrySettled` → 顺序用例红;去掉上下文写入的项目路径守卫 → 劫持用例红;去掉生命周期守卫 → 卸载/隐藏两条用例红;去掉命名任务的 live 登记 → 在途记录被误收口为失败(Rust 用例红)。 ## 验证(实跑) ``` npx vitest run apps/ai-game-creator-shell/tests/homeProjectNamingAsync.test.tsx # 12 passed npx vitest run apps/ai-game-creator-shell/tests/projectNamingGenerationTaskRow.test.tsx # 3 passed npx vitest run apps/ai-game-creator-shell/tests/appSurface.test.ts # 200 passed / 9 skipped npm run test -- apps/ai-game-creator-shell/tests # 1934 passed / 17 skipped cargo test --locked --features=cocos-editor-execute,unity-editor-execute,godot-editor-execute --bin genarrative-ai-game-creator-shell asset_generation_task # 27 passed cargo test --locked --features=cocos-editor-execute,unity-editor-execute,godot-editor-execute --bin genarrative-ai-game-creator-shell conditional_project_rename_tests # 7 passed cargo fmt --all --manifest-path apps/ai-game-creator-shell/src-tauri/Cargo.toml -- --check # 通过 npm run agc:typecheck # 通过(含 check:tests:types 与 check-config) npm run check:encoding # 通过(5234 个文件) npm run check:spacetime-schema # SpacetimeDB schema guard passed for 91 table(s) git diff --check # 干净 ``` ## 独立评审修复(0 P0/P1,4 P2 + 1 P3 全部处理) - **P2-2 命名行可能永久停在「命名中」**:事件订阅与快照读原本是两条独立 effect(先读后订阅),落在读的 IPC 往返里的终态事件会丢。改为**先订阅再读快照**(与素材队列「先订阅后派发」同口径),并在「生成任务」面板打开时补读一次兜底。新增两条 appSurface 用例(`recovers the naming row when the terminal event lands before the subscription is ready`、`re-reads the naming ledger when the generation tasks panel opens`),并做了「改前红」验证:把实现退回「先读后订阅 / 面板打开不补读」,两条即红。 - **P2-3 `update_local_project_naming_task` 门禁不对称 + 终态可被覆盖**:补 `enforce_project_permission_policy(root, "project.rename")`(与 enqueue 同口径)、校验记录 `project_id` 与该项目一致、终态只接受同状态幂等重放(`completed → failed` 一律 Err 且不写盘)。新增 3 条 Rust 用例(权限拒绝、跨项目记录拒绝、终态不同状态拒绝 + 同状态重放通过)。 - **P2-4 失败关闭与 schema 分支缺测试**:补超长项目名 → Err 且 manifest/revision 不变、不支持 `schemaVersion` → Err、缺 `schemaVersion` 按 v1 接受,共 3 条 Rust 用例。 - **P3-1 legacy 兼容用例钉死文案常量**:改为断言兼容性质(v1 记录读回后字段原样保留),不再引用 phase 常量。 ## 兼容性边界(单向,已知并记录) - **兼容是单向的:新构建读旧账本 OK;旧构建读新账本会失败。** 命名记录写 `"kind": null`,而旧构建的 `AssetGenerationTaskRecord.kind` 必填、`read_ledger` 也不校验 schema,解析含命名记录的账本会**整份失败** → 「生成任务」面板报「生成任务列表读取失败」,同一批读里在途的素材任务会被按「上次运行中断」收口为失败。触发条件:同一项目先被新构建写过命名任务,之后又被旧构建(降级 / 新旧混跑)打开。 - 潜在的低成本正向兼容方向(**未实施,需先确认**):命名记录也写一个合法 `kind`(例如 `unknown`),只靠 `taskType` 区分——旧构建会把它渲染成一张名为「AI 项目命名」的素材任务卡,但账本能解析、不会误伤在途素材任务。涉及是否允许命名记录借用素材 kind 词表,留给用户定。 ## 合并与剩余风险 - 本分支已连续合并:`origin/master` tip `ccd271572`(merge `eeac2c759`)、`origin/fix/ci-master-red` tip `8b11dc4e7`(merge `459fe5b43`,PR #609 修掉共享红)、`origin/master` tip `33552d2ed`(merge `3dcfbb1d1`,仅 `decision-log.md` / `pitfalls.md` 两处文档冲突,两边条目全部保留)与 `origin/master` tip `a2f250364`(merge `160b7e6cf`,零冲突),全部用 merge、未 rebase。 - 合并 master 后按其新增的测试类型门禁(`check:tests:types`)把直接赋值 `window.__TAURI__` 迁到 `installTauriRuntime` / `resetTauriRuntime`;命名任务的账本推进命令名写成**字面量**,满足 `check-config.mjs` 的静态枚举门禁。 - 首轮 CI 的 `Repository checks` 红在 `lint:eslint`(`simple-import-sort/imports`:`view/project-development/index.tsx` 的 type 导入与值导入混排),已用 `eslint --fix` 归位并实跑 `npm run lint:eslint`(0 problems);该修复见提交 `cd07b7375`。 - 未覆盖:真机(真实 Tauri 客户端)建项 smoke 未跑——CDP 9222 只在 WebView2 创建前生效,需要重启在跑的 AGC 客户端;该权限已交给另一 executor。
suzmii self-assigned this 2026-10-03 19:45:37 +08:00
suzmii added 1 commit 2026-10-03 19:45:37 +08:00
修复 #599:首页建项不再等待 AI 项目命名,改为后台条件改名
Project CI / Backend tests (pull_request) Failing after 17s
Project CI / AI game creator shell Rust crates (pull_request) Successful in 2m31s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Successful in 4m6s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Successful in 5m9s
Project CI / Repository checks (pull_request) Failing after 30s
Project CI / Frontend tests (pull_request) Successful in 3m21s
Project CI / AI game creator shell web tests (pull_request) Successful in 3m24s
Project CI / Native shell tests (pull_request) Successful in 7m5s
e3c91c4e2c
- 首页自动建项:预检通过后立即以宿主兜底名创建工作区并进入项目,create_automatic_local_game_project 固定传 name: null,不再等待 suggest_automatic_project_name。
- 命名请求在建项前并行打出,结果交给后台任务;命名慢、超时、失败或返回非法内容都不影响建项、附件导入与首轮创作。
- 新增 Rust 命令 rename_local_game_project_if_unchanged(expectedProjectId + expectedName 条件改名):项目 ID 不符、用户已手动改名、名称未变一律返回 renamed: false 并保持现状,空名/控制字符/超长失败关闭,改名不推进项目 revision。
- 后台改名结果等「建项主体收尾」再写当前项目上下文与最近项目行重检,避免被进项目的兜底上下文覆盖。
- 新增前端时序测试 tests/homeProjectNamingAsync.test.tsx(9 例,promise 闸门而非 sleep)与 appSurface 客户端流程用例。
- 新增 Rust 条件改名单测 5 例;更新实施计划专题、decision-log、pitfalls。

验证:npx vitest run apps/ai-game-creator-shell/tests/homeProjectNamingAsync.test.tsx(9 passed);npm run test -- apps/ai-game-creator-shell/tests(1910 passed / 17 skipped);cargo test --features=cocos-editor-execute,unity-editor-execute,godot-editor-execute --bin genarrative-ai-game-creator-shell conditional_project_rename_tests(5 passed);cargo check 同 features 通过;npm run agc:typecheck、cargo fmt --check、npm run check:encoding、git diff --check 全绿。
suzmii added 1 commit 2026-10-04 00:12:18 +08:00
补充 #599 自审回归用例:用户已切到别的工作区时后台改名不劫持上下文
Project CI / Backend tests (pull_request) Failing after 22s
Project CI / AI game creator shell Rust crates (pull_request) Successful in 3m5s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Successful in 4m14s
Project CI / Repository checks (pull_request) Failing after 15s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Successful in 4m46s
Project CI / Frontend tests (pull_request) Successful in 2m7s
Project CI / Native shell tests (pull_request) Successful in 7m10s
Project CI / AI game creator shell web tests (pull_request) Successful in 3m3s
6c7033221d
- 新增用例 does not hijack the project the user already moved to when the AI name lands:后台 AI 名称落定时若当前项目上下文已是另一个工作区(看门狗解围后打开别项目走同一条「上下文已被接管」判据),只重检刚建工作区的最近项目行,不把用户切走的上下文换成刚建的工作区。
- 该用例经反向证伪:临时去掉 applyAutomaticProjectName 的项目路径守卫后失败(projectName 由 另一个项目 变成 星河远征),随后已还原源码。
- 自审复核结论(无代码改动):entrySettled 在预检失败、建项失败、看门狗让位、进项目失败、成功五条路径都由建项主体的 finally 落定;看门狗与后台 creation 竞速没有重复 rememberRecentWorkspace、没有未捕获拒绝;expectedName 取建项回传的 manifest.name(与宿主落盘同源,宿主侧 trim 为 no-op);新命令已注册且旧 rename_local_game_project 的调用方未受影响。
- 定向验证:homeProjectNamingAsync 10 passed;appSurface 两条相关用例 passed;cargo conditional_project_rename_tests 5 passed;cargo fmt --check 与 npm run agc:typecheck 通过。
suzmii added 2 commits 2026-10-04 00:39:10 +08:00
自审收口 #599:空 expected 失败关闭、异步回填生命周期守卫、合并 master 后过测试类型门禁
Project CI / AI game creator shell Rust crates (pull_request) Successful in 3m0s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Successful in 3m49s
Project CI / Backend tests (pull_request) Failing after 3m43s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Successful in 6m23s
Project CI / Frontend tests (pull_request) Successful in 3m11s
Project CI / Repository checks (pull_request) Failing after 4m13s
Project CI / AI game creator shell web tests (pull_request) Successful in 3m5s
Project CI / Native shell tests (pull_request) Successful in 7m13s
4144c04b1c
- 合并 origin/master tip ccd271572(merge commit eeac2c759),无冲突;本分支不修 master 既有共享红(check:nginx-spa-routes / external_mcp::semantic 32 与 30 / http_tracing 偶发 / AGC Rust shard 4/4,由 fix/ci-master-red 处理)。
- 按 master 新增的测试类型门禁(check:tests:types)把两处直接赋值 window.__TAURI__ 迁到 installTauriRuntime / resetTauriRuntime(tests/tauriRuntimeFake.ts 是全仓唯一安装点),否则 appSurface/home.suite.ts 报 TS2322。
- Rust:rename_local_game_project_if_unchanged_at 对空 expectedProjectId / expectedName 按仓库同类入口口径失败关闭(不再静默 renamed:false);非空但不匹配仍是正常跳过。新增 1 条单测,含「非空不匹配不是错误」的反向保护。
- 前端:applyAutomaticProjectName 在 await entrySettled 之后加壳生命周期守卫(mounted + 代次,与 enterProjectDevelopment 同口径):关窗 / 卸载后只保留已落盘的改名,不再写当前项目上下文、不再重检最近项目。新增 2 条 hook 用例(unmount / pagehide),去掉守卫即变红。
- 注释口径:把 suggestName 判据与 ProjectStartMode 的映射写清(planning 覆盖「做方案」与勾选「策划补全」的「做游戏」,见 view/home/homeStartMode.ts),行为不变。
- 文档:decision-log 的验证数字更新为 12 例 / 6 例 / 全量 1922 passed,并补记生命周期守卫与空 expected 口径。
- 定向验证:homeProjectNamingAsync 12 passed;appSurface.test.ts 197 passed / 9 skipped;AGC 全量 1922 passed / 17 skipped;cargo conditional_project_rename_tests 6 passed;cargo fmt --check、npm run agc:typecheck(含 check:tests:types)、check:encoding、git diff --check 全过。
suzmii added 7 commits 2026-10-04 03:43:12 +08:00
- src/routing/activeAppPageRoutes.ts 的 STAGE_ROUTE_ENTRIES 已把 payment-checkout→/pay、
  profile-payment→/profile/payment 定义为主站对外可直达的 SPA 路由(分别渲染
  PaymentCheckoutView 与 PlatformPaymentServiceView),但三份 Nginx 模板的 SPA allowlist
  仍停留在加入支付入口之前的集合,导致 check:nginx-spa-routes 在 master 上失败
- deploy/nginx/genarrative.conf、deploy/nginx/genarrative-dev-http.conf、
  deploy/container/nginx.conf 的 SPA regex 补上 pay、profile/payment,
  保持既有形状(锚定完整路径 + 大小写不敏感 + 允许一个尾部斜杠)
- 本地实跑:node scripts/check-nginx-spa-routes.mjs → OK (14 SPA routes, 3 Nginx templates)
- 断言 MCP_OPERATIONS.len() == 30 自 #592 起写死。4b529a895(新增支付服务接入与订单收银台)
  在内置 OpenAPI 中新增 createExternalPaymentOrder、getExternalPaymentOrder 两条未被
  x-mcp-excluded 排除的操作,legacy 可调用工具由 30 合法增至 32(非重复注册、非覆盖)
- 断言更新为 32 并注明来源(内置 OpenAPI 去掉 5 条元数据/入口操作后的可调用集合),
  保留“语义工具是增量、不得覆盖 legacy 工具”的原意;总数断言改为
  MCP_OPERATIONS.len() + TOOLS.len() 派生,避免再次写死
- 本地实跑:cargo test --locked -p api-server --no-fail-fast --manifest-path server-rs/Cargo.toml semantic
  → 18 passed(含 semantic_catalog_adds_fifteen_tools_without_replacing_legacy_tools)
- 根因:span!/info_span! 宏在 callsite 的缓存 interest 为 never 时会静默返回空 span
  (tracing-0.1.44/src/macros.rs 的 span! 分支),而 DefaultCallsite::register 只在调用点
  首次被命中时计算一次 interest,且当进程里只注册过一个 dispatcher 时会退化成
  dispatcher::get_default()——也就是命中线程自己的 dispatcher(tracing-core-0.1.36
  callsite.rs 的 Rebuilder::JustOne)。libtest 默认并发下,没有 subscriber 的普通测试线程
  一旦抢到 http.request / llm.request 调用点的首次注册,就会把它永久缓存成 never,
  于是 CI 偶发看到 0 个 span(app.rs:603、observability_tests.rs:98)
- 关键:set_default 与 with_subscriber 在 interest 缓存这件事上**等价**(都只是新建 Dispatch
  并触发一次 rebuild_interest,只能纠正“已经注册过”的调用点,纠正不了 JustOne→get_default
  这条首次注册分支),所以真正起作用的是**在自家 subscriber 下命中同一个调用点做热身**,
  把首次注册的顺序握在自己手里;register_callsite 覆写只用于避免本 subscriber 触发的重建
  把其它调用点永久标记成 never
- api-server app::tests::http_tracing:不再用 with_subscriber,改为在请求前用同一
  http.request 调用点热身到连续两轮采集成功,并在整个请求期间持有 scoped default
  (已注明 set_default 是线程绑定,只适用于默认的 current_thread #[tokio::test])
- platform-llm observability_tests:新增 run_under_capture 固定整段流程的 scoped default,
  并新增 warm_up_provider_span_callsite(用指向刚释放 loopback 端口的最小失败请求命中同一
  llm.request 调用点,连续两轮采集成功才继续)
- 断言未放宽:仍要求每个被拒绝请求恰好 1 个 HTTP span、每次 Provider 调用恰好 1 个
  llm.request span;未使用 sleep
- 本地实跑:cargo test -p api-server --bin api-server app::tests::http_tracing(默认并发与
  --test-threads=1 各 20 次全绿)、app::tests:: 91 用例并发 10 次全绿、--skip bgfilter_worker
  --skip wallet_refund_outbox 的 1133 用例全量 bin 2 次全绿;cargo test -p platform-llm --lib
  (162 passed,含 3 条观测用例)连跑 20 次全绿
- agent::thread_manager::tests::active_turn_changes_publish_one_notification_per_real_change
  偶发 left 8/right 7:测试计数器 DIRECT_ACTIVE_TURNS_EVENT_TEST_COUNT 曾在 2026-10-01 按线程
  作用域隔离,2026-10-02 退役 runtime_driver 把它搬进 agent/direct_events.rs 时降级回进程级
  static AtomicU64,于是断言会取到宿主 tauri::async_runtime 后台回合在别的线程上的广播
  (--test-threads=1 只串行测试线程)。改回 thread_local! Cell,并注明跨线程口径的覆盖取舍
- process_session::tests::process_session_graceful_terminate_keeps_wrapper_alive_for_target_cleanup
  偶发 left "exited"/right "terminated":测试命令里 leader 打印 READY 后立刻 exit 0,同组后代
  仍存活,trampoline 从 leader 被回收起开始 800ms 宽限;客户端只要晚于宽限才发出 terminate
  就只能读到既成事实。改为 leader 用 wait 等后台子进程,让 terminate 必然落在会话仍 running 时
  (trap / sleep 0.4 / marker / 断言均未改),并注明该用例的确定性来自 400ms < 800ms 的时间余量
- docs/project-memory/shared-memory/pitfalls.md:更新 graceful terminate 那条已过时的验证口径,
  补三条 2026-10-04 条目(tracing interest 竞态、AGC 两条偶发的串台根因、SPA 深链前缀路由)
- 本地实跑:修复前把计数器临时改回 AtomicU64 时同一并行口径 17/20 红;修复后并行 20 次全绿、
  agent::thread_manager:: 连跑 5 次 72 passed;第 2 条用例是 #[cfg(target_os = "linux")],
  Windows 本机跑不到,已用真实 Linux 内核(WSL Alpine)验证命令形状(leader 活到 TERM、
  同组后代完成 400ms 延迟清理 marker=done 0.41s、清理后组内零残留),CI 侧仍需跑
  node apps/ai-game-creator-shell/scripts/run-rust-shell-test-shards.mjs --shards=4 --shard-index=4 复核
- 现状:只把 `/pay`、`/profile/payment` 加进 allowlist 只能让 check:nginx-spa-routes 变绿;
  payment.rs 生成的 checkoutUrl 是 `/pay/<checkoutToken>`,深链仍落默认 location 的
  try_files → 404。同一批漂移里 check:pingora-route-parity 也是红的(Pingora MAIN_SPA_PATHS
  缺 /pay、/profile/payment),只是被 lint 链里先失败的门禁掩盖,修一条要跑到链尾
- 真相源:src/routing/activeAppPageRoutes.ts 新增 APP_PREFIX_ROUTE_ENTRIES
  ('/pay' → payment-checkout),resolveSelectionStageFromPath 改用它
- 门禁:scripts/check-nginx-spa-routes.mjs 要求三份模板都有锚定前缀 location
  `location ~* "^/pay/[^/]+/?$"`(裸前缀仍由精确 location 负责;前缀 location 必须镜像精确
  location 的维护闸与 try_files 回退);新增 scripts/check-nginx-spa-routes.test.mjs 正/反用例
  (把前缀写成精确匹配或过宽裸前缀都会红),由 npm run check:nginx-spa-routes 一起执行;
  check-pingora-route-parity 新增 MAIN_SPA_PREFIX_PATHS 与前端前缀路由的逐条比对
- 模板:deploy/nginx/genarrative.conf、deploy/nginx/genarrative-dev-http.conf、
  deploy/container/nginx.conf 各加一条锚定前缀 location
- Pingora:MAIN_SPA_PATHS 补 /pay、/profile/payment;新增 MAIN_SPA_PREFIX_PATHS 与
  is_main_spa_prefix_path(大小写不敏感,只认「前缀 + 恰好一段」),矩阵新增
  pay_checkout_spa_fallback 用例,并给网关补一条前缀正/反单测
- 文档:Pingora 试点文档的路由表与门禁说明、deploy/nginx/README 与本地开发/生产运维文档
  同步前缀路由口径与线上 curl 复验方式
- 本地实跑:node --test scripts/check-nginx-spa-routes.test.mjs(4 passed)、
  node scripts/check-nginx-spa-routes.mjs(OK,14 SPA routes / 1 prefix routes / 3 templates)、
  npm run check:pingora-route-parity(OK,25 routes)、
  cargo test -p pingora-gateway -- pay_checkout_deep_link matches_nginx_route_parity_matrix(2 passed)
语义目录测试的总数断言改回字面量,避免恒真
Project CI / AI game creator shell Rust crates (pull_request) Successful in 2m50s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Successful in 4m10s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Successful in 5m5s
Project CI / Backend tests (pull_request) Successful in 6m30s
Project CI / Frontend tests (pull_request) Successful in 3m12s
Project CI / AI game creator shell web tests (pull_request) Successful in 2m44s
Project CI / Native shell tests (pull_request) Successful in 6m33s
Project CI / Repository checks (pull_request) Successful in 5m5s
8b11dc4e7a
- names.len() == MCP_OPERATIONS.len() + TOOLS.len() 在 BTreeSet 去重构造下恒真、没有判别力;
  改回固定 47(32 条 legacy 工具 + 15 条语义工具),并注明上面的插入断言已保证两组名字互不覆盖
- 本地实跑:cargo test --locked -p api-server --manifest-path server-rs/Cargo.toml --bin api-server semantic
  → 18 passed
Merge remote-tracking branch 'origin/fix/ci-master-red' into fix/home-project-naming-async
Project CI / AI game creator shell Rust crates (pull_request) Successful in 2m8s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Successful in 3m35s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Successful in 5m51s
Project CI / Frontend tests (pull_request) Successful in 3m17s
Project CI / Backend tests (pull_request) Successful in 6m24s
Project CI / Native shell tests (pull_request) Successful in 7m27s
Project CI / AI game creator shell web tests (pull_request) Successful in 2m56s
Project CI / Repository checks (pull_request) Successful in 6m15s
459fe5b432
suzmii added 3 commits 2026-10-04 17:56:48 +08:00
- 账本 v2:记录新增 taskType(asset-generation / project-naming,缺省 asset-generation,旧壳按素材任务读回),kind 改为可选;schema 升 agc-asset-generation-task.v2,读取同时接受 v1 与 v2。
- 新增 enqueue_local_project_naming_task / update_local_project_naming_task:命名任务入队时登记 live、终态写盘后摘除(写盘失败只回滚本轮插入),中断残留按命名口径收口为 failed(文案不含「目标素材」)。
- 首页命名链路改为推进这条任务:入队 → 命名中 → 已完成(已应用 AI 名称 / 建议名与现状一致 / 用户已手动改名而跳过)或失败(无可用名称 / 自动改名失败,均保留兜底名);账本只作展示旁路,推进失败只写诊断日志。
- 前端模型与面板支持命名行:按 taskType 把命名记录从素材任务分支剔除,命名行固定展示名「AI 项目命名」、只在终态显示结论、不渲染缩略图/提示词/定位动作;命令名写字面量以满足 check-config 静态枚举门禁。
- 工作台订阅账本变更事件做一次受控快照读取刷新命名行(不新增固定频率轮询);另修:命名行的 useLiveNow 收进独立组件,避免行种类变化导致 hooks 数量变化。
- 测试:homeProjectNamingAsync 12 例(新增入队→命名中→终态推进序列断言)、projectNamingGenerationTaskRow 3 例、appSurface 建项后命名行可见 + 事件刷新出终态结论 + 标题/列表同步;Rust asset_generation_task 22 例(含 naming_task_stays_in_flight_across_ledger_reads_until_terminal、legacy_v1_ledger_reads_back_*)。
- 文档:decision-log 改写为「生成任务体系里的后台任务」、pitfalls 新增 taskType/live 集合条目、实施计划专题补任务接入口径。
- 门禁:AGC 全量 1925 passed / 17 skipped;cargo 定向 22 + 6;npm run agc:typecheck(含 check:tests:types 与 check-config);cargo fmt --check;check:encoding;git diff --check。
- 冲突仅在 docs/project-memory/shared-memory/decision-log.md 与 pitfalls.md(双方都在文件顶部追加最新条目)。
- 解决方式:两边条目全部保留——本分支的 Issue 599 条目(AI 项目命名解耦 / 生成任务账本 taskType)在前,master 侧的新条目(#604 游戏游玩次数两条、#602 AGC 画布引用、AGC 渠道更新两条)原样保留,逐条核对无丢失。
Merge remote-tracking branch 'origin/master' into fix/home-project-naming-async
Project CI / AI game creator shell Rust crates (pull_request) Successful in 6m11s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Successful in 6m33s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Successful in 8m34s
Project CI / Frontend tests (pull_request) Successful in 2m35s
Project CI / Backend tests (pull_request) Successful in 9m11s
Project CI / AI game creator shell web tests (pull_request) Successful in 3m20s
Project CI / Repository checks (pull_request) Failing after 4m28s
Project CI / Native shell tests (pull_request) Successful in 8m8s
160b7e6cfa
suzmii added 1 commit 2026-10-04 18:36:24 +08:00
修复 Repository checks 的 ESLint 报错:import 顺序按 simple-import-sort 归位
Project CI / Backend tests (pull_request) Failing after 22s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Successful in 7m33s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Successful in 5m30s
Project CI / AI game creator shell Rust crates (pull_request) Successful in 4m14s
Project CI / Repository checks (pull_request) Failing after 34s
Project CI / Frontend tests (pull_request) Successful in 3m39s
Project CI / AI game creator shell web tests (pull_request) Successful in 3m43s
Project CI / Native shell tests (pull_request) Successful in 9m12s
cd07b7375a
- CI「Repository checks」红在 lint:eslint:apps/ai-game-creator-shell/src/view/project-development/index.tsx 的 import 顺序未按 simple-import-sort 排序(type 导入与值导入混排)。
- 只调整该文件 import 顺序(1 行),无行为改动;已实跑 npm run lint:eslint(0 problems)、npx prettier --check、homeProjectNamingAsync + projectNamingGenerationTaskRow 15 passed、npm run agc:typecheck。
suzmii added 1 commit 2026-10-04 19:03:30 +08:00
Merge remote-tracking branch 'origin/master' into fix/home-project-naming-async
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Successful in 8m30s
Project CI / AI game creator shell Rust crates (pull_request) Successful in 5m18s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Successful in 6m25s
Project CI / Frontend tests (pull_request) Successful in 2m33s
Project CI / Backend tests (pull_request) Successful in 7m46s
Project CI / AI game creator shell web tests (pull_request) Successful in 3m33s
Project CI / Repository checks (pull_request) Successful in 7m33s
Project CI / Native shell tests (pull_request) Successful in 10m6s
93deb694b0
suzmii added 1 commit 2026-10-04 23:43:47 +08:00
合并 master 6c9566058:保留 v2 账本/命名任务与 master 侧改动
Project CI / AI game creator shell Rust crates (pull_request) Successful in 4m17s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Successful in 5m44s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Successful in 7m26s
Project CI / Frontend tests (pull_request) Successful in 2m58s
Project CI / Backend tests (pull_request) Successful in 7m24s
Project CI / Repository checks (pull_request) Successful in 6m23s
Project CI / AI game creator shell web tests (pull_request) Successful in 3m47s
Project CI / Native shell tests (pull_request) Successful in 9m34s
d550d882d4
- 冲突仅在 docs/project-memory/shared-memory/decision-log.md 与 pitfalls.md(双方都在顶部追加条目):两边条目全部保留——本分支的 Issue 599 条目(AI 项目命名解耦 / 生成任务账本 taskType + live 集合)在前,master 侧新增条目(发布版本标签派生、随包 plugins feature 档位、.worktrees 下 Vite 忽略规则、并发 dev 栈、Windows 阻塞模态用例)原样保留,逐条核对无丢失。
- 其余文件(desktop.rs、check-config.mjs、view/project-development/index.tsx、src-tauri 多处、admin-web、game distribution 等)均为自动合并,无冲突。
- 合并后自证:v2 账本常量/命令注册/命名任务命令名/条件改名空 expected 校验/entrySettled 顺序/命名行与事件刷新/三个测试文件的关键断言全部在位(脚本逐条核对 ALL_OK)。
suzmii added 1 commit 2026-10-05 00:55:47 +08:00
独立评审收口 #599:命名行事件时序、命名任务 update 门禁、失败关闭与 legacy 用例
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Successful in 8m1s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Successful in 5m35s
Project CI / AI game creator shell Rust crates (pull_request) Successful in 4m21s
Project CI / Frontend tests (pull_request) Successful in 3m36s
Project CI / Backend tests (pull_request) Successful in 7m48s
Project CI / AI game creator shell web tests (pull_request) Successful in 3m12s
Project CI / Repository checks (pull_request) Successful in 7m2s
Project CI / Native shell tests (pull_request) Successful in 8m41s
67d06bc168
- P2-2:命名行的账本订阅与恢复读合并为同一次 effect 的「先订阅、后读、只读一次」(与素材队列先订阅后派发同口径),并在「生成任务」面板打开且**确有在途命名行**时补读一次兜底——多读会改变既有生成任务链路可观测的轮询计数,因此不给没有命名任务的项目加读。新增 2 条 appSurface 用例(错过终态事件仍到终态 / 面板打开补读),并做了「改前红」验证:退回「先读后订阅 + 面板不补读」两条即红。
- P2-3:update_local_project_naming_task 补 project.rename 权限门禁(与 enqueue 同口径)、校验记录 project_id 与项目一致、终态只接受同状态幂等重放(completed→failed 一律 Err 且不写盘);新增 3 条 Rust 用例。
- P2-4:新增失败关闭与 schema 分支用例——超长项目名条件改名 Err 且 manifest/revision 不变、不支持 schemaVersion Err、缺 schemaVersion 按 v1 接受。
- P3-1:legacy v1 兼容用例改为断言兼容性质(字段原样保留),不再引用 phase 文案常量。
- 文档:pitfalls 补「兼容是单向的(旧构建读含命名记录的账本会整份失败,含影响面/触发条件/潜在低成本正向兼容方向)」与「先订阅后读快照」「update 门禁与终态语义」三条边界;decision-log 同步边界与数字。
- 门禁:AGC 全量 1964 passed / 17 skipped(appSurface 200 passed / 9 skipped);cargo asset_generation_task 27 passed、conditional_project_rename_tests 7 passed;agc:typecheck、lint:eslint(0 problems)、check:encoding、check:spacetime-schema、cargo fmt --check、git diff --check 全过。
Some checks are pending
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Successful in 8m1s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Successful in 5m35s
Project CI / AI game creator shell Rust crates (pull_request) Successful in 4m21s
Project CI / Frontend tests (pull_request) Successful in 3m36s
Project CI / Backend tests (pull_request) Successful in 7m48s
Project CI / AI game creator shell web tests (pull_request) Successful in 3m12s
Project CI / Repository checks (pull_request) Successful in 7m2s
Project CI / Native shell tests (pull_request) Successful in 8m41s
This pull request can be merged automatically.
You are not authorized to merge this pull request.
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin fix/home-project-naming-async:fix/home-project-naming-async
git checkout fix/home-project-naming-async
Sign in to join this conversation.