/** * apps/worker — CHANGE_STATUS executor (TASK-0112 §5). * * Set a STATUS/SELECT property on a record to an option (by NAME — the * Phase-5 stored value is `{optionId: }`). Re-checks at runtime * that the property exists, belongs to the record's database, is of type * STATUS or SELECT, and that the option name is valid (fatal otherwise). * Delegates to the shared record-write tail (permission + coercion + * record.updated). */ import { resolveRecordId, writeRecordPropertyById } from "./record-write.js"; import { FatalError } from "./classify.js"; import type { ActionExecutor, ActionResult } from "./types.js"; const executor: ActionExecutor = { type: "CHANGE_STATUS", async execute(cfg, ctx, deps): Promise { const recordId = resolveRecordId(cfg, ctx); const propertyId = cfg.propertyId as string; const value = cfg.value as string; // Re-check the property type + option at runtime (the API verified at // save; drift is fatal). const prop = await deps.prisma.databaseProperty.findFirst({ where: { id: propertyId, deletedAt: null }, select: { id: true, name: true, type: true, config: true } }); if (!prop) throw new FatalError("PROPERTY_UNKNOWN", `property ${propertyId} not found`); if (prop.type !== "STATUS" && prop.type !== "SELECT") { throw new FatalError("PROPERTY_TYPE_MISMATCH", `property ${propertyId} is ${prop.type}, not STATUS/SELECT`); } const options: { name?: string; id?: string }[] = Array.isArray((prop.config as Record)?.options) ? ((prop.config as Record).options as { name?: string; id?: string }[]) : []; const known = options.some((o) => o.name === value || o.id === value); if (options.length > 0 && !known) { throw new FatalError("PROPERTY_OPTION_UNKNOWN", `option "${value}" not found on property ${propertyId}`); } const result = await writeRecordPropertyById(deps, ctx, recordId, propertyId, value); return { output: { recordId: result.recordId, propertyId, value, eventEmitted: result.eventEmitted }, emittedEvents: result.eventEmitted ? ["record.updated"] : [], audit: { action: "automation.status_changed", resourceType: "record", resourceId: result.recordId, metadata: { propertyId, value } } }; } }; export default executor;