Skip to content

feat(workhub): queue new delegations behind active Sessions - #9

Merged
sunrioa merged 3 commits into
mainfrom
codex/workhub-step-1-queued-delegation
Oct 7, 2026
Merged

sunrioa merged 3 commits into
mainfrom
codex/workhub-step-1-queued-delegation

Conversation

@sunrioa

@sunrioa sunrioa commented Oct 7, 2026 •

Copy link
Copy Markdown
Owner

Summary

Refs #8(分步交付计划中的 PR 1)

将 WorkHub 发给忙碌 Session 的新委派改为现有的 next_turn / followup 持久消息:

  • 运行中或等待用户时先排队,原工作结束后按接收顺序进入各自独立的 Turn;空闲目标仍立即启动。
  • 不把新任务自动 Steering 到原模型调用,不中断原任务,不覆盖目标 Session 的权限、模型或 cwd。
  • 在同一 Session admission lease 内复用普通消息队列的数量、字节、快照与后继 Turn 容量检查,再原子提交 assignment 与 Message;容量不足时拒绝,已提交 action 的重放不重复入队。
  • SQLite 与 Memory 参考实现同步接受该准入形态;沿用原事务、消息身份和恢复链路,没有新增数据库或迁移。
  • 补充实际 Host composition、Storage conformance、取消/改投及进程崩溃恢复测试,并更新现有 WorkHub ADR。

针对 首轮审查 和 后续复审 的 P2,补充完成:

  • 改投先预留容量。 写入 replacement intent 或撤销源委派前,对目标检查并预留数量/字节等准入容量。等待源 Stop 时,共享 Session 快照准入统一计入预留,包括普通入队、编辑、Interaction 和 sandbox boundary;提交后消耗、失败后释放,不把预留发布为虚假消息。容量拒绝保留源委派,释放容量后可用新的 action ID 重试。没有跨异步 Stop 持有 Session admission lock,也没有新增持久队列。
  • 容量投影与公开队列分开。 fitsCandidate 使用 admission-only 容量投影,公开 read() 保持无虚假消息;按 Message ID 去重,避免准入自己的预留时重复计数。合法且仍能容纳的小问题继续准入,预留释放后恢复相应容量。
  • 状态跟随 Message。 新委派回执返回 targetMessageId;排队尚未执行时不再把祖先 admission Turn 当作执行 Turn 返回。Desktop 的 status 先查询 Message 执行所有权,未启动返回 pending,已启动再查询实际 Turn/Run,并校验身份。历史 Turn-only 回执与 linked resume 仍兼容。
  • 新回执与状态工具的身份契约变化使协议兼容 epoch 从 204 升至 205;旧持久 assignment 的 admission Turn 保持不变。

本阶段不加入新权限设置、全局并发配置、跨 Session 通信或新前端。后续阶段等本 PR 由仓库所有者检查合并后再继续。

Verification

最新验证提交:da83162b963eae89cb31d4953eaab9201f4a3782。环境:macOS arm64,Node v24.19.0,npm 11.19.0。基线:dcef64275ef435049a4a0c65a0d81199b45ed498。

  • 初始排队改动和上一轮 P2 的失败回归记录保留在 [Tracking] WorkHub:单入口异步任务协调、跨 Session 协作与执行级权限 #8。本次修改生产代码前,3 项新增回归全部失败:共享快照预检遗漏 question/sandbox boundary 的预留,以及真实 Host 中的问题准入占用已预留容量。
  • 本次完整直接相关的 10 个测试文件:389 passed / 0 failed,包含 Interaction、Message、WorkHub 改投/状态、Storage conformance、Core 回执及 Desktop runtime。
  • 干净构建目录的完整 Runtime Host 套件:2240 passed / 0 failed / 12 skipped,使用 --test-concurrency=2。
  • Core、Storage、Runtime、UI、Desktop 完整套件合并运行:10137 passed / 0 failed / 22 skipped,使用 --test-concurrency=2。
  • 新旧定向容量、改投重试及 FIFO 回归共 10 项,连续 5 次:50/50。
  • 现有 Electron workhub-pending-question.spec.ts:1/1 通过(重载、浮出/停靠、原问题选项与焦点)。
  • 根 build、typecheck、lint、format、Desktop/UI knip、ASF headers、Windows inventory、Astryx surface inventory、E2E budget、diff whitespace 均通过。
  • check:release 通过,其中 release 测试 214/214;本次仅修改内部容量检查,epoch 保持 205,提交后相对 main 的协议兼容检查通过 204 -> 205。

