合并 origin/master:保留 3D 契约与 provider checkpoint,旧玩法表随主线退役

- server-rs/crates/shared-contracts/src/lib.rs:采用主线的模块裁剪(删掉 cfg(any()) 遗留模块、补上 agc_analytics 与 game_distribution),同时保留本分支的 editor_canvas、model3d 模块与 EDITOR_GENERATION_OPERATION_KINDS 导出
- server-rs/crates/spacetime-module/src/migration.rs:主线删除的 410 行旧玩法表 normalize 段保持删除,保留本分支的 provider_kind / provider_task_id 兼容段与对应用例
- docs/【开发运维】本地开发验证与生产运维-2026-05-15.md:校验清单同时保留 Tripo 3D 生成任务与 editor_background_music_generation / model3d_text_to_model / model3d_image_to_model
- .gitignore:补回本分支新增的 3D 模型文件忽略规则(*.glb / *.gltf 等 12 行),压测数据段随主线一并删除
- 共享记忆:本分支的 3D 决策与踩坑条目保留在主线重排后的 decision-log.md 与 pitfalls.md 中
This commit is contained in:
2026-09-23 19:39:30 +08:00
997 changed files with 56264 additions and 80302 deletions
@@ -51,5 +51,7 @@ AGC 项目开发聊天框当前同时从三处取数据:Direct 回合事件(
- 旧项目磁盘上遗留的 `turn-stream.jsonl` / `tool-calls.jsonl` 保留不动,不迁移、不清理、不再由 DirectProject 聊天框读取。
- 工具卡片的脱敏与截断必须在读取期执行一次,不能因为"原始条目已在磁盘"就把未脱敏内容直接渲染到界面。
- 回合结束语义务必由 `turn.completed` 判定;缺少该事件的残留回合不得被渲染成运行中。
- 「活动回合的唯一判据」约束的是**原生回合**:界面上的「本地已发出、原生还没认领」是投影的展示态(`DirectChatTurn.state = 'awaiting-start'`),由本地在途用户条目身份派生,不构成第二套原生生命周期,也不参与 `turnRunning` 的判定。
- 三层数据流、变量归属与一次发送的时序写在代码里:`apps/ai-game-creator-shell/src/view/project-development/chat/controller/useDirectProjectChatController.ts` 的模块注释;回合三态的定义与判据真值表在 `apps/ai-game-creator-shell/src/view/project-development/chat/conversation/directTurnPresentation.ts` 的 `DirectChatTurnState`。改判据时同步这两处与对应测试。
- 验收证据是端到端行为,不是单元测试:回合进行中杀掉应用进程后重开项目,应看到部分文本与工具卡片按原顺序出现且不显示忙碌;正常结束后重进应与实时渲染一致;文件系统不得再新增 `turn-stream.jsonl` / `tool-calls.jsonl`。
- id 空间已用源码核对:codex-rs `app-server-protocol/src/protocol/thread_history.rs` 中所有工具 item 都是 `id: payload.call_id.clone()`,而 `project.jsonl` 落盘的是原始 response item。真实 app-server 会话核对仍列为运行时验收项。
@@ -0,0 +1,36 @@
# 引用候选由宿主注入
状态:已接受
引用输入区(`ResourceReferenceInput`)不再自己去拿候选。素材清单、Skill 目录、缩略图预览这些项目级或应用级事实全部改由宿主注入:输入区只接受一组「引用 provider」(每种引用一份,暴露触发符、候选、身份解析与正文文本形态;需要为菜单开合拉一次数据的 provider 另有 `onMenuQueryChange`,由输入区在 effect 里回调),宿主按需选择性注入——只注入资源 provider 就只存在 `@`,再注入 Skill provider 才出现 `$`。素材选择面板同时拆成独立组件(`ResourceReferencePicker`),自己拿数据、由宿主渲染,确认后经输入区句柄的 `insertReferences` 交回。
这么改的理由是双向的。留在组件里的读取属于后端副作用,越过了「共享表现组件不拥有后端副作用与正式业务状态」的边界;而「组件自己查 Skill 目录」又让所有宿主无差别获得 `$` 候选,可只有 DirectProject 那条路径会把 `agc_skill_reference` 解析成真 Skill,其余宿主只把它退化成字面文本,形成误导入口。注入之后「没注入就没有这类引用」成为默认,可见性不再需要额外的开关。
## 备选与取舍
- 只把 Skill 目录外移、素材继续留在组件内部:改动更小,但输入区仍要知道资源种类的字段(显示名、版本 scope、可提及过滤),「只认接口」不成立。
- 一个总装 builder 统一产出全部候选:调用方接线更短,但所有种类被焊死在同一层,无法只注入资源、也无法单独测试某一种引用。
- 附件继续留在正文之外(待发送列表):改动更小,但本轮附件会有两份事实源(正文芯片与列表条目),提交时还要再拼一遍;收敛成一份之后,移除语义、上限口径与排队路径都自然归位。
## 影响
- 输入区删除 `versions` / `activeVersionId` / `showTriggerButton` / `onReferencePickerOpen` / `skills`,`assets` 被注入的 provider 取代;`projectPath` 只保留给输入区自己的润色链路。
- 输入区与宿主之间只留三个通用接缝:`providers`(引用来源)、`inputActions`(操作排里的宿主控件,例如 `@` 触发钮)、`submitSuppressed`(宿主浮层打开时 Enter 让位)。引用种类一个都不进输入区。
- `provider.fuzzyLookup` 必须是纯函数(输入区在渲染阶段调它取候选);懒加载走 provider 的可选 `onMenuQueryChange(query)`,由输入区在 `useEffect` 里回调,菜单关闭时收到 `null`。Skill 目录因此第一次敲出 `$` 时才读,渲染期不再有 invokes 或 ref 写入。
- 附件并入 `ChatReference`,编辑器收敛为单一引用节点类型,附件 chip 的 DOM 契约逐字保留;附件导入成功后以芯片进入正文,失败不插入;控制器不再持有附件数组,`MAX_CHAT_COMPOSER_ATTACHMENTS` 改为按草稿中的附件芯片数计算,导入进行中禁止发送。
- 用户可见行为保持不变,唯一例外是已裁决的缺陷修复:非 DirectProject 宿主不再出现 `$` Skill 候选。
## 修订(2026-09-22):粘贴解析需要第二道只读缝
粘贴进来的纯文本要按同一套引用文本语法反解析回正文芯片,因此 provider 的两个查询能力按「模糊 / 精确」分开命名,各自说清自己的语义:
- `fuzzyLookup(query)`:候选菜单那条路——按 query 做包含匹配、大小写不敏感,并在 provider 内部截断到候选上限。名字写明它是模糊的,避免被拿去反查 token。
- `lookup()`:精确查找那条路——某一刻 provider 真正能解析出的全部引用,不做模糊过滤、不截断,与菜单共用同一份候选来源;仍是纯函数,由输入区在粘贴事件里同步调用,数据没到就是空数组。
这不推翻本 ADR 的懒加载结论:`onMenuQueryChange` 仍是唯一的懒加载入口,粘贴解析**只用此刻就绪的候选**,不等待、不补读。Skill 目录因此还是「用户第一次敲出 `$` 才读」——冷启动时粘贴 `$名称` 就按字面文本保留(看得见、不是猜错),不为了粘贴去提前读盘。
附件与运行画面区域仍是静默 provider:它们没有候选,所以粘贴解析不认 `@附件名` / `@区域标签`,这两类 token 粘贴时逐字保留(附件与运行区域的身份来自文件与 run,纯文本重建不出来)。
歧义口径:同一个 token 对应多条引用身份(同名素材)时一律按文本保留;解析只认显示名逐字一致(不认扩展名、resourceId、大小写变体),未命中的 token 与其余文字逐字保留。
(本次把上一条同名决策里的 `match` / `candidates` 改名为 `fuzzyLookup` / `lookup`,语义不变;旧名不再保留。)
@@ -0,0 +1,34 @@
# 【ADR】退役AGC项目对话斜杠命令与终端swarm入口-2026-09-22
状态:已接受
## 背景
AGC(`apps/ai-game-creator-shell`)的项目对话曾把用户能力挂在「聊天输入 `/<cmd>`」上:除真正带执行语义的 `/history` 外,还累积了几十个只生成后续草稿或只读摘要的命令(`/brief`、`/status`、`/read`、`/trace`、`/art`、`/export`、`/cover`、`/remember` 等)。无 GUI 的终端 swarm chat 入口 `--swarm-chat` 另有一套控制命令(`/help`、`/agents`、`/status`、`/history`、`/compact`、`/resume`、`/goal`、`/quit`)。
这些命令的实际状态是:正式对话面已经换成 DirectProject 单容器,斜杠命令列表不再有渲染入口,`/history` 之外没有任何现役调用方;但命令字面量仍分散在控制器分支、`chatPromptPolish` 的 `/` 前缀绕过、命令参数校验文案、`projectSummaryConstants` 的命令清单、只服务已退役摘要面板的 `project-summary/*Summaries.ts`、构建期门禁 `scripts/check-config.mjs`、无人调用的 Tauri 能力清单命令、`swarm_cli` 的终端输入解析与帮助输出,以及多份权威文档的承诺里。
保留它的代价持续存在:每次调整对话形态都要同步维护这套死词汇表和它的门禁,而且「命令」一词在 AGC 里同时指用户斜杠命令与项目权限命令 id(`GAME_CREATION_APP_COMMANDS`)两件事,术语歧义会直接误导后续改动。
## 决策
- 斜杠命令语义整体退役,按「从未存在」处理:不保留入口、不做兼容提示、不写 tombstone,实现、专属测试、构建期门禁条目与文档承诺一并删除,历史由 Git 保存。
- 删除项:Direct 聊天的 `/history` 精确匹配分支与 `reloadHistory`;`chatPromptPolish` 的 `/` 前缀绕过;`chatCommandMetadata`、`projectSummaryConstants.chatCommandHelp`、`memoryCommands.parseRememberInput` 等命令清单与参数解析;只服务退役 Supervisor 摘要面板、零外部调用的 `project-summary/*Summaries.ts` 与 `agentTrace.ts`;草稿回填死链(前端 `agentPresentation.ts` 的草稿推导与 Rust `suggested_canvas_tool_call`);无人调用的 Tauri 命令 `get_game_creation_agent_capabilities` 与 `get_limited_local_commands`;钉住上述字符串的门禁条目与专属测试。
- 终端 swarm chat 入口连带其命令层整体退役:`--swarm-chat`、`src-tauri/src/swarm_cli.rs` 与整个 `swarm_cli/` 目录(`/help`、`/agents`、`/status`、`/history`、`/compact`、`/resume`、`/goal`、`/quit` 的解析、帮助输出、turn 派发、观察器、报告与专属测试)一并删除;`SwarmChatFlow`、`SwarmTurnObservation`、`SwarmTurnOutcome::Quit`、`SwarmConfirmationResolution::Quit`、`SWARM_TURN_*_ERROR`、只服务终端命令的 `agent.compact` / `agent.resume` / `agent.run_status` 校验(`swarm_cli/input.rs` 内那份)与 `print_runtime_response_stream_status` 也随之消失;同名权限 id 在现役 Tauri 命令与 Runtime 生命周期上的门禁保持不动。只服务终端交互内核的 `agent/interaction.rs` 整层(`AgentInteractionAction`、tool registry、`game_creator_agent_uses_interaction_kernel`、`decide_game_creator_agent_interaction_turn_for_session_at`、`AgentInteractionProviderStreamSink`)同样删除;其上仅存的自然语言 steer 决策路径 `decide_game_creator_agent_runtime_steer_at` 在收尾复查后一并删除(见「影响」的复查收尾)。
- 保留项(它们不是斜杠命令):命令 id 注册表 `GAME_CREATION_APP_COMMANDS` 与 `GameCreationAppPermission`(Rust 运行期项目权限策略的词汇表;App 前端只用 `GameCreationAppCommandDescriptor` 类型表达权限判定与审计日志粒度,数组本体由 Rust 策略路径与跨语言一致性门禁消费)、`needsInitializedChatProject` 与项目权限策略链路、Rust 侧路径与路由的 `/` 前缀校验、`--agent-*` CLI 控制命令(Goal、steer、cancel、retry、context compact、resume、状态查询)与 Tauri IPC 注册。
- 应用内项目对话的输入只剩自然语言回合(外加 `@` 素材引用与附件);需要动作时由 Runtime 工具、确认卡和既有 CLI 控制命令承接,不由聊天文本解析控制词。
- 需要显式控制时改用现有 `--agent-*` CLI 命令:手动压缩是 `--agent-context-compact`,恢复扫描是 `--agent-resume`,Goal 生命周期是 `--agent-goal-*`。
## 备选方案与取舍
1. **只删正式用户窗口的入口,保留解析层**:看似省事,但命令字面量与分支继续存在,新对话形态仍要绕过它们,正是本次要消除的持续维护成本。
2. **保留 `/history` 作为唯一命令**:它确实是唯一有执行语义的入口,但保留一个精确匹配的 `/` 语法就要求保留前缀绕过判断、命令被发现与文档承诺的整套口径;DirectProject 的历史重读改由重新进入对话/重新订阅自然完成,不需要用户输入控制词。
3. **加兼容层(识别到已知命令时给提示或忽略)**:等于把死词汇表永久固化在解析层,与「按从未存在处理」相反,且会长期占据用户可见面。
## 影响
- 术语收敛:AGC 里的「命令」此后指项目内部命令 id 与权限位,「斜杠命令」作为已退役说法不再出现在权威文档与代码注释中。
- 应用内项目对话的可见行为不变:正式对话面本来就不渲染命令列表,`/history` 之外没有可执行路径;删除后唯一的用户可见差异是输入以 `/` 开头时按普通文本处理。终端侧不再有 swarm chat 入口,也没有任何斜杠命令面。
- 删除范围包含构建期门禁条目,因此不得为退役概念新增守卫测试或断言残留字符串的 check 条目;防止概念回归依靠架构边界(没有解析层可写)而不是字符串钉桩。
- 复查收尾(同一决定的后续提交):配置向导里指向已删 npm 脚本的 `test:chat` 调用与失去含义的 `--configure-only` 开关、App 内 `请先用 /project …` 用户文案、`interaction.json` 中只服务已删交互内核的 7 个 prompt 键、harness 里因调用方被删而零引用的死导出,以及过期注释一并清理;无前端调用方的 Tauri 命令 `start_game_creator_agent_runtime_task` 随本次清理删除(`main.rs` 注册与 `check-config.mjs` 条目同步移除);`steer_game_creator_agent_runtime_task` 同时是 `decide_game_creator_agent_runtime_steer_at` 的唯一非测试入口,删掉该命令后整条 LLM steer 判定链(`agent/interaction.rs`、`runtime.interrupt_for_steer_decision`、`agent.runtime.steer_decision` 持久记录、`steer_decision_*` prompt 键与只测该链的专属用例)一并退役。
- `.agent/logs/command.log`、项目权限确认卡与 `commandRuns` 属于项目命令审计,继续保留,不受本次退役影响。