From 58ad53e8a326dbc94b844fc8bf96e4cebb5730e7 Mon Sep 17 00:00:00 2001 From: Harsh Date: Thu, 12 Mar 2026 01:58:55 +0530 Subject: [PATCH] fix: close remaining Bugbot findings Carry legacy project ownership through recovery, drop the redundant dashboard worker filter, and remove the now-unused orchestrator helper so the PR branch stays aligned with the shared per-project orchestration contract. --- .../src/__tests__/recovery-actions.test.ts | 23 +++++++++++++++++++ packages/core/src/recovery/actions.ts | 1 + packages/web/src/app/page.tsx | 5 +--- packages/web/src/lib/project-utils.ts | 16 ------------- 4 files changed, 25 insertions(+), 20 deletions(-) diff --git a/packages/core/src/__tests__/recovery-actions.test.ts b/packages/core/src/__tests__/recovery-actions.test.ts index 1f9cfc392..546262dd6 100644 --- a/packages/core/src/__tests__/recovery-actions.test.ts +++ b/packages/core/src/__tests__/recovery-actions.test.ts @@ -126,6 +126,29 @@ describe("recoverSession", () => { expect(metadata?.["recoveredAt"]).toBeUndefined(); }); + it("preserves project ownership when legacy metadata omits the project field", async () => { + rootDir = join(tmpdir(), `ao-recovery-${randomUUID()}`); + mkdirSync(rootDir, { recursive: true }); + mkdirSync(join(rootDir, "project"), { recursive: true }); + writeFileSync(join(rootDir, "agent-orchestrator.yaml"), "projects: {}\n", "utf-8"); + + const config = makeConfig(rootDir); + const registry = makeRegistry(); + const assessment = makeAssessment({ + rawMetadata: { + branch: "feature/recover", + worktree: join(rootDir, "project"), + status: "needs_input", + }, + }); + const context = makeContext(rootDir); + + const result = await recoverSession(assessment, config, registry, context); + + expect(result.success).toBe(true); + expect(result.session?.projectId).toBe("app"); + }); + it("returns the max-attempt reason when recovery escalates", async () => { rootDir = join(tmpdir(), `ao-recovery-${randomUUID()}`); mkdirSync(rootDir, { recursive: true }); diff --git a/packages/core/src/recovery/actions.ts b/packages/core/src/recovery/actions.ts index 11d4bb762..1df1b5681 100644 --- a/packages/core/src/recovery/actions.ts +++ b/packages/core/src/recovery/actions.ts @@ -72,6 +72,7 @@ export async function recoverSession( }; const session = sessionFromMetadata(sessionId, updatedMetadata, { + projectId: assessment.projectId, status: preservedStatus, runtimeHandle: assessment.runtimeHandle, lastActivityAt: new Date(), diff --git a/packages/web/src/app/page.tsx b/packages/web/src/app/page.tsx index 0e081d39c..87f4c301a 100644 --- a/packages/web/src/app/page.tsx +++ b/packages/web/src/app/page.tsx @@ -3,7 +3,6 @@ import type { Metadata } from "next"; export const dynamic = "force-dynamic"; import { Dashboard } from "@/components/Dashboard"; import type { DashboardSession } from "@/lib/types"; -import { isOrchestratorSession } from "@composio/ao-core"; import { getServices, getSCM } from "@/lib/services"; import { sessionToDashboard, @@ -60,9 +59,7 @@ export default async function Home(props: { searchParams: Promise<{ project?: st orchestrators = listDashboardOrchestrators(visibleSessions, config.projects); - const coreSessions = filterWorkerSessions(allSessions, projectFilter, config.projects).filter( - (session) => !isOrchestratorSession(session), - ); + const coreSessions = filterWorkerSessions(allSessions, projectFilter, config.projects); sessions = coreSessions.map(sessionToDashboard); const metaTimeout = new Promise((resolve) => setTimeout(resolve, 3_000)); diff --git a/packages/web/src/lib/project-utils.ts b/packages/web/src/lib/project-utils.ts index 4089a7393..8b0868b48 100644 --- a/packages/web/src/lib/project-utils.ts +++ b/packages/web/src/lib/project-utils.ts @@ -22,22 +22,6 @@ function matchesProject( return false; } -export function findOrchestratorSessionId( - sessions: T[], - projectFilter: string | null | undefined, - projects: Record, -): string | null { - if (projectFilter && projectFilter !== "all") { - const session = sessions.find( - (s) => isOrchestratorSession(s) && matchesProject(s, projectFilter, projects), - ); - return session?.id ?? null; - } - - const session = sessions.find((s) => isOrchestratorSession(s)); - return session?.id ?? null; -} - export function filterWorkerSessions( sessions: T[], projectFilter: string | null | undefined,