主要复跑命令:

node --test --test-concurrency=2 \
  packages/core/dist/__tests__/runtime-event.test.js \
  packages/storage/dist/__tests__/execution-provider-conformance.test.js \
  packages/runtime-host/dist/__tests__/canonical-session-projection.test.js \
  packages/runtime-host/dist/__tests__/interaction-coordinator.test.js \
  packages/runtime-host/dist/__tests__/execution-composition.test.js \
  packages/runtime-host/dist/__tests__/message-coordinator.test.js \
  packages/runtime-host/dist/__tests__/workhub-coordination-action-gate.test.js \
  packages/runtime-host/dist/__tests__/workhub-coordination-coordinator.test.js \
  packages/runtime-host/dist/__tests__/workhub-assignment-crash-recovery.test.js \
  apps/desktop/dist/main/__tests__/workhub-runtime.test.js

node --test --test-concurrency=2 'packages/runtime-host/dist/**/*.test.js'

Verification limits and compatibility

初始提交 44480144695cfda557912d77f80e4985c0a133c3 的两轮全仓库并行测试不是全绿,未修改的用例出现以下失败。保留这些历史记录;最新提交上述有界并发运行已通过:

  • Storage single-flights one limit-bound writer and snapshots admitted inputs:首次完整运行失败;本分支独立复跑 30 次有 3 次失败。另在从精确基线提取、重新编译的源代码上复跑 30 次,有 1 次相同的 lease / limits 断言失败,确认问题在本补丁之前已存在。
  • Desktop kills login-shell descendants when capture times out:第二轮并行运行未生成 PID 文件;独立复跑通过,基线独立复跑也通过。
  • Host real health probe falls back to GET and bounds stalled responses:第二轮并行运行在 100ms 探测窗口中超时;独立复跑通过,完整 Host 的双进程复跑通过。

这些用例及对应生产模块没有被本 PR 修改,也没有增加重试、延长超时或跳过断言来让它们变绿。

本机没有运行真实 Windows smoke、Windows 打包或完整 Electron 全套;Windows inventory 不是 Windows 运行证据。本阶段未修改 Windows 后端。

本次 hosted test 首跑 在未修改的 Windows opens the store page in installed Chrome, not the default browser 用例失败:期望 URL,实际读到空字符串。夹具只等待临时结果文件存在,没有等待内容写完;该测试及生产模块与 main 的 Git blob 完全相同。本机原样连续复跑 20 次,80/80 通过,未修改断言或增加代码重试。已发起失败工作流的第二次运行,尚未确认全绿;首跑后续 Runtime Host 和 Electron E2E 被跳过,不能计为远端通过。另一个 request-review 因缺少 COPILOT_REVIEW_TOKEN 失败,未改动工作流或 secrets。

兼容性边界:

  • 已存储的 Steering assignment 仍可读取,历史共享 Turn 的 ownership 与 Stop 保护保留。
  • queued assignment 的 admission Turn 不是执行所有权;实际执行以目标 Message 的证据为准。
  • 多个 working delegation 指向同一 Session 时,既有 Stop 歧义检查仍 fail closed,不隐式挑选或停止无关工作。
  • 崩溃恢复复用 durable pending admission;正常 Host drain 仍执行原有队列取消策略,不承诺关闭应用后所有排队任务都会保留。

AI use

Select exactly one:

  • No generative tool made a substantive contribution
  • Generative tooling made a substantive contribution

