Codemod db pass-through calls to domain modules: tasks + agents (C1 part 1/3)

Rewrite ~525 call sites from `db.<method>()` to `db.<module>.<method>()`
for tasks and agents domains (including `getAgent`/`findAgent`/`getTask`
which live on the `entityCrud` field). Delete the corresponding
pass-through methods on AppDatabaseBase. Structural types that narrowed
`db` to a bag of flat methods (CapabilityMatchDb, EscalationDeps,
SweepStaleReviewsDeps) are reshaped to match the new namespace. Internal
callback references inside database.ts/database-base.ts now invoke the
owning module directly (e.g. `this.agents.saveAgent`) rather than the
deleted pass-through.

The codemod lives at scripts/codemod/remove-appdatabase-passthrough.mjs
and filters by owning module + optional explicit method list. It is
idempotent and does not re-rewrite already-namespaced calls.
This commit is contained in:
ZacharyZhang-NY committed 2026-04-20 13:17:01 -04:00
1 parent 411294afa6
commit 5e9b7acbc6
95 files changed
+1123 -793

No files matched your search

+13 -13
View File
@@ -76,7 +76,7 @@ export function executeAutomationAction(
switch (action) {
case "assign_task": {
if (ctx.taskId) {
const assignedId = db.autoAssignTask(ctx.taskId, ctx.companyId);
const assignedId = db.tasks.autoAssignTask(ctx.taskId, ctx.companyId);
if (assignedId) {
wakeAgentIfPossible(assignedId, ctx.companyId, "assignment");
}
@@ -90,7 +90,7 @@ export function executeAutomationAction(
});
if (unassigned.length > 0) {
const task = unassigned[0]!;
db.saveTask({
db.tasks.saveTask({
id: task.id,
companyId: task.companyId,
projectId: task.projectId,
@@ -120,7 +120,7 @@ export function executeAutomationAction(
logger.warn(`[automation] Skipping create_task — validation failed: ${taskValidation.errors.join("; ")}`);
break;
}
const taskId = db.saveTask({
const taskId = db.tasks.saveTask({
companyId: ctx.companyId,
projectId: ctx.projectId ?? cfg.projectId ?? null,
title,
@@ -147,7 +147,7 @@ export function executeAutomationAction(
let channelTargetId: string | null = cfg.channelTargetId ?? cfg.department ?? ctx.department ?? null;
if (channel === "department" && !channelTargetId && fromAgentId) {
try {
const sender = db.getAgent(fromAgentId);
const sender = db.entityCrud.getAgent(fromAgentId);
channelTargetId = sender.department ?? null;
} catch (err) {
logger.warn(`[automation] getAgent failed for sender ${fromAgentId}; dropping department target:`, err instanceof Error ? err.message : String(err));
@@ -195,7 +195,7 @@ export function executeAutomationAction(
if (!targetAgentId && ctx.agentId) {
const triggerType = rule.trigger ?? "";
if (triggerType === "run_completed" || triggerType === "run_failed") {
const manager = db.getManagerForAgent(ctx.agentId);
const manager = db.agents.getManagerForAgent(ctx.agentId);
targetAgentId = manager?.id ?? ctx.agentId;
} else {
targetAgentId = ctx.agentId;
@@ -221,8 +221,8 @@ export function executeAutomationAction(
case "update_task_status": {
if (!ctx.taskId) break;
const newStatus = (config.status as string) ?? "todo";
const previousTask = db.getTask(ctx.taskId);
db.advanceTaskStatus(ctx.taskId, newStatus, "Status changed by automation rule");
const previousTask = db.entityCrud.getTask(ctx.taskId);
db.tasks.advanceTaskStatus(ctx.taskId, newStatus, "Status changed by automation rule");
publishDomainChanged();
if (previousTask.status !== newStatus && rule.trigger !== "task_status_changed") {
handleTaskStatusChange(ctx.taskId, ctx.companyId, previousTask.status, newStatus);
@@ -234,17 +234,17 @@ export function executeAutomationAction(
if (!targetAgentId) break;
let agentName = "an agent";
try {
agentName = db.getAgent(targetAgentId).name;
agentName = db.entityCrud.getAgent(targetAgentId).name;
} catch (err) {
logger.warn(`[automation] getAgent failed for escalation target ${targetAgentId}; using generic label:`, err instanceof Error ? err.message : String(err));
}
let taskTitle = ctx.taskId ?? "unknown";
try {
if (ctx.taskId) taskTitle = db.getTask(ctx.taskId).title;
if (ctx.taskId) taskTitle = db.entityCrud.getTask(ctx.taskId).title;
} catch (err) {
logger.warn(`[automation] getTask failed for escalation task ${ctx.taskId}; using id as title:`, err instanceof Error ? err.message : String(err));
}
const manager = db.getManagerForAgent(targetAgentId);
const manager = db.agents.getManagerForAgent(targetAgentId);
const escalationBody = (config.body as string) ?? `Task "${taskTitle}" needs your attention. ${agentName} has flagged this as requiring manager intervention.`;
const wakeTrigger = deriveEscalationWakeTrigger(ctx.newStatus ?? null, rule.trigger ?? null);
if (manager) {
@@ -277,7 +277,7 @@ export function executeAutomationAction(
case "cross_department_notify": {
const targetDepartment = rule.targetDepartment ?? (config.department as string);
if (!targetDepartment) break;
const departmentAgents = db.getIdleAgentsForDepartment(ctx.companyId, targetDepartment);
const departmentAgents = db.agents.getIdleAgentsForDepartment(ctx.companyId, targetDepartment);
const senderAgentId = ctx.agentId ?? departmentAgents[0]?.id;
if (senderAgentId) {
const dispatchResult = dispatchAgentMessage(
@@ -352,7 +352,7 @@ export function executeAutomationAction(
}
case "reassign_task": {
if (!ctx.taskId) break;
const newAssignee = db.autoAssignTask(ctx.taskId, ctx.companyId);
const newAssignee = db.tasks.autoAssignTask(ctx.taskId, ctx.companyId);
if (newAssignee) {
wakeAgentIfPossible(newAssignee, ctx.companyId, "assignment");
}
@@ -361,7 +361,7 @@ export function executeAutomationAction(
case "run_task": {
const targetTaskId = (config.taskId as string) ?? ctx.taskId;
if (!targetTaskId) break;
const task = db.getTask(targetTaskId);
const task = db.entityCrud.getTask(targetTaskId);
if (!task.assigneeAgentId) break;
wakeAgentIfPossible(task.assigneeAgentId, ctx.companyId, "assignment");
break;
@@ -30,7 +30,7 @@ function recordReviewFeedbackKnowledge(
if (!approval.relatedTaskId) return;
if (decision === "approved") return;
const task = db.getTask(approval.relatedTaskId);
const task = db.entityCrud.getTask(approval.relatedTaskId);
const feedback = approval.decisionNote?.trim() || "Board requested revisions before approval.";
const topic = `Review feedback: ${task.title}`;
const content = [
@@ -95,7 +95,7 @@ export function handleApprovalResolved(
});
if (approval.type === "hire_agent" && decision === "approved" && approval.relatedAgentId) {
const agent = db.getAgent(approval.relatedAgentId);
const agent = db.entityCrud.getAgent(approval.relatedAgentId);
if (agent.status === "idle") {
// Deterministic default skill kit. The CEO's operating prompt
@@ -136,7 +136,7 @@ export function handleApprovalResolved(
});
wakeAgentIfPossible(agent.id, companyId, "assignment");
const ceo = db.getCeoForCompany(companyId);
const ceo = db.agents.getCeoForCompany(companyId);
if (ceo && ceo.id !== agent.id) {
wakeAgentIfPossible(ceo.id, companyId, "assignment");
}
@@ -174,27 +174,27 @@ export function handleApprovalResolved(
}
if (approval.relatedTaskId) {
const task = db.getTask(approval.relatedTaskId);
const task = db.entityCrud.getTask(approval.relatedTaskId);
if (task.assigneeAgentId) {
const agent = db.getAgent(task.assigneeAgentId);
const agent = db.entityCrud.getAgent(task.assigneeAgentId);
if (!agent.heartbeatEnabled) {
db.setHeartbeat(task.assigneeAgentId, true, agent.heartbeatIntervalSec || 120);
db.agents.setHeartbeat(task.assigneeAgentId, true, agent.heartbeatIntervalSec || 120);
logger.info(`[deliverable-review] Resumed heartbeat for ${agent.name} after user ${decision}.`);
}
}
if (decision === "approved") {
db.advanceTaskStatus(task.id, "done", "Board approved deliverable.");
db.tasks.advanceTaskStatus(task.id, "done", "Board approved deliverable.");
handleTaskStatusChange(task.id, companyId, task.status, "done");
db.addActivity({ companyId, actor: "board", action: "deliverable.approved", entityType: "task", entityId: task.id, detail: approval.decisionNote || "Deliverable approved by board." });
} else if (decision === "rejected") {
recordReviewFeedbackKnowledge(db, approval, decision);
db.advanceTaskStatus(task.id, "in_progress", `Board rejected deliverable: ${approval.decisionNote || "No reason given."}`);
db.tasks.advanceTaskStatus(task.id, "in_progress", `Board rejected deliverable: ${approval.decisionNote || "No reason given."}`);
handleTaskStatusChange(task.id, companyId, task.status, "in_progress");
if (task.assigneeAgentId) {
wakeAgentIfPossible(task.assigneeAgentId, companyId, "revision_needed");
}
} else if (decision === "revision_requested") {
db.advanceTaskStatus(task.id, "in_progress", `Board requested revisions: ${approval.decisionNote || "No reason given."}`);
db.tasks.advanceTaskStatus(task.id, "in_progress", `Board requested revisions: ${approval.decisionNote || "No reason given."}`);
handleTaskStatusChange(task.id, companyId, task.status, "in_progress");
recordReviewFeedbackKnowledge(db, approval, decision);
if (task.assigneeAgentId) {
@@ -202,9 +202,9 @@ export function handleApprovalResolved(
}
}
} else if (approval.requestedByAgentId) {
const agent = db.getAgent(approval.requestedByAgentId);
const agent = db.entityCrud.getAgent(approval.requestedByAgentId);
if (!agent.heartbeatEnabled) {
db.setHeartbeat(approval.requestedByAgentId, true, agent.heartbeatIntervalSec || 120);
db.agents.setHeartbeat(approval.requestedByAgentId, true, agent.heartbeatIntervalSec || 120);
logger.info(`[deliverable-review] Resumed heartbeat for ${agent.name} after user ${decision}.`);
}
if (decision !== "approved") {
+2 -2
View File
@@ -23,7 +23,7 @@ export function matchesEventConfig(
if (conditions.previousStatus && ctx.previousStatus && conditions.previousStatus !== ctx.previousStatus) return false;
if (conditions.priority) {
try {
const task = ctx.taskId ? db.getTask(ctx.taskId) : null;
const task = ctx.taskId ? db.entityCrud.getTask(ctx.taskId) : null;
if (task && task.priority !== conditions.priority) return false;
// If task was deleted, skip this condition instead of rejecting the rule.
} catch (err) {
@@ -32,7 +32,7 @@ export function matchesEventConfig(
}
if (typeof conditions.budgetThresholdPct === "number" && ctx.agentId) {
try {
const agent = db.getAgent(ctx.agentId);
const agent = db.entityCrud.getAgent(ctx.agentId);
if (agent.budgetMonthlyUsd > 0) {
const utilization = agent.spentMonthlyUsd / agent.budgetMonthlyUsd;
if (utilization < (conditions.budgetThresholdPct as number) / 100) return false;
+10 -10
View File
@@ -41,9 +41,9 @@ export function handleTaskStatusChange(
const { db, wakeAgentIfPossible } = deps;
const { dispatchAutomation } = callbacks;
let task: ReturnType<typeof db.getTask> | null = null;
let task: ReturnType<typeof db.entityCrud.getTask> | null = null;
try {
task = db.getTask(taskId);
task = db.entityCrud.getTask(taskId);
} catch (error) {
deps.logger.warn(`[automation] handleTaskStatusChange: task ${taskId} not found:`, error);
return;
@@ -68,7 +68,7 @@ export function handleTaskStatusChange(
};
try {
if (task.assigneeAgentId) {
const agent = db.getAgent(task.assigneeAgentId);
const agent = db.entityCrud.getAgent(task.assigneeAgentId);
context.department = agent.department ?? undefined;
}
} catch (error) {
@@ -80,7 +80,7 @@ export function handleTaskStatusChange(
if (newStatus === "blocked") {
dispatchAutomation("task_blocked", context);
if (task.assigneeAgentId) {
const manager = db.getManagerForAgent(task.assigneeAgentId);
const manager = db.agents.getManagerForAgent(task.assigneeAgentId);
if (manager) {
wakeAgentIfPossible(manager.id, companyId, "report_blocked");
}
@@ -89,14 +89,14 @@ export function handleTaskStatusChange(
if (newStatus === "done" && task.parentId) {
try {
const siblings = db.listChildTasks(task.parentId, companyId);
const siblings = db.tasks.listChildTasks(task.parentId, companyId);
const allDone = siblings.every((entry) => entry.id === taskId ? true : entry.status === "done");
if (allDone) {
const parent = db.getTask(task.parentId);
const parent = db.entityCrud.getTask(task.parentId);
if (parent.assigneeAgentId) {
wakeAgentIfPossible(parent.assigneeAgentId, companyId, "subtask_completed");
} else {
const assignedId = db.autoAssignTask(parent.id, companyId);
const assignedId = db.tasks.autoAssignTask(parent.id, companyId);
if (assignedId) {
wakeAgentIfPossible(assignedId, companyId, "subtask_completed");
}
@@ -118,9 +118,9 @@ export function handleTaskCreated(
const { db, wakeAgentIfPossible } = deps;
const { dispatchAutomation } = callbacks;
let task: ReturnType<typeof db.getTask> | null = null;
let task: ReturnType<typeof db.entityCrud.getTask> | null = null;
try {
task = db.getTask(taskId);
task = db.entityCrud.getTask(taskId);
} catch (error) {
deps.logger.warn(`[automation] handleTaskCreated: task ${taskId} not found:`, error);
return;
@@ -134,7 +134,7 @@ export function handleTaskCreated(
});
if (!task.assigneeAgentId) {
const assignedId = db.autoAssignTask(taskId, companyId);
const assignedId = db.tasks.autoAssignTask(taskId, companyId);
if (assignedId) {
wakeAgentIfPossible(assignedId, companyId, "assignment");
}
@@ -190,10 +190,10 @@ function createDeliverableReviewApproval(
impactSummary: reviewerNote ?? "Agent completed work with important deliverables. Awaiting your final review.",
payloadJson: JSON.stringify({ documentIds: docs.map((d) => d.id), documents: docs }),
});
db.setTaskRequiresUserReview(taskId, false);
const agent = db.getAgent(agentId);
db.tasks.setTaskRequiresUserReview(taskId, false);
const agent = db.entityCrud.getAgent(agentId);
if (agent.heartbeatEnabled) {
db.setHeartbeat(agentId, false, agent.heartbeatIntervalSec || 120);
db.agents.setHeartbeat(agentId, false, agent.heartbeatIntervalSec || 120);
logger.info(`[deliverable-review] Paused heartbeat for ${agent.name} — awaiting user review.`);
}
notify({
@@ -228,7 +228,7 @@ function createDeliverableReviewApproval(
*/
function decideTerminalStatus(
db: RunCompletionDeps["db"],
agent: ReturnType<RunCompletionDeps["db"]["getAgent"]>,
agent: ReturnType<RunCompletionDeps["db"]["entityCrud"]["getAgent"]>,
companyId: string,
): "in_review" | "done" {
if (agent.reportsTo !== null) return "in_review";
@@ -247,7 +247,7 @@ function advanceToDone(
statusMessage: string,
): void {
const { db, publishDomainChanged } = deps;
db.advanceTaskStatus(taskId, "done", statusMessage);
db.tasks.advanceTaskStatus(taskId, "done", statusMessage);
callbacks.handleTaskStatusChange(taskId, run.companyId, "in_progress", "done");
publishDomainChanged();
}
@@ -268,22 +268,22 @@ function advanceToInReview(
// expected (`ai_cofounder` mode or `reviewDeliverables = true`), so
// pre-flagging here is the right semantics — the human IS the
// reviewer in those configurations.
const agent = db.getAgent(run.agentId);
const agent = db.entityCrud.getAgent(run.agentId);
if (agent.reportsTo === null) {
db.setTaskRequiresUserReview(taskId, true);
db.tasks.setTaskRequiresUserReview(taskId, true);
}
db.advanceTaskStatus(taskId, "in_review", statusMessage);
db.tasks.advanceTaskStatus(taskId, "in_review", statusMessage);
callbacks.handleTaskStatusChange(taskId, run.companyId, "in_progress", "in_review");
const company = db.listSnapshot().companies.find((c) => c.id === run.companyId);
if (company?.reviewDeliverables) {
const importantDocs = findImportantDocsForRun(db, run.id, run.companyId);
if (importantDocs.length > 0) {
db.setTaskRequiresUserReview(taskId, true);
db.tasks.setTaskRequiresUserReview(taskId, true);
if (agent.heartbeatEnabled) {
db.setHeartbeat(agent.id, false, agent.heartbeatIntervalSec || 120);
db.agents.setHeartbeat(agent.id, false, agent.heartbeatIntervalSec || 120);
logger.info(`[deliverable-review] Paused heartbeat for ${agent.name} — deliverable flagged for review.`);
}
const chain = db.getChainOfCommand(agent.id);
const chain = db.agents.getChainOfCommand(agent.id);
if (chain.length === 0) {
createDeliverableReviewApproval(deps, run.companyId, taskId, run.agentId, importantDocs);
}
@@ -305,7 +305,7 @@ function advanceSuccessfulRun(
taskId: string,
statusMessage: string,
): "in_review" | "done" {
const agent = deps.db.getAgent(run.agentId);
const agent = deps.db.entityCrud.getAgent(run.agentId);
const target = decideTerminalStatus(deps.db, agent, run.companyId);
if (target === "done") {
advanceToDone(deps, callbacks, run, taskId, statusMessage);
@@ -327,9 +327,9 @@ export function processRunCompletion(
const { dispatchAutomation, handleTaskStatusChange } = callbacks;
if (!run.taskId) return;
let task: ReturnType<typeof db.getTask> | null = null;
let task: ReturnType<typeof db.entityCrud.getTask> | null = null;
try {
task = db.getTask(run.taskId);
task = db.entityCrud.getTask(run.taskId);
} catch (error) {
deps.logger.warn(`[automation] processRunCompletion: task ${run.taskId} not found for run ${run.id}:`, error);
return;
@@ -363,7 +363,7 @@ export function processRunCompletion(
// the task in the manager's blocker queue with the diagnostic
// comment attached; the manager patches it back to `todo` (or
// `cancelled`) after deciding how to proceed.
db.advanceTaskStatus(
db.tasks.advanceTaskStatus(
task.id,
"blocked",
`${taskType} task run completed but no deliverable artifact was detected. Flagging for manager review.`,
@@ -371,8 +371,8 @@ export function processRunCompletion(
handleTaskStatusChange(task.id, run.companyId, "in_progress", "blocked");
// Wake manager for review
try {
const agent = db.getAgent(run.agentId);
const chain = db.getChainOfCommand(agent.id);
const agent = db.entityCrud.getAgent(run.agentId);
const chain = db.agents.getChainOfCommand(agent.id);
if (chain.length > 0) {
wakeAgentIfPossible(chain[0].id, run.companyId, "review_needed");
}
@@ -414,7 +414,7 @@ export function processRunCompletion(
runId: run.id,
};
try {
const agent = db.getAgent(run.agentId);
const agent = db.entityCrud.getAgent(run.agentId);
automationContext.department = agent.department ?? undefined;
} catch (err) {
deps.logger.warn(`[automation] getAgent failed for ${run.agentId} during run completion; dropping department from context:`, err instanceof Error ? err.message : String(err));
@@ -424,14 +424,14 @@ export function processRunCompletion(
dispatchAutomation("run_completed", automationContext);
if (task.parentId) {
try {
const siblings = db.listChildTasks(task.parentId, run.companyId);
const siblings = db.tasks.listChildTasks(task.parentId, run.companyId);
const allSiblingsDoneOrReview = siblings.every((entry) => entry.status === "done" || entry.status === "in_review");
if (allSiblingsDoneOrReview) {
const parent = db.getTask(task.parentId);
const parent = db.entityCrud.getTask(task.parentId);
if (parent.assigneeAgentId) {
wakeAgentIfPossible(parent.assigneeAgentId, run.companyId, "subtask_completed");
} else {
const assignedId = db.autoAssignTask(parent.id, run.companyId);
const assignedId = db.tasks.autoAssignTask(parent.id, run.companyId);
if (assignedId) {
wakeAgentIfPossible(assignedId, run.companyId, "subtask_completed");
}
@@ -452,7 +452,7 @@ export function processRunCompletion(
// just read the post-run spend and fire if it crosses 80%.
if (isSuccess) {
try {
const agent = db.getAgent(run.agentId);
const agent = db.entityCrud.getAgent(run.agentId);
if (agent.budgetMonthlyUsd > 0) {
const utilization = agent.spentMonthlyUsd / agent.budgetMonthlyUsd;
if (utilization >= 0.8) {
+3 -3
View File
@@ -59,7 +59,7 @@ export function resolveWorkflowStepActor(
companyId: string,
): AgentRecord | null {
if (step.assigneeAgentId) {
const agent = db.getAgent(step.assigneeAgentId);
const agent = db.entityCrud.getAgent(step.assigneeAgentId);
if (agent.companyId !== companyId) {
throw new Error(`Workflow step assignee ${step.assigneeAgentId} does not belong to company ${companyId}.`);
}
@@ -67,9 +67,9 @@ export function resolveWorkflowStepActor(
}
if (step.assigneeDepartment) {
const idleAgents = db.getIdleAgentsForDepartment(companyId, step.assigneeDepartment);
const idleAgents = db.agents.getIdleAgentsForDepartment(companyId, step.assigneeDepartment);
if (idleAgents.length > 0) {
return db.getAgent(idleAgents[0]!.id);
return db.entityCrud.getAgent(idleAgents[0]!.id);
}
}