docs: document subagent registry lifecycle tests · openclaw/openclaw@ec4a871
steipete
·
2026-06-05
·
via Recent Commits to openclaw:main
| Original file line number | Diff line number | Diff line change |
|---|
|
| 1 | +// Subagent registry lifecycle tests cover completion, cleanup, announce retry, |
| 2 | +// detached task status, and resource retirement around child-run endings. |
1 | 3 | import { beforeEach, describe, expect, it, vi } from "vitest"; |
2 | 4 | import type { CallGatewayOptions } from "../gateway/call.js"; |
3 | 5 | import { |
@@ -203,6 +205,8 @@ async function runNoReplyMirrorScenario(params: {
|
203 | 205 | idempotencyKey?: string; |
204 | 206 | idempotencyKeyForEntry?: (entry: SubagentRunRecord) => string; |
205 | 207 | }): Promise<SubagentRunRecord> { |
| 208 | +// A failed direct announce can still be mirrored from the requester history; |
| 209 | +// the idempotency key prevents stale or unrelated assistant text from winning. |
206 | 210 | const entry = createRunEntry({ |
207 | 211 | endedAt: 4_000, |
208 | 212 | expectsCompletionMessage: true, |
|
| Original file line number | Diff line number | Diff line change |
|---|
|
| 1 | +// Subagent registry query tests cover liveness, descendant counting, requester |
| 2 | +// lookup, and stale-row handling for in-memory run snapshots. |
1 | 3 | import { describe, expect, it } from "vitest"; |
2 | 4 | import { |
3 | 5 | countActiveRunsForSessionFromRuns, |
@@ -223,7 +225,7 @@ describe("subagent registry query regressions", () => {
|
223 | 225 | }); |
224 | 226 | |
225 | 227 | it("regression nested parallel counting, traversal includes child and grandchildren pending states", () => { |
226 | | -// Regression guard: nested fan-out once under-counted grandchildren and announced too early. |
| 228 | +// Nested fan-out once under-counted grandchildren and announced too early. |
227 | 229 | const parentSessionKey = "agent:main:subagent:parent-nested"; |
228 | 230 | const middleSessionKey = `${parentSessionKey}:subagent:middle`; |
229 | 231 | const runs = toRunMap([ |
|
| Original file line number | Diff line number | Diff line change |
|---|
|
| 1 | +// Subagent registry read-context tests cover the indexed snapshot used by hot |
| 2 | +// prompt/control paths instead of repeatedly scanning the run map. |
1 | 3 | import { describe, expect, it } from "vitest"; |
2 | 4 | import { |
3 | 5 | buildSubagentRunReadIndexFromRuns, |
@@ -160,6 +162,8 @@ describe("subagent registry read index", () => {
|
160 | 162 | }); |
161 | 163 | |
162 | 164 | it("keeps one snapshot stable for the lifetime of the context", () => { |
| 165 | +// Read indexes are process-local snapshots; callers can reuse them through |
| 166 | +// one prompt assembly without observing later registry mutations. |
163 | 167 | const root = "agent:main:main"; |
164 | 168 | const runs = toRunMap([ |
165 | 169 | makeRun({ |
|
| Original file line number | Diff line number | Diff line change |
|---|
|
| 1 | +// Announce loop-guard tests prove deferred subagent delivery eventually gives |
| 2 | +// up instead of retrying forever after gateway delivery keeps returning false. |
1 | 3 | import { afterEach, beforeAll, beforeEach, describe, expect, test, vi } from "vitest"; |
2 | 4 | import type { SubagentRunRecord } from "./subagent-registry.types.js"; |
3 | 5 | |
4 | | -/** |
5 | | - * Regression test for #18264: Gateway announcement delivery loop. |
6 | | - * |
7 | | - * When `runSubagentAnnounceFlow` repeatedly returns `false` (deferred), |
8 | | - * `finalizeSubagentCleanup` must eventually give up rather than retrying |
9 | | - * forever via the max-retry and expiration guards. |
10 | | - */ |
11 | | - |
12 | 6 | const mocks = vi.hoisted(() => ({ |
13 | 7 | getRuntimeConfig: vi.fn(() => ({ |
14 | 8 | session: { store: "/tmp/test-store", mainKey: "main" }, |
@@ -200,7 +194,8 @@ describe("announce loop guard (#18264)", () => {
|
200 | 194 | const entry = createEntry(Date.now()); |
201 | 195 | mocks.loadSubagentRegistryFromSqlite.mockReturnValue(new Map([[entry.runId, entry]])); |
202 | 196 | |
203 | | -// Initialization attempts resume once, then gives up for exhausted entries. |
| 197 | +// Initialization attempts one resume, then relies on expiry/retry-budget |
| 198 | +// guards so old pending rows do not loop after restart. |
204 | 199 | const beforeInit = Date.now(); |
205 | 200 | registry.initSubagentRegistry(); |
206 | 201 | await flushAsync(); |
|
| Original file line number | Diff line number | Diff line change |
|---|
|
| 1 | +// Lifecycle retry-grace e2e tests cover completion delivery retry behavior when |
| 2 | +// lifecycle events race gateway waits or transient announce failures. |
1 | 3 | import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; |
2 | 4 | import { testing as subagentAnnounceDeliveryTesting } from "./subagent-announce-delivery.js"; |
3 | 5 | import { testing as subagentAnnounceOutputTesting } from "./subagent-announce-output.js"; |
@@ -220,6 +222,8 @@ describe("subagent registry lifecycle error grace", () => {
|
220 | 222 | }; |
221 | 223 | |
222 | 224 | const waitForCleanupHandledFalse = async (runId: string) => { |
| 225 | +// Cleanup can be released asynchronously after announce failure; poll fake |
| 226 | +// time until the retry-grace state is observable. |
223 | 227 | for (let attempt = 0; attempt < 40; attempt += 1) { |
224 | 228 | const run = mod |
225 | 229 | .listSubagentRunsForRequester(MAIN_REQUESTER_SESSION_KEY) |
|
| Original file line number | Diff line number | Diff line change |
|---|
|
| 1 | +// Nested subagent registry e2e tests cover requester/controller relationships |
| 2 | +// across orchestrator and leaf child sessions. |
1 | 3 | import { afterEach, beforeAll, describe, expect, it, vi } from "vitest"; |
2 | 4 | import "./subagent-registry.mocks.shared.js"; |
3 | 5 | |
@@ -76,7 +78,7 @@ describe("subagent registry nested agent tracking", () => {
|
76 | 78 | |
77 | 79 | it("announce uses requesterSessionKey to route to the correct parent", () => { |
78 | 80 | const { registerSubagentRun } = subagentRegistry; |
79 | | -// Register a sub-sub-agent whose parent is a sub-agent |
| 81 | +// Register a sub-sub-agent whose parent is a sub-agent. |
80 | 82 | registerSubagentRun({ |
81 | 83 | runId: "run-subsub", |
82 | 84 | childSessionKey: "agent:main:subagent:orch:subagent:child", |
@@ -87,9 +89,8 @@ describe("subagent registry nested agent tracking", () => {
|
87 | 89 | label: "nested-leaf", |
88 | 90 | }); |
89 | 91 | |
90 | | -// When announce fires for the sub-sub-agent, it should target the sub-agent (depth-1), |
91 | | -// NOT the main session. The registry entry's requesterSessionKey ensures this. |
92 | | -// We verify the registry entry has the correct requesterSessionKey. |
| 92 | +// Announce should target the depth-1 parent, not the main session. The |
| 93 | +// registry entry's requesterSessionKey carries that routing boundary. |
93 | 94 | const { listSubagentRunsForRequester } = subagentRegistry; |
94 | 95 | const orchRuns = listSubagentRunsForRequester("agent:main:subagent:orch"); |
95 | 96 | expect(orchRuns).toHaveLength(1); |
|
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。