Tool(s) and scope: OpenAI Codex assisted with source inspection, implementation, tests, the ADR update and this PR at the repository owner's request. The owner will independently review and merge; this PR has not been automatically merged. The commit includes a Generated-by: OpenAI Codex trailer.

Checklist

  • Tests cover the change and fail without it
  • Lint, format, typecheck and the affected suites pass locally

Does this PR entail a change in behavior?

  • Yes — described under Summary above
  • No

Reuse ordinary next-turn Message admission for busy and waiting targets, with bounded preflight under the existing Session admission lease. Preserve target settings, native cancellation and legacy shared-Turn protection.

Refs #8

Generated-by: OpenAI Codex
@sunrioa

sunrioa commented Oct 7, 2026

Copy link
Copy Markdown
Owner Author

审查提交:44480144695cfda557912d77f80e4985c0a133c3。

确认 2 项 P2,未发现已验证的 P1。建议修复后再合并。

[P2] 在取消源委派前检查并预留改投目标的队列容量

这段新增检查在 WorkHubCoordinationActionGate.#replace 已调用 retireDelegation 之后才执行。可复现场景:源 Session 有一个尚未执行的 WorkHub 委派,目标 Session 正在运行且队列达到 MESSAGE_QUEUE_MAX_ENTRIES,随后将源委派 correct 到目标。结果是 correct 返回 session_busy: Message queue capacity is full,但源 Message 已变为 cancelled,替代 assignment 没有提交,同时留下未终结的 replacement intent。

进一步验证:即使释放目标容量,再用新的 actionId 重试同一改投,也返回 operation_conflict: WorkHub delegation already has a different replacement intent;只有精确复用原 actionId 才能恢复。Desktop 的 WorkHubTasks 每次新工具调用会派生新的 actionId,correct 参数不提供重放该 actionId 的入口,因此普通重试不能恢复这项任务。

应在取消源委派前完成目标容量检查及预留;容量不足时保留源 Message 和正常重试能力,避免一次被拒绝的改投先取消原任务。

代码位置:execution-composition.ts:2590–2596。

[P2] 排队委派不能把原任务 Turn ID 作为实际执行进度的标识返回

新委派已改成独立的 next_turn / followup,但这里仍把正在执行的原 Turn ID 存入 assignment,并经 action result 返回为 targetTurnId。现有 apps/desktop/src/main/workhub-runtime.ts 的 WorkHubTasks.status 直接用该 ID 查询 Turn,并把 completed 标为 completionVerified: true;其工具契约也要求使用先前委派返回的 Turn ID 查询进度。

实际 Host + Desktop 适配器复现:手工 Turn A 运行时提交委派 D;A 完成后,D 已进入另一个独立 Turn,实际状态仍为 running。此时使用委派返回的 targetTurnId 调用 status,却得到 status: completed, completionVerified: true,查询到的是 A 的完成状态,而非 D。

持久 admission Turn 可以继续用于准入记录,但状态查询必须依据委派 Message 的执行证据解析实际 Turn;D 尚未开始时应返回 pending,不能用原任务的完成事实证明新委派已经完成。

代码位置:execution-composition.ts:2573–2575。

验证结果:

  • 直接受影响的 5 个测试文件:279 passed / 0 failed。
  • 相邻的 WorkHub 结果回传、协调器、队列状态、Storage assignment 和 Desktop runtime 测试:88 passed / 0 failed。
  • Storage / Runtime Host 类型检查与 git diff --check 通过。
  • 额外运行 3 项专项检查:复现上述两项 P2,并确认既有 correction guard 会保留其他排队委派,因此没有把该怀疑列为缺陷。

专项复现使用真实 Host composition、SQLite 持久化及现有 Desktop status 适配器,模型后端使用 FakeBackend。上述既有测试通过不代表覆盖了这两项缺陷;本次没有运行完整 Electron / Windows 测试。

Reserve replacement queue capacity before source retirement and report delegation progress through Message execution ownership.

Generated-by: OpenAI Codex
@sunrioa

sunrioa commented Oct 7, 2026

