From 1a0fdc6f311b628be7a5b203368b4e6eb855d6f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9B=B7=E7=94=B5=E8=8A=BD=E8=A1=A3?= Date: Sun, 19 Apr 2026 15:01:41 -0400 Subject: [PATCH] Collapse the GET /api/agents/me/standup duplicate to one module MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two registrations of the same route lived in the codebase: one inline in routes-agents.ts that filtered three full company-wide SELECTs in memory, and the dedicated routes-agents-standup.ts that uses targeted standup-shaped DB queries. The router keeps the first match so the inline copy was serving every request and the focused module was dead code — any future fix to standup would have silently missed production. Remove the inline standup handler and its three companion helpers (`listAgentsForStandup`, `listTasksForStandup`, `listApprovalsForStandup`) plus the unused `AgentStandupRow` type. The dedicated standup module is now the single source of truth. --- src/main/api-server/routes-agents.ts | 107 +------------------------- src/main/database-base.ts | 3 - src/main/database/agents-hierarchy.ts | 24 ------ src/main/database/approvals.ts | 19 ----- src/main/database/task-queries.ts | 18 ----- 5 files changed, 4 insertions(+), 167 deletions(-) diff --git a/src/main/api-server/routes-agents.ts b/src/main/api-server/routes-agents.ts index 659b2530..17d5ca7c 100644 --- a/src/main/api-server/routes-agents.ts +++ b/src/main/api-server/routes-agents.ts @@ -23,109 +23,10 @@ export function registerAgentRoutes(server: AgentApiServer) { server.json(res, 200, { agent, chainOfCommand, directReports }); }); - server.addRoute("GET", "/api/agents/me/standup", (_req, res, _params, token) => { - const agent = db.getAgent(token.agentId); - if (!agent || agent.companyId !== token.companyId) { - server.json(res, 404, { error: "Agent not found" }); - return; - } - - const companyAgents = db.listAgentsForStandup(token.companyId); - const companyTasks = db.listTasksForStandup(token.companyId); - const companyApprovals = db.listApprovalsForStandup(token.companyId); - - const myTasks = companyTasks.filter((t) => t.assigneeAgentId === token.agentId); - const directReports = companyAgents.filter((a) => a.reportsTo === token.agentId); - const reportIds = new Set(directReports.map((a) => a.id)); - const reportTasks = companyTasks.filter((t) => t.assigneeAgentId && reportIds.has(t.assigneeAgentId)); - - const tasksAwaitingMyReview = reportTasks.filter((t) => t.status === "in_review"); - const blockedReportTasks = reportTasks.filter((t) => t.status === "blocked"); - const myBlockedTasks = myTasks.filter((t) => t.status === "blocked"); - const myInProgressTasks = myTasks.filter((t) => t.status === "in_progress"); - const myTodoTasks = myTasks.filter((t) => t.status === "todo"); - - const allRelevantTaskIds = new Set([...myTasks.map((t) => t.id), ...reportTasks.map((t) => t.id)]); - const twoHoursAgo = new Date(Date.now() - 2 * 60 * 60 * 1000).toISOString(); - const recentComments = db.listRecentTaskComments([...allRelevantTaskIds], twoHoursAgo, 20); - - const myPendingApprovals = companyApprovals.filter( - (a) => a.requestedByAgentId === token.agentId && a.state === "pending", - ); - const myResolvedApprovals = companyApprovals.filter( - (a) => a.requestedByAgentId === token.agentId && a.state !== "pending", - ).slice(0, 5); - - const agentNameMap = new Map(companyAgents.map((a) => [a.id, a.name])); - const taskTitleMap = new Map(companyTasks.map((t) => [t.id, t.title])); - const socialAccounts = db.listSocialAccounts(token.companyId); - - server.json(res, 200, { - agent: { id: agent.id, name: agent.name, role: agent.role, status: agent.status }, - summary: { - tasksAwaitingReview: tasksAwaitingMyReview.length, - blockedReportTasks: blockedReportTasks.length, - myBlockedTasks: myBlockedTasks.length, - myInProgress: myInProgressTasks.length, - myTodo: myTodoTasks.length, - pendingApprovals: myPendingApprovals.length, - directReports: directReports.length, - }, - tasksAwaitingReview: tasksAwaitingMyReview.map((t) => ({ - id: t.id, - title: t.title, - assignee: agentNameMap.get(t.assigneeAgentId ?? "") ?? "Unknown", - priority: t.priority, - })), - blockedTasks: [...blockedReportTasks, ...myBlockedTasks].map((t) => ({ - id: t.id, - title: t.title, - assignee: agentNameMap.get(t.assigneeAgentId ?? "") ?? "Unknown", - priority: t.priority, - })), - recentComments: recentComments.map((c) => ({ - taskId: c.taskId, - taskTitle: taskTitleMap.get(c.taskId) ?? "", - author: c.authorName, - body: c.body.slice(0, 200), - createdAt: c.createdAt, - })), - pendingApprovals: myPendingApprovals.map((a) => ({ - id: a.id, - type: a.type, - summary: a.payloadSummary, - state: a.state, - })), - resolvedApprovals: myResolvedApprovals.map((a) => ({ - id: a.id, - type: a.type, - summary: a.payloadSummary, - state: a.state, - decisionNote: a.decisionNote, - })), - reportStatuses: directReports.map((r) => { - const rTasks = companyTasks.filter((t) => t.assigneeAgentId === r.id); - return { - id: r.id, - name: r.name, - role: r.role, - status: r.status, - tasksInProgress: rTasks.filter((t) => t.status === "in_progress").length, - tasksInReview: rTasks.filter((t) => t.status === "in_review").length, - tasksBlocked: rTasks.filter((t) => t.status === "blocked").length, - tasksDone: rTasks.filter((t) => t.status === "done").length, - }; - }), - socialAccounts: socialAccounts.map((a) => ({ - id: a.id, - platform: a.platform, - accountName: a.accountName, - displayName: a.displayName, - status: a.status, - requireApproval: a.requireApproval, - })), - }); - }); + // GET /api/agents/me/standup lives in routes-agents-standup.ts, which + // uses targeted standup-shaped DB queries. The inline duplicate that + // used to sit here was dead code — the router keeps the first match + // and the standup module is registered separately in api-server/index.ts. server.addRoute("GET", "/api/agents/me/metrics", (_req, res, _params, token) => { const companyMetrics = db.getCompanyMetrics(token.companyId, 30); diff --git a/src/main/database-base.ts b/src/main/database-base.ts index 54f670a2..d8a38392 100644 --- a/src/main/database-base.ts +++ b/src/main/database-base.ts @@ -233,7 +233,6 @@ export class AppDatabaseBase { getCeoForCompany(companyId: string) { return this.agents.getCeoForCompany(companyId); } listActiveAgentsForCompany(companyId: string) { return this.agents.listActiveAgentsForCompany(companyId); } listAgentsForApi(companyId: string) { return this.agents.listAgentsForApi(companyId); } - listAgentsForStandup(companyId: string) { return this.agents.listAgentsForStandup(companyId); } listDirectReportsForStandup(companyId: string, managerId: string) { return this.agents.listDirectReportsForStandup(companyId, managerId); } listReportStatusesForStandup(companyId: string, managerId: string) { return this.agents.listReportStatusesForStandup(companyId, managerId); } listExistingAgentsForHireCheck(companyId: string) { return this.agents.listExistingAgentsForHireCheck(companyId); } @@ -276,7 +275,6 @@ export class AppDatabaseBase { renewTaskLease(taskId: string, runId: string) { return this.tasks.renewTaskLease(taskId, runId); } listAgentTasks(agentId: string, companyId: string, statuses: string[]) { return this.tasks.listAgentTasks(agentId, companyId, statuses); } listTasksByStatuses(companyId: string, statuses: string[]) { return this.tasks.listTasksByStatuses(companyId, statuses); } - listTasksForStandup(companyId: string) { return this.tasks.listTasksForStandup(companyId); } listAssigneeTasksForStandup(companyId: string, agentId: string) { return this.tasks.listAssigneeTasksForStandup(companyId, agentId); } listTasksForAssignees(companyId: string, assigneeIds: string[]) { return this.tasks.listTasksForAssignees(companyId, assigneeIds); } listReviewTasksForManager(managerId: string, companyId: string) { return this.tasks.listReviewTasksForManager(managerId, companyId); } @@ -334,7 +332,6 @@ export class AppDatabaseBase { listApprovalsForTask(taskId: string) { return this.approvalsMod.listApprovalsForTask(taskId); } listTasksForApproval(approvalId: string) { return this.approvalsMod.listTasksForApproval(approvalId); } linkManyTasksToApproval(approvalId: string, taskIds: string[], companyId: string, linkedByAgentId?: string | null) { this.approvalsMod.linkManyTasksToApproval(approvalId, taskIds, companyId, linkedByAgentId); } - listApprovalsForStandup(companyId: string) { return this.approvalsMod.listApprovalsForStandup(companyId); } listAgentPendingApprovals(companyId: string, agentId: string) { return this.approvalsMod.listAgentPendingApprovals(companyId, agentId); } listAgentResolvedApprovals(companyId: string, agentId: string, limit?: number) { return this.approvalsMod.listAgentResolvedApprovals(companyId, agentId, limit); } listPendingHireApprovalPayloads(companyId: string) { return this.approvalsMod.listPendingHireApprovalPayloads(companyId); } diff --git a/src/main/database/agents-hierarchy.ts b/src/main/database/agents-hierarchy.ts index 2db09557..0dd8781a 100644 --- a/src/main/database/agents-hierarchy.ts +++ b/src/main/database/agents-hierarchy.ts @@ -35,18 +35,6 @@ export type AgentApiRow = { updatedAt: string; }; -/** - * Slim agent projection used by the me/standup aggregation. - */ -export type AgentStandupRow = { - id: string; - companyId: string; - name: string; - role: string; - status: string; - reportsTo: string | null; - department: string | null; -}; export function buildAgentHierarchyMethods( getDb: () => DatabaseSync, @@ -126,18 +114,6 @@ export function buildAgentHierarchyMethods( ).map((a) => ({ ...a, heartbeatEnabled: Boolean(a.heartbeatEnabled) })) as AgentApiRow[]; }, - /** - * Returns the slim standup projection used by - * GET /api/agents/me/standup — only the fields that feed the - * aggregation (id, name, role, status, reportsTo, department). - */ - listAgentsForStandup(companyId: string): AgentStandupRow[] { - return readAll( - "select id, company_id as companyId, name, role, status, reports_to as reportsTo, department from agents where company_id = ? order by created_at asc", - companyId, - ); - }, - /** * Returns the slim direct-reports projection used by * GET /api/agents/me/standup — only (id, name, role, status). diff --git a/src/main/database/approvals.ts b/src/main/database/approvals.ts index e203eae2..b5b74231 100644 --- a/src/main/database/approvals.ts +++ b/src/main/database/approvals.ts @@ -179,25 +179,6 @@ export function buildApprovalMethods( ); }, - /** - * Returns the slim approval projection used by - * GET /api/agents/me/standup. Ordered by created_at desc. - */ - listApprovalsForStandup(companyId: string): Array<{ - id: string; - companyId: string; - type: string; - requestedByAgentId: string | null; - state: string; - payloadSummary: string | null; - decisionNote: string | null; - }> { - return readAll( - "select id, company_id as companyId, type, requested_by_agent_id as requestedByAgentId, state, payload_summary as payloadSummary, decision_note as decisionNote from approvals where company_id = ? order by created_at desc", - companyId, - ); - }, - /** * Returns pending `hire_agent` approval payloads for a company, used * during hire-request creation to detect duplicate names across diff --git a/src/main/database/task-queries.ts b/src/main/database/task-queries.ts index 7168a8f0..4d7fca43 100644 --- a/src/main/database/task-queries.ts +++ b/src/main/database/task-queries.ts @@ -82,24 +82,6 @@ export function buildTaskQueryMethods( ); }, - /** - * Returns the slim task projection used by the me/standup aggregation. - * Ordered by created_at desc. - */ - listTasksForStandup(companyId: string): Array<{ - id: string; - companyId: string; - title: string; - assigneeAgentId: string | null; - status: string; - priority: string; - }> { - return readAll( - "select id, company_id as companyId, title, assignee_agent_id as assigneeAgentId, status, priority from tasks where company_id = ? order by created_at desc", - companyId, - ); - }, - /** * Returns tasks filtered by status. */