合并 origin/master(33 个提交):DirectProject 入队化与事件投影
Project CI / AI game creator shell Rust crates (pull_request) Successful in 1m30s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Failing after 1m54s
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 1m57s
Project CI / Frontend tests (pull_request) Successful in 3m39s
Project CI / Backend tests (pull_request) Successful in 6m13s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Failing after 7m59s
Project CI / Repository checks (pull_request) Successful in 3m29s
Project CI / Native shell tests (pull_request) Successful in 8m0s
Project CI / AI game creator shell web tests (pull_request) Successful in 2m58s
Project CI / AI game creator shell Rust crates (pull_request) Successful in 1m30s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Failing after 1m54s
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 1m57s
Project CI / Frontend tests (pull_request) Successful in 3m39s
Project CI / Backend tests (pull_request) Successful in 6m13s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Failing after 7m59s
Project CI / Repository checks (pull_request) Successful in 3m29s
Project CI / Native shell tests (pull_request) Successful in 8m0s
Project CI / AI game creator shell web tests (pull_request) Successful in 2m58s
- 合并 origin/master(425fbcf63..9ee34809b),唯一冲突是共享决策日志两侧各插条目,取「两侧并存」 - 本轮 master 未触及随包资源声明、准备步骤与三份 tauri 配置,build.rs 只读校验保持不变 - 决策日志与踩坑记录同步两侧条目;docs/README.md 与项目索引按 master 收敛 - 构建期重新生成的 ts-rs 绑定经 prettier 归一后与 master 逐字节一致(合并未丢类型) - 校验:cargo test --no-run 全 target 通过、AGC 应用 vitest 191 files / 1889 tests passed、check-package-layout、prepare-bundled-resources 18 passed、check-config、check:encoding、prettier、cargo fmt --check 全绿
This commit is contained in:
@@ -0,0 +1,253 @@
|
||||
# DirectProject 命令入队化与待发消息队列归宿主实施计划
|
||||
|
||||
更新时间:`2026-09-24`
|
||||
|
||||
状态:**第 0–7 步全部落地**(2026-09-30 修订并收口)
|
||||
|
||||
> 文件路径口径(2026-09-30):本文里 2026-09-24 写的 `direct_thread_manager.rs` / `direct_thread_wire.rs` /
|
||||
> `direct_thread_queue.rs` / `direct_turn_dispatch.rs` 现在分别是 `agent/thread_manager/{mod,wire,queue,dispatch}.rs`,
|
||||
> 类型与函数去掉 `Direct` 前缀(`ThreadEvent`、`PendingTurn`、`subscribe_thread` …,见提交 `f121257cc`)。
|
||||
> 同一次归位里 thread_manager 之外的几个函数也去掉了 `direct_` 前缀:`direct_active_turn_id_at` → `active_turn_id_at`、
|
||||
> `direct_stale_turn_for_release` → `stale_turn_for_release`、`kick_direct_queue_dispatch` → `kick_queue_dispatch`、
|
||||
> `complete_direct_thread_turn` → `complete_turn`。
|
||||
|
||||
设计口径见 [`【ADR】DirectProject命令入队化与待发消息队列归宿主-2026-09-24`](../adr/【ADR】DirectProject命令入队化与待发消息队列归宿主-2026-09-24.md)。
|
||||
本文件只排实施顺序、不变式与验收,不重复设计理由。
|
||||
|
||||
## 落地进度
|
||||
|
||||
| 步骤 | 状态 | 落地说明 |
|
||||
| --- | --- | --- |
|
||||
| 第 0 步 词表切换 | 已落地 | `rg "接单\|拒单"` 只剩 `direct_runtime/mod.rs` 对旧 ADR 文件名的引用(链接完整性,故意保留)与 `codex_app_server/mod.rs` 的一处假阳性 |
|
||||
| 第 1 步 命令 = 入队 | 已落地 | `enqueue_direct_codex_turn`(`+_typed`);队列条目落在 `agent/direct_thread_queue.rs`(第 6 步改为事件投影,不再另存 `prompt` / canonical 形状) |
|
||||
| 第 2 步 队列归 Thread Manager | 已落地 | `StoredEvent.pending` 产物字段 + `enqueue_pending_turn` / `remove_pending_turn` / `claim_pending_turn`;放行在 `agent/direct_turn_dispatch.rs`(`kick_direct_queue_dispatch` + `DirectTurnReservation`) |
|
||||
| 第 3 步 前端收口 | 已落地 | 见下面「第 3 步的落地细则」 |
|
||||
| 第 4 步 CLI 与夹具退役 | 已落地 | 删 `CliCommand::DirectCodexChat`(变体 / `project_path_mut` / 解析 / 派发)、`run_direct_game_creator_turn_at` 一对包装函数与夹具脚本;顺带删掉只剩测试在用的 `direct_turn_error_boundary_text`(判据只剩 `direct_turn_enqueue_failure` 一处),三条边界测试改打 `direct_turn_enqueue_failure(...).message`。**`TurnAlreadyRunning` 挪到第 5 步**(它最后一个生产点在调用身份守卫里) |
|
||||
| 第 5 步 守卫清理 | 已落地 | 五个身份读者改读 `direct_active_turn_id_at`;删 `DirectTaonierActiveInvocationGuard` / `DirectActiveTurnView` / 只读探测与 `release_stale_direct_taonier_active_invocation`(改 `direct_stale_turn_for_release` 只做前置校验);删 `DirectTurnError::TurnAlreadyRunning` 与前端分支 / 生成绑定;补前置判据用例 |
|
||||
| 第 6 步 条目不再另存产物 | 已落地 | `PendingTurn` 就是 `queue.enqueued` 的投影(`from_event` / `enqueued_event` 往返),删掉 `canonical_user_item` 与 `prompt` 两个宿主私有产物字段;`StoredEvent` 上没有队列条目字段(`pending_turns` 一次事件折叠);引用解析文本落 `AgcResourceReference.resolved_text` 并随条目持久化;`canonical` 与 `prompt` 放行时重投影(提交 `15a495dc2` / `28b64cf25`) |
|
||||
| 第 7 步 埋点成绩归宿主 | 已落地 | 删候选 / settle 两阶段与 `analyticsAttemptId`(`Request::DirectCandidate` / `Request::Settle` / `settle` / `pending_runs` / `settle_direct_run_analytics` / 前端句柄表与结算 effect);放行读一次 `identity_generation`、终态再读一次后直写 `Request::Terminal`(提交 `0270cd601`) |
|
||||
|
||||
## 第 0 步:词表切换(与代码同批,不单独提交)
|
||||
|
||||
「接单 / 拒单」退役,改成「入队 / 入队失败 / 放行」。逐处按角色改,**不做字面替换**:
|
||||
|
||||
| 旧写法 | 新写法 |
|
||||
| --- | --- |
|
||||
| 命令的成功 / 失败(接单 / 拒单) | 入队 / 入队失败 |
|
||||
| 这一轮真正成立的那一刻(接单) | 放行 |
|
||||
| 接单前的检查 | 入队时的检查 |
|
||||
| 接单成立 ⇔ 事件流里有开始有结束 | **放行成立** ⇔ 事件流里有开始有结束 |
|
||||
| 接单后的失败都是回合失败 | **放行后**的失败都是回合失败 |
|
||||
|
||||
三类出现点必须分开处理(全仓 384 处):
|
||||
|
||||
1. DirectProject 域(文档、注释、标识符、测试名):按上表改。主要落点 `docs/adr/【ADR】DirectProject命令接单化-2026-09-23.md`、
|
||||
`docs/technical/【实施计划】DirectProject命令接单化-2026-09-23.md`、
|
||||
`docs/technical/【技术方案】DirectProject Codex原始历史与异常恢复-2026-09-04.md`、`CONTEXT.md`、
|
||||
`agent/direct_runtime/user_input.rs`、`agent/direct_turn_accept.rs`、`agent/direct_turn_error.rs`、`agent/direct_thread_manager.rs`、
|
||||
`chat/controller/useDirectProjectChatController.ts`、`chat/conversation/directTurnPresentation.ts`、`tests/appSurface/chat-composer.suite.ts`。
|
||||
2. `features/agent-runtime` 域的「拒单文案」(`model.ts`、`tests/agentRuntimeModel.test.ts`):那里没有队列,
|
||||
改成「请求被拒 / 拒绝」,不要写成「入队失败」。
|
||||
3. 假阳性:`单测` 这类词不动(如 `server-rs/crates/api-server/src/editor_project.rs`)。
|
||||
|
||||
标识符同批改(映射表):
|
||||
|
||||
| 旧 | 新 |
|
||||
| --- | --- |
|
||||
| `chat_with_game_creator_direct_codex` | `enqueue_direct_codex_turn` |
|
||||
| `DirectTurnRejection`(ts-rs 导出) | `DirectTurnEnqueueFailure` |
|
||||
| 前端 `readDirectTurnRejection` / `directTurnRejectionNotice*` | `readDirectTurnEnqueueFailure` / `directTurnEnqueueFailureNotice*` |
|
||||
| Rust `direct_turn_rejection` | `direct_turn_enqueue_failure` |
|
||||
| `DirectTurnReservation::accept` | `DirectTurnReservation::start` |
|
||||
| `accept_direct_thread_turn` / `DirectThreadManager::accept_turn` | `start_direct_thread_turn` / `start_turn` |
|
||||
| `agent/direct_turn_accept.rs` | `agent/direct_turn_dispatch.rs` |
|
||||
| `DirectTurnError::TurnAlreadyRunning` | 删除(第 4 步判据确认零调用方后) |
|
||||
|
||||
两份已接受的 ADR 保留正文与文件名,只在顶部加一行词表注记,避免正文里的旧词变成假命题。
|
||||
|
||||
验收:`rg -n "接单|拒单"` 在 DirectProject 域与 `agent-runtime` 域均为 0;生成绑定重跑(`cargo test export_bindings`)后
|
||||
`git diff` 只剩映射表内的改动。
|
||||
|
||||
## 第 1 步:命令 = 入队(Rust)
|
||||
|
||||
改动点:
|
||||
|
||||
- `agent/direct_runtime/user_input.rs`:把命令主体拆成两半。
|
||||
**入队侧**:`clientTurnId` 校验 → 工作流恢复 → 用户条目校验 → prompt 投影 → 前置条件 → 容量预判 → 工程准备 → 入队;
|
||||
任何一步失败返回 typed 入队失败。容量预判只是一次提前的快速失败,权威判据仍在入队临界区里(工程准备是分钟级、会
|
||||
在磁盘留产物的活,满了就不该先做它)。**放行侧**(第 2 步)从占用登记起。
|
||||
其中"工作流恢复"只在**真的需要恢复**时才去取整轮任务锁(`direct-codex-art`):正在跑的那一轮整包重生成整轮持有
|
||||
它,入队无条件取锁会先同步阻塞约 1 秒再被拒(见 ADR §6"入队不取整轮持有的锁")。
|
||||
- 队列条目(宿主侧产物,只在内存):`PendingDirectTurn { client_turn_id, user_item: Value, prompt: String, creation_type: Option<String>, at: u64 }`。
|
||||
`prompt` 与 `creation_type` 是入队检查的产物,放行不再重算;事件里**不带** `prompt`。
|
||||
- `agent/direct_thread_manager.rs`:
|
||||
- `MAX_PENDING_DIRECT_TURNS = 5` 落在 Rust,只数在队条目;满队 → typed 入队失败。
|
||||
- 队列的成员与顺序**就是事件列表本身**:宿主侧产物挂在对应的 `queue.enqueued` 事件上(`StoredEvent` 增加一个非序列化的
|
||||
可选产物字段),不另建队列表。
|
||||
- `observe_event`:`queue.enqueued` 在队期间**不可回收**;`queue.removed` 可回收,并把同 `clientTurnId` 的 enqueued 标记为可回收。
|
||||
`is_bootstrap_event` 不改——live-set bootstrap 因此自动把当前队列交给新订阅者。
|
||||
- 入队幂等:判重范围「在队 ∪ 正在跑的那一轮」,重复入队返回同一次成功。
|
||||
- `remove_direct_project_pending_turn(project_path, client_turn_id)`:typed 结果枚举 `Removed | AlreadyDispatched | NotFound`,
|
||||
在临界区里判「是否仍未被认领」。
|
||||
- 线上形状:`DirectThreadEvent::QueueEnqueued { client_turn_id, user_item, creation_type?, at }`(`queue.enqueued`)、
|
||||
`QueueRemoved { client_turn_id, reason: DirectQueueRemovalReason }`(`queue.removed`),
|
||||
`DirectQueueRemovalReason` 是 ts-rs 导出的 typed 枚举 `cancelled | dispatched`。
|
||||
|
||||
不变式:入队不登记占用、不落盘、不发回合事件、不起 codex;入队失败不写用户条目、不产生事件。
|
||||
|
||||
## 第 2 步:放行与 kick
|
||||
|
||||
- `start_direct_thread_turn`:同一个临界区里「取队首 → 占用登记 → `turn.started` + `queue.removed{dispatched}`」;
|
||||
之后落盘用户条目 → 下发用户条目 → spawn 整轮(顺序与今天的接单后半段一致,`accept` 必须早于落盘与 `turn/start`)。
|
||||
- `kick_direct_queue_dispatch(thread_id)`:幂等;在临界区里判「无占用 + 队首存在 + 未被认领」,认领后 spawn 放行任务。
|
||||
调用点三个:回合任务收尾(正常 / 失败共用)、中止路径、入队之后。
|
||||
- panic 兜底:回合任务里的一个 drop 守卫负责踢一脚,保证任务 panic 或 future 被丢弃时队列不会永久停住。
|
||||
- 入队不取 `DirectTaonierActiveInvocationGuard`;该守卫继续由整轮持有。
|
||||
- 不变式:放行不重跑检查、没有放行失败;放行之后的一切失败都走 `turn.completed.failure`。
|
||||
|
||||
验收(Rust 单测):放行原子性(`queue.removed{dispatched}` 与 `turn.started` 同批、无中间窗口);
|
||||
kick 幂等(并发两次只认领一次);队首在放行后被移除、remove 对已放行条目返回 `AlreadyDispatched`;
|
||||
回合失败 / 中止后队列继续放行下一条;任务 panic 后队列仍能继续;多订阅者游标各自独立时 bootstrap 仍重建完整队列。
|
||||
|
||||
## 第 3 步:前端收口
|
||||
|
||||
### 第 3 步的落地细则
|
||||
|
||||
- 队列投影是新文件 `chat/conversation/directPendingTurns.ts`:只做 `enqueuePendingTurn` /
|
||||
`removePendingTurn` 两个纯函数,**不判上限、不排期、不排序**;上限与认领顺序只在宿主。
|
||||
reducer 的 `queue.enqueued` / `queue.removed` 两个分支是它唯一的调用方。
|
||||
- chip 文案派生搬到 `chat/components/DirectProjectComposer/pendingTurnChipLabel.ts`
|
||||
(`pendingTurnChipLabel`),与 `chatComposerQueue.ts` 一起把"队列在本地"的最后一份实现删掉。
|
||||
- **草稿清不清由命令的入队结果回答**:`onSubmit` 改成返回 `Promise<boolean>`,composer 只在
|
||||
`true` 时清草稿。返回 `false` 的口子是"用户自己就能改的入队失败"(队列已满、参数无效这类)——
|
||||
宿主已经给了同级提示,草稿再没了就等于让用户重打一遍。写权限门让路给确认流程时返回 `true`
|
||||
(内容已经在重跑的入参里),与入队化之前一致。
|
||||
- **埋点句柄按 `clientTurnId` 存成一张表**(`pendingRunAnalyticsRef`),回合终态按本轮开口条目的
|
||||
canonical 身份(`turn.completed.userItemId` ↔ `directCodexConversationMessageId(clientTurnId,'user')`)
|
||||
认领结算;身份缺失时退回结算最早的那一条(放行严格按队首顺序,收口顺序就是入队顺序)。
|
||||
入队失败的那一轮直接把句柄删掉,不结算。
|
||||
- `displayBusy` = 原生在跑 ∨ IPC 在飞 ∨ 待发消息非空;`commandInFlight` 收窄成"IPC 在飞"。
|
||||
- 取消 chip 调 `remove_direct_project_pending_turn`,按 typed 结果 `removed / alreadyDispatched /
|
||||
notFound` 说清楚;chip 的撤除仍然只认 `queue.removed` 事件(界面不改本地队列)。
|
||||
|
||||
|
||||
退役:
|
||||
|
||||
- `chatComposerQueue.ts` 的 `enqueueChatTurn` / `dequeueChatTurn` / `removeQueuedChatTurn` / `isChatTurnQueueFull` /
|
||||
`MAX_QUEUED_CHAT_TURNS`(提示文案 `chatQueueFullNotice` 保留,改由 Rust 的 typed 入队失败驱动)。
|
||||
- controller 的 `queuedTurns` / `queuedTurnsRef` / `queueSequenceRef` / `completionPendingRef` / `handledCompletedTurnCountRef` /
|
||||
`busyBaselineTurnCountRef` / `dispatchNextQueuedTurn` / `beginTurnBusy` / `endTurnBusy` / `turnBusyRef` 与排序用的计数 effect。
|
||||
- 排队条目那部分 `pendingRunAnalyticsRef` 与 `beginDirectRunAnalytics` 的调用时序(埋点句柄改由宿主在放行时开)。
|
||||
|
||||
保留与改写:
|
||||
|
||||
- chip 由运行态事件的投影驱动(新增 pending 列表投影与两条队列事件的 reducer 分支);
|
||||
chip 文案仍用现成的派生(`directCodexContentToPromptText` + `resourceLabelResolver`),只是输入换成事件里的 `userItem`。
|
||||
- 取消 chip 改调 `remove_direct_project_pending_turn`;入队失败只由命令返回值驱动提示(保留"不丢草稿"行为)。
|
||||
- 忙态 = 事件投影 + 「队列非空」指示;`directProjectTurnStatus` 的"命令在飞"分支收成 IPC 在飞。
|
||||
- 写权限门 `ensureConversationWriteAllowed` 留在入队之前(确认框必须在用户在场时弹)。
|
||||
|
||||
验收:`tests/directThreadChat.test.ts` 补队列事件投影用例(顺序、幂等、按身份移除、bootstrap 带出在队条目、
|
||||
回合收口不清队列);`tests/appSurface/chat-composer.suite.ts` 的排队 / 取消 / 满队 / 放行四组用例改成新语义,
|
||||
并新增「入队只发一次 IPC」「IPC 在飞时挡住第二次提交」两条;`npm --workspace apps/ai-game-creator-shell run typecheck` 通过。
|
||||
|
||||
## 第 4 步:CLI 与夹具退役
|
||||
|
||||
- `cli.rs`:删 `CliCommand::DirectCodexChat` 变体、`project_path_mut` 分支(`cli.rs:181`)与派发分支(`cli.rs:899-935`)。
|
||||
- `agent/direct_runtime/mod.rs`:删 `run_direct_game_creator_turn_at` 与 `run_direct_game_creator_turn_at_with_creation_type`
|
||||
(各自只有彼此与 CLI 一个调用方)。
|
||||
- 删 `apps/ai-game-creator-shell/scripts/direct-execution-production-fixture.mjs`(CLI 的唯一消费者)。
|
||||
- 删 `scripts/run-agc-direct-execution-fixture.mjs` 与 `package.json` 的 `check:agc-direct-execution-fixture`
|
||||
(只服务该夹具,夹具没了没有第二个消费者)。
|
||||
- `DirectTurnError::TurnAlreadyRunning` 与前端"同一轮消息仍在处理中"文案、专属分支、`project-conversation.suite.ts`
|
||||
的对应断言**挪到第 5 步**(它最后一个生产点在调用身份守卫里)。
|
||||
- 文档同步:09-22 里程碑把 `--direct-codex-chat` 从"范围外(保留)"改成退役项;两份 Direct 技术方案的 CLI 承诺删掉;
|
||||
09-23 ADR 与 `decision-log.md` 里"CLI 保持 await"的口径改掉。
|
||||
|
||||
验收:`rg -n -- "--direct-codex-chat"` 与 `rg -n "run_direct_game_creator_turn_at"` 零命中;
|
||||
`cargo check --tests` 无新增 `dead_code` 告警;`check-config.mjs` 与 `.gitea/workflows/project-ci.yml` 不受影响(已核实无引用)。
|
||||
|
||||
## 第 5 步:守卫清理(CLI 退役之后)
|
||||
|
||||
> 前置判据**已核实(2026-09-30)**:`d833ca9d3` 的取消兜底本来就同时做三件事——释放占用、往事件流补一条
|
||||
> `turn.completed{aborted}`(否则前端永远停在运行中)、解除 `complete_direct_thread_turn` 占用;所以删守卫
|
||||
> **不需要**新增任何"取消路径补无条件终态"。连接死亡那条失败事实由 app-server 连接层自己落地(`ab970b9fd`),
|
||||
> 也与守卫无关。守卫唯一独有的东西是 60 秒启动窗口闸门,改挂在 Thread Manager 的占用登记年龄上。
|
||||
|
||||
- 五个身份读者(`agent/direct_execution.rs`、`agent/direct_tool_bridge.rs`、`agent/direct_validation.rs`、
|
||||
`agent/direct_project_context.rs`、`agent/direct_runtime/mod.rs` 的付费美术重生成)改读
|
||||
`direct_active_turn_id_at`(Thread Manager 的活动回合,只读、不改占用)。
|
||||
- 删 `DirectTaonierActiveInvocationGuard`、`DirectActiveTurnView`、`read_direct_taonier_active_invocation_at` 与
|
||||
`release_stale_direct_taonier_active_invocation` 及它们的测试;`codex_app_server` 的兜底目标改
|
||||
`DirectStaleTurnReleaseReason`,释放本身仍由既有的 `complete_direct_thread_turn` + `kick_direct_queue_dispatch` 完成。
|
||||
- 删 `DirectTurnError::TurnAlreadyRunning`(最后一个生产点在守卫里)与前端"同一轮消息仍在处理中"文案、专属分支、
|
||||
生成绑定与 `tests/appSurface/project-conversation.suite.ts` 的对应断言。
|
||||
- 前置判据测试:`cancel_direct_codex_turn_at` 在"回合任务泄漏、app-server 侧没有可中断句柄"时仍能解除占用,
|
||||
并把队首放行出去(`codex_app_server` 的兜底用例 + `direct_stale_turn_for_release` 的单测)。
|
||||
|
||||
## 第 6 步:条目不再另存产物(2026-09-30 修订)
|
||||
|
||||
判据:**在队条目除 `queue.enqueued` 的载荷之外不得有第二个字段**,`prompt` 也不得整条另存。
|
||||
|
||||
- `agent/direct_codex_user_item/model.rs`:`AgcResourceReference` 增加 `resolved_text: Option<String>`,
|
||||
存这个引用 part 被解析出来的文本(素材摘要 + 引用 UI 设计文档时要展开的代码上下文)。它是条目事实的一部分、
|
||||
**随条目持久化**:放行、历史回读、turn input 三个读点共用这一份,历史因此回放出当初那条消息;
|
||||
字段缺省合法(`#[serde(default)]`,序列化时 `None` 不写字段),旧历史没有它照样解析、只在缺省时按当前 manifest 现算。
|
||||
- `agent/direct_codex_user_item/wire.rs`:prompt 投影拆成两半——入队检查时 `freeze`(校验 + 算片段 + 写盘 + 冻结进 part + 判空),
|
||||
之后 `direct_codex_user_item_to_prompt(&item)` 是纯折叠(零 IO、零校验、无失败出口)。
|
||||
- `agent/thread_manager/queue.rs`:`PendingTurn` 改成 `queue.enqueued` 的**投影**(`from_event` / `enqueued_event` 往返),
|
||||
不再有 `canonical_user_item` 与 `prompt`;新增"在队 = 有 enqueued、没有配对 removed"的事件折叠。
|
||||
- `agent/thread_manager/mod.rs`:删 `StoredEvent.pending`(`append_inner` 并入 `append`);`pending_turns` / 容量 /
|
||||
判重 / `remove_pending_turn` / `claim_pending_turn` 全部改读事件折叠;`mark_queue_events_cleanable` 删掉
|
||||
"产物还在就不回收"的防御。
|
||||
- `agent/thread_manager/dispatch.rs`:放行的 canonical 形状 = `serde_json::to_value(&pending.user_item)`,prompt = 纯投影。
|
||||
|
||||
不变式:放行侧不写盘、不读 manifest、不重跑校验;入队检查仍然是这条消息唯一的失败出口。
|
||||
|
||||
验收(Rust 单测):`PendingTurn` 与 `queue.enqueued` 往返一致;队列折叠与原有 `pending_turn_ids` 语义一致
|
||||
(顺序、按身份移除、已放行返回 `AlreadyDispatched`);`resolved_text` 缺省的历史条目仍能解析并投影;
|
||||
同一批 `d341a9be1` / `e0ca5ad9b` 的中途加入 bootstrap 用例仍通过。
|
||||
|
||||
## 第 7 步:埋点成绩由宿主自己结算(2026-09-30 修订)
|
||||
|
||||
判据:`analyticsAttemptId` 整条链删除,且**不换字段存**。
|
||||
|
||||
- `agent/direct_runtime/user_input.rs`:入队命令不再收 `analyticsAttemptId`;`agent/thread_manager/wire.rs` 的
|
||||
`queue.enqueued` 不加埋点字段(队列事件相对第 5 步零新增字段)。
|
||||
- `agent/thread_manager/dispatch.rs`:放行时读一次 `platform_session` 的 `identity_generation`,随放行调用链交给回合。
|
||||
- `agent/direct_runtime/mod.rs`:回合终态再读一次,与放行时不同则整条不记;相同则直接写 `Request::Terminal`。
|
||||
- `analytics/run.rs`:删 `Request::DirectCandidate` / `Request::Settle` 与 `settle`,`direct_finished` 改收代际并直接落终态。
|
||||
- `analytics/store.rs`:删 `pending_runs` / `pending_run_bytes` 与两条候选分支(连带 16 条上限)。
|
||||
- `analytics/gui.rs` + `main.rs`:删 `settle_direct_run_analytics` 命令与注册。
|
||||
- 渲染层:删 `services/clientAnalytics.ts` 的 `beginDirectRunAnalytics`、控制器的句柄表与结算 effect、
|
||||
`TauriInvoke` 里的命令签名;`userItemId` 不再用于认领结算。
|
||||
|
||||
不变式:成绩不再依赖"恰好有一个窗口在消费终态事件";判据口径随放行搬家——入队后、放行前发生的账号切换不再丢弃成绩。
|
||||
|
||||
验收(Rust 单测):`analytics::store_tests` 的候选 / settle 用例改为终态直写用例(含"放行后代际变化不记");
|
||||
`cargo test` 定向 + 前端 `npx vitest run tests/appSurface.test.ts`、`npm --workspace apps/ai-game-creator-shell run typecheck`。
|
||||
|
||||
## 验收与证据
|
||||
|
||||
- Rust:第 1、2、5 步各自的单测;`cargo test` 定向 + `cargo check --tests` 无新增告警。
|
||||
- Node:`npx vitest run tests/appSurface.test.ts`、`npm --workspace apps/ai-game-creator-shell run typecheck`。
|
||||
- 端到端(`chat-composer.suite.ts`):回合运行中入队两条 → 取消一条 → 终态后只放行剩下那条;
|
||||
入队只发生一次 IPC、没有第二次发送命令;满队提示;入队失败时草稿不丢。
|
||||
- 手工:两个窗口看同一项目(A 排队 B 可见可取消);离开工作台再回来队列仍在并继续放行;
|
||||
`kill -9` 后重进队列消失(与 ADR 的已知边界一致)。
|
||||
- 全仓:`npm run check:encoding`、`npm run check:doc-index`、`git diff --check`。
|
||||
- 不涉及 SpacetimeDB schema,不需要 `npm run check:spacetime-schema`。
|
||||
|
||||
## 2026-09-30 收口记录
|
||||
|
||||
第 6、7 步按上面两节落地,提交按"文档定稿 → 引用解析文本持久化 → 埋点归宿主 → 条目改事件投影"切开:
|
||||
`2b98b022c`(文档)、`15a495dc2`(`resolved_text` 与 prompt 拆 freeze / 纯折叠)、`0270cd601`(埋点成绩归宿主)、
|
||||
`28b64cf25`(条目改事件投影)。
|
||||
|
||||
- Rust:`cargo test --bin genarrative-ai-game-creator-shell -- thread_manager::`(62 passed,进程级计数器用例单线程跑)、
|
||||
`-- agent::`(948 passed;`design_runtime` 与两条历史并发用例在整包并行下偶发,单跑通过,与本次改动无关)、
|
||||
`-- direct_runtime:: analytics:: direct_codex_user_item`(182 passed)、`-- analytics_real_file_write_...`(1 passed)。
|
||||
- Node:`npx vitest run tests/appSurface.test.ts`(194 passed)、`tests/directThreadChat.test.ts`、
|
||||
`tests/directHistoryPaging.test.ts`、`tests/directProjectTurn.test.ts` 等 chat / direct 单测(249 passed)、
|
||||
`npm --workspace apps/ai-game-creator-shell run typecheck` 通过。
|
||||
- 全仓:`npm run check:encoding`、`npm run check:doc-index`、`git diff --check`。
|
||||
@@ -4,6 +4,9 @@
|
||||
|
||||
状态:**四步全部落地**。
|
||||
|
||||
后续:09-24 起命令边界改叫「入队 / 入队失败 / 放行」,本文里的「接单 / 拒单」与 `TurnAlreadyRunning`
|
||||
都已退役(见 09-24 ADR 与 09-30 决策记录);正文保留当时口径,不再回改。
|
||||
|
||||
设计口径见 [`【ADR】DirectProject命令接单化-2026-09-23`](../adr/【ADR】DirectProject命令接单化-2026-09-23.md)。
|
||||
本文件只排实施顺序、不变式与验收,不重复设计理由。
|
||||
|
||||
|
||||
@@ -165,7 +165,8 @@ Supervisor / 做方案首轮忽略 `attachments`,行为不变。
|
||||
- `chat_with_game_creator_direct_codex` 增加 `attachments: Option<Vec<DirectCodexTurnAttachment>>`,先渲染再调用现有 `run_direct_game_creator_turn_at_with_creation_type_and_emitter`
|
||||
- 把现有 Home 渲染测试迁到新文件;本文件不再保留一份平行实现
|
||||
|
||||
不要把 attachments 顺着 inner turn / emitter / CLI 往下传。CLI `run_direct_game_creator_turn_at` 不变。
|
||||
不要把 attachments 顺着 inner turn / emitter 往下传。(CLI 入口 `run_direct_game_creator_turn_at` 已随命令入队化退役,见
|
||||
[`【ADR】DirectProject命令入队化与待发消息队列归宿主-2026-09-24`](../adr/【ADR】DirectProject命令入队化与待发消息队列归宿主-2026-09-24.md)。)
|
||||
|
||||
### 5.2 前端
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ Direct GUI 回合已经能看见 Codex `item/completed`,但只收成 UI 活动
|
||||
- 不拷隔离 `CODEX_HOME`、不落 `auth.json`、不落 `aggregated_output` / MCP `result` / patch `diff` / `FunctionCallOutput` 正文。
|
||||
- 不把原始 item JSON 送进 Tauri 前端事件(现有 `DirectCodexTurnObservation` 仍只允许安全活动词和流式正文)。
|
||||
- 不扫 `kind=uploaded` 历史附件;只记本轮 sidecar 提供的集合。
|
||||
- DirectHome、ToolHost、CLI `--direct-codex-chat`(无 `clientTurnId`)本期不写这份账本。
|
||||
- DirectHome、ToolHost 本期不写这份账本。(`--direct-codex-chat` 已随命令入队化退役,不再是入口。)
|
||||
- 本期不改 UI,不在聊天面板展示审计。
|
||||
- 不把 issue #212 标成已修复;sidecar 与本账本是两段工作。
|
||||
|
||||
@@ -292,7 +292,9 @@ chat_with_game_creator_direct_codex
|
||||
→ 写 turn_end + agent.db 摘要
|
||||
```
|
||||
|
||||
- CLI `run_direct_game_creator_turn_at` **不** 接 audit(无 `clientTurnId`)。
|
||||
- CLI 入口已退役:`run_direct_game_creator_turn_at` 与 `--direct-codex-chat` 一起删除(见
|
||||
[`【ADR】DirectProject命令入队化与待发消息队列归宿主-2026-09-24`](../adr/【ADR】DirectProject命令入队化与待发消息队列归宿主-2026-09-24.md)),
|
||||
所以「无 `clientTurnId` 的 Direct 回合」这条路径不存在了。
|
||||
- Home command 不接 audit。
|
||||
- 不要把 attachments / audit 顺着 CLI inner、pool、ToolHost 往下传。
|
||||
- `DirectCodexTurnObservation` **不** 增加原始 `params`。审计走独立 `DirectCodexTurnAudit`,避免 stdout 正文进入 Tauri 事件。
|
||||
|
||||
Reference in New Issue
Block a user