Copy link
Copy Markdown
Owner Author

已按审查中的两项 P2 修复,并普通推送到同一分支:da0451c3dc5f12dac9699d21ad714f3cd468f3c3。

  1. 改投先检查并预留目标容量。 数量/字节等容量不足时,不写 replacement intent,也不撤销源委派。等待源 Stop 时,其他入队与编辑计入预留;提交消耗、失败释放。释放容量后,用新的 action ID 改投可以成功。
  2. 状态改为跟随目标 Message。 回执返回 targetMessageId;排队期间不再返回祖先 admission Turn 作为执行身份。status 先查询 Message 所有权,pending 不宣称完成,实际开始后再查对应 Turn/Run。旧 Turn-only 回执及 linked resume 保留兼容。

回执契约变化已将协议兼容 epoch 升至 205。没有新增数据库/持久队列,也没有更改 Session 权限、模型或 cwd。

精确提交的本地验证(macOS):

  • 完整受影响文件:262 passed / 0 failed。
  • 干净 Host 全套:2237 passed / 0 failed / 12 skipped。
  • Core/Storage/Runtime/UI/Desktop 合并完整套件:10135 passed / 0 failed / 22 skipped。
  • 6 项 P2 回归连续 5 次:30/30;两个 FIFO 场景连续 5 次:10/10。
  • 现有 WorkHub pending-question Electron E2E:1/1;release:214/214。
  • build、typecheck、lint、format、knip、许可证、清单、E2E budget 与协议检查通过。

PR 描述与 #8 已同步。未运行真实 Windows smoke/打包,不以 inventory 代替平台验证。Hosted 主 CI 仍在运行;自动 request-review 检查仍因缺少 COPILOT_REVIEW_TOKEN 失败,未修改该工作流或 secrets。

请重新审查;本次不自动合并,也不启动下一步。审查报告未确认 P1,这不等于证明所有路径不存在 P1。

AI assistance: OpenAI Codex assisted with reproducing, implementing, testing and reporting these fixes at the repository owner's request. Owner review is still pending.

@sunrioa

sunrioa commented Oct 7, 2026

Copy link
Copy Markdown
Owner Author

复审提交:da0451c3dc5f12dac9699d21ad714f3cd468f3c3(发评论前确认仍为 PR 当前 head)。

确认仍有 1 项 P2,未发现已验证的 P1。建议修复后再合并。 上一轮的 Message 状态误报已修复;满队列拒绝时保留原委派、释放容量后用新 actionId 重试,也已独立验证通过。但容量预留仍漏掉目标新产生的 Interaction。

[P2] 将消息容量预留纳入 Interaction 的 Session 快照准入检查

这里新增的预留只保存在 #capacityReservations,经 #withReservedCapacity 参与消息提交/编辑的容量计算。CanonicalSessionProjectionReader.read() 读取的仍是排除预留的公开 queue;HostInteractionCoordinator 的 preflightSessionSnapshot 直接调用 fitsCandidate({ interactions }),因此准入新问题/权限交互时不会计入预留的消息字节。等待源任务异步 Stop 时,这些交互可以占用已经预留的 56 KiB Session snapshot 预算。

已用真实 Host composition + SQLite、FakeBackend 控制 Stop 时序复现两次,未改变生产代码或容量常量:

  1. 源 Session 中的 WorkHub 委派正在执行;目标 Session 的手工 Turn 正在运行,目标队列为空。
  2. correct 到该目标,delegationText 为 48 KiB;容量预留成功并写入 replacement intent,让源 backend 的 stop() 暂时等待。
  3. 在等待期间,让目标 backend 提出一个合法的三问题 Interaction(问题及选项均在既有字段限制内;持久请求编码为 5,840 字节)。该 Interaction 被成功准入。
  4. 放行源 Stop。源 Turn 变为 cancelled,随后 correct 返回 session_busy: Session projection capacity is full;替代 assignment 未提交,replacement intent 仍存在,也没有 terminal abort。
  5. 回答目标问题、释放相应容量后,以新的 actionId 再次 correct,仍返回 operation_conflict: WorkHub delegation already has a different replacement intent。Desktop 每次工具调用派生新 actionId,普通重试无法恢复这次改投。

