From ab52805d3c406aa65a164e623ba5376e2830b009 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=BE=B7=E5=AE=87?= Date: Mon, 21 Sep 2026 14:11:18 +0800 Subject: [PATCH] =?UTF-8?q?=E6=94=B6=E5=8F=A3DirectProject=E8=BE=93?= =?UTF-8?q?=E5=85=A5=E5=8C=BAcanonical=20content=E5=94=AF=E4=B8=80?= =?UTF-8?q?=E4=BA=8B=E5=AE=9E=E6=BA=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 删除输入区 text/references 双轨草稿状态 Direct 与 Planning 改为从 Lexical content 读取 润色保留 chip 并保护附件 canonical content 增加 legacy content DTO 单向翻译与附件回归测试 补充 DirectProject composer canonical content 实施计划 --- .../chat/DirectProjectChatView.tsx | 6 +- .../useDirectProjectChatController.ts | 3 - ...ect composer canonical content闭环-2026-09-21.md | 55 +++++++++++++++++++ ...ct composer canonical content闭环-2026-09-21.md | 54 ++++++++++++++++++ 4 files changed, 110 insertions(+), 8 deletions(-) create mode 100644 docs/project-memory/plans/【实施计划】DirectProject composer canonical content闭环-2026-09-21.md create mode 100644 docs/project-memory/plans/【里程碑】DirectProject composer canonical content闭环-2026-09-21.md diff --git a/apps/ai-game-creator-shell/src/view/project-development/chat/DirectProjectChatView.tsx b/apps/ai-game-creator-shell/src/view/project-development/chat/DirectProjectChatView.tsx index bd51ea53a..4c7dcba9f 100644 --- a/apps/ai-game-creator-shell/src/view/project-development/chat/DirectProjectChatView.tsx +++ b/apps/ai-game-creator-shell/src/view/project-development/chat/DirectProjectChatView.tsx @@ -5,10 +5,7 @@ import { useEffect, useImperativeHandle, useRef, useState } from 'react'; import { AGENT_CHAT_SCROLL_BOTTOM_THRESHOLD } from '../../../app/constants'; import { claimInitialTurnForPage } from '../../../app/initialTurnClaims'; import { projectNameFromPath } from '../../../features/agent-runtime'; -import { - directCodexContentToPromptText, - directCodexUserItemFromContent, -} from '../../../features/project-workspace/resourceReferences'; +import { directCodexUserItemFromContent } from '../../../features/project-workspace/resourceReferences'; import { type ApprovalMode, approvalModeLabel } from '../approvalMode'; import { ApprovalModeDialog } from '../ApprovalModeDialog'; import { DirectProjectChatHeader } from './components/DirectProjectChatHeader/DirectProjectChatHeader'; @@ -163,7 +160,6 @@ export function DirectProjectChatView({ ); shouldFollowLatestRef.current = true; startInitialTurn({ - prompt: directCodexContentToPromptText(userItem.content, assets), // 首轮那条用户消息按入口原话展示,引用/附件仍按 canonical content 发给运行时。 messageText: directInitialTurnText(initialTurn.content), clientTurnId, diff --git a/apps/ai-game-creator-shell/src/view/project-development/chat/controller/useDirectProjectChatController.ts b/apps/ai-game-creator-shell/src/view/project-development/chat/controller/useDirectProjectChatController.ts index 1122b7d59..1576be7e5 100644 --- a/apps/ai-game-creator-shell/src/view/project-development/chat/controller/useDirectProjectChatController.ts +++ b/apps/ai-game-creator-shell/src/view/project-development/chat/controller/useDirectProjectChatController.ts @@ -223,7 +223,6 @@ export function useDirectProjectChatController({ if (rest.length === 0) setComposerNotice(''); startTurn({ clientTurnId: next.clientTurnId, - prompt: directCodexContentToPromptText(next.userItem.content, assets), userItem: next.userItem, }); } @@ -394,7 +393,6 @@ export function useDirectProjectChatController({ directCodexConversationMessageId(clientTurnId, 'user'), ); const turn: DirectProjectTurnInput = { - prompt: directCodexContentToPromptText(userItem.content, assets), clientTurnId, userItem, }; @@ -498,7 +496,6 @@ export function useDirectProjectChatController({ await withDirectCodexSessionRefresh(() => invoke('chat_with_game_creator_direct_codex', { projectPath: nextProjectPath, - prompt: input.prompt, clientTurnId: input.clientTurnId, userItem: input.userItem, ...(input.creationType ? { creationType: input.creationType } : {}), diff --git a/docs/project-memory/plans/【实施计划】DirectProject composer canonical content闭环-2026-09-21.md b/docs/project-memory/plans/【实施计划】DirectProject composer canonical content闭环-2026-09-21.md new file mode 100644 index 000000000..501900fe8 --- /dev/null +++ b/docs/project-memory/plans/【实施计划】DirectProject composer canonical content闭环-2026-09-21.md @@ -0,0 +1,55 @@ +# 【实施计划】DirectProject composer canonical content 闭环 + +| 字段 | 值 | +| --------- | --- | +| Milestone | `docs/project-memory/plans/【里程碑】DirectProject composer canonical content闭环-2026-09-21.md` | +| Status | planned | +| Owner | Codex | + +## 交付结果 + +删除 Direct composer 的 `text + references` 回写事实源,改为完整 `content[]` 的单向读写;同时修复附件在 editor root 重建、润色回填和提交合并中的丢失窗口。Planning V2 / 快速编辑只保留明确的非 Direct adapter,不进入 Direct canonical 发送路径。 + +## 实施顺序 + +1. **锁定当前失败面**:补一个最小回归测试,先证明 `applyDraftToRoot` 对含 `agc_attachment_reference` 的 root 重建会丢附件,并记录 Direct 当前的三份并行状态与附件 sidecar 合并点。 +2. **收窄编辑器接口**:把 `ChatComposerDraft` 改成 content-only;将 `ResourceReferenceInput` 的受控恢复改为 `initialContent` / `content` 语义,删除 Direct 使用的 `value` + `references` 同步 effect。保留 `ChatReference` 仅用于 picker 节点创建和只读投影。 +3. **替换所有 root 写入**:删除 `buildDraftSegments` / `applyDraftToRoot` 的 Direct 调用;提供一个完整的 `applyContentToRoot`(或同等深模块接口),保留文本换写、引用和附件的顺序与字段。AI 润色改为“读取 content → 只替换文本 part 的受控变换 → 按原序写回 content”,禁止用 prompt 文本重新解析 chip。 +4. **收口 Direct controller**:以 `content[]` 作为草稿事实源;上传完成的附件在一个明确的 `append`/`merge` seam 合并,不再长期维护 `chatInput`、`chatReferences`、`chatContent` 三份状态。prompt 只在调用边界派生,队列和策略重试保留原 user item。 +5. **隔离非 Direct caller**:Planning V2 和资源快速编辑按各自需求选择 content-only 或独立文本 adapter;不得为了兼容它们把旧字段放回共享 Direct 接口。若快速编辑只需要文本,使用不承诺引用/附件语义的窄接口。 +6. **补齐验收证据并清理**:删除无调用方的旧 helper、字段和注释;更新测试 fixture、专题文档和本计划状态。完成后把稳定结论合并回功能说明/共享记忆,并删除已完成的临时计划。 + +## 重点修改边界 + +- `apps/ai-game-creator-shell/src/features/project-workspace/ResourceReferenceInput.tsx` +- `apps/ai-game-creator-shell/src/features/project-workspace/resourceReferences.ts` +- `apps/ai-game-creator-shell/src/features/project-workspace/usePromptPolish.ts` / `chatPromptPolish.ts`(仅在需要支持 content 变换时) +- `apps/ai-game-creator-shell/src/view/project-development/chat/controller/useDirectProjectChatController.ts` +- `apps/ai-game-creator-shell/src/view/project-development/chat/components/DirectProjectComposer/` +- `apps/ai-game-creator-shell/src/view/project-development/planning/PlanningChatView.tsx` 与 `App.tsx` 中 Planning adapter +- `apps/ai-game-creator-shell/src/view/project-development/index.tsx` 的快速编辑输入 adapter +- 对应 `resourceReferences`、`resourceReferenceInput`、`chatPromptPolish`、Direct composer/appSurface 测试 + +## 关键不变量 + +- `content[]` 是唯一可发送事实;任何 `prompt`、label、chip 文案都是派生值。 +- `agc_attachment_reference` 不属于 `ChatReference`,不能借 `references` 传递。 +- 文本润色只允许改变文本 part;若产品决定重排/删除引用,必须显式产生新的 content 变换,不得通过字符串 token 猜测。 +- Direct 初始首轮、普通提交、队列、权限确认重试都使用同一个 `DirectCodexUserItem` 语义。 + +## 验证命令 + +```bash +npm run test -- apps/ai-game-creator-shell/tests/resourceReferences.test.ts apps/ai-game-creator-shell/tests/resourceReferenceInput.test.tsx apps/ai-game-creator-shell/tests/chatPromptPolish.test.tsx apps/ai-game-creator-shell/tests/appSurface/chat-composer.suite.ts apps/ai-game-creator-shell/tests/appSurface/project-conversation.suite.ts +npm run ai-game-creator-shell:typecheck +npm run check:encoding +npm run check:doc-index +git diff --check +``` + +## 风险与回滚点 + +- `ResourceReferenceInput` 当前同时服务 Direct、Planning、快速编辑;先建立 adapter seam,再删除旧 props,避免一次性把策划链路打坏。 +- 附件目前在 Direct controller 里作为待发 sidecar,若直接移入编辑器必须同步处理上传失败、移除、清空和项目切换;任何阶段都不能把附件只留在展示 chip 而未进入 user item。 +- 润色服务只返回文本,不能让其响应覆盖原 content;失败时保留原 content。回滚使用 Git revert,不增加运行时开关或兼容分支。 +- 工作树已有用户未提交修改,实施时只 stage 本里程碑涉及的 hunk。 diff --git a/docs/project-memory/plans/【里程碑】DirectProject composer canonical content闭环-2026-09-21.md b/docs/project-memory/plans/【里程碑】DirectProject composer canonical content闭环-2026-09-21.md new file mode 100644 index 000000000..022560c43 --- /dev/null +++ b/docs/project-memory/plans/【里程碑】DirectProject composer canonical content闭环-2026-09-21.md @@ -0,0 +1,54 @@ +# 【里程碑】DirectProject composer canonical content 闭环 + +| 字段 | 值 | +| ----------- | --- | +| Version | 1.0 | +| Status | planned | +| Date | 2026-09-21 | +| Parent Spec | `docs/【功能说明】AGC聊天素材引用-2026-09-08.md` | +| Related ADR | `docs/adr/【ADR】DirectProject独立聊天容器与工作台钱包布局-2026-09-18.md` | +| Supersedes | `docs/project-memory/plans/【实施计划】DirectProject canonical content严格边界-2026-09-16.md` 的已实现边界清理,补齐其遗漏的编辑器回写路径 | + +## 问题确认 + +上一里程碑把 Direct 发送、队列和 Rust user item 收口到了 `content[]`,但输入编辑器仍保留一条旧的派生状态接口: + +- `ChatComposerDraft` 仍包含 `text`、`references`、`content` 三个字段。 +- `ResourceReferenceInput` 仍接收 `value` + `references`,并在受控同步 effect 中调用 `applyDraftToRoot(value, references)`。 +- `replaceText` 与 `usePromptPolish` 的 `applyPromptText` 也调用 `applyDraftToRoot(text, current.references)`。 +- `applyDraftToRoot` 只重建文本和 `ChatReference` 节点,不能表达 `agc_attachment_reference`,会在任何包含附件节点的 editor root 重建中丢附件。 +- Direct controller 仍把同一份 `content` 拆成 `chatContent`、`chatInput`、`chatReferences` 三份状态;提交时又把附件 sidecar 追加回 `content`,导致编辑器 canonical content 与本轮完整 canonical user content 不是同一份事实源。 + +## 目标 + +让 DirectProject 的 composer 从读取、润色、受控恢复、队列、附件到提交都只围绕一份有序 `content[]` 运作: + +1. 编辑器对外草稿类型只有 `content`。 +2. 文本展示、提醒、队列 chip 和 prompt 参数全部由 `content[]` 派生。 +3. 任意编辑器回写只能接收完整 `content[]`;不能用文本和引用列表重建 root。 +4. `agc_attachment_reference` 在恢复、润色回写、队列和提交中保持顺序和值,不得丢失、搬移或重复。 +5. DirectProject 不再维护 `chatInput` / `chatReferences` / `chatContent` 并行事实源;待发上传附件也必须在进入提交前显式合并为同一 canonical content。 + +## 边界与非目标 + +- 保留 `directCodexContentToPromptText` 作为只读展示投影,不把它反向解析成 content。 +- 保留 `ChatReference` 作为 picker/UI 的候选与节点显示模型;它不是 Direct 草稿或发送模型。 +- Planning V2 与资源快速编辑若仍需要文本 props,只能通过明确的 adapter/独立表象接口接入,不能让这些 legacy props 继续成为 Direct composer 的接口。 +- 不修改 Rust user item schema、SpacetimeDB、HTTP API 或附件协议字段。 +- 不引入兼容 alias、`text + references → content` fallback、隐式附件 sidecar 拼接或双写状态。 + +## 验收标准 + +- `ChatComposerDraft`、`ResourceReferenceInput` 的 Direct 使用面和 Direct composer props 不再暴露 `text` / `references` 双轨草稿。 +- 代码中不存在 Direct 路径对 `applyDraftToRoot(value, references)`、`replaceText(text)` 或等价 legacy 重建器的调用。 +- `content[]` 中交错的文本、资源引用、运行区域引用、Skill 和附件经过:初始恢复 → 读取 → 润色回写 → 再读取,顺序和值完全一致。 +- 附件-only、文本+附件、资源引用+附件、文本+资源引用+附件四类提交均只生成一份 canonical user item,附件字段完整保留。 +- 队列入队、出队、策略确认重试复用同一份 `userItem.content`,不从 prompt 文本重建。 +- Direct 提交清理草稿后,待发附件和 editor content 同时清空;失败或取消不会悄悄丢失待发附件。 +- Planning/快速编辑的迁移或隔离有明确类型接口和定向测试,不再借 Direct 的双轨 props。 + +## 证据要求 + +- ResourceReferenceInput 纯投影/回写测试:包含附件节点的 round-trip 与润色回写不丢附件。 +- Direct composer appSurface:四类 content 组合、队列、策略重试及清理语义。 +- 类型检查、定向 Vitest、`npm run check:encoding`、`npm run check:doc-index`、`git diff --check`。