Remove final AppDatabase pass-through methods with ESLint guard (C1 part 3/3)

Migrate all remaining pass-through call sites (social, skills, budget,
entityCrud, documents, knowledge, automation, wakeup, backup, threads,
settings, snapshot, connectors, workspaces, activity) to their owning
domain modules. `AppDatabase` now carries only three methods:

  - createHireRequest (multi-module composite: agents + approvalsMod)
  - rollbackConfigRevision (multi-module composite: documents + entityCrud)
  - queryAll (raw-SQL escape hatch, @internal)

The vault/env-binding infrastructure previously passed argument-by-
argument into `entityCrud.saveSecret`/`resolveSecretEnv` now binds at
module construction, letting callers invoke `db.entityCrud.saveSecret({...})`
without carrying vault plumbing.

Structural types that narrowed AppDatabase to a bag of flat methods
(`ConnectorModelPolicyDeps` callers, `CapabilityMatchDb`, test mocks in
budget-enforcement / budget-incidents / task-quality-gates /
failure-escalation / task-quality-gates-sweep) are reshaped to the new
namespace. `connectorPolicyDepsFromDb` replaces the old pattern of
handing the whole AppDatabase to `validateConnectorModelSelection`.

A new ESLint rule in eslint.config.mjs blocks new top-level methods on
AppDatabase so the pass-through layer cannot silently grow back.
`AppDatabase.ts` dropped from 193 lines to 58; `AppDatabaseBase` lost
the 270-line trailing pass-through block.
This commit is contained in:
ZacharyZhang-NY committed 2026-04-20 13:31:39 -04:00
1 parent a69fbb053b
commit 118e3d8795
120 files changed
+838 -933

No files matched your search