关键实测结果:

{
  "correction": { "ok": false, "code": "session_busy", "message": "Session projection capacity is full" },
  "sourceTurnStatus": "cancelled",
  "replacementCommitted": false,
  "replacementIntent": true,
  "terminalAbort": false,
  "newActionRetry": { "ok": false, "code": "operation_conflict", "message": "WorkHub delegation already has a different replacement intent" }
}

需要让所有会增长同一 Session snapshot 的准入路径都计入尚未消耗的预留,尤其是 Interaction/sandbox boundary 的 fitsCandidate;可以使用独立的容量投影,继续保持公开 queue 无虚假消息。否则当前修复仍允许容量拒绝发生在撤销源任务之后。

代码位置:新增 reservation 存储、仅在消息路径合并 reservation、Interaction 快照预检。

本次验证:

  • 相关 9 个测试文件:365 passed / 0 failed,覆盖 Runtime Host、Core receipt、Storage conformance 和 Desktop runtime。
  • 独立验证状态 pending → running → completed:pending 回执不含祖先 Turn ID,running 查询实际委派 Turn,只有该 Turn 完成后才返回 completionVerified: true。
  • 独立验证满队列 correct:失败后源 Message 仍 pending、没有 replacement intent;释放容量后新的 actionId 成功。
  • 上述 Interaction 并发缺陷独立复现及再次复跑均成立。既有测试全部通过,但尚未覆盖该交错。
  • Runtime Host 类型检查、dist freshness、git diff --check 通过。
  • 当前 head 的 GitHub test CI 已成功;request-review 失败原因是缺少 COPILOT_REVIEW_TOKEN,不作为本次代码缺陷。

本次本地验证环境为 macOS / Node v24.19.0;未运行完整 Electron 或真实 Windows 测试。

Account for queued replacement reservations in shared Session snapshot preflight, including Interactions and sandbox boundaries, without exposing phantom Messages or double-counting admission.

Generated-by: OpenAI Codex
@sunrioa

sunrioa commented Oct 7, 2026 •

Copy link
Copy Markdown
Owner Author

复审指出的 P2 已在 da83162b963eae89cb31d4953eaab9201f4a3782 修复。此前预留只进入普通消息的准入检查,确实遗漏了 Interaction 和 sandbox boundary 使用的共享 Session 快照预算。

本次将预留统一接入现有 fitsCandidate 的 admission-only 容量投影:

  • 普通消息、编辑、Interaction 和 sandbox boundary 都计入同一预留预算。
  • 公开 read() 不发布虚假排队消息;按 Message ID 去重,避免准入自己的预留时重复计数。
  • 仍能容纳的小问题继续准入;预留释放后容量恢复。

已加入实际 Host + SQLite + FakeBackend 的 Stop 屏障回归:目标预留 48 KiB 后,3 个大问题在持久化前收到 capacity_exceeded 拒绝;释放源 Stop 后,改投成功提交 replacement assignment,未产生终止 abort。另有 question 和 sandbox boundary 的共享快照回归。修改生产代码前,新增 3 项均在行为断言处失败;修复后通过。

精确提交的本地验证:

  • 10 个直接相关测试文件:389 通过、0 失败。
  • 完整 Runtime Host:2,240 通过、0 失败,12 个既有跳过。
  • Core、Storage、Runtime、UI、Desktop 完整套件:10,137 通过、0 失败,22 个既有跳过。
  • 新旧容量、改投和 FIFO 定向用例连续 5 次:50/50。
  • 现有 workhub-pending-question Electron E2E:1/1;release 检查:214/214。
  • build、typecheck、lint、format、knip、许可证、inventory、协议兼容和 diff 检查通过。

仅修改 5 个相关文件;未提高容量上限、新增数据库或持久队列,也未增加测试重试、放宽断言或更改协议字段。本次 epoch 保持 205。PR 描述与 #8 已更新。

