refactor(workspace): extract resolveWorkspaceWithLockMode shared helper

P1-21: worker-crash-recovery reinvented workspace resolution inline and
silently dropped the exclusive/shared distinction — a recovered run
could claim shared mode that the original run held exclusively.
Introduce resolve-workspace.ts as the single source of truth for the
task→agent→project→company fallback chain and its lockMode, used by
both run-dispatch and crash recovery.
This commit is contained in:
ZacharyZhang-NY committed 2026-04-16 23:00:13 -04:00
1 parent 6ce4fd1fe4
commit a852597d2e
3 files changed
+49 -37

No files matched your search

@@ -0,0 +1,43 @@
import type { AppDatabase } from "../database";
import type { WorkspaceLockMode } from "./types";
/**
* Resolve the workspace and lock semantics for a given run context.
*
* Previously this logic lived both in run-dispatch.ts (normal dispatch path)
* and worker-crash-recovery.ts (re-enqueue after crash); the recovery
* copy silently dropped the exclusive/shared distinction. Sharing the
* helper guarantees both paths agree on which runs hold exclusive locks.
*/
export function resolveWorkspaceWithLockMode(
db: AppDatabase,
task: { workspaceId: string | null; projectId?: string | null },
agent: { workspaceId: string | null },
companyId: string,
): {
workspace: ReturnType<AppDatabase["getWorkspace"]>;
lockMode: WorkspaceLockMode;
source: "task" | "agent" | "project" | "company_fallback" | "none";
} {
const taskWorkspace = db.getWorkspace(task.workspaceId);
if (taskWorkspace) {
return { workspace: taskWorkspace, lockMode: "exclusive", source: "task" };
}
const agentWorkspace = db.getWorkspace(agent.workspaceId);
if (agentWorkspace) {
return { workspace: agentWorkspace, lockMode: "exclusive", source: "agent" };
}
const projectWorkspace = db.getProjectPrimaryWorkspace(task.projectId ?? null, companyId);
if (projectWorkspace) {
return { workspace: projectWorkspace, lockMode: "exclusive", source: "project" };
}
const fallbackWorkspace = db.getAnyCompanyWorkspace(companyId);
if (fallbackWorkspace) {
return { workspace: fallbackWorkspace, lockMode: "shared", source: "company_fallback" };
}
return { workspace: null, lockMode: "none", source: "none" };
}
+3 -22
View File
@@ -1,8 +1,9 @@
import { getConnectorExecutionReadinessIssue } from "@shared/connector-policy";
import type { ConnectorRecord } from "@shared/types";
import type { WorkspaceLockMode, OrchestrationDependencies } from "./types";
import type { OrchestrationDependencies } from "./types";
import { buildRunPayload, type RunDispatchParams } from "./turn-context";
import { validateConnectorModelSelection } from "../connector-model-policy";
import { resolveWorkspaceWithLockMode } from "./resolve-workspace";
export function createRunDispatch(deps: OrchestrationDependencies) {
const {
@@ -24,27 +25,7 @@ export function createRunDispatch(deps: OrchestrationDependencies) {
agent: { workspaceId: string | null },
companyId: string,
) {
const taskWorkspace = db.getWorkspace(task.workspaceId);
if (taskWorkspace) {
return { workspace: taskWorkspace, lockMode: "exclusive" as WorkspaceLockMode, source: "task" as const };
}
const agentWorkspace = db.getWorkspace(agent.workspaceId);
if (agentWorkspace) {
return { workspace: agentWorkspace, lockMode: "exclusive" as WorkspaceLockMode, source: "agent" as const };
}
const projectWorkspace = db.getProjectPrimaryWorkspace(task.projectId ?? null, companyId);
if (projectWorkspace) {
return { workspace: projectWorkspace, lockMode: "exclusive" as WorkspaceLockMode, source: "project" as const };
}
const fallbackWorkspace = db.getAnyCompanyWorkspace(companyId);
if (fallbackWorkspace) {
return { workspace: fallbackWorkspace, lockMode: "shared" as WorkspaceLockMode, source: "company_fallback" as const };
}
return { workspace: null, lockMode: "none" as WorkspaceLockMode, source: "none" as const };
return resolveWorkspaceWithLockMode(db, task, agent, companyId);
}
function getRunEligibilityFailure(