From 89a766ea35e53418cab85497a5db4ce5bd6abc8c Mon Sep 17 00:00:00 2001 From: Hieu Le Date: Thu, 5 Nov 2026 00:11:10 +0100 Subject: [PATCH] perf(client-runtime): bucket workflow phase members once --- .../src/state/subagentRuntime.test.ts | 118 +++++++++++++----- .../src/state/subagentRuntime.ts | 16 ++- 2 files changed, 100 insertions(+), 34 deletions(-) diff --git a/packages/client-runtime/src/state/subagentRuntime.test.ts b/packages/client-runtime/src/state/subagentRuntime.test.ts index c40c9fe42d81e..b45dffadabf6c 100644 --- a/packages/client-runtime/src/state/subagentRuntime.test.ts +++ b/packages/client-runtime/src/state/subagentRuntime.test.ts @@ -122,51 +122,108 @@ describe("projectedSubagentsToRuntime workflow expansion", () => { }); describe("deriveWorkflowGroups over a dynamic workflow", () => { - it("groups members under their phases and counts the run", () => { - const groups = groupsOf([ + it("groups and orders members without changing the plan, titles, or phase liveness", () => { + const runtime = projectedSubagentsToRuntime([ subagent({ workflow: { name: "probe-wf", phases: [ + { index: 10, title: "Empty" }, + { index: 2, title: "Old Beta" }, { index: 1, title: "Alpha" }, { index: 2, title: "Beta" }, ], agents: [ - member({ state: "completed" }), + member({ index: 3, label: "alpha:three", state: "completed" }), member({ index: 2, label: "alpha:two", state: "failed" }), - member({ index: 3, label: "beta:one", phaseIndex: 2, phaseTitle: "Beta" }), - member({ - index: 4, - label: "beta:two", - phaseIndex: 2, - phaseTitle: "Beta", - state: "queued", - }), + member({ index: 2, label: "alpha:tie", state: "completed" }), + member({ index: 4, label: "cancelled" }), + member({ index: 5, label: "interrupted" }), + member({ index: 3, label: "unphased:three", phaseIndex: undefined }), + member({ index: 1, label: "unphased:null", phaseIndex: undefined }), + member({ index: 0, label: "unphased:zero", phaseIndex: undefined }), + member({ index: 2, label: "beta:queued", phaseIndex: 2, state: "queued" }), + member({ index: 1, label: "idle", phaseIndex: 3, phaseTitle: "Resume" }), + member({ index: 1, label: "waiting", phaseIndex: 4, phaseTitle: "Wait" }), + member({ index: 1, label: "dynamic", phaseIndex: 5, phaseTitle: undefined }), ], }, }), + ]).map((agent) => { + if (agent.title === "unphased:null") return { ...agent, agentIndex: null }; + if (agent.title === "cancelled") return { ...agent, status: "cancelled" as const }; + if (agent.title === "interrupted") return { ...agent, status: "interrupted" as const }; + if (agent.title === "idle") return { ...agent, status: "idle" as const }; + if (agent.title === "waiting") return { ...agent, status: "waiting" as const }; + return agent; + }); + const before = structuredClone(runtime); + for (const agent of runtime) { + for (const phase of agent.phases) Object.freeze(phase); + Object.freeze(agent.phases); + Object.freeze(agent); + } + Object.freeze(runtime); + const groups = deriveWorkflowGroups(runtime); + expect(groups.map((group) => group.workflow.id)).toEqual(["coordinator"]); + const group = groups[0]!; + expect( + group.phases.map((phase) => [ + phase.index, + phase.title, + phase.state, + phase.settledCount, + phase.members.map((agent) => agent.title), + ]), + ).toEqual([ + [ + 1, + "Alpha", + "done", + 5, + ["alpha:two", "alpha:tie", "alpha:three", "cancelled", "interrupted"], + ], + [2, "Beta", "running", 0, ["beta:queued"]], + [3, "Resume", "running", 0, ["idle"]], + [4, "Wait", "running", 0, ["waiting"]], + [5, "Phase 6", "running", 0, ["dynamic"]], + [10, "Empty", "pending", 0, []], ]); - const group = groups[0]; - expect(group?.phases.map((phase) => [phase.title, phase.state])).toEqual([ - ["Alpha", "done"], - ["Beta", "running"], + expect(group.unphasedMembers.map((agent) => agent.title)).toEqual([ + "unphased:null", + "unphased:zero", + "unphased:three", ]); - expect(group?.phases.map((phase) => phase.settledCount)).toEqual([2, 0]); - expect(group?.phases.flatMap((phase) => phase.members)).toHaveLength(4); - expect(group?.unphasedMembers).toEqual([]); + expect(group.workflow).toBe(runtime[0]); + for (const agent of [ + ...group.phases.flatMap((phase) => phase.members), + ...group.unphasedMembers, + ]) { + expect(agent).toBe(runtime.find((input) => input.title === agent.title)); + } + expect(runtime).toEqual(before); }); - it.each([false, true])("keeps a reported phase title, missing title first=%s", (missingFirst) => { - const agents = [member({ phaseTitle: "Review" }), member({ index: 2, phaseTitle: undefined })]; - const groups = groupsOf([ - subagent({ workflow: { phases: [], agents: missingFirst ? agents.toReversed() : agents } }), - ]); - expect(groups[0]?.phases[0]?.title).toBe("Review"); + it.each([ + ["Review", undefined, "Review"], + [undefined, "Review", "Review"], + ["Old", "Review", "Review"], + [undefined, undefined, "Phase 2"], + ["Review", "", ""], + ])("keeps reported phase titles %s then %s", (first, second, expected) => { + const agents = [member({ phaseTitle: first }), member({ index: 2, phaseTitle: second })]; + const groups = groupsOf([subagent({ workflow: { phases: [], agents } })]); + expect(groups[0]?.phases[0]?.title).toBe(expected); }); it("keeps unphased members with their workflow and excludes unrelated agents", () => { const runtime = projectedSubagentsToRuntime([ subagent({ id: "ordinary" }), + subagent({ + id: "earlier", + startedAt: at("2026-08-01T09:00:00.000Z"), + workflow: { phases: [], agents: [member({ phaseIndex: undefined })] }, + }), subagent({ id: "__proto__", workflow: { @@ -178,11 +235,16 @@ describe("deriveWorkflowGroups over a dynamic workflow", () => { subagent({ id: "orphan", workflow: { phases: [], agents: [member()] } }), ]); const groups = deriveWorkflowGroups(runtime.filter((agent) => agent.id !== "orphan")); - expect(groups.map((group) => group.workflow.id)).toEqual(["__proto__", "constructor"]); - expect(groups[0]?.phases).toEqual([]); - expect(groups[0]?.unphasedMembers.map((agent) => agent.title)).toEqual(["alpha:one"]); + expect(groups.map((group) => group.workflow.id)).toEqual([ + "earlier", + "__proto__", + "constructor", + ]); + expect(groups[0]?.unphasedMembers.map((agent) => agent.id)).toEqual(["earlier:agent:1"]); expect(groups[1]?.phases).toEqual([]); - expect(groups[1]?.unphasedMembers).toEqual([]); + expect(groups[1]?.unphasedMembers.map((agent) => agent.id)).toEqual(["__proto__:agent:1"]); + expect(groups[2]?.phases).toEqual([]); + expect(groups[2]?.unphasedMembers).toEqual([]); }); }); diff --git a/packages/client-runtime/src/state/subagentRuntime.ts b/packages/client-runtime/src/state/subagentRuntime.ts index e3807362fbdf6..5d9ac850d6acc 100644 --- a/packages/client-runtime/src/state/subagentRuntime.ts +++ b/packages/client-runtime/src/state/subagentRuntime.ts @@ -252,7 +252,11 @@ export function deriveWorkflowGroups( // provider only admits a phase once something in it starts. Members seed // the map; the declared titles then overwrite whatever they guessed. const phaseTitles = new Map(); + const membersByPhase = new Map(); for (const member of workflowMembers) { + const phaseMembers = membersByPhase.get(member.phaseIndex); + if (phaseMembers === undefined) membersByPhase.set(member.phaseIndex, [member]); + else phaseMembers.push(member); if (member.phaseIndex === null) continue; phaseTitles.set( member.phaseIndex, @@ -265,9 +269,9 @@ export function deriveWorkflowGroups( .sort((a, b) => a.index - b.index); const phases = knownPhases.map((phase) => { - const phaseMembers = workflowMembers - .filter((member) => member.phaseIndex === phase.index) - .sort((a, b) => (a.agentIndex ?? 0) - (b.agentIndex ?? 0)); + const phaseMembers = (membersByPhase.get(phase.index) ?? []).sort( + (a, b) => (a.agentIndex ?? 0) - (b.agentIndex ?? 0), + ); const hasActiveMember = phaseMembers.some( // Idle members count as active for phase-liveness: a resumable Codex // member has not finished the phase. @@ -287,9 +291,9 @@ export function deriveWorkflowGroups( }; }); - const unphasedMembers = workflowMembers - .filter((member) => member.phaseIndex === null) - .sort((a, b) => (a.agentIndex ?? 0) - (b.agentIndex ?? 0)); + const unphasedMembers = (membersByPhase.get(null) ?? []).sort( + (a, b) => (a.agentIndex ?? 0) - (b.agentIndex ?? 0), + ); return { workflow, phases, unphasedMembers }; });