以上是本地验证结果;新提交的 hosted CI 单独确认,不沿用上一提交的绿色状态。本机未运行真实 Windows smoke 或 Windows 打包。仍等待仓库所有者复审与合并,未自动合并,也未开始下一阶段。

本次 hosted test 首跑 在未修改的 Windows opens the store page in installed Chrome, not the default browser 用例失败:期望 URL,实际读到空字符串。夹具只等待临时结果文件存在,没有等待内容写完;该测试及生产模块与 main 的 Git blob 完全相同。本机原样连续复跑 20 次,80/80 通过,未修改断言或增加代码重试。已发起失败工作流的第二次运行,尚未确认全绿;首跑后续 Runtime Host 和 Electron E2E 被跳过,不能计为远端通过。另一个 request-review 因缺少 COPILOT_REVIEW_TOKEN 失败,未改动工作流或 secrets。

AI assistance: OpenAI Codex assisted with the investigation, fix, tests and this follow-up at the repository owner's request. Owner review of the updated implementation is pending.

@sunrioa

sunrioa commented Oct 7, 2026

Copy link
Copy Markdown
Owner Author

再次复审:da83162b963eae89cb31d4953eaab9201f4a3782,发评论前确认仍为 PR 当前 head。

本轮未发现已验证的 P1/P2;上一轮报告的 Interaction 占用预留容量 P2,已验证修复。

本次改动把 admission-only capacityProjection 统一接入 CanonicalSessionProjectionReader.fitsCandidate。因此 Interaction 和 sandbox boundary 的共享快照准入也会计入尚未消耗的预留;公开 read() 仍返回真实队列。按 Message ID 去重也避免了准入替代消息时把它与自己的预留重复计算。

使用独立复核脚本、真实 Host composition + SQLite + FakeBackend,验证了以下 4 项实际调用路径:

  • 上一轮并发复现已消除: 目标预留 48 KiB 后,在源 Stop 等待期间提出同样的三个大问题。现在问题收到 RuntimeInteractionAdmissionRejectedError / capacity_exceeded,持久 pending Interaction 为 0;放行 Stop 后 correct 成功,replacement assignment 与 supersession 均已提交,没有 terminal abort。
  • 小交互仍可正常准入: 同样预留 48 KiB,目标的小问题成功持久化;correct 成功,委派 Message 保持 pending。回答原问题后,委派 Message 获得实际执行 Turn,正常开始。
  • 满队列拒绝和重试未回退: correct 失败后原 Message 仍 pending,没有 replacement intent;释放目标容量后,用新的 actionId 改投成功。
  • Message 状态修复未回退: 独立检查 pending → running → completed;排队回执不含祖先 Turn,运行时查询实际委派 Turn,仅在该 Turn 完成后返回 completionVerified: true。

相关既有及新增测试也验证了 sandbox boundary 的共享预算、小问题可容纳、释放预留后容量恢复、Message 去重和公开队列不包含虚假预留。

本轮验证结果:

  • 12 个相关测试文件:482 passed / 0 failed / 0 skipped。范围包含 Core receipt、Runtime Host composition、消息队列、WorkHub 协调与崩溃恢复、canonical snapshot、Interaction、Goal/root Turn、Storage conformance、Desktop runtime。
  • 上述 4 项独立验证:4 passed / 0 failed。
  • Runtime Host 类型检查、dist freshness、git diff --check 通过;生产工作树保持干净。

验证环境为 macOS / Node v24.19.0。本轮未运行完整 Electron 或真实 Windows 测试,也未独立确认最新 hosted CI:CI 读取工具当前提示需要连接 GitHub 账户,因此不沿用上一提交的绿色状态或将修复说明中的 CI 结果作为本轮验证。这里的结论仅覆盖已审查代码与上述本地验证,不等于证明所有执行路径不存在缺陷。

@sunrioa
sunrioa merged commit 01b9a6e into main Oct 7, 2026
1 of 3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant