Slices 2-4: Work becomes executable
Domain primitives: versioned WorkDefinition/DesignPacket with risk, questions, and approval gates; lifecycle transition table with invalidation rules; Resolver (Run/Attempt/outcomes/CodingKitV0); provider-neutral HarnessRuntime with normalized events and a deterministic FakeHarnessLive that always reaches a terminal classification and never claims implementation. Convex durable model: workDefinitions, workQuestions, workApprovals, designPackets, workSlices, workRuns, workAttempts, workAttemptEvents tables; broadened works.status union; generalized workEvents with optional signalId, referenceId, and payloadJson. Authenticated commands for definition request/ save/revise/approve, question answer/withdraw, design save/revise/approve, and simulated execution start/cancel/retry with leased attempts, checkpointed events, and expired-lease reconciliation. Private work-planner FLUE agent with proposal-only tools (definition, design, question). Convex validates and stores every proposal; FLUE never approves or advances Work. Expanded Work card with Outcome, Design, and Build sections wired to the new mutations. Slice 1 conversation and provenance preserved.
This commit is contained in:
69
packages/primitives/src/work-lifecycle.ts
Normal file
69
packages/primitives/src/work-lifecycle.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
import { Schema } from "effect";
|
||||
|
||||
export const WorkStatus = Schema.Literals([
|
||||
"proposed",
|
||||
"defining",
|
||||
"awaiting-definition-approval",
|
||||
"designing",
|
||||
"awaiting-design-approval",
|
||||
"ready",
|
||||
"executing",
|
||||
"needs-input",
|
||||
"blocked",
|
||||
"completed",
|
||||
"failed",
|
||||
"cancelled",
|
||||
]);
|
||||
export type WorkStatus = typeof WorkStatus.Type;
|
||||
|
||||
export const WORK_TRANSITIONS: Readonly<
|
||||
Record<WorkStatus, readonly WorkStatus[]>
|
||||
> = {
|
||||
"awaiting-definition-approval": ["designing", "defining"],
|
||||
"awaiting-design-approval": [
|
||||
"ready",
|
||||
"designing",
|
||||
"awaiting-definition-approval",
|
||||
],
|
||||
blocked: ["ready", "executing", "cancelled"],
|
||||
cancelled: ["ready", "proposed"],
|
||||
completed: ["proposed", "defining"],
|
||||
defining: ["awaiting-definition-approval", "proposed"],
|
||||
designing: ["awaiting-design-approval", "awaiting-definition-approval"],
|
||||
executing: [
|
||||
"ready",
|
||||
"needs-input",
|
||||
"blocked",
|
||||
"completed",
|
||||
"failed",
|
||||
"cancelled",
|
||||
],
|
||||
failed: ["ready", "executing", "cancelled"],
|
||||
"needs-input": ["ready", "executing", "cancelled"],
|
||||
proposed: ["defining"],
|
||||
ready: ["executing", "designing", "cancelled"],
|
||||
};
|
||||
|
||||
export const canTransitionWork = (from: WorkStatus, to: WorkStatus): boolean =>
|
||||
WORK_TRANSITIONS[from].includes(to);
|
||||
|
||||
export const assertWorkTransition = (
|
||||
from: WorkStatus,
|
||||
to: WorkStatus
|
||||
): void => {
|
||||
if (!canTransitionWork(from, to)) {
|
||||
throw new Error(`Invalid Work transition: ${from} -> ${to}`);
|
||||
}
|
||||
};
|
||||
|
||||
export const definitionRevisionInvalidation = {
|
||||
definitionApproval: "invalidate" as const,
|
||||
dependentDesign: "invalidate" as const,
|
||||
designApproval: "invalidate" as const,
|
||||
workStatus: "defining" as const,
|
||||
};
|
||||
|
||||
export const designRevisionInvalidation = {
|
||||
designApproval: "invalidate" as const,
|
||||
workStatus: "designing" as const,
|
||||
};
|
||||
Reference in New Issue
Block a user