fix(db): make filesystem lock intent explicit

This commit is contained in:
ZacharyZhang-NY committed 2026-04-22 13:51:41 -04:00
1 parent 0785c9ba47
commit 19f8494d09
15 files changed
+188 -125

No files matched your search

@@ -0,0 +1,50 @@
import { DatabaseSync } from "node:sqlite";
import { describe, expect, it } from "vitest";
import { createMigrationsV52V52 } from "../../src/main/database/migrations/v52-v52";
describe("v52 requires_filesystem migration", () => {
it("materializes legacy role evidence into the task column", () => {
const db = new DatabaseSync(":memory:");
try {
db.exec(`
create table agents (
id text primary key,
role text not null,
department text
);
create table tasks (
id text primary key,
status text not null,
active_run_id text,
assignee_agent_id text
);
insert into agents (id, role, department) values
('agent-cto', 'CTO', 'engineering'),
('agent-cmo', 'CMO', 'marketing');
insert into tasks (id, status, active_run_id, assignee_agent_id) values
('task-coding', 'todo', null, 'agent-cto'),
('task-cognitive', 'todo', null, 'agent-cmo'),
('task-inflight', 'in_progress', 'run-1', 'agent-cmo');
`);
const migration = createMigrationsV52V52({
db,
readAll: <T>(sql: string) => db.prepare(sql).all() as T[],
})[0];
migration.fn?.();
const rows = db.prepare(
"select id, requires_filesystem as requiresFilesystem from tasks order by id",
).all() as Array<{ id: string; requiresFilesystem: number }>;
expect(rows).toEqual([
{ id: "task-coding", requiresFilesystem: 1 },
{ id: "task-cognitive", requiresFilesystem: 0 },
{ id: "task-inflight", requiresFilesystem: 1 },
]);
} finally {
db.close();
}
});
});
+50 -15
View File
@@ -21,14 +21,14 @@ import {
/**
* Workspace lock policy (v52+).
*
* `lockMode='exclusive'` is opt-in — either the task carries an
* explicit workspaceId, or the task's requiresFilesystem column is
* true, or the resolving agent is in a coding role. Every other
* case returns `lockMode='none'` and `workspace=null`, so four
* pure-cognitive agents run fully in parallel without queueing on
* a single workspace lock. The previous `company_fallback` step
* (which returned any workspace in the company as the lock target)
* has been removed and now throws `WorkspaceConfigurationError`.
* `lockMode='exclusive'` is opt-in: the task carries an explicit
* workspaceId or the task's requiresFilesystem column is true.
* Every other case returns `lockMode='none'` and `workspace=null`,
* so four pure-cognitive agents run fully in parallel without
* queueing on a single workspace lock. The previous
* `company_fallback` step has been removed and now throws
* `WorkspaceConfigurationError` for filesystem tasks with no
* resolvable workspace.
*/
function seedCompany(db: AppDatabase) {
@@ -82,7 +82,7 @@ describe("resolveWorkspaceWithLockMode", () => {
expect(result.workspace?.id).toBe(workspaceId);
});
it("coding-role agent + agent workspace → exclusive on agent workspace", () => {
it("agent role and department do not infer filesystem locking", () => {
const { companyId, workspaceId } = seedCompany(db);
const result = resolveWorkspaceWithLockMode(
db,
@@ -90,12 +90,47 @@ describe("resolveWorkspaceWithLockMode", () => {
{ workspaceId, role: "CTO", department: "engineering" },
companyId,
);
expect(result.source).toBe("agent");
expect(result.lockMode).toBe("exclusive");
expect(result.workspace?.id).toBe(workspaceId);
expect(result.source).toBe("none");
expect(result.lockMode).toBe("none");
expect(result.workspace).toBeNull();
});
it("coding-role agent + project primary workspace → exclusive on project workspace", () => {
it("persisted requiresFilesystem=false stays lock-free for engineering-assigned tasks", () => {
const { companyId, workspaceId } = seedCompany(db);
const agentId = db.agents.saveAgent({
companyId,
name: "Engineering Lead",
role: "CTO",
title: "CTO",
department: "engineering",
status: "active",
reportsTo: null,
connectorId: "openai-codex",
workspaceId,
model: null,
capabilities: "Architecture",
budgetMonthlyUsd: 100,
});
const taskId = db.tasks.saveTask({
companyId,
title: "Review launch plan",
description: "Read the plan and respond with strategic comments.",
assigneeAgentId: agentId,
workspaceId: null,
priority: "medium",
status: "todo",
requiresFilesystem: false,
});
const task = db.entityCrud.getTask(taskId);
const agent = db.entityCrud.getAgent(agentId);
const result = resolveWorkspaceWithLockMode(db, task, agent, companyId);
expect(task.requiresFilesystem).toBe(false);
expect(result.source).toBe("none");
expect(result.lockMode).toBe("none");
expect(result.workspace).toBeNull();
});
it("requiresFilesystem + project primary workspace → exclusive on project workspace", () => {
const { companyId } = seedCompany(db);
const projectId = db.entityCrud.saveProject({
companyId,
@@ -135,7 +170,7 @@ describe("resolveWorkspaceWithLockMode", () => {
expect(result.source).toBe("agent");
});
it("pure cognitive task (no coding role, no requiresFilesystem) → none", () => {
it("pure cognitive task without requiresFilesystem → none", () => {
const { companyId } = seedCompany(db);
const result = resolveWorkspaceWithLockMode(
db,
@@ -255,7 +290,7 @@ describe("resolveWorkspaceWithLockMode", () => {
const { companyId, workspaceId } = seedCompany(db);
const result = resolveWorkspaceWithLockMode(
db,
null,
{ workspaceId: null, projectId: null, requiresFilesystem: true },
{ workspaceId, role: "CTO", department: "engineering" },
companyId,
);
+23
View File
@@ -0,0 +1,23 @@
import { describe, expect, it } from "vitest";
import { taskInputSchema } from "../../src/shared/contracts";
describe("taskInputSchema", () => {
const baseTask = {
companyId: "00000000-0000-4000-8000-000000000001",
title: "Prepare launch review",
};
it("normalizes omitted requiresFilesystem to false", () => {
const parsed = taskInputSchema.parse(baseTask);
expect(parsed.requiresFilesystem).toBe(false);
});
it("preserves explicit requiresFilesystem intent", () => {
const parsed = taskInputSchema.parse({
...baseTask,
requiresFilesystem: true,
});
expect(parsed.requiresFilesystem).toBe(true);
});
});