OOS Investigation Workflow
Out-of-Specification investigations are long-running quality processes. When lab results fall outside limits, the investigation must be replayable and attributable.
Investigation Phases
Phase I: Laboratory Investigation
- Immediate assessment of raw data and calculations
- Supervisor review
- Route on the recorded finding:
- obvious error → QA review
- non-obvious → durable parallel analyses
Phase II: Manufacturing Investigation
If Phase I does not identify a lab error:
- process, equipment, and material review
- root-cause analysis with CAPA
Implementation
dart
final oosInvestigation = Workflow.define<OosInput, OosResult>(
code: 'quality.oos',
version: 1,
fingerprint: 'quality.oos:v1',
input: oosInputCodec,
output: oosResultCodec,
execute: (flow, input) async {
final assessment = await flow.userTask(
initialAssessment,
input,
id: 'assess',
title: 'OOS initial assessment',
assignment: Assignment(roleIds: ['qc_supervisor']),
);
if (assessment.finding == Finding.nonObvious) {
await flow.parallel(
id: 'phase-1-analyses',
branches: {
'repeat': (branch) => branch.userTask(
labAnalysis,
input,
id: 'repeat',
title: 'Repeat analysis',
assignment: Assignment(roleIds: ['qc_analyst']),
),
'fresh': (branch) => branch.userTask(
labAnalysis,
input,
id: 'fresh',
title: 'Fresh analysis',
assignment: Assignment(roleIds: ['qc_analyst']),
),
},
);
}
final qa = await flow.userTask(
qaReview,
input,
id: 'qa',
title: 'QA review',
assignment: Assignment(roleIds: ['qa_reviewer']),
);
if (qa.confirmed && qa.requiresCapa) {
await flow.serviceTask(createCapa, input, id: 'capa');
}
return qa.confirmed ? OosResult.confirmed() : OosResult.invalidated();
},
);Parallel analyses are named durable branches, not token splits. QA review is an assigned user task. CAPA creation is a versioned activity.
Compliance
- Two-phase investigation with recorded decisions
- Mandatory assigned reviews
- Append-only history for every completion
- Idempotent activities for CAPA writes