退出路径统一收尾本地预览,陈旧预览记录不再留给下次进入

- preview.rs:新增 stop_local_game_preview_on_exit——退出时若 registry 里确有 running 预览,就用 stop_local_game_preview_for_root 把它真正停掉并写 stopped(顺带落 preview.log 与 trace 收尾)。预览服务器是进程内线程,进程一退 URL 就永久失效;不在这里收尾,.agent/manifest.json 会一直写着 running。
- main.rs:handle_game_creator_gui_run_event 的 RunEvent::Exit 分支先做预览收尾,再做既有的 Agent server / runner 关闭;失败按 `preview.gui_exit.stop_failed` 记日志,不阻断退出。
- 语义变更(用户已确认接受):关窗不再保留预览——退出即停止预览并把记录写成 stopped,下次进项目一律落到资源管理。与"进入项目时按 registry 活体核对"配套:退出路径覆盖正常关闭,强杀 / 断电那类不走退出路径的情况由进入时的核对自愈。不引入 stale DTO 字段(判定仍要活体探测,且会把状态扩到跨端契约与全部消费方)。
- tests/project.rs:新增 gui_exit_stops_the_preview_and_clears_the_stale_record——退出收尾后 registry 归零且 manifest 记录为 stopped。
- tests/sessionPreview.test.ts:补两条 Rust 侧结构性守卫(本仓既有做法:直接解析源码)。① RunEvent::Exit 分支必须出现 preview::stop_local_game_preview_on_exit——这条接线没有别的行为测试覆盖(注册全局 registry 的用例会互相打架);② 同项目重启预览的替换分支里,收尾必须排在"旧预览是否属于另一个项目"的判定之后。变异验证:把 main.rs 的退出收尾摘掉 → 守卫①立即红灯(已实测)。
- decision-log.md:新增 2026-09-11 决策条目,逐条写下背景(偶发进运行界面的成因链:进程内预览线程 + 随机临时端口 + 退出零清理 + 进门不核对 + 同项目重启覆盖刚写的 running)、A/B/C 三项决策、关窗不再保留预览这条语义变更及"为什么两者都做而不加 stale 字段"、不做什么与验证方式。同文件被 prettier 补了几处标题前空行(格式规范化,无内容变化)。
- 验证:cargo check --locked --all-targets exit 0;Rust 定向用例 restarting_preview_for_the_same_project / replacing_preview_for_another_project / stopping_without_a_live_preview / gui_exit_stops_the_preview 各 1 passed;AGC 子集 1198 passed / 4 skipped / 0 failed;共享组件 1385 passed;typecheck exit 0;编码 4378 文件;git diff --check 干净。
This commit is contained in:
2026-09-11 12:31:00 +08:00
parent 669c71d7e8
commit 531392e506
5 changed files with 101 additions and 0 deletions
@@ -151,6 +151,7 @@
- 关联文档:`docs/technical/【技术方案】Direct回合行为审计账本-2026-08-31.md`、分支 `feat/agc-llm-router-official-chain`。
---
## 2026-09-02 GDD 审批卡的后台 hydrate 不抢占已加载决定
- 背景:项目页首次加载和运行态刷新可能并发 hydrate。卡片已经显示后,短暂的 `hydrateBusy` 会让已打开的评论弹层提交按钮瞬时变灰,用户无法提交已输入的修改意见。
@@ -8128,6 +8129,7 @@ CI 上 `background_agent_runtime_recovers_stale_running_before_pending_task` 在
- 文档措辞约束:凡出现“流式响应”“流式事件”或 `text_delta`,均须注明其属于 Provider adapter/Runtime 内部实现能力;不得将其描述为前端必须逐条接收的用户可见消息。V2 的唯一用户交互结果是 `plan_ask_question` 和 `plan_submit_gdd` 的结构化工具结果,Provider 完成前是否产生多个 delta 不参与验收。
## 2026-08-29 AGC 官方 LLM 代理与 Windows 私有路径修复
## 2026-08-29 AGC 官方 LLM 代理与 Windows 私有路径修复
- AGC 正式发行版不再让用户配置 Provider、Base URL 或 API Key。客户端只携带登录 access token 调用 `api-server`;`api-server` 按 access token 的 owner 查询 `llm_router_account`,解密服务端密文后调用固定 `https://router.genarrative.world/v1`。模型目录由后台 owner 管理并持久化到 `agc_model_catalog`,客户端仅显示别名,在对话框右下角选择稳定标识,服务端映射实际模型名;设置页不承载模型选择或方案管理。真实 Router Key 不进入聊天、manifest、trace、日志、项目文件、Codex argv/环境变量或普通 IPC payload。
@@ -8186,6 +8188,7 @@ CI 上 `background_agent_runtime_recovers_stale_running_before_pending_task` 在
- 保留项:V2 与 V1 共用的 GDD 数据模型收敛到 `runtime_protocol/planning_gdd_model.rs`;`.agent/planning/` 旧文件不迁移、不删除,旧 `fast_gdd.md` 仍可只读打开;前端 `PROJECT_SUPERVISOR_PLAN_SOURCE` 字符串与 `GddApprovalCard.tsx`(`PlanGddSurface`)是 V2 现役的适配/展示面,不属于退役对象。
- 旧 sidecar 中带已删字段(`clarification`、`continuation`、`requestId`、`turnId`、`pendingApproval` 等)的记录会因 `deny_unknown_fields` 拒绝反序列化,这是退役语义的一部分,不做迁移。
- 测试基线说明:收尾时测试套件存在 25 个既有失败(mock LLM 时序敏感类,HEAD worktree 对照验证与本次无关),后续清理时不要误记到本次退役头上。
## 2026-09-08 Planning V2 正常 run 优先的恢复旁路
- 恢复只利用已经落盘的合法 question、GDD 和 approval receipt;不调用 Provider、不要求模型额外输出恢复字段、不设置复杂状态机或自动重试。
@@ -8269,6 +8272,7 @@ CI 上 `background_agent_runtime_recovers_stale_running_before_pending_task` 在
- 边界:不改 manifest 结构、不改对外 OpenAPI 或 SpacetimeDB schema。该投影同时被 `agc_create_or_derive_resource` 的完成回包复用,两条路径语义一致。
- 影响范围:`apps/ai-game-creator-shell/src-tauri/src/agent/direct_tool_bridge.rs`、`agent/direct_tools_mcp.rs`。
- 验证:定向 `cargo test -- bridge_registered_resource bridge_art_resource` 3 passed;新增 `bridge_registered_resource_projects_manifest_classification_verbatim` 锁定“显式分类与标签原样透出、不泄漏 prompt/model”;`cargo check --all-targets` 与 AGC 全量套件(1049 passed / 4 skipped / 0 failed)不倒退。
## 2026-09-10 AGC 素材重命名命令(文件改名 + localPath 更新,id 不变)
- 语义:manifest 资产条目没有 `name` 字段,资源显示名来自 `fileName(localPath)`(`view/project-development/resourceProjectionModel.ts` 的 `label`),所以“重命名”只能是**磁盘文件改名 + manifest `localPath` 更新**,资产 `id` 不变;不新增 manifest / 契约字段。
@@ -8319,3 +8323,12 @@ CI 上 `background_agent_runtime_recovers_stale_running_before_pending_task` 在
- 不做什么:不放宽 `.agent/project.lock` 的项目级串行化语义,不引入可重入项目锁,不改“同一调用链禁止二次获取 `.agent/project.lock`”的既有约定,不改 AGC 多进程拓扑,不改 `pendingOperations` 语义,也不改“活持有者始终不回收”的既有判据。其它仍用零等待取锁的入口(`command.exec / project.verify / memory / conversation / task / checkpoint / 预览 / UI 编辑器 / 资源编辑器 / Tauri 命令`)不在本次范围。
- 验证方式:`project_lock_recovery` 追加持锁方身份与权限拒绝两条;`direct_tool_bridge` 追加同进程重叠写等待、同轮并行写、有界等待不占 runtime worker(默认 `current_thread` runtime 加心跳任务,同步阻塞会立刻让心跳停摆)、ACL 拒绝不投影成争用四条;`project/write_lock` 追加“重试性只由错误码决定”与“终态改判三条件”两条平台无关用例(平台作参数传入,Linux CI 覆盖 Windows 分支)。Windows 本机定向结果:`project_write_lock` 18 条、`bridge_write_file` 4 条、`parent_wake` 16 条、`waits_across` 3 条全过;CI(`71e9ad313`)四个 job 全绿,其中 Native shell tests 的首轮失败正是第 ⑤ 条平台判据缺陷。
- 关联文档:`docs/project-memory/shared-memory/pitfalls.md`、`docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md`、Issue #318。
## 2026-09-11 运行视图的进入只认活体预览,退出时统一收尾落盘预览记录
- 背景:用户报"有时候进入项目时看到的是运行界面,而不是资源管理页面"(偶发)。排查定性:**不是"记住上次视图"**(`WorkbenchMode` 无任何持久化),而是**视图派生自业务状态**——工作台有一条 effect「只要有预览 URL 就 `setMode('run')`」,而这个 URL 的初值来自**按项目落盘的 `manifest.preview`**。该记录会与真相长期分叉:本地预览服务器是进程内线程(`preview.rs` 的 `thread::spawn` + loopback listener)、非 Linux 平台端口是随机临时端口(`TcpListener::bind(..., 0)`),客户端退出后记录仍写着 `running`;退出路径当时不 stop 预览、不写 `stopped`(`main.rs` 的 `handle_game_creator_gui_run_event` 只关 Agent server/runner),进入项目时也不核对活体(`get_local_game_preview_status` 只在 `/preview-status` 与「预览状态」按钮里调用)。于是"上一次跑过预览且没显式停止"的项目会被直接丢进一个打不开的运行界面。另有一条反方向失真:同项目重启预览时,`registry.set_running` 返回的旧预览收尾(`record_replaced_preview_stop`)会把刚写进去的 `running` 覆盖成 `stopped`。
- 决策:① 自动切运行只认**本次会话确认过的预览**(`previewOverride`),落盘记录不再触发切换;运行入口(运行 tab / 播放按钮)与 `runAvailable` 不变,"回到仍在运行的预览"从自动变手动点一下。② 进入项目时按**内存 registry 核对活体**(`resolveSessionPreviewOnProjectOpen`):确实 `running` 才作为会话预览(此时自动进运行视图);记录陈旧则复用现役 `stop_local_game_preview` 把落盘记录对齐成 `stopped`,并把会话 manifest 按真相投影,避免运行入口继续拿打不开的 URL 渲染。③ 旧预览收尾**只对另一个项目**写 `stopped`,同项目重启不再覆盖新写的 `running`。④ **退出路径统一收尾**:`preview::stop_local_game_preview_on_exit` 在 `RunEvent::Exit` 上把当前预览真正停掉并写 `stopped`。
- 语义变更(用户已确认接受):**关窗不再保留预览**——退出时预览被停止、记录写成 `stopped`,下次进项目一律落到资源管理。之所以两者都做(退出写 `stopped` + 进入时活体核对)而不是只加一个 `stale` DTO 状态:进程被强杀 / 断电不走退出路径,记录仍会分叉,必须有一道进入时的核对自愈;反过来只做核对则每次进项目都要多问一次"上次到底停没停",记录本身也没有在自己的生命周期里结束。选择不做 `stale` 字段:它既不解决"谁来判定 stale"(判定仍要活体探测),又要把状态扩到跨端契约与所有消费方。
- 不做什么:不改 `runAvailable` 与 `showRunView` 的门槛(PRD「运行入口不可用时仍允许点击…但不切换状态」继续成立),不改 PRD :122 的"播放即切运行视图 + 启动预览",不给预览加心跳 / 轮询。
- 验证方式:AGC 子集新增两条工作台用例(仅落盘记录为 running → 初始仍是资源管理且运行入口可用;会话预览为 running → 自动进运行视图)与五条 `sessionPreview` 用例(活体优先 / 陈旧记录对齐并投影 / registry 不可读按没有在跑处理 / 记录非 running 时不发多余停止命令 / 无 invoke 零请求);Rust 定向用例三条(同项目重启后记录仍为 running、换项目时旧项目写 stopped、无活体时 stop 对齐陈旧记录)。变异验证:撤掉自动切换的收窄 → 第一条用例红灯;撤掉活体核对(直接信落盘记录)→ `sessionPreview` 两条用例红灯(均已实测)。
- 关联文档:`docs/prd/【AI游戏创作】项目开发工作台PRD-2026-07-20.md`(§4.1 已按当前状态改写)、`docs/project-memory/shared-memory/pitfalls.md`。