+4 -4
View File
@@ -81,7 +81,7 @@ export function executeAutomationAction(
wakeAgentIfPossible(assignedId, ctx.companyId, "assignment");
}
} else if (ctx.agentId) {
const snapshot = db.listSnapshot();
const snapshot = db.snap.listSnapshot();
const unassigned = snapshot.tasks
.filter((task) => task.companyId === ctx.companyId && !task.assigneeAgentId && ["backlog", "todo"].includes(task.status))
.sort((a, b) => {
@@ -310,7 +310,7 @@ export function executeAutomationAction(
// Default to +60min only when the rule specifies no time at all.
const offsetMinutes = cfg.scheduleOffsetMinutes ?? 60;
const scheduledAt = cfg.scheduledAt ?? new Date(Date.now() + offsetMinutes * 60_000).toISOString();
db.saveMeeting({
db.knowledgeMod.saveMeeting({
companyId: ctx.companyId,
type: meetingType,
title,
@@ -324,7 +324,7 @@ export function executeAutomationAction(
case "create_document": {
const documentType = (config.documentType as string) ?? "status_report";
const title = (config.title as string) ?? "Auto-generated document";
db.saveDocument({
db.documentsMod.saveDocument({
companyId: ctx.companyId,
type: documentType,
title,
@@ -335,7 +335,7 @@ export function executeAutomationAction(
break;
}
case "auto_approve": {
const snapshot = db.listSnapshot();
const snapshot = db.snap.listSnapshot();
const pendingApprovals = snapshot.approvals.filter(
(approval) => approval.companyId === ctx.companyId && approval.state === "pending",
);
@@ -39,7 +39,7 @@ function recordReviewFeedbackKnowledge(
"Use this review guidance to improve future deliverables for similar work.",
].join("\n");
db.saveKnowledgeEntry({
db.knowledgeMod.saveKnowledgeEntry({
companyId: approval.companyId,
category: "lesson_learned",
topic,
@@ -84,7 +84,7 @@ export function handleApprovalResolved(
const { db, logger, publishDomainChanged, notify, wakeAgentIfPossible, dispatchBrowserAction } = deps;
const { dispatchAutomation, handleTaskStatusChange, runWorkflowPipeline, trackTimer } = callbacks;
const snapshot = db.listSnapshot();
const snapshot = db.snap.listSnapshot();
const approval = snapshot.approvals.find((entry) => entry.id === approvalId);
if (!approval) return;
@@ -111,7 +111,7 @@ export function handleApprovalResolved(
department: agent.department,
});
if (assignedSkillIds.length > 0) {
db.addActivity({
db.activityMod.addActivity({
companyId,
actor: "system",
action: "agent.skills_defaulted",
@@ -121,7 +121,7 @@ export function handleApprovalResolved(
});
}
db.addActivity({
db.activityMod.addActivity({
companyId,
actor: "system",
action: "agent.hired",
@@ -142,7 +142,7 @@ export function handleApprovalResolved(
}
publishDomainChanged();
const hireLocale = db.listSnapshot().locale;
const hireLocale = db.snap.listSnapshot().locale;
notify({
title: localizeNotification(hireLocale, "Agent Hired", "新员工入职"),
body: localizeNotification(hireLocale, `${agent.name} joined as ${agent.role}`, `${agent.name} 已加入,职位:${agent.role}`),
@@ -170,7 +170,7 @@ export function handleApprovalResolved(
? "draft"
: "in_review";
for (const docId of documentIds) {
db.updateDocumentStatus(docId, companyId, docStatus);
db.documentsMod.updateDocumentStatus(docId, companyId, docStatus);
}
if (approval.relatedTaskId) {
@@ -185,7 +185,7 @@ export function handleApprovalResolved(
if (decision === "approved") {
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." });
db.activityMod.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.tasks.advanceTaskStatus(task.id, "in_progress", `Board rejected deliverable: ${approval.decisionNote || "No reason given."}`);
@@ -219,7 +219,7 @@ export function handleApprovalResolved(
const documentIds = extractApprovalDocumentIds(approval.payloadJson);
const docStatus = decision === "approved" ? "approved" : "draft";
for (const docId of documentIds) {
db.updateDocumentStatus(docId, companyId, docStatus);
db.documentsMod.updateDocumentStatus(docId, companyId, docStatus);
}
publishDomainChanged();
}
@@ -235,8 +235,8 @@ export function handleApprovalResolved(
};
if (payload.policyId) {
// Resolve the budget incident that triggered this approval
db.resolveBudgetIncident(payload.policyId, companyId);
db.addActivity({
db.budget.resolveBudgetIncident(payload.policyId, companyId);
db.activityMod.addActivity({
companyId,
actor: "board",
action: "budget.override_approved",
@@ -250,10 +250,10 @@ export function handleApprovalResolved(
}
}
const browserAction = db.getBrowserActionByApprovalId(approvalId, companyId);
const browserAction = db.social.getBrowserActionByApprovalId(approvalId, companyId);
if (browserAction) {
if (decision === "approved") {
db.updateBrowserAction(browserAction.id, {
db.social.updateBrowserAction(browserAction.id, {
status: "queued",
resultSummary: "Approval granted. Dispatching browser action.",
errorMessage: null,
@@ -263,7 +263,7 @@ export function handleApprovalResolved(
logger.error(`[browser] Failed to dispatch approved browser action ${browserAction.id}: ${error instanceof Error ? error.message : String(error)}`);
});
} else if (decision === "rejected") {
db.updateBrowserAction(browserAction.id, {
db.social.updateBrowserAction(browserAction.id, {
status: "cancelled",
resultSummary: "Browser action was rejected during approval.",
errorMessage: "Approval rejected by board.",
@@ -271,7 +271,7 @@ export function handleApprovalResolved(
});
publishDomainChanged();
} else if (decision === "revision_requested") {
db.updateBrowserAction(browserAction.id, {
db.social.updateBrowserAction(browserAction.id, {
status: "approval_required",
resultSummary: "Board requested revisions before execution.",
errorMessage: "Approval requires revisions before the browser action can run.",
@@ -291,7 +291,7 @@ export function handleApprovalResolved(
};
const dispatchSocialAction = () => {
const actionId = db.saveBrowserAction({
const actionId = db.social.saveBrowserAction({
companyId: approval.companyId,
socialAccountId: draft.socialAccountId,
agentId: approval.requestedByAgentId ?? "",
@@ -333,14 +333,14 @@ export function handleApprovalResolved(
const payload = workflowApprovalPayloadSchema.safeParse(JSON.parse(approval.payloadJson));
if (payload.success && payload.data.workflowId && payload.data.stepId) {
const { workflowId, stepId } = payload.data;
const workflow = db.getWorkflowRecord(workflowId, companyId);
const workflow = db.automationMod.getWorkflowRecord(workflowId, companyId);
if (workflow) {
const steps = normalizeWorkflowSteps(JSON.parse(workflow.stepsJson || "[]"));
const stepIndex = steps.findIndex((step) => step.id === stepId);
if (stepIndex >= 0) {
if (decision === "approved") {
steps[stepIndex]!.status = "pending";
db.updateWorkflowExecution({
db.automationMod.updateWorkflowExecution({
id: workflow.id,
companyId,
status: "paused",
@@ -354,7 +354,7 @@ export function handleApprovalResolved(
});
} else {
steps[stepIndex]!.status = decision === "revision_requested" ? "pending" : "failed";
db.updateWorkflowExecution({
db.automationMod.updateWorkflowExecution({
id: workflow.id,
companyId,
status: decision === "revision_requested" ? "paused" : "failed",
+1 -1
View File
@@ -50,7 +50,7 @@ export function handleTaskStatusChange(
}
if (!task) return;
db.addActivity({
db.activityMod.addActivity({
companyId,
actor: task.assigneeAgentId ?? "board",
action: "task.status_changed",
@@ -151,7 +151,7 @@ function checkRunProducedDeliverable(
taskId: string,
taskType: string,
): boolean {
const signals = db.collectRunDeliverableSignals(runId, companyId, taskId);
const signals = db.knowledgeMod.collectRunDeliverableSignals(runId, companyId, taskId);
return hasValidDeliverableFor(taskType, signals);
}
@@ -274,7 +274,7 @@ function advanceToInReview(
}
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);
const company = db.snap.listSnapshot().companies.find((c) => c.id === run.companyId);
if (company?.reviewDeliverables) {
const importantDocs = findImportantDocsForRun(db, run.id, run.companyId);
if (importantDocs.length > 0) {
@@ -462,7 +462,7 @@ export function processRunCompletion(
});
}
}
const budgetLocale = db.listSnapshot().locale;
const budgetLocale = db.snap.listSnapshot().locale;
checkBudgetThreshold(
agent,
notify,
+10 -10
View File
@@ -73,7 +73,7 @@ export function resolveWorkflowStepActor(
}
}
const snapshot = db.listSnapshot();
const snapshot = db.snap.listSnapshot();
return snapshot.agents.find((agent) => agent.companyId === companyId && !agent.reportsTo && agent.status !== "terminated") ?? null;
}
@@ -112,7 +112,7 @@ export async function runWorkflowPipeline(
): Promise<void> {
const { db, publishDomainChanged } = deps;
const workflow = db.getWorkflowRecord(workflowId, companyId);
const workflow = db.automationMod.getWorkflowRecord(workflowId, companyId);
if (!workflow) {
throw new Error("Workflow not found.");
}
@@ -132,7 +132,7 @@ export async function runWorkflowPipeline(
return step;
});
db.updateWorkflowExecution({
db.automationMod.updateWorkflowExecution({
id: workflowId,
companyId,
status: "active",
@@ -146,7 +146,7 @@ export async function runWorkflowPipeline(
for (let index = resumeFrom; index < steps.length; index += 1) {
const step = steps[index]!;
step.status = "running";
db.updateWorkflowExecution({
db.automationMod.updateWorkflowExecution({
id: workflowId,
companyId,
status: "active",
@@ -170,7 +170,7 @@ export async function runWorkflowPipeline(
payloadJson: JSON.stringify({ workflowId, stepId: step.id, stepName: step.name }),
});
step.status = "pending";
db.updateWorkflowExecution({
db.automationMod.updateWorkflowExecution({
id: workflowId,
companyId,
status: "paused",
@@ -178,7 +178,7 @@ export async function runWorkflowPipeline(
stepsJson: JSON.stringify(steps),
lastRunAt: new Date().toISOString(),
});
db.addActivity({
db.activityMod.addActivity({
companyId,
actor: actor?.id ?? "system",
action: "workflow.paused_for_approval",
@@ -203,7 +203,7 @@ export async function runWorkflowPipeline(
ctx,
);
step.status = "completed";
db.addActivity({
db.activityMod.addActivity({
companyId,
actor: actor?.id ?? "system",
action: "workflow.step_completed",
@@ -213,7 +213,7 @@ export async function runWorkflowPipeline(
});
const isLastStep = index === steps.length - 1;
const completionStatus = isLastStep ? "completed" : "active";
db.updateWorkflowExecution({
db.automationMod.updateWorkflowExecution({
id: workflowId,
companyId,
status: completionStatus,
@@ -224,7 +224,7 @@ export async function runWorkflowPipeline(
publishDomainChanged();
} catch (error) {
step.status = "failed";
db.updateWorkflowExecution({
db.automationMod.updateWorkflowExecution({
id: workflowId,
companyId,
status: "failed",
@@ -232,7 +232,7 @@ export async function runWorkflowPipeline(
stepsJson: JSON.stringify(steps),
lastRunAt: new Date().toISOString(),
});
db.addActivity({
db.activityMod.addActivity({
companyId,
actor: actor?.id ?? "system",
action: "workflow.step_failed",
+4 -4
View File
@@ -107,7 +107,7 @@ export function createAutomationService({
function evaluateAutomationRules(trigger: string, ctx: AutomationEventContext) {
try {
const rules = db.listActiveAutomationRules(ctx.companyId, trigger);
const rules = db.automationMod.listActiveAutomationRules(ctx.companyId, trigger);
for (const rule of rules) {
if (!matchesConditions(db, rule, ctx, logger)) continue;
const executionKey = JSON.stringify({
@@ -127,7 +127,7 @@ export function createAutomationService({
activeAutomationExecutions.add(executionKey);
try {
executeAutomationAction(actionDeps, actionCallbacks, rule, ctx);
db.recordAutomationExecution(
db.automationMod.recordAutomationExecution(
rule.id,
ctx.companyId,
rule.name,
@@ -145,7 +145,7 @@ export function createAutomationService({
logger.info(`[automation] Fired rule "${rule.name}" (${trigger} → ${rule.action})`);
} catch (error) {
logger.error(`[automation] Failed to execute action for rule "${rule.name}": ${error instanceof Error ? error.message : String(error)}`);
db.recordAutomationExecution(
db.automationMod.recordAutomationExecution(
rule.id,
ctx.companyId,
rule.name,
@@ -164,7 +164,7 @@ export function createAutomationService({
function evaluateWorkflowTriggers(trigger: string, ctx: AutomationEventContext) {
try {
const workflows = db.listTriggerableWorkflows(ctx.companyId, trigger);
const workflows = db.automationMod.listTriggerableWorkflows(ctx.companyId, trigger);
for (const workflow of workflows) {
if (!matchesEventConfig(db, workflow.triggerConfigJson, null, ctx, logger)) continue;
const executionKey = JSON.stringify({