Files
paseo/docs/qa-evidence/pi-resumed-subagents/validation.txt
T
xxachangxx a4878656e2 fix(pi): track resumed pi-subagents runs and transcripts (#9261)
* 修复 Pi 恢复子任务的状态和对话跟踪

关联 #9200。

改动:
- 仅在 action:resume 确认成功启动新的 single async run 后登记独立子任务条目,按新 run ID 和 asyncDir 跟踪状态,拒绝失败及管理调用产生的假条目。
- 将对话去重从文件级调整为子任务条目与文件组合,支持恢复运行复用 session JSONL,避免旧通知结束新运行。
- 扩展原适配器回归测试,并附脱敏的命令输出、真实工具结果和 macOS Electron 截图,未改 UI、协议或共享生命周期。

实际验证:
- 原适配器下恢复条目回归为 RED;恢复旧文件级去重后共享对话回归为 RED。
- 基线 d46ee6558c 上 npm run build:server、npm run format、npm run typecheck、npm run lint、git diff --check 均成功。
- 在 packages/server 执行 npx vitest run src/server/agent/providers/pi/extensions/pi-subagents/index.test.ts --bail=1,21/21 通过。
- 已在此前 0.15.3 开发版中,通过真实 macOS Electron、Pi 1.5.0、pi-subagents 0.86.0 验证 A→B→C 恢复、运行条目和对话、完成状态、归档保留运行任务、失败恢复不产生假条目;普通启动走 CLI/API,恢复通过真实 composer。截图不是 0.15.4 发布包验收。
- 同步后适配器与测试文件字节和第三方锁定依赖均未变,复用独立代码评审与上述桌面证据。公开材料已做隐私扫描。
- 未执行全量测试(仓库禁止本地全量执行)、CI、其他平台或发布安装包验证;短暂重复 Shell 文案原因未定位,不宣称本次修复。

回滚:
- 本提交仍为 HEAD 时执行 git revert HEAD;否则先用 git log --oneline --grep='修复 Pi 恢复子任务的状态和对话跟踪' 确认本提交哈希,再 git revert <确认的哈希>,不要重置或覆盖其他修改。
- 回滚后重跑该适配器测试并执行 npm run build:server;若需要部署回滚产物,另行安排服务切换,禁止未经授权重启正在使用的 Paseo daemon。
- 本提交仅改源码、测试和 QA 证据,无数据迁移;回滚不应删除已有 Pi 会话或异步目录。独立 QA 服务不由本提交启停。

* 补强 Pi 恢复对话的新消息与实时回放断言

回应 PR #9261 的 Greptile P2 评论 discussion_r4521416122。

改动:
- 只修改 pi-subagents/index.test.ts,按子任务 ID 提取 assistant_message 文本,精确检查 C 的历史与本轮内容。
- 观察 C reader 初始读取后再追加消息,在写入终态前验证新消息实时到达且 C 仍运行,避免只靠完成时刷新通过。
- 回放精确校验三条预期消息、顺序与 resume-c 归属,保留旧通知隔离、完成状态和 bg_wait 断言。
- 不修改生产实现、历史 QA 证据或普通启动行为。

实际验证:
- 临时在测试观察回调中丢弃 C 本轮消息:定向测试 exit 1,只有 Attempt b 的历史不能通过初始断言。
- 临时只丢弃 C 后追加消息:定向测试 exit 1,缺少 Attempt c follow-up 的断言在写入完成状态前失败。
- 两次故障注入已移除;最终在 packages/server 执行 npx vitest run src/server/agent/providers/pi/extensions/pi-subagents/index.test.ts --bail=1,21/21 通过。
- npm run format、npm run typecheck、npm run lint 和 git diff --check 均通过;全仓 lint 为 0 warnings / 0 errors。
- LSP 推断项目模式仅报告未修改旧测试行的两处 results 类型警告;项目正式 typecheck 通过。
- 未运行全量测试、远端 CI 或重复桌面验收。本次只有测试变更,复用原产品评审和真实桌面验证,不将临时故障注入当作生产缺陷。

回滚:
- 本提交仍为 HEAD 时执行 git revert HEAD;否则用 git log --oneline --grep='补强 Pi 恢复对话的新消息与实时回放断言' 确认哈希后 git revert <该哈希>。
- 回滚后重跑上述适配器测试;这只撤销测试增强,不应回滚先前 #9200 产品修复,也无需重启 daemon 或删除任何会话数据。
2026-11-11 12:01:35 +00:00

197 lines
5.7 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
Captured command output. Only private absolute paths are replaced with <REPO_ROOT>/<USER_HOME>.
RED runs are intentional regression failures, not final failures.
=== Before fix: resume arguments rejected (expected RED, exit 1) ===
$ cd packages/server && npx vitest run src/server/agent/providers/pi/extensions/pi-subagents/index.test.ts --bail=1
RUN v4.1.6 <REPO_ROOT>/packages/server
stdout | src/server/agent/providers/pi/extensions/pi-subagents/index.test.ts
[dotenv@17.3.1] injecting env (0) from .env.test -- tip: ⚙️ suppress all logs with { quiet: true }
[dotenv@17.3.1] injecting env (0) from ../.env -- tip: ⚙️ suppress all logs with { quiet: true }
❯ src/server/agent/providers/pi/extensions/pi-subagents/index.test.ts (10 tests | 1 failed) 7ms
× registers a successful resume as a new async attempt without spawn arguments 6ms
⎯⎯⎯⎯⎯⎯⎯ Failed Tests 1 ⎯⎯⎯⎯⎯⎯⎯
FAIL src/server/agent/providers/pi/extensions/pi-subagents/index.test.ts > pi-subagents adapter > registers a successful resume as a new async attempt without spawn arguments
AssertionError: expected undefined to deeply equal [ { type: 'upsert', …(5) } ]
- Expected:
[
{
"description": "Continue",
"id": "resume-b",
"status": "running",
"title": "Subagent",
"toolCallId": "resume-b",
"type": "upsert",
},
]
+ Received:
undefined
❯ src/server/agent/providers/pi/extensions/pi-subagents/index.test.ts:66:32
64| },
65| });
66| expect(resumed?.subagents).toEqual([
| ^
67| {
68| type: "upsert",
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/1]⎯
Test Files 1 failed (1)
Tests 1 failed (10)
Start at 21:05:21
Duration 474ms (transform 245ms, setup 31ms, import 328ms, tests 7ms, environment 0ms)
=== Intermediate implementation: old file-only transcript deduplication (expected RED, exit 1) ===
$ cd packages/server && npx vitest run src/server/agent/providers/pi/extensions/pi-subagents/index.test.ts --bail=1
RUN v4.1.6 <REPO_ROOT>/packages/server
stdout | src/server/agent/providers/pi/extensions/pi-subagents/index.test.ts
[dotenv@17.3.1] injecting env (0) from .env.test -- tip: ⚡️ secrets for agents: https://dotenvx.com/as2
[dotenv@17.3.1] injecting env (0) from ../.env -- tip: ⚙️ override existing env vars with { override: true }
❯ src/server/agent/providers/pi/extensions/pi-subagents/index.test.ts (16 tests | 1 failed) 1329ms
× follows consecutive resumed attempts and keeps stale notices on their own rows 1320ms
⎯⎯⎯⎯⎯⎯⎯ Failed Tests 1 ⎯⎯⎯⎯⎯⎯⎯
FAIL src/server/agent/providers/pi/extensions/pi-subagents/index.test.ts > pi-subagents adapter > follows consecutive resumed attempts and keeps stale notices on their own rows
AssertionError: expected false to be true // Object.is equality
- Expected
+ Received
- true
+ false
❯ src/server/agent/providers/pi/extensions/pi-subagents/index.test.ts:155:78
153| ]);
154| replay.mapToolCall(call);
155| await expect.poll(() => hasChildTimeline(received, `resume-${r…
| ^
156| }
157| const oldNotice = {
Caused by: Error: Matcher did not succeed in time.
❯ src/server/agent/providers/pi/extensions/pi-subagents/index.test.ts:155:9
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/1]⎯
Test Files 1 failed (1)
Tests 1 failed | 6 passed (16)
Start at 21:09:04
Duration 1.85s (transform 247ms, setup 24ms, import 389ms, tests 1.33s, environment 0ms)
=== Final baseline d46ee655: GREEN, exit 0 ===
$ cd packages/server && npx vitest run src/server/agent/providers/pi/extensions/pi-subagents/index.test.ts --bail=1
RUN v4.1.6 <REPO_ROOT>/packages/server
Test Files 1 passed (1)
Tests 21 passed (21)
Start at 13:07:04
Duration 1.97s (transform 256ms, setup 26ms, import 355ms, tests 1.45s, environment 0ms)
=== Final typecheck, exit 0 ===
$ npm run typecheck
> paseo@0.15.4 typecheck
> npm run typecheck --workspaces --if-present
> @getpaseo/builtin-plugins@0.15.4 typecheck
> tsgo -p tsconfig.json --noEmit
> @getpaseo/expo-two-way-audio@0.15.4 typecheck
> tsgo --noEmit
> @getpaseo/highlight@0.15.4 typecheck
> tsgo --noEmit
> @getpaseo/plugin@0.15.4 typecheck
> tsgo --noEmit && npm run typecheck:examples
> @getpaseo/plugin@0.15.4 typecheck:examples
> npm --prefix ../../plugin-examples/markdown-panel install --ignore-scripts --no-audit --no-fund && tsgo -p tsconfig.examples.json --noEmit
up to date in 433ms
> @getpaseo/protocol@0.15.4 pretypecheck
> npm run generate:validators
> @getpaseo/protocol@0.15.4 generate:validators
> node scripts/generate-validation-aot.mjs
generated src/generated/validation/ws-outbound.aot.ts: envelope + 250 session message validators
> @getpaseo/protocol@0.15.4 typecheck
> tsgo --noEmit
> @getpaseo/client@0.15.4 typecheck
> tsgo --noEmit
> @getpaseo/server@0.15.4 typecheck
> tsgo -p tsconfig.server.typecheck.json --noEmit
> @getpaseo/app@0.15.4 typecheck
> tsgo --noEmit
> @getpaseo/relay@0.15.4 typecheck
> tsgo --noEmit
> @getpaseo/website@0.15.4 typecheck
> tsgo --noEmit
> @getpaseo/desktop@0.15.4 typecheck
> tsgo --noEmit -p tsconfig.json
> @getpaseo/cli@0.15.4 typecheck
> tsgo --noEmit
=== Final lint, exit 0 ===
$ npm run lint
> paseo@0.15.4 lint
> oxlint
Found 0 warnings and 0 errors.
Finished in 2.3s on 5442 files with 177 rules using 12 threads.
=== Final full formatter, exit 0; no unrelated changes ===
$ npm run format
> paseo@0.15.4 format
> oxfmt .
Finished in 2684ms on 5928 files using 12 threads.
$ git diff --check
(exit 0; no output)