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:
1 parent
411294afa6
commit
5e9b7acbc6
95 files changed
+1123
-793
No files matched your search
@@ -91,7 +91,7 @@ describe("agent model switching", () => {
|
||||
repoRef: "",
|
||||
isPrimary: true,
|
||||
});
|
||||
const agentId = db.saveAgent({
|
||||
const agentId = db.agents.saveAgent({
|
||||
companyId,
|
||||
name: "Engineer",
|
||||
role: "Engineer",
|
||||
@@ -107,7 +107,7 @@ describe("agent model switching", () => {
|
||||
|
||||
const { enqueueWorkerRun, taskRunner } = createHarness(db);
|
||||
|
||||
const firstTaskId = db.saveTask({
|
||||
const firstTaskId = db.tasks.saveTask({
|
||||
companyId,
|
||||
title: "Initial model task",
|
||||
description: "Use the first model for this run.",
|
||||
@@ -137,7 +137,7 @@ describe("agent model switching", () => {
|
||||
unattributedCost: true,
|
||||
});
|
||||
|
||||
db.saveAgent({
|
||||
db.agents.saveAgent({
|
||||
id: agentId,
|
||||
companyId,
|
||||
name: "Engineer",
|
||||
@@ -152,7 +152,7 @@ describe("agent model switching", () => {
|
||||
budgetMonthlyUsd: 200,
|
||||
});
|
||||
|
||||
const secondTaskId = db.saveTask({
|
||||
const secondTaskId = db.tasks.saveTask({
|
||||
companyId,
|
||||
title: "Updated model task",
|
||||
description: "Use the new model for this run.",
|
||||
|
||||
@@ -45,7 +45,7 @@ async function createHarness() {
|
||||
repoRef: "",
|
||||
isPrimary: true,
|
||||
});
|
||||
const ceoId = db.saveAgent({
|
||||
const ceoId = db.agents.saveAgent({
|
||||
companyId,
|
||||
name: "CEO",
|
||||
role: "CEO",
|
||||
@@ -58,7 +58,7 @@ async function createHarness() {
|
||||
capabilities: "Lead the company",
|
||||
budgetMonthlyUsd: 250,
|
||||
});
|
||||
const ctoId = db.saveAgent({
|
||||
const ctoId = db.agents.saveAgent({
|
||||
companyId,
|
||||
name: "CTO",
|
||||
role: "CTO",
|
||||
@@ -72,7 +72,7 @@ async function createHarness() {
|
||||
budgetMonthlyUsd: 220,
|
||||
department: "engineering",
|
||||
});
|
||||
const taskId = db.saveTask({
|
||||
const taskId = db.tasks.saveTask({
|
||||
companyId,
|
||||
title: "Build the org",
|
||||
description: "CTO should hire the first IC under engineering.",
|
||||
@@ -126,7 +126,7 @@ describe("recursive org execution", () => {
|
||||
const body = await response.json() as { agentId: string; approvalId: string; autoApproved: boolean };
|
||||
expect(body.autoApproved).toBe(false);
|
||||
|
||||
const pendingHire = harness.db.getAgent(body.agentId);
|
||||
const pendingHire = harness.db.entityCrud.getAgent(body.agentId);
|
||||
expect(pendingHire.status).toBe("pending_approval");
|
||||
expect(pendingHire.reportsTo).toBe(harness.ctoId);
|
||||
|
||||
@@ -141,11 +141,11 @@ describe("recursive org execution", () => {
|
||||
decisionNote: "Approved by board",
|
||||
});
|
||||
|
||||
const approvedHire = harness.db.getAgent(body.agentId);
|
||||
const approvedHire = harness.db.entityCrud.getAgent(body.agentId);
|
||||
expect(approvedHire.status).toBe("idle");
|
||||
expect(harness.db.getDirectReports(harness.ctoId, harness.companyId).map((agent) => agent.id)).toContain(body.agentId);
|
||||
expect(harness.db.getDirectReports(harness.ceoId, harness.companyId).map((agent) => agent.id)).not.toContain(body.agentId);
|
||||
expect(harness.db.getChainOfCommand(body.agentId).map((agent) => agent.id)).toEqual([harness.ctoId, harness.ceoId]);
|
||||
expect(harness.db.agents.getDirectReports(harness.ctoId, harness.companyId).map((agent) => agent.id)).toContain(body.agentId);
|
||||
expect(harness.db.agents.getDirectReports(harness.ceoId, harness.companyId).map((agent) => agent.id)).not.toContain(body.agentId);
|
||||
expect(harness.db.agents.getChainOfCommand(body.agentId).map((agent) => agent.id)).toEqual([harness.ctoId, harness.ceoId]);
|
||||
|
||||
const agentResponse = await fetch(`${harness.baseUrl}/api/agents/${body.agentId}`, {
|
||||
headers: harness.headers,
|
||||
@@ -169,7 +169,7 @@ describe("recursive org execution", () => {
|
||||
it("routes cross-department hires to the relevant department lead when reportsTo is omitted", async () => {
|
||||
const harness = await createHarness();
|
||||
try {
|
||||
const hrId = harness.db.saveAgent({
|
||||
const hrId = harness.db.agents.saveAgent({
|
||||
companyId: harness.companyId,
|
||||
name: "HR Director",
|
||||
role: "HR Director",
|
||||
@@ -183,7 +183,7 @@ describe("recursive org execution", () => {
|
||||
budgetMonthlyUsd: 180,
|
||||
department: "hr",
|
||||
});
|
||||
const hrTaskId = harness.db.saveTask({
|
||||
const hrTaskId = harness.db.tasks.saveTask({
|
||||
companyId: harness.companyId,
|
||||
title: "Fill engineering gap",
|
||||
description: "Request a backend engineer for the engineering team.",
|
||||
@@ -223,7 +223,7 @@ describe("recursive org execution", () => {
|
||||
|
||||
expect(response.status).toBe(201);
|
||||
const body = await response.json() as { agentId: string };
|
||||
const pendingHire = harness.db.getAgent(body.agentId);
|
||||
const pendingHire = harness.db.entityCrud.getAgent(body.agentId);
|
||||
expect(pendingHire.department).toBe("engineering");
|
||||
expect(pendingHire.reportsTo).toBe(harness.ctoId);
|
||||
} finally {
|
||||
@@ -263,7 +263,7 @@ describe("recursive org execution", () => {
|
||||
it("manager cannot route a hire outside their reporting branch", async () => {
|
||||
const harness = await createHarness();
|
||||
try {
|
||||
const cmoId = harness.db.saveAgent({
|
||||
const cmoId = harness.db.agents.saveAgent({
|
||||
companyId: harness.companyId,
|
||||
name: "CMO",
|
||||
role: "CMO",
|
||||
|
||||
@@ -33,7 +33,7 @@ async function createHarness() {
|
||||
repoRef: "",
|
||||
isPrimary: true,
|
||||
});
|
||||
const agentId = db.saveAgent({
|
||||
const agentId = db.agents.saveAgent({
|
||||
companyId,
|
||||
name: "Helios",
|
||||
role: "CEO",
|
||||
@@ -46,7 +46,7 @@ async function createHarness() {
|
||||
capabilities: "Operate the company",
|
||||
budgetMonthlyUsd: 100,
|
||||
});
|
||||
const taskId = db.saveTask({
|
||||
const taskId = db.tasks.saveTask({
|
||||
companyId,
|
||||
title: "Ship the API",
|
||||
description: "Capture structured run events.",
|
||||
|
||||
@@ -29,7 +29,7 @@ describe("Step 4: Hire approval + decideApproval", () => {
|
||||
decisionNote: "Approved by board",
|
||||
});
|
||||
|
||||
const updatedAgent = shared.db.getAgent(hireId);
|
||||
const updatedAgent = shared.db.entityCrud.getAgent(hireId);
|
||||
expect(updatedAgent.status).toBe("idle");
|
||||
});
|
||||
|
||||
@@ -52,7 +52,7 @@ describe("Step 4: Hire approval + decideApproval", () => {
|
||||
decisionNote: "Not needed",
|
||||
});
|
||||
|
||||
const updatedAgent = shared.db.getAgent(hireId);
|
||||
const updatedAgent = shared.db.entityCrud.getAgent(hireId);
|
||||
expect(updatedAgent.status).toBe("terminated");
|
||||
});
|
||||
|
||||
|
||||
@@ -89,13 +89,13 @@ describe("Agent Messages", () => {
|
||||
let agentBId: string;
|
||||
|
||||
beforeEach(() => {
|
||||
agentAId = shared.db.saveAgent({
|
||||
agentAId = shared.db.agents.saveAgent({
|
||||
companyId: shared.companyId, name: "Alice", role: "Engineer", title: "Senior Engineer",
|
||||
department: "engineering", connectorId: "claude-pro", status: "active",
|
||||
capabilities: "Backend development, API design, code review",
|
||||
budgetMonthlyUsd: 100,
|
||||
});
|
||||
agentBId = shared.db.saveAgent({
|
||||
agentBId = shared.db.agents.saveAgent({
|
||||
companyId: shared.companyId, name: "Bob", role: "Designer", title: "UX Designer",
|
||||
department: "design", connectorId: "claude-pro", status: "active",
|
||||
capabilities: "UI design, wireframing, user research",
|
||||
@@ -178,7 +178,7 @@ describe("Agent Messages", () => {
|
||||
leadAgentId: engineeringLeadId,
|
||||
status: "active",
|
||||
});
|
||||
shared.db.saveTask({
|
||||
shared.db.tasks.saveTask({
|
||||
companyId: shared.companyId,
|
||||
title: "Project task",
|
||||
description: "Do work",
|
||||
@@ -240,13 +240,13 @@ describe("Auto Task Assignment", () => {
|
||||
let frontendId: string;
|
||||
|
||||
beforeEach(() => {
|
||||
backendId = shared.db.saveAgent({
|
||||
backendId = shared.db.agents.saveAgent({
|
||||
companyId: shared.companyId, name: "Backend Dev", role: "Backend Engineer", title: "Senior Backend",
|
||||
department: "engineering", connectorId: "openai-codex", status: "active",
|
||||
capabilities: "API development, database design, Node.js, PostgreSQL, backend testing",
|
||||
budgetMonthlyUsd: 200,
|
||||
});
|
||||
frontendId = shared.db.saveAgent({
|
||||
frontendId = shared.db.agents.saveAgent({
|
||||
companyId: shared.companyId, name: "Frontend Dev", role: "Frontend Engineer", title: "Senior Frontend",
|
||||
department: "engineering", connectorId: "openai-codex", status: "active",
|
||||
capabilities: "React, TypeScript, CSS, Tailwind, responsive design, frontend testing, UI components",
|
||||
@@ -255,61 +255,61 @@ describe("Auto Task Assignment", () => {
|
||||
});
|
||||
|
||||
it("assigns to the best-matching agent by capability keywords", () => {
|
||||
const taskId = shared.db.saveTask({
|
||||
const taskId = shared.db.tasks.saveTask({
|
||||
companyId: shared.companyId, title: "Build React component for user dashboard",
|
||||
description: "Create a responsive React component with Tailwind CSS styling",
|
||||
priority: "medium", status: "todo",
|
||||
});
|
||||
|
||||
const assignedId = shared.db.autoAssignTask(taskId, shared.companyId);
|
||||
const assignedId = shared.db.tasks.autoAssignTask(taskId, shared.companyId);
|
||||
expect(assignedId).toBe(frontendId); // "React", "responsive", "Tailwind" match frontend
|
||||
});
|
||||
|
||||
it("assigns backend task to backend agent", () => {
|
||||
const taskId = shared.db.saveTask({
|
||||
const taskId = shared.db.tasks.saveTask({
|
||||
companyId: shared.companyId, title: "Design database schema for orders",
|
||||
description: "PostgreSQL database design with API endpoints for order management",
|
||||
priority: "high", status: "todo",
|
||||
});
|
||||
|
||||
const assignedId = shared.db.autoAssignTask(taskId, shared.companyId);
|
||||
const assignedId = shared.db.tasks.autoAssignTask(taskId, shared.companyId);
|
||||
expect(assignedId).toBe(backendId); // "database", "PostgreSQL", "API" match backend
|
||||
});
|
||||
|
||||
it("returns existing assignee if task already assigned", () => {
|
||||
const taskId = shared.db.saveTask({
|
||||
const taskId = shared.db.tasks.saveTask({
|
||||
companyId: shared.companyId, title: "Already assigned task",
|
||||
description: "This task has an assignee",
|
||||
assigneeAgentId: frontendId,
|
||||
priority: "medium", status: "todo",
|
||||
});
|
||||
|
||||
const result = shared.db.autoAssignTask(taskId, shared.companyId);
|
||||
const result = shared.db.tasks.autoAssignTask(taskId, shared.companyId);
|
||||
expect(result).toBe(frontendId);
|
||||
});
|
||||
|
||||
it("returns null if all agents have exhausted their budget", () => {
|
||||
// Set positive budgets that are fully spent
|
||||
shared.db.saveAgent({ id: backendId, companyId: shared.companyId, name: "Backend Dev", role: "Backend", title: "Backend", connectorId: "openai-codex", status: "active", capabilities: "backend", budgetMonthlyUsd: 100 });
|
||||
shared.db.saveAgent({ id: frontendId, companyId: shared.companyId, name: "Frontend Dev", role: "Frontend", title: "Frontend", connectorId: "openai-codex", status: "active", capabilities: "frontend", budgetMonthlyUsd: 100 });
|
||||
shared.db.agents.saveAgent({ id: backendId, companyId: shared.companyId, name: "Backend Dev", role: "Backend", title: "Backend", connectorId: "openai-codex", status: "active", capabilities: "backend", budgetMonthlyUsd: 100 });
|
||||
shared.db.agents.saveAgent({ id: frontendId, companyId: shared.companyId, name: "Frontend Dev", role: "Frontend", title: "Frontend", connectorId: "openai-codex", status: "active", capabilities: "frontend", budgetMonthlyUsd: 100 });
|
||||
// Exhaust budgets by recording spending via direct SQL (since finishRun would need a real run)
|
||||
(shared.db as unknown as { db: { prepare: (sql: string) => { run: (...args: unknown[]) => void } } }).db.prepare("update agents set spent_monthly_usd = 100 where id = ?").run(backendId);
|
||||
(shared.db as unknown as { db: { prepare: (sql: string) => { run: (...args: unknown[]) => void } } }).db.prepare("update agents set spent_monthly_usd = 100 where id = ?").run(frontendId);
|
||||
|
||||
const taskId = shared.db.saveTask({ companyId: shared.companyId, title: "No budget task", description: "Test", priority: "low", status: "todo" });
|
||||
const result = shared.db.autoAssignTask(taskId, shared.companyId);
|
||||
const taskId = shared.db.tasks.saveTask({ companyId: shared.companyId, title: "No budget task", description: "Test", priority: "low", status: "todo" });
|
||||
const result = shared.db.tasks.autoAssignTask(taskId, shared.companyId);
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
|
||||
it("penalizes agents with many active tasks", () => {
|
||||
// Give backend agent 5 active tasks
|
||||
for (let i = 0; i < 5; i++) {
|
||||
shared.db.saveTask({ companyId: shared.companyId, title: `Backend task ${i}`, description: "", assigneeAgentId: backendId, priority: "medium", status: "in_progress" });
|
||||
shared.db.tasks.saveTask({ companyId: shared.companyId, title: `Backend task ${i}`, description: "", assigneeAgentId: backendId, priority: "medium", status: "in_progress" });
|
||||
}
|
||||
|
||||
// Generic task with no strong keyword match — should go to less busy frontend agent
|
||||
const taskId = shared.db.saveTask({ companyId: shared.companyId, title: "Implement feature", description: "Build the feature", priority: "medium", status: "todo" });
|
||||
const assignedId = shared.db.autoAssignTask(taskId, shared.companyId);
|
||||
const taskId = shared.db.tasks.saveTask({ companyId: shared.companyId, title: "Implement feature", description: "Build the feature", priority: "medium", status: "todo" });
|
||||
const assignedId = shared.db.tasks.autoAssignTask(taskId, shared.companyId);
|
||||
expect(assignedId).toBe(frontendId); // Frontend has 0 active tasks vs backend's 5
|
||||
});
|
||||
});
|
||||
|
||||
@@ -40,7 +40,7 @@ describe("Step 2: CEO phased prompts", () => {
|
||||
const ceoId = createAgent({ name: "CEO", role: "CEO" });
|
||||
|
||||
const hireId = createAgent({ name: "CTO", role: "CTO", reportsTo: ceoId, status: "pending_approval" as "idle" });
|
||||
const hire = shared.db.getAgent(hireId);
|
||||
const hire = shared.db.entityCrud.getAgent(hireId);
|
||||
|
||||
const ctx = buildCtx({
|
||||
agentId: ceoId,
|
||||
@@ -88,7 +88,7 @@ describe("Step 2: CEO phased prompts", () => {
|
||||
it("Phase 2: shows normal executive instructions when active reports and no pending org approval", () => {
|
||||
const ceoId = createAgent({ name: "CEO", role: "CEO" });
|
||||
const ctoId = createAgent({ name: "CTO", role: "CTO", reportsTo: ceoId, status: "idle" });
|
||||
const cto = shared.db.getAgent(ctoId);
|
||||
const cto = shared.db.entityCrud.getAgent(ctoId);
|
||||
|
||||
const ctx = buildCtx({
|
||||
agentId: ceoId,
|
||||
@@ -109,7 +109,7 @@ describe("Step 2: CEO phased prompts", () => {
|
||||
it("includes pending approvals section in prompt", () => {
|
||||
const ceoId = createAgent({ name: "CEO", role: "CEO" });
|
||||
const ctoId = createAgent({ name: "CTO", role: "CTO", reportsTo: ceoId, status: "idle" });
|
||||
const cto = shared.db.getAgent(ctoId);
|
||||
const cto = shared.db.entityCrud.getAgent(ctoId);
|
||||
|
||||
const ctx = buildCtx({
|
||||
agentId: ceoId,
|
||||
@@ -139,10 +139,10 @@ describe("Step 2: CEO phased prompts", () => {
|
||||
|
||||
it("Manager prompt includes credential escalation guidance", () => {
|
||||
const ceoId = createAgent({ name: "CEO", role: "CEO" });
|
||||
const ceo = shared.db.getAgent(ceoId);
|
||||
const ceo = shared.db.entityCrud.getAgent(ceoId);
|
||||
const managerId = createAgent({ name: "CTO", role: "CTO", reportsTo: ceoId });
|
||||
const icId = createAgent({ name: "Dev", role: "Engineer", reportsTo: managerId });
|
||||
const ic = shared.db.getAgent(icId);
|
||||
const ic = shared.db.entityCrud.getAgent(icId);
|
||||
|
||||
const ctx = buildCtx({
|
||||
agentId: managerId,
|
||||
@@ -156,7 +156,7 @@ describe("Step 2: CEO phased prompts", () => {
|
||||
|
||||
it("IC prompt includes credential escalation guidance", () => {
|
||||
const ceoId = createAgent({ name: "CEO", role: "CEO" });
|
||||
const ceo = shared.db.getAgent(ceoId);
|
||||
const ceo = shared.db.entityCrud.getAgent(ceoId);
|
||||
const icId = createAgent({ name: "Dev", role: "Engineer", reportsTo: ceoId });
|
||||
|
||||
const ctx = buildCtx({
|
||||
|
||||
@@ -13,7 +13,7 @@ const { createAgent } = shared;
|
||||
describe("Step 7: Worker crash recovery (database layer)", () => {
|
||||
it("interruptStaleRuns clears running and queued runs on init", async () => {
|
||||
const agentId = createAgent({ name: "Agent" });
|
||||
const taskId = shared.db.saveTask({
|
||||
const taskId = shared.db.tasks.saveTask({
|
||||
companyId: shared.companyId,
|
||||
title: "In-progress task",
|
||||
description: "Running when crash happened",
|
||||
@@ -51,7 +51,7 @@ describe("Step 7: Worker crash recovery (database layer)", () => {
|
||||
|
||||
it("finishRun can mark interrupted runs from crash recovery", () => {
|
||||
const agentId = createAgent({ name: "Agent" });
|
||||
const taskId = shared.db.saveTask({
|
||||
const taskId = shared.db.tasks.saveTask({
|
||||
companyId: shared.companyId,
|
||||
title: "Crash recovery task",
|
||||
description: "Test",
|
||||
|
||||
@@ -40,8 +40,8 @@ describe("E2E: Full autonomous CEO cold start flow", () => {
|
||||
});
|
||||
|
||||
// CEO now in Phase 1
|
||||
const ctoRecord = shared.db.getAgent(ctoId);
|
||||
const cmoRecord = shared.db.getAgent(cmoId);
|
||||
const ctoRecord = shared.db.entityCrud.getAgent(ctoId);
|
||||
const cmoRecord = shared.db.entityCrud.getAgent(cmoId);
|
||||
const phase1Ctx = buildCtx({
|
||||
agentId: ceoId,
|
||||
directReports: [ctoRecord, cmoRecord],
|
||||
@@ -85,15 +85,15 @@ describe("E2E: Full autonomous CEO cold start flow", () => {
|
||||
shared.db.decideApproval({ approvalId: hireApprovalCMO, state: "approved", decisionNote: "Approved" });
|
||||
|
||||
// Agents now idle
|
||||
expect(shared.db.getAgent(ctoId).status).toBe("idle");
|
||||
expect(shared.db.getAgent(cmoId).status).toBe("idle");
|
||||
expect(shared.db.entityCrud.getAgent(ctoId).status).toBe("idle");
|
||||
expect(shared.db.entityCrud.getAgent(cmoId).status).toBe("idle");
|
||||
|
||||
// 5. Board approves org strategy
|
||||
shared.db.decideApproval({ approvalId: orgApprovalId, state: "approved", decisionNote: "Go ahead" });
|
||||
|
||||
// 6. CEO now in Phase 2
|
||||
const updatedCto = shared.db.getAgent(ctoId);
|
||||
const updatedCmo = shared.db.getAgent(cmoId);
|
||||
const updatedCto = shared.db.entityCrud.getAgent(ctoId);
|
||||
const updatedCmo = shared.db.entityCrud.getAgent(cmoId);
|
||||
const phase2Ctx = buildCtx({
|
||||
agentId: ceoId,
|
||||
directReports: [updatedCto, updatedCmo],
|
||||
@@ -106,7 +106,7 @@ describe("E2E: Full autonomous CEO cold start flow", () => {
|
||||
expect(phase2Prompt).toContain("Delegation");
|
||||
|
||||
// 7. Verify hierarchical chain
|
||||
const ctoChain = shared.db.getChainOfCommand(ctoId);
|
||||
const ctoChain = shared.db.agents.getChainOfCommand(ctoId);
|
||||
expect(ctoChain).toHaveLength(1);
|
||||
expect(ctoChain[0].id).toBe(ceoId);
|
||||
|
||||
@@ -120,7 +120,7 @@ describe("E2E: Full autonomous CEO cold start flow", () => {
|
||||
status: "in_progress",
|
||||
});
|
||||
|
||||
const parentTaskId = shared.db.saveTask({
|
||||
const parentTaskId = shared.db.tasks.saveTask({
|
||||
companyId: shared.companyId,
|
||||
title: "Build backend",
|
||||
description: "Implement API",
|
||||
@@ -133,7 +133,7 @@ describe("E2E: Full autonomous CEO cold start flow", () => {
|
||||
|
||||
// CTO creates subtask for IC (would be done via API)
|
||||
const icId = createAgent({ name: "Dev", role: "Engineer", reportsTo: ctoId, status: "idle" });
|
||||
const subtaskId = shared.db.saveTask({
|
||||
const subtaskId = shared.db.tasks.saveTask({
|
||||
companyId: shared.companyId,
|
||||
title: "Implement auth",
|
||||
description: "JWT auth",
|
||||
@@ -146,7 +146,7 @@ describe("E2E: Full autonomous CEO cold start flow", () => {
|
||||
});
|
||||
|
||||
// Verify parent-child relationship
|
||||
const subtask = shared.db.getTask(subtaskId);
|
||||
const subtask = shared.db.entityCrud.getTask(subtaskId);
|
||||
expect(subtask.parentId).toBe(parentTaskId);
|
||||
|
||||
// 9. Wakeup request test: enqueue wake while IC is busy
|
||||
@@ -159,7 +159,7 @@ describe("E2E: Full autonomous CEO cold start flow", () => {
|
||||
const icPromptCtx = buildCtx({
|
||||
agentId: icId,
|
||||
task: subtask,
|
||||
chainOfCommand: [shared.db.getAgent(ctoId), shared.db.getAgent(ceoId)],
|
||||
chainOfCommand: [shared.db.entityCrud.getAgent(ctoId), shared.db.entityCrud.getAgent(ceoId)],
|
||||
wakeReason: "assignment",
|
||||
});
|
||||
const icPrompt = buildTaskPrompt(icPromptCtx);
|
||||
@@ -202,11 +202,11 @@ describe("E2E: Blocked escalation through org hierarchy", () => {
|
||||
const icId = createAgent({ name: "Dev", role: "Engineer", reportsTo: ctoId });
|
||||
|
||||
// When IC marks task blocked, we need to find its manager
|
||||
const chain = shared.db.getChainOfCommand(icId);
|
||||
const chain = shared.db.agents.getChainOfCommand(icId);
|
||||
expect(chain[0].id).toBe(ctoId); // immediate manager gets woken
|
||||
|
||||
// When CTO needs to escalate further
|
||||
const ctoChain = shared.db.getChainOfCommand(ctoId);
|
||||
const ctoChain = shared.db.agents.getChainOfCommand(ctoId);
|
||||
expect(ctoChain[0].id).toBe(ceoId);
|
||||
});
|
||||
});
|
||||
@@ -216,7 +216,7 @@ describe("Review Fix: CEO phase ordering edge case", () => {
|
||||
const ceoId = createAgent({ name: "CEO", role: "CEO" });
|
||||
// Hires submitted but not yet approved
|
||||
const hireId = createAgent({ name: "CTO", role: "CTO", reportsTo: ceoId, status: "pending_approval" as "idle" });
|
||||
const hire = shared.db.getAgent(hireId);
|
||||
const hire = shared.db.entityCrud.getAgent(hireId);
|
||||
|
||||
// Org strategy already approved (not in pendingApprovals), but hires still pending
|
||||
const ctx = buildCtx({
|
||||
@@ -236,7 +236,7 @@ describe("Review Fix: CEO phase ordering edge case", () => {
|
||||
it("moves to Phase 2 once hires are approved (no pending org approval)", () => {
|
||||
const ceoId = createAgent({ name: "CEO", role: "CEO" });
|
||||
const ctoId = createAgent({ name: "CTO", role: "CTO", reportsTo: ceoId, status: "idle" });
|
||||
const cto = shared.db.getAgent(ctoId);
|
||||
const cto = shared.db.entityCrud.getAgent(ctoId);
|
||||
|
||||
const ctx = buildCtx({
|
||||
agentId: ceoId,
|
||||
@@ -315,7 +315,7 @@ describe("Review Fix: Root task completion notification", () => {
|
||||
const ctoId = createAgent({ name: "CTO", role: "CTO", reportsTo: ceoId });
|
||||
|
||||
// CTO has a root-level task (no parentId)
|
||||
const taskId = shared.db.saveTask({
|
||||
const taskId = shared.db.tasks.saveTask({
|
||||
companyId: shared.companyId,
|
||||
title: "Standalone task from CEO",
|
||||
description: "No parent",
|
||||
@@ -325,11 +325,11 @@ describe("Review Fix: Root task completion notification", () => {
|
||||
status: "done",
|
||||
});
|
||||
|
||||
const task = shared.db.getTask(taskId);
|
||||
const task = shared.db.entityCrud.getTask(taskId);
|
||||
expect(task.parentId).toBeNull();
|
||||
|
||||
// CTO's chain of command includes CEO — so api-server can wake CEO on completion
|
||||
const chain = shared.db.getChainOfCommand(ctoId);
|
||||
const chain = shared.db.agents.getChainOfCommand(ctoId);
|
||||
expect(chain).toHaveLength(1);
|
||||
expect(chain[0].id).toBe(ceoId);
|
||||
});
|
||||
|
||||
@@ -31,9 +31,9 @@ describe("Step 1: CEO role detection", () => {
|
||||
it("identifies manager when has reports and has chain of command", () => {
|
||||
const ceoId = createAgent({ name: "CEO", role: "CEO" });
|
||||
const managerId = createAgent({ name: "CTO", role: "CTO", reportsTo: ceoId });
|
||||
const ceo = shared.db.getAgent(ceoId);
|
||||
const ceo = shared.db.entityCrud.getAgent(ceoId);
|
||||
const icId = createAgent({ name: "IC", role: "Engineer", reportsTo: managerId });
|
||||
const ic = shared.db.getAgent(icId);
|
||||
const ic = shared.db.entityCrud.getAgent(icId);
|
||||
|
||||
const ctx = buildCtx({
|
||||
agentId: managerId,
|
||||
@@ -49,7 +49,7 @@ describe("Step 1: CEO role detection", () => {
|
||||
|
||||
it("identifies IC when has chain of command and no direct reports", () => {
|
||||
const ceoId = createAgent({ name: "CEO", role: "CEO" });
|
||||
const ceo = shared.db.getAgent(ceoId);
|
||||
const ceo = shared.db.entityCrud.getAgent(ceoId);
|
||||
const icId = createAgent({ name: "Dev", role: "Engineer", reportsTo: ceoId });
|
||||
|
||||
const ctx = buildCtx({
|
||||
|
||||
@@ -12,7 +12,7 @@ const { createAgent } = shared;
|
||||
describe("Step 6: Run failure escalation (database layer)", () => {
|
||||
it("finishRun marks run as failed and returns outcome", () => {
|
||||
const agentId = createAgent({ name: "IC", role: "Engineer" });
|
||||
const taskId = shared.db.saveTask({
|
||||
const taskId = shared.db.tasks.saveTask({
|
||||
companyId: shared.companyId,
|
||||
title: "Failing task",
|
||||
description: "Will fail",
|
||||
@@ -56,7 +56,7 @@ describe("Step 6: Run failure escalation (database layer)", () => {
|
||||
const ctoId = createAgent({ name: "CTO", role: "CTO", reportsTo: ceoId });
|
||||
const icId = createAgent({ name: "Dev", role: "Engineer", reportsTo: ctoId });
|
||||
|
||||
const chain = shared.db.getChainOfCommand(icId);
|
||||
const chain = shared.db.agents.getChainOfCommand(icId);
|
||||
// First in chain is the immediate manager (CTO)
|
||||
expect(chain.length).toBeGreaterThan(0);
|
||||
expect(chain[0].name).toBe("CTO");
|
||||
|
||||
@@ -30,7 +30,7 @@ export interface SharedHandle {
|
||||
set db(value: AppDatabase);
|
||||
get companyId(): string;
|
||||
get workspaceId(): string;
|
||||
createAgent(overrides?: Partial<Parameters<AppDatabase["saveAgent"]>[0]>): string;
|
||||
createAgent(overrides?: Partial<Parameters<AppDatabase["agents"]["saveAgent"]>[0]>): string;
|
||||
buildCtx(overrides?: Record<string, unknown>): Parameters<typeof buildSystemPrompt>[0];
|
||||
tempDirs: string[];
|
||||
}
|
||||
@@ -80,8 +80,8 @@ export function installSharedSetup(): SharedHandle {
|
||||
return state.db;
|
||||
}
|
||||
|
||||
function createAgent(overrides: Partial<Parameters<AppDatabase["saveAgent"]>[0]> = {}): string {
|
||||
return currentDb().saveAgent({
|
||||
function createAgent(overrides: Partial<Parameters<AppDatabase["agents"]["saveAgent"]>[0]> = {}): string {
|
||||
return currentDb().agents.saveAgent({
|
||||
companyId: state.companyId,
|
||||
name: "TestAgent",
|
||||
role: "CEO",
|
||||
@@ -98,7 +98,7 @@ export function installSharedSetup(): SharedHandle {
|
||||
}
|
||||
|
||||
function buildCtx(overrides: Record<string, unknown> = {}): Parameters<typeof buildSystemPrompt>[0] {
|
||||
const agent = currentDb().getAgent(overrides.agentId as string ?? createAgent());
|
||||
const agent = currentDb().entityCrud.getAgent(overrides.agentId as string ?? createAgent());
|
||||
return {
|
||||
agent,
|
||||
apiUrl: "http://127.0.0.1:3000",
|
||||
|
||||
@@ -15,7 +15,7 @@ describe("Step 5: Task status + comment wake propagation", () => {
|
||||
const ctoId = createAgent({ name: "CTO", role: "CTO", reportsTo: ceoId });
|
||||
const icId = createAgent({ name: "Dev", role: "Engineer", reportsTo: ctoId });
|
||||
|
||||
const chain = shared.db.getChainOfCommand(icId);
|
||||
const chain = shared.db.agents.getChainOfCommand(icId);
|
||||
expect(chain).toHaveLength(2);
|
||||
expect(chain[0].id).toBe(ctoId); // immediate manager
|
||||
expect(chain[1].id).toBe(ceoId); // top level
|
||||
@@ -25,7 +25,7 @@ describe("Step 5: Task status + comment wake propagation", () => {
|
||||
// Create a potential cycle (shouldn't happen in practice, but guard against it)
|
||||
const agent1 = createAgent({ name: "Agent1" });
|
||||
// agent1 reports to no one (CEO), so chain should be empty
|
||||
const chain = shared.db.getChainOfCommand(agent1);
|
||||
const chain = shared.db.agents.getChainOfCommand(agent1);
|
||||
expect(chain).toHaveLength(0);
|
||||
});
|
||||
|
||||
@@ -33,7 +33,7 @@ describe("Step 5: Task status + comment wake propagation", () => {
|
||||
const ceoId = createAgent({ name: "CEO", role: "CEO" });
|
||||
const ctoId = createAgent({ name: "CTO", role: "CTO", reportsTo: ceoId });
|
||||
|
||||
const parentTaskId = shared.db.saveTask({
|
||||
const parentTaskId = shared.db.tasks.saveTask({
|
||||
companyId: shared.companyId,
|
||||
title: "Build feature X",
|
||||
description: "Top-level task",
|
||||
@@ -43,7 +43,7 @@ describe("Step 5: Task status + comment wake propagation", () => {
|
||||
status: "in_progress",
|
||||
});
|
||||
|
||||
const childTaskId = shared.db.saveTask({
|
||||
const childTaskId = shared.db.tasks.saveTask({
|
||||
companyId: shared.companyId,
|
||||
title: "Implement module A",
|
||||
description: "Subtask",
|
||||
@@ -54,10 +54,10 @@ describe("Step 5: Task status + comment wake propagation", () => {
|
||||
status: "todo",
|
||||
});
|
||||
|
||||
const childTask = shared.db.getTask(childTaskId);
|
||||
const childTask = shared.db.entityCrud.getTask(childTaskId);
|
||||
expect(childTask.parentId).toBe(parentTaskId);
|
||||
|
||||
const parentTask = shared.db.getTask(parentTaskId);
|
||||
const parentTask = shared.db.entityCrud.getTask(parentTaskId);
|
||||
expect(parentTask.assigneeAgentId).toBe(ctoId);
|
||||
});
|
||||
});
|
||||
@@ -14,7 +14,7 @@ describe("Step 8: Wake reason injection", () => {
|
||||
it("includes wake reason section for non-timer triggers", () => {
|
||||
const agentId = createAgent({ name: "CTO", role: "CTO" });
|
||||
const ceoId = createAgent({ name: "CEO", role: "CEO" });
|
||||
const ceo = shared.db.getAgent(ceoId);
|
||||
const ceo = shared.db.entityCrud.getAgent(ceoId);
|
||||
|
||||
const ctx = buildCtx({
|
||||
agentId,
|
||||
@@ -92,7 +92,7 @@ describe("Step 8: Wake reason injection", () => {
|
||||
|
||||
it("includes wake reason before task description", () => {
|
||||
const agentId = createAgent({ name: "Agent" });
|
||||
const taskId = shared.db.saveTask({
|
||||
const taskId = shared.db.tasks.saveTask({
|
||||
companyId: shared.companyId,
|
||||
title: "Do the thing",
|
||||
description: "Test task",
|
||||
@@ -101,7 +101,7 @@ describe("Step 8: Wake reason injection", () => {
|
||||
priority: "medium",
|
||||
status: "todo",
|
||||
});
|
||||
const task = shared.db.getTask(taskId);
|
||||
const task = shared.db.entityCrud.getTask(taskId);
|
||||
|
||||
const ctx = buildCtx({
|
||||
agentId,
|
||||
|
||||
@@ -90,7 +90,7 @@ describe("connector model enforcement", () => {
|
||||
repoRef: "",
|
||||
isPrimary: true,
|
||||
});
|
||||
const agentId = db.saveAgent({
|
||||
const agentId = db.agents.saveAgent({
|
||||
companyId,
|
||||
name: "CEO",
|
||||
role: "CEO",
|
||||
@@ -103,7 +103,7 @@ describe("connector model enforcement", () => {
|
||||
capabilities: "Lead the company",
|
||||
budgetMonthlyUsd: 250,
|
||||
});
|
||||
const taskId = db.saveTask({
|
||||
const taskId = db.tasks.saveTask({
|
||||
companyId,
|
||||
title: "Run with invalid model",
|
||||
description: "This task should be blocked because the agent model is disabled.",
|
||||
@@ -154,7 +154,7 @@ describe("connector model enforcement", () => {
|
||||
repoRef: "",
|
||||
isPrimary: true,
|
||||
});
|
||||
const agentId = db.saveAgent({
|
||||
const agentId = db.agents.saveAgent({
|
||||
companyId,
|
||||
name: "CEO",
|
||||
role: "CEO",
|
||||
@@ -167,7 +167,7 @@ describe("connector model enforcement", () => {
|
||||
capabilities: "Lead the company",
|
||||
budgetMonthlyUsd: 250,
|
||||
});
|
||||
const taskId = db.saveTask({
|
||||
const taskId = db.tasks.saveTask({
|
||||
companyId,
|
||||
title: "Recover queued run",
|
||||
description: "This queued run should fail on recovery because the model is disabled.",
|
||||
@@ -184,7 +184,7 @@ describe("connector model enforcement", () => {
|
||||
workspaceId,
|
||||
connectorId: "openai-codex",
|
||||
});
|
||||
const checkoutSuccess = db.checkoutTask(taskId, run.id);
|
||||
const checkoutSuccess = db.tasks.checkoutTask(taskId, run.id);
|
||||
expect(checkoutSuccess).toBe(true);
|
||||
|
||||
db.setDisabledModels(companyId, "openai-codex", [{ id: "gpt-5.4", label: "gpt-5.4" }]);
|
||||
|
||||
@@ -79,7 +79,7 @@ function scaffold(db: AppDatabase, budgetAmountCents = 500): ScaffoldResult {
|
||||
isPrimary: true,
|
||||
});
|
||||
|
||||
const agentId = db.saveAgent({
|
||||
const agentId = db.agents.saveAgent({
|
||||
companyId,
|
||||
name: "PipelineAgent",
|
||||
role: "IC",
|
||||
@@ -175,7 +175,7 @@ describe("Cross-Subsystem Pipeline (D-04)", () => {
|
||||
expect(validation.errors).toHaveLength(0);
|
||||
|
||||
// Create the task in the DB
|
||||
const taskId = db.saveTask({
|
||||
const taskId = db.tasks.saveTask({
|
||||
companyId: s.companyId,
|
||||
title: "Build login page",
|
||||
description:
|
||||
@@ -194,7 +194,7 @@ describe("Cross-Subsystem Pipeline (D-04)", () => {
|
||||
workspaceId: s.workspaceId,
|
||||
connectorId: s.connectorId,
|
||||
});
|
||||
const checkoutResult = db.checkoutTask(taskId, runId);
|
||||
const checkoutResult = db.tasks.checkoutTask(taskId, runId);
|
||||
expect(checkoutResult).toBe(true);
|
||||
|
||||
// 3. Budget gate: verify run is allowed
|
||||
@@ -221,7 +221,7 @@ describe("Cross-Subsystem Pipeline (D-04)", () => {
|
||||
});
|
||||
|
||||
// 5. Session management: create session and track usage
|
||||
const sessionId = db.createNewSession({
|
||||
const sessionId = db.tasks.createNewSession({
|
||||
companyId: s.companyId,
|
||||
agentId: s.agentId,
|
||||
adapterType: "gemini-cli",
|
||||
@@ -232,10 +232,10 @@ describe("Cross-Subsystem Pipeline (D-04)", () => {
|
||||
const run = db.getRun(runId);
|
||||
expect(run.status).toBe("succeeded");
|
||||
|
||||
const task = db.getTask(taskId);
|
||||
const task = db.entityCrud.getTask(taskId);
|
||||
expect(task.costUsd).toBeGreaterThan(0);
|
||||
|
||||
const session = db.getSessionById(sessionId);
|
||||
const session = db.tasks.getSessionById(sessionId);
|
||||
expect(session).not.toBeNull();
|
||||
expect(session!.totalInputTokens).toBe(5000);
|
||||
expect(session!.totalOutputTokens).toBe(2000);
|
||||
@@ -254,7 +254,7 @@ describe("Cross-Subsystem Pipeline (D-04)", () => {
|
||||
it("CAS prevents double checkout within pipeline", () => {
|
||||
const s = scaffold(db);
|
||||
|
||||
const taskId = db.saveTask({
|
||||
const taskId = db.tasks.saveTask({
|
||||
companyId: s.companyId,
|
||||
title: "Concurrent Task",
|
||||
description:
|
||||
@@ -273,10 +273,10 @@ describe("Cross-Subsystem Pipeline (D-04)", () => {
|
||||
workspaceId: s.workspaceId,
|
||||
connectorId: s.connectorId,
|
||||
});
|
||||
expect(db.checkoutTask(taskId, runId1)).toBe(true);
|
||||
expect(db.tasks.checkoutTask(taskId, runId1)).toBe(true);
|
||||
|
||||
// Second agent creates a run but cannot check out
|
||||
const agent2Id = db.saveAgent({
|
||||
const agent2Id = db.agents.saveAgent({
|
||||
companyId: s.companyId,
|
||||
name: "Agent2",
|
||||
role: "IC",
|
||||
@@ -296,7 +296,7 @@ describe("Cross-Subsystem Pipeline (D-04)", () => {
|
||||
workspaceId: s.workspaceId,
|
||||
connectorId: s.connectorId,
|
||||
});
|
||||
expect(db.checkoutTask(taskId, runId2)).toBe(false);
|
||||
expect(db.tasks.checkoutTask(taskId, runId2)).toBe(false);
|
||||
});
|
||||
|
||||
it("budget gate blocks run when policy exhausted", () => {
|
||||
@@ -311,7 +311,7 @@ describe("Cross-Subsystem Pipeline (D-04)", () => {
|
||||
});
|
||||
|
||||
// Create and complete a run that exceeds the budget
|
||||
const taskId = db.saveTask({
|
||||
const taskId = db.tasks.saveTask({
|
||||
companyId: s.companyId,
|
||||
title: "Expensive Task",
|
||||
description:
|
||||
@@ -330,7 +330,7 @@ describe("Cross-Subsystem Pipeline (D-04)", () => {
|
||||
connectorId: s.connectorId,
|
||||
});
|
||||
|
||||
db.checkoutTask(taskId, runId);
|
||||
db.tasks.checkoutTask(taskId, runId);
|
||||
db.markRunStatus(runId, "running", "Starting");
|
||||
db.recordCostEntry({ runId, companyId: s.companyId, agentId: s.agentId, taskId, connectorId: s.connectorId, amountUsd: 1.5, unattributed: false });
|
||||
db.finishRun({
|
||||
@@ -363,7 +363,7 @@ describe("Cross-Subsystem Pipeline (D-04)", () => {
|
||||
logger: mockLogger,
|
||||
});
|
||||
|
||||
const taskId = db.saveTask({
|
||||
const taskId = db.tasks.saveTask({
|
||||
companyId: s.companyId,
|
||||
title: "Multi-Run Task",
|
||||
description:
|
||||
@@ -375,7 +375,7 @@ describe("Cross-Subsystem Pipeline (D-04)", () => {
|
||||
});
|
||||
|
||||
// Create session
|
||||
const sessionId = db.createNewSession({
|
||||
const sessionId = db.tasks.createNewSession({
|
||||
companyId: s.companyId,
|
||||
agentId: s.agentId,
|
||||
adapterType: "gemini-cli",
|
||||
@@ -402,7 +402,7 @@ describe("Cross-Subsystem Pipeline (D-04)", () => {
|
||||
sessionService.accumulateTokenUsage(sessionId, 3000, 1000, runId2);
|
||||
|
||||
// Verify accumulated session state
|
||||
const session = db.getSessionById(sessionId);
|
||||
const session = db.tasks.getSessionById(sessionId);
|
||||
expect(session).not.toBeNull();
|
||||
expect(session!.runCount).toBe(2);
|
||||
expect(session!.totalInputTokens).toBe(8000);
|
||||
@@ -421,7 +421,7 @@ describe("Cross-Subsystem Pipeline (D-04)", () => {
|
||||
});
|
||||
|
||||
// --- First run: costs $3, budget allows ---
|
||||
const taskId1 = db.saveTask({
|
||||
const taskId1 = db.tasks.saveTask({
|
||||
companyId: s.companyId,
|
||||
title: "First Pipeline Task",
|
||||
description:
|
||||
@@ -439,7 +439,7 @@ describe("Cross-Subsystem Pipeline (D-04)", () => {
|
||||
workspaceId: s.workspaceId,
|
||||
connectorId: s.connectorId,
|
||||
});
|
||||
db.checkoutTask(taskId1, runId1);
|
||||
db.tasks.checkoutTask(taskId1, runId1);
|
||||
db.markRunStatus(runId1, "running", "Starting");
|
||||
db.recordCostEntry({ runId: runId1, companyId: s.companyId, agentId: s.agentId, taskId: taskId1, connectorId: s.connectorId, amountUsd: 3.0, unattributed: false });
|
||||
db.finishRun({
|
||||
@@ -462,7 +462,7 @@ describe("Cross-Subsystem Pipeline (D-04)", () => {
|
||||
expect(afterFirst.allowed).toBe(true);
|
||||
|
||||
// --- Second run: costs $3, now total = $6 > $5 budget ---
|
||||
const taskId2 = db.saveTask({
|
||||
const taskId2 = db.tasks.saveTask({
|
||||
companyId: s.companyId,
|
||||
title: "Second Pipeline Task",
|
||||
description:
|
||||
@@ -480,7 +480,7 @@ describe("Cross-Subsystem Pipeline (D-04)", () => {
|
||||
workspaceId: s.workspaceId,
|
||||
connectorId: s.connectorId,
|
||||
});
|
||||
db.checkoutTask(taskId2, runId2);
|
||||
db.tasks.checkoutTask(taskId2, runId2);
|
||||
db.markRunStatus(runId2, "running", "Starting");
|
||||
db.recordCostEntry({ runId: runId2, companyId: s.companyId, agentId: s.agentId, taskId: taskId2, connectorId: s.connectorId, amountUsd: 3.0, unattributed: false });
|
||||
db.finishRun({
|
||||
|
||||
@@ -52,7 +52,7 @@ describe("AppDatabase", () => {
|
||||
isPrimary: true,
|
||||
});
|
||||
|
||||
const agentId = db.saveAgent({
|
||||
const agentId = db.agents.saveAgent({
|
||||
companyId,
|
||||
name: "Helios",
|
||||
role: "CEO",
|
||||
@@ -83,7 +83,7 @@ describe("AppDatabase", () => {
|
||||
status: "in_progress",
|
||||
});
|
||||
|
||||
const taskId = db.saveTask({
|
||||
const taskId = db.tasks.saveTask({
|
||||
companyId,
|
||||
projectId,
|
||||
goalId,
|
||||
@@ -194,7 +194,7 @@ describe("AppDatabase", () => {
|
||||
isPrimary: true,
|
||||
});
|
||||
|
||||
const alphaAgentId = db.saveAgent({
|
||||
const alphaAgentId = db.agents.saveAgent({
|
||||
companyId: alphaCompanyId,
|
||||
name: "Alpha CEO",
|
||||
role: "CEO",
|
||||
@@ -208,7 +208,7 @@ describe("AppDatabase", () => {
|
||||
budgetMonthlyUsd: 100,
|
||||
});
|
||||
|
||||
db.saveAgent({
|
||||
db.agents.saveAgent({
|
||||
companyId: betaCompanyId,
|
||||
name: "Beta CEO",
|
||||
role: "CEO",
|
||||
@@ -222,7 +222,7 @@ describe("AppDatabase", () => {
|
||||
budgetMonthlyUsd: 100,
|
||||
});
|
||||
|
||||
db.saveTask({
|
||||
db.tasks.saveTask({
|
||||
companyId: alphaCompanyId,
|
||||
title: "Alpha task",
|
||||
description: "Alpha work",
|
||||
@@ -232,7 +232,7 @@ describe("AppDatabase", () => {
|
||||
status: "todo",
|
||||
});
|
||||
|
||||
db.saveTask({
|
||||
db.tasks.saveTask({
|
||||
companyId: betaCompanyId,
|
||||
title: "Beta task",
|
||||
description: "Beta work",
|
||||
|
||||
@@ -47,7 +47,7 @@ async function createHarness() {
|
||||
repoRef: "",
|
||||
isPrimary: true,
|
||||
});
|
||||
const agentId = db.saveAgent({
|
||||
const agentId = db.agents.saveAgent({
|
||||
companyId,
|
||||
name: "Research Lead",
|
||||
role: "Researcher",
|
||||
@@ -60,7 +60,7 @@ async function createHarness() {
|
||||
capabilities: "Research and synthesis",
|
||||
budgetMonthlyUsd: 180,
|
||||
});
|
||||
const taskId = db.saveTask({
|
||||
const taskId = db.tasks.saveTask({
|
||||
companyId,
|
||||
title: "Refresh the market report",
|
||||
description: "Update the report with new findings and submit it for review.",
|
||||
@@ -69,7 +69,7 @@ async function createHarness() {
|
||||
priority: "high",
|
||||
status: "in_progress",
|
||||
});
|
||||
db.setTaskType(taskId, "research");
|
||||
db.tasks.setTaskType(taskId, "research");
|
||||
|
||||
const existingDocumentId = db.saveDocument({
|
||||
companyId,
|
||||
@@ -172,7 +172,7 @@ describe("document run provenance", () => {
|
||||
// ai_ceo + top-level CEO without `reviewDeliverables` advances
|
||||
// straight to `done` — there is no reviewer in that configuration
|
||||
// so the prior `in_review` state was a dead letter.
|
||||
const task = harness.db.getTask(harness.taskId);
|
||||
const task = harness.db.entityCrud.getTask(harness.taskId);
|
||||
expect(task.status).toBe("done");
|
||||
} finally {
|
||||
harness.server.stop();
|
||||
|
||||
@@ -52,8 +52,8 @@ function buildKickoffPayload(db: AppDatabase, summary: { companyId: string; lead
|
||||
throw new Error("Missing lead agent or task in onboarding summary.");
|
||||
}
|
||||
|
||||
const agent = db.getAgent(summary.leadAgentId);
|
||||
const task = db.getTask(summary.taskId);
|
||||
const agent = db.entityCrud.getAgent(summary.leadAgentId);
|
||||
const task = db.entityCrud.getTask(summary.taskId);
|
||||
const connector = db.getConnector(agent.connectorId);
|
||||
const workspace = summary.workspaceId ? db.getWorkspace(summary.workspaceId) : null;
|
||||
const company = db.listSnapshot().companies.find((entry) => entry.id === summary.companyId);
|
||||
@@ -125,7 +125,7 @@ describe("onboarding model pool integration", () => {
|
||||
throw new Error("Expected lead agent id from onboarding bootstrap.");
|
||||
}
|
||||
|
||||
const agent = db.getAgent(summary.leadAgentId);
|
||||
const agent = db.entityCrud.getAgent(summary.leadAgentId);
|
||||
expect(agent.connectorId).toBe("openai-codex");
|
||||
expect(agent.model).toBe(enabledModel);
|
||||
expect(db.getDisabledModels(summary.companyId, "openai-codex")).toEqual([{ id: disabledModel, label: disabledModel }]);
|
||||
@@ -230,7 +230,7 @@ describe("onboarding model pool integration", () => {
|
||||
throw new Error("Expected lead agent id from onboarding bootstrap.");
|
||||
}
|
||||
|
||||
const agent = db.getAgent(summary.leadAgentId);
|
||||
const agent = db.entityCrud.getAgent(summary.leadAgentId);
|
||||
expect(agent.model).toBeNull();
|
||||
|
||||
const payload = buildKickoffPayload(db, summary);
|
||||
|
||||
@@ -79,7 +79,7 @@ function scaffold(db: AppDatabase): ScaffoldResult {
|
||||
});
|
||||
|
||||
// Agent
|
||||
const agentId = db.saveAgent({
|
||||
const agentId = db.agents.saveAgent({
|
||||
companyId,
|
||||
name: "RecoveryAgent",
|
||||
role: "IC",
|
||||
@@ -145,7 +145,7 @@ function createAndStartRun(
|
||||
): { runId: string; taskId: string } {
|
||||
const taskId =
|
||||
opts.taskId ??
|
||||
db.saveTask({
|
||||
db.tasks.saveTask({
|
||||
companyId: opts.companyId,
|
||||
title: opts.taskTitle ?? "Test Task",
|
||||
description:
|
||||
@@ -165,7 +165,7 @@ function createAndStartRun(
|
||||
connectorId: opts.connectorId,
|
||||
});
|
||||
|
||||
db.checkoutTask(taskId, runId);
|
||||
db.tasks.checkoutTask(taskId, runId);
|
||||
db.markRunStatus(runId, "running", "Starting execution");
|
||||
|
||||
if (opts.claimWorkspace !== false) {
|
||||
@@ -243,7 +243,7 @@ describe("Process Recovery -- Worker Crash Detection", () => {
|
||||
const s = scaffold(db);
|
||||
|
||||
// Create a second agent for a second run
|
||||
const agent2Id = db.saveAgent({
|
||||
const agent2Id = db.agents.saveAgent({
|
||||
companyId: s.companyId,
|
||||
name: "Agent2",
|
||||
role: "IC",
|
||||
@@ -306,7 +306,7 @@ describe("Process Recovery -- Worker Crash Detection", () => {
|
||||
// However, after crash, the task is set to "blocked" and a subsequent
|
||||
// stale lock recovery pass handles the checkout_run_id.
|
||||
// For this test, verify the task status is blocked (the recovery mechanism).
|
||||
const task = db.getTask(taskId);
|
||||
const task = db.entityCrud.getTask(taskId);
|
||||
expect(task.status).toBe("blocked");
|
||||
});
|
||||
|
||||
@@ -320,11 +320,11 @@ describe("Process Recovery -- Worker Crash Detection", () => {
|
||||
});
|
||||
|
||||
// Verify agent is running before crash
|
||||
expect(db.getAgent(s.agentId).status).toBe("running");
|
||||
expect(db.entityCrud.getAgent(s.agentId).status).toBe("running");
|
||||
|
||||
simulateWorkerCrash(db);
|
||||
|
||||
expect(db.getAgent(s.agentId).status).toBe("idle");
|
||||
expect(db.entityCrud.getAgent(s.agentId).status).toBe("idle");
|
||||
});
|
||||
|
||||
it("releases workspace locks after crash", () => {
|
||||
@@ -356,7 +356,7 @@ describe("Process Recovery -- Worker Crash Detection", () => {
|
||||
expect(wsAfter.lock_holder_run_id).toBeNull();
|
||||
|
||||
// Verify we can re-claim the workspace (lock is truly released)
|
||||
const agent2Id = db.saveAgent({
|
||||
const agent2Id = db.agents.saveAgent({
|
||||
companyId: s.companyId,
|
||||
name: "Agent2",
|
||||
role: "IC",
|
||||
@@ -369,7 +369,7 @@ describe("Process Recovery -- Worker Crash Detection", () => {
|
||||
capabilities: "Testing",
|
||||
budgetMonthlyUsd: 100,
|
||||
});
|
||||
const taskId2 = db.saveTask({
|
||||
const taskId2 = db.tasks.saveTask({
|
||||
companyId: s.companyId,
|
||||
title: "Recovery Task",
|
||||
description: "A recovery task after crash to verify workspace lock is released properly.",
|
||||
@@ -398,12 +398,12 @@ describe("Process Recovery -- Worker Crash Detection", () => {
|
||||
});
|
||||
|
||||
// Verify task is in_progress before crash
|
||||
expect(db.getTask(taskId).status).toBe("in_progress");
|
||||
expect(db.entityCrud.getTask(taskId).status).toBe("in_progress");
|
||||
|
||||
simulateWorkerCrash(db);
|
||||
|
||||
// Task should be blocked (prevents heartbeat re-queuing)
|
||||
expect(db.getTask(taskId).status).toBe("blocked");
|
||||
expect(db.entityCrud.getTask(taskId).status).toBe("blocked");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -422,12 +422,12 @@ describe("Process Recovery -- Context Preservation (D-02)", () => {
|
||||
});
|
||||
|
||||
// Verify assignment before crash
|
||||
expect(db.getTask(taskId).assigneeAgentId).toBe(s.agentId);
|
||||
expect(db.entityCrud.getTask(taskId).assigneeAgentId).toBe(s.agentId);
|
||||
|
||||
simulateWorkerCrash(db);
|
||||
|
||||
// Agent assignment must be preserved for retry context
|
||||
expect(db.getTask(taskId).assigneeAgentId).toBe(s.agentId);
|
||||
expect(db.entityCrud.getTask(taskId).assigneeAgentId).toBe(s.agentId);
|
||||
});
|
||||
|
||||
it("session handoff markdown generated on crash", () => {
|
||||
@@ -440,7 +440,7 @@ describe("Process Recovery -- Context Preservation (D-02)", () => {
|
||||
});
|
||||
|
||||
// Create a task session (simulating what happens during normal execution)
|
||||
const sessionId = db.createNewSession({
|
||||
const sessionId = db.tasks.createNewSession({
|
||||
companyId: s.companyId,
|
||||
agentId: s.agentId,
|
||||
adapterType: "gemini-cli",
|
||||
@@ -448,7 +448,7 @@ describe("Process Recovery -- Context Preservation (D-02)", () => {
|
||||
|
||||
// Drive the session past the maxRuns=20 threshold so rotation fires.
|
||||
for (let i = 0; i < 20; i++) {
|
||||
db.incrementSessionUsage(sessionId, 1000, 500, `${runId}-${i}`);
|
||||
db.tasks.incrementSessionUsage(sessionId, 1000, 500, `${runId}-${i}`);
|
||||
}
|
||||
|
||||
const sessionService = createSessionManagementService({
|
||||
@@ -456,7 +456,7 @@ describe("Process Recovery -- Context Preservation (D-02)", () => {
|
||||
logger: { info: vi.fn(), warn: vi.fn(), error: vi.fn() },
|
||||
});
|
||||
|
||||
const session = db.getSessionById(sessionId);
|
||||
const session = db.tasks.getSessionById(sessionId);
|
||||
expect(session).not.toBeNull();
|
||||
|
||||
const rotationResult = sessionService.evaluateRotation(session!);
|
||||
@@ -499,7 +499,7 @@ describe("Process Recovery -- Multiple Concurrent Runs", () => {
|
||||
const agents = [s.agentId];
|
||||
for (let i = 2; i <= 3; i++) {
|
||||
agents.push(
|
||||
db.saveAgent({
|
||||
db.agents.saveAgent({
|
||||
companyId: s.companyId,
|
||||
name: `Agent${i}`,
|
||||
role: "IC",
|
||||
@@ -540,7 +540,7 @@ describe("Process Recovery -- Multiple Concurrent Runs", () => {
|
||||
const agents = [s.agentId];
|
||||
for (let i = 2; i <= 3; i++) {
|
||||
agents.push(
|
||||
db.saveAgent({
|
||||
db.agents.saveAgent({
|
||||
companyId: s.companyId,
|
||||
name: `Agent${i}`,
|
||||
role: "IC",
|
||||
@@ -571,7 +571,7 @@ describe("Process Recovery -- Multiple Concurrent Runs", () => {
|
||||
|
||||
// All tasks should be blocked (crash recovery sets them to blocked)
|
||||
for (const run of runs) {
|
||||
const task = db.getTask(run.taskId);
|
||||
const task = db.entityCrud.getTask(run.taskId);
|
||||
expect(task.status).toBe("blocked");
|
||||
}
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ describe("skill-aware capability registry", () => {
|
||||
isPrimary: true,
|
||||
});
|
||||
|
||||
db.saveAgent({
|
||||
db.agents.saveAgent({
|
||||
companyId,
|
||||
name: "Generic Engineer",
|
||||
role: "Engineer",
|
||||
@@ -59,7 +59,7 @@ describe("skill-aware capability registry", () => {
|
||||
capabilities: "General software delivery",
|
||||
budgetMonthlyUsd: 200,
|
||||
});
|
||||
const styledEngineerId = db.saveAgent({
|
||||
const styledEngineerId = db.agents.saveAgent({
|
||||
companyId,
|
||||
name: "Frontend Specialist",
|
||||
role: "Engineer",
|
||||
@@ -94,7 +94,7 @@ describe("skill-aware capability registry", () => {
|
||||
});
|
||||
db.assignSkillToAgent(styledEngineerId, skillId);
|
||||
|
||||
const taskId = db.saveTask({
|
||||
const taskId = db.tasks.saveTask({
|
||||
companyId,
|
||||
title: "Build React dashboard widget",
|
||||
description: "Create an accessible React component with hooks and Tailwind styling.",
|
||||
@@ -103,7 +103,7 @@ describe("skill-aware capability registry", () => {
|
||||
status: "todo",
|
||||
});
|
||||
|
||||
const assignedId = db.autoAssignTask(taskId, companyId);
|
||||
const assignedId = db.tasks.autoAssignTask(taskId, companyId);
|
||||
expect(assignedId).toBe(styledEngineerId);
|
||||
|
||||
db.close();
|
||||
@@ -135,7 +135,7 @@ describe("skill-aware capability registry", () => {
|
||||
repoRef: "",
|
||||
isPrimary: true,
|
||||
});
|
||||
const agentId = db.saveAgent({
|
||||
const agentId = db.agents.saveAgent({
|
||||
companyId,
|
||||
name: "CEO",
|
||||
role: "CEO",
|
||||
@@ -171,7 +171,7 @@ describe("skill-aware capability registry", () => {
|
||||
|
||||
const snapshot = db.listSnapshot();
|
||||
const prompt = buildTaskPrompt({
|
||||
agent: db.getAgent(agentId),
|
||||
agent: db.entityCrud.getAgent(agentId),
|
||||
apiUrl: "http://127.0.0.1:3210",
|
||||
apiKey: "token",
|
||||
runId: "run-1",
|
||||
|
||||
@@ -50,7 +50,7 @@ function createScaffold(db: AppDatabase) {
|
||||
repoRef: "",
|
||||
isPrimary: true,
|
||||
});
|
||||
const ceoId = db.saveAgent({
|
||||
const ceoId = db.agents.saveAgent({
|
||||
companyId,
|
||||
name: "CEO",
|
||||
role: "CEO",
|
||||
@@ -63,7 +63,7 @@ function createScaffold(db: AppDatabase) {
|
||||
capabilities: "Lead the company",
|
||||
budgetMonthlyUsd: 250,
|
||||
});
|
||||
const engineerId = db.saveAgent({
|
||||
const engineerId = db.agents.saveAgent({
|
||||
companyId,
|
||||
name: "Engineer",
|
||||
role: "Engineer",
|
||||
@@ -76,7 +76,7 @@ function createScaffold(db: AppDatabase) {
|
||||
capabilities: "Ship features",
|
||||
budgetMonthlyUsd: 180,
|
||||
});
|
||||
const taskId = db.saveTask({
|
||||
const taskId = db.tasks.saveTask({
|
||||
companyId,
|
||||
title: "Ship the feature",
|
||||
description: "Implement the approved deliverable and wait for co-founder review.",
|
||||
@@ -133,7 +133,7 @@ describe("thread wait + heartbeat integration", () => {
|
||||
expect(waitStates[0]?.waitingFor).toBe("user_reply");
|
||||
expect(checkpointEngine.shouldPauseHeartbeat(companyId, engineerId)).toBe(true);
|
||||
|
||||
const otherAgentId = db.saveAgent({
|
||||
const otherAgentId = db.agents.saveAgent({
|
||||
companyId,
|
||||
name: "Marketer",
|
||||
role: "CMO",
|
||||
@@ -141,7 +141,7 @@ describe("thread wait + heartbeat integration", () => {
|
||||
status: "idle",
|
||||
reportsTo: null,
|
||||
connectorId: "openai-codex",
|
||||
workspaceId: db.getTask(taskId).workspaceId ?? null,
|
||||
workspaceId: db.entityCrud.getTask(taskId).workspaceId ?? null,
|
||||
model: null,
|
||||
capabilities: "Drive marketing work",
|
||||
budgetMonthlyUsd: 180,
|
||||
|
||||
@@ -66,7 +66,7 @@ async function createHarness(): Promise<Harness> {
|
||||
repoRef: "",
|
||||
isPrimary: true,
|
||||
});
|
||||
const agentId = db.saveAgent({
|
||||
const agentId = db.agents.saveAgent({
|
||||
companyId,
|
||||
name: "Artifact Engineer",
|
||||
role: "Engineer",
|
||||
@@ -79,7 +79,7 @@ async function createHarness(): Promise<Harness> {
|
||||
capabilities: "Writes files",
|
||||
budgetMonthlyUsd: 100,
|
||||
});
|
||||
const taskId = db.saveTask({
|
||||
const taskId = db.tasks.saveTask({
|
||||
companyId,
|
||||
title: "Produce artifact",
|
||||
description: "",
|
||||
|
||||
Reference in new issue
Block a user