chore(07-02): bring in plan 01 prerequisites

Cherry-pick plan 01 task quality gate validation module and its
integration into IPC, API server, and automation service.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
ZacharyZhang-NYandClaude Opus 4.6 committed 2026-03-27 00:37:36 -04:00
1 parent c1de05a7f1
commit 4d1085fc04
6 files changed
+412 -2

No files matched your search

+18 -1
View File
@@ -11,6 +11,7 @@ import {
import { getConnectorExecutionReadinessIssue } from "@shared/connector-policy";
import { resolveApprovalDecision, shouldAutoApproveApproval } from "./approval-policy";
import { fail, ok, type RegisterCoreHandlersDependencies } from "./ipc-core-common";
import { validateTaskCreation, checkCapabilityMatch } from "./task-quality-gates";
export function registerCoreWorkGraphHandlers({
registerHandle,
@@ -314,6 +315,13 @@ export function registerCoreWorkGraphHandlers({
return fail("INVALID_COMPANY_REFERENCE", `The selected ${invalidReference.label} does not belong to the current company.`);
}
const isNew = !parsed.id;
if (isNew) {
const isHeartbeat = parsed.title.startsWith("[Heartbeat]");
const validation = validateTaskCreation(parsed, isHeartbeat);
if (!validation.valid) {
return fail("TASK_VALIDATION_FAILED", validation.errors.join("; "));
}
}
let previousStatus: string | null = null;
let previousAssigneeAgentId: string | null = null;
if (!isNew) {
@@ -355,8 +363,17 @@ export function registerCoreWorkGraphHandlers({
wakeAgentIfPossible(parsed.assigneeAgentId, parsed.companyId, "assignment");
}
}
let capabilityWarning: string | undefined;
if (isNew && parsed.assigneeAgentId) {
try {
const match = checkCapabilityMatch(db, parsed.assigneeAgentId, parsed.description, parsed.companyId);
if (!match.matched) {
capabilityWarning = match.warning;
}
} catch { /* capability check is advisory — never block task creation */ }
}
publishDomainChanged();
return ok(id);
return ok({ id, capabilityWarning });
});
registerHandle(IPC_CHANNELS.deleteAgent, async (_event, payload) => {