Skip to main content

Work Order State Machine — Guard Specification

Status: Accepted | Version: 2.0 | Date: 2026-02-13

Scope: Master WO + Linked WO Lifecycle with 21 CFR Part 11 Guards


1. State Inventory

Both Master and Linked Work Orders share the same 9-state lifecycle. The distinction lies in guard conditions and aggregation rules, not in the states themselves.

StateCategoryDescriptionEditable Fields
DRAFTInitialWO created, metadata incompleteAll fields
PLANNEDPlanningRequirements defined, awaiting schedulingJob plan, priority, requirements
SCHEDULEDPlanningResources allocated, timeline setSchedule only
IN_PROGRESSExecutionActive work underwayTime entries, execution notes
PENDING_REVIEWReviewWork complete, awaiting approvalNone (locked)
APPROVEDApprovalSystem Owner + QA (if reg) signed offNone (locked)
REJECTEDTerminal†Review found deficienciesNone (clone to rework)
COMPLETEDTerminalAll post-approval tasks confirmedNone (immutable)
CANCELLEDTerminalWO abandoned with justificationNone (immutable)

†REJECTED is terminal for that WO version — rework requires a DRAFT clone with lineage reference.


2. Transition Map

                    ┌──────────────────────────────────────────────┐
│ CANCELLED │
│ (from any non-terminal) │
└──────────────────────────────────────────────┘
▲ ▲ ▲ ▲
│ │ │ │
┌───────┐ T1 ┌────────┐ │T2 ┌──────────┐ T3 ┌─────────────┐
│ DRAFT │─────▶│PLANNED │──┼──▶│SCHEDULED │───▶│IN_PROGRESS │
└───────┘ └────────┘ │ └──────────┘ └──────┬──────┘
│ │ T4
│ ▼
│ ┌──────────────┐
│ │PENDING_REVIEW│
│ └───┬──────┬───┘
│ T5a │ │ T5b
│ ▼ ▼
│ ┌─────────┐ ┌────────┐
│ │APPROVED │ │REJECTED│
│ └────┬─────┘ └────────┘
│ T6 │
│ ▼
│ ┌──────────┐
└──────────────│COMPLETED │
└──────────┘

3. Guard Conditions by Transition

T1: DRAFT → PLANNED

interface T1Guards {
// Data completeness
originator_set: boolean; // originator_id NOT NULL
item_set: boolean; // item_id NOT NULL, ChangeItem exists
summary_present: boolean; // summary.length > 0
detail_present: boolean; // detail.length > 0

// RBAC
performed_by_role: 'ORIGINATOR' | 'ASSIGNER';

// Audit
audit_action: 'STATUS_CHANGE';
audit_previous: 'DRAFT';
audit_new: 'PLANNED';
}

Rationale: Minimum viable record completeness before resource planning begins. No schedule or job plan required yet — those come at PLANNED→SCHEDULED.

Part 11 implication: Audit trail entry captures who planned it and when. The version field increments.

T2: PLANNED → SCHEDULED

interface T2Guards {
// Resource readiness
job_plan_assigned: boolean; // job_plan_id NOT NULL
schedule_assigned: boolean; // schedule_id NOT NULL
minimum_requirements_defined: boolean; // ≥1 WorkOrderMinimumRequirement row

// Job plan validation
job_plan_has_tools: boolean; // ≥1 JobPlanToolRequirement
job_plan_has_experience: boolean; // ≥1 JobPlanExperienceRequirement

// Assignee
assignee_set: boolean; // assignee_id NOT NULL
assignee_meets_experience: boolean; // PersonExperience.rating ≥ JobPlanExperienceRequirement.minRating

// RBAC
performed_by_role: 'ASSIGNER' | 'SYSTEM_OWNER';
}

Rationale: Cannot schedule work without knowing what tools, skills, and people are needed. The experience match check prevents under-qualified assignments.

Part 11 implication: Job plan linkage creates traceability from execution method to the change record.

T3: SCHEDULED → IN_PROGRESS

interface T3Guards {
// Acknowledgment
assignee_acknowledged: boolean; // Assignee confirms start via authenticated action

// E-signature (optional per tenant config)
start_signature_required: boolean; // Tenant config: require_start_signature
signature_valid: boolean; // If required: ElectronicSignature.signerId === assignee.personId

// Time tracking
time_entry_created: boolean; // TimeEntry with actual_start_at set

// Dependencies (for Linked WOs)
predecessor_dependencies_met: boolean; // All predecessor WOs in COMPLETED|IN_PROGRESS|APPROVED

// RBAC
performed_by_role: 'ASSIGNEE';
}

Rationale: Assignee must explicitly start — prevents phantom work orders that show progress without actual execution. Dependency check for Linked WOs prevents out-of-order execution.

Part 11 implication: Login-bound action establishes who started the work. First TimeEntry.actual_start_at becomes the official execution start timestamp.

T4: IN_PROGRESS → PENDING_REVIEW

interface T4Guards {
// Execution completeness
all_time_entries_closed: boolean; // All TimeEntry rows have actual_end_at set

// Master WO aggregation
is_master_wo: boolean; // master_id IS NULL AND has children
children_ready_for_review: boolean; // If master: ALL children in PENDING_REVIEW|APPROVED|COMPLETED|CANCELLED

// Linked WO
execution_notes_present: boolean; // Detail field updated with execution results

// Regulatory artifacts (if regulatory_flag)
regulatory_evidence_attached: boolean; // IQ/OQ docs, golden image, WI updates linked

// RBAC
performed_by_role: 'ASSIGNEE' | 'SYSTEM_OWNER';
}

Rationale: Work must actually be done (time entries closed) before review. Master WOs cannot enter review while children are still executing — this prevents premature sign-off.

Part 11 implication: Regulatory evidence requirement ensures validation documentation is attached before any approval workflow begins. This is the "complete before you review" principle.

T5a: PENDING_REVIEW → APPROVED

interface T5aGuards {
// Approval chain
system_owner_approved: boolean; // Approval row: role=SYSTEM_OWNER, decision=APPROVED
qa_approved_if_required: boolean; // If regulatory_flag: Approval row: role=QA, decision=APPROVED

// E-signature (mandatory for approval)
system_owner_signature: boolean; // Approval.signatureId links to valid ElectronicSignature
qa_signature_if_required: boolean; // If regulatory: QA Approval.signatureId valid

// Signature validation
signature_signer_matches_approver: boolean; // sig.signerId === approval.approverId
signature_is_recent: boolean; // sig.signedAt within configurable window (e.g., 30 min)
signature_auth_method_valid: boolean; // authMethod in tenant-allowed methods

// No open child WOs (for Master)
all_children_terminal_or_approved: boolean;

// Separation of duties
approver_not_assignee: boolean; // approval.approverId !== wo.assigneeId

// RBAC
performed_by_role: 'SYSTEM_OWNER' | 'QA';
}

Rationale: This is the critical compliance gate. Dual approval (System Owner + QA for regulatory changes) with e-signatures is the Part 11 cornerstone. Separation of duties prevents self-approval.

Part 11 implication: §11.50 — Signature manifestation (signer identity, date/time, meaning). §11.10(e) — Authority checks. §11.70 — Signature/record linking.

T5b: PENDING_REVIEW → REJECTED

interface T5bGuards {
// Rejection record
rejection_decision_recorded: boolean; // Approval row: decision=REJECTED
rejection_comment_present: boolean; // comment.length > 0 (must explain deficiency)

// E-signature
rejection_signature_valid: boolean; // Valid ElectronicSignature linked

// RBAC
performed_by_role: 'SYSTEM_OWNER' | 'QA';
}

Post-transition action: System creates a DRAFT clone WO with previous_version_id pointing to the rejected WO, preserving lineage.

T6: APPROVED → COMPLETED

interface T6Guards {
// Post-approval verification
asset_state_updated: boolean; // Asset status reflects change (e.g., IN_PRODUCTION)
golden_image_captured: boolean; // If applicable: evidence document linked
work_instructions_updated: boolean; // If applicable: Document version linked

// Master WO aggregation
all_children_completed_or_cancelled: boolean; // No child in APPROVED (must progress to COMPLETED)
cancelled_children_justified: boolean; // Cancelled children have cancellation reason

// RBAC
performed_by_role: 'SYSTEM_OWNER' | 'ASSIGNER';
}

Rationale: APPROVED ≠ COMPLETED. The gap between these states captures post-approval tasks: updating asset registers, capturing golden images, updating controlled documents. This is frequently missed in naive implementations.

TC: Any Non-Terminal → CANCELLED

interface TCGuards {
// Authorization
cancellation_authorized: boolean; // Role = SYSTEM_OWNER | QA | ADMIN

// Justification
cancellation_reason_present: boolean; // reason.length > 0

// E-signature (if IN_PROGRESS or later)
cancellation_signature: boolean; // Required if status >= IN_PROGRESS

// Child WO cascade
children_cancellation_handled: boolean; // All active children also cancelled or completed

// Impact assessment (if regulatory_flag)
regulatory_impact_assessed: boolean; // For reg WOs: impact statement attached
}

4. Master ↔ Linked WO Aggregation Rules

Master WO Cannot Advance Past Children

Master Target StateRequired Child State(s)
PLANNEDNo restriction
SCHEDULED≥1 child WO exists
IN_PROGRESS≥1 child IN_PROGRESS
PENDING_REVIEWAll children: PENDING_REVIEW | APPROVED | COMPLETED | CANCELLED
APPROVEDAll children: APPROVED | COMPLETED | CANCELLED
COMPLETEDAll children: COMPLETED | CANCELLED (with justification)

Child WO Constraints

  • Child cannot move to COMPLETED if Master is CANCELLED (unless explicitly overridden by SYSTEM_OWNER with justification).
  • Child CANCELLED does not automatically cancel Master — Master requires its own cancellation decision.
  • Child WO dependency graph must be a DAG (no cycles). Validated at JobPlan creation.

5. Guard Implementation Pattern

// Composable guard pattern — each guard is a pure function
type Guard = (wo: WorkOrder, context: TransitionContext) => GuardResult;

interface GuardResult {
passed: boolean;
violation?: string;
part11_ref?: string; // e.g., "§11.10(e)"
}

// Guard composition
function composeGuards(...guards: Guard[]): Guard {
return (wo, ctx) => {
for (const guard of guards) {
const result = guard(wo, ctx);
if (!result.passed) return result;
}
return { passed: true };
};
}

// Transition registry
const TRANSITION_GUARDS: Record<string, Guard> = {
'DRAFT→PLANNED': composeGuards(
requireField('originatorId'),
requireField('itemId'),
requireField('summary'),
requireField('detail'),
requireRole('ORIGINATOR', 'ASSIGNER'),
),
'PLANNED→SCHEDULED': composeGuards(
requireField('jobPlanId'),
requireField('scheduleId'),
requireField('assigneeId'),
requireMinimumRequirements(),
requireExperienceMatch(),
requireRole('ASSIGNER', 'SYSTEM_OWNER'),
),
'SCHEDULED→IN_PROGRESS': composeGuards(
requireAssigneeAcknowledgment(),
requireDependenciesMet(),
requireRole('ASSIGNEE'),
),
'IN_PROGRESS→PENDING_REVIEW': composeGuards(
requireTimeEntriesClosed(),
requireChildrenReady('PENDING_REVIEW'),
requireRegulatoryEvidence(),
requireRole('ASSIGNEE', 'SYSTEM_OWNER'),
),
'PENDING_REVIEW→APPROVED': composeGuards(
requireApproval('SYSTEM_OWNER'),
requireApprovalIfRegulatory('QA'),
requireESignatures(),
requireSeparationOfDuties(),
requireChildrenTerminalOrApproved(),
requireRole('SYSTEM_OWNER', 'QA'),
),
'PENDING_REVIEW→REJECTED': composeGuards(
requireRejectionWithComment(),
requireESignature(),
requireRole('SYSTEM_OWNER', 'QA'),
),
'APPROVED→COMPLETED': composeGuards(
requirePostApprovalTasks(),
requireAllChildrenTerminal(),
requireRole('SYSTEM_OWNER', 'ASSIGNER'),
),
};

6. Audit Trail Event Schema

Every transition generates an immutable audit event:

interface AuditTrailEvent {
id: string; // UUID
entity_type: 'WORK_ORDER';
entity_id: string; // work_order.id
action: 'STATUS_CHANGE';
performed_by: string; // person.id
performed_at: string; // ISO 8601
previous_value: {
status: WorkOrderStatus;
version: number;
};
new_value: {
status: WorkOrderStatus;
version: number;
transition_guards_evaluated: GuardResult[];
signature_id?: string;
};
// Part 11 metadata
part11_context: {
regulatory_flag: boolean;
approval_chain: ApprovalRef[];
signature_refs: SignatureRef[];
};
}

7. CODITECT Agent Mapping

State TransitionCODITECT PatternAgent(s) Involved
DRAFT → PLANNEDPrompt ChainingWO Orchestrator → Task Classifier
PLANNED → SCHEDULEDOrchestrator-WorkersWO Orchestrator → Scheduling Agent, Experience Matching Agent
SCHEDULED → IN_PROGRESSRoutingWO Orchestrator → Asset Mgmt Agent, Vendor Coordinator
IN_PROGRESS → PENDING_REVIEWEvaluator-OptimizerWO Orchestrator → Documentation Agent, QA Review Agent
PENDING_REVIEW → APPROVEDHuman CheckpointQA Review Agent → Human (System Owner, QA)
APPROVED → COMPLETEDParallelizationAsset Mgmt Agent ∥ Documentation Agent ∥ WO Orchestrator
Any → CANCELLEDCircuit BreakerWO Orchestrator (with cascade to all active children)

The critical insight: PENDING_REVIEW → APPROVED is always a human checkpoint in CODITECT. No autonomous agent can approve regulatory changes — this is a hard architectural constraint derived from Part 11 §11.10(e) (authority checks) and §11.50 (signature manifestation).