Complete deviation user flow — Sai + Anita

Both user-only flows together, showing creation through closure with the time-gap between them.
See
Click
Type / Fill
Choose
Sign
Done
S
Sai — Discoverer
quality_lead deviation_discoverer site=Chennai
Part 1 · Creation
1
See
Home dashboard, signed in
Banner shows Site: Chennai · Role: quality_lead. Left nav shows the modules.
Behind the scenes
Session bootstrap (URS-01) returns baseRole + effectivePermissions + tenantId + matrixVersion in a single response. Active scope persisted on session row. Phase-1 single-site lock → scope-switch UI hidden (WP-1).
2
Click
Left nav → Deviations
Deviations list view opens.
Behind the scenes
PermissionGuard wraps the nav item — only shown if deviations:read permission is true. RLS + active-context filter applied to list query so only Chennai-scope deviations return.
3
Click
Top-right + New Deviation
Empty form opens.
Behind the scenes
Button gated by deviations:create. Server returns the form schema (field list + required flags + practice-specific module_options for the classification dropdown).
4
Fill
Fill the deviation form · 9 mandatory fields
Practice Domain *
GMP (Manufacturing)
Classification *
Documentation deviation (from GMP module_options)
Severity *
Minor (preliminary — QA may change)
Title *
(max 200 chars)
"Late line-clearance entry on Line 3 / shift 2"
Symptom Description *
(max 5000 chars)
"Checklist signed at 21:35; production resumed at 21:30. 5-min gap. No product affected."
Immediate Action Taken *
"Production paused 5 min retroactively; checklist completed."
Discovery / Detection Date *
Today 21:35 (default)
Priority *
Standard
Scope Anchors *
(≥1 of: site / product / study / supplier / batch / equipment)
Site: Chennai (pre-filled) · Product: antibiotic-line
Behind the scenes
9 mandatory fields enforced. Severity here is preliminary — QA reclassifies authoritatively at triage; original preserved in audit. Auto-populated & immutable from this point: discoverer_user_id, tenant_id, created_at. discovery_date default = now.
5
Choose
Save Draft or Submit?
Two buttons at the bottom of the form.
Behind the scenes
Save Draft → state stays draft, lighter validation. Submit → full validation per DEC-16-01 + DEC-16-14 + DEC-16-25 (immediate containment mandatory at intake). Server returns 400 DEVIATION_SCOPE_ANCHOR_REQUIRED / 422 VALIDATION_FAILED on missing fields.
Save Draft
5a
Click
Save Draft
Comes back later, edits, then Submits (joins right branch).
Submit
5b
Click
Submit for investigation
Confirms in popup.
6
See
Lands on the record
URL: /deviations/DEV-2026-000412. State badge INVESTIGATING. Form is read-only for him.
"Deviation DEV-2026-000412 submitted."
Behind the scenes
Server mints DEV-YYYY-NNNNNN race-safe via DB sequence (DEC-16-03). State machine transitions draft → investigating. Audit rows: DEVIATION_CREATED + DEVIATION_SUBMITTED with full payload (URS-06 hash chain). Intake section frozen — Sai's entries become permanently read-only.
DONE · Sai goes back to work. No more notifications until closure.
P
Priya — QA Lead
quality_lead qa_reviewer site=Chennai
Part 2 · Triage
7
See
Notification: new deviation needs triage
Bell icon shows count. Inbox shows: "DEV-2026-000412 submitted by Sai — needs triage."
Behind the scenes · routing
Pool-based broadcast. URS-30 resolves recipient list = all users at site Chennai holding qa_reviewer Authority Profile + active status. All 4 QA reviewers (Priya + Anjali + Karan + Deepika) get the same notification simultaneously. First-claimer-wins pattern.
8
Click
Open the deviation
Lands at /deviations/DEV-2026-000412.
Behind the scenes · claim
Opening the record marks Priya as the triage owner (triage_claimed_by = priya, triage_claimed_at = now). Other inbox entries auto-clear to "claimed by Priya · 2 min ago". No duplicate work.
9
See
Reads Sai's submission
Symptom · containment · preliminary classification (Minor) · scope (Chennai / antibiotic-line). She decides whether to accept Sai's call.
Behind the scenes · view layering
Same record, multiple sections. Intake tab is read-only for everyone after Sai's submit. Triage tab is editable only for qa_reviewer at this site. Priya cannot edit Sai's entries — only add her own.
10
Decide
Authoritative severity
Picks Minor (matches Sai). If she'd disagreed, she'd change it — the system audits the reclassification automatically.
Behind the scenes · reclassification
Single severity field; original preserved via audit. If she changes Minor→Major, system requires a reason (form field appears) + writes DEVIATION_STATUS_CHANGED with before/after/reason. Cascades: closure matrix updated, CAPA requirement re-evaluated, notification fan-out widened.
11
Fill
Fill triage details
Authoritative severity
Minor
Impact areas
data_integrity (low)
Assign investigator
Ravi (operator supervisor — SoD-16-01: ≠ Sai)
Due date
+7 days
Behind the scenes · investigator picker (5 server-side gates)
  • Holds deviation_investigator Authority Profile
  • Authority scope intersects record scope on every required dimension
  • URS-28 training qualification current (not lapsed)
  • ≠ discoverer (SoD-16-01) — server enforces, not just UI
  • User status = active (excludes PTO/leave)
Direct API bypass attempt (e.g., assign Sai) → 403 DEVIATION_SOD_VIOLATION_DISCOVERER_CANNOT_INVESTIGATE.
12
Click
Click Confirm Triage
Record saved. Triage event audited.
"Triage confirmed. Ravi notified."
Behind the scenes · post-triage lock
Triage section flips to read-only summary. Amend Triage button available to qa_reviewer with Controlled Approval Modal (reason + e-sig + MFA) — until Ravi signs conclusion, after which hard-locked forever (DEC-16-08 immutability cascade). Audit: DEVIATION_TRIAGED. URS-30 fires single notification to Ravi (named recipient — not pool).
DONE · Priya hands off to Ravi.
R
Ravi — Investigator
quality_lead deviation_investigator site=Chennai
Part 3 · Investigation
13
See
Notification: assigned to investigate
Inbox: "You've been assigned to investigate DEV-2026-000412. Due in 7 days."
Behind the scenes · direct assignment
Unlike Priya's broadcast, this is a named-recipient notification — only Ravi (no pool). His "My open investigations" dashboard count increments by 1. SoD-16-01 already verified at assignment.
14
Click
Open the deviation
Lands at /deviations/DEV-2026-000412/investigate.
Behind the scenes · scope check
Route guard: withAuthority({key:'deviation_investigator', scopeSource:'context'}). Record scope ∩ Ravi's authority scope must be non-empty on every required dimension or 403 APPROVAL_SCOPE_DENIED.
15
See
Reads symptom + containment + triage
Sees what Sai reported and what Priya decided. Knows scope is data_integrity (low), no batch.
16
Fill
Document investigation
Evidence (attachments)
Line 3 checklist photo · shift handover log
Timeline
21:25 verbal line-clearance complete · 21:30 production resumed · 21:35 paperwork signed
Witnesses
Shift supervisor Manoj (corroborates)
Batch impact
None — no batch in scope
Affected documents
None
Behind the scenes · evidence + child rows
Attachments stored as URS-12 controlled documents with linkage rows in deviation_attachments. Batch lookup checks batch_id IS NULL → no URS-23 trigger fires. Child rows (impact_areas, affected_documents) mutable while parent in investigating; immutable once parent closed/voided (DEC-16-08).
17
Fill
Inline root cause + conclusion
Root cause
"Paperwork lag during shift handover — operator prioritised line restart over signature."
Conclusion
"No product or data quality impact. Procedural lapse only. Reinforce SOP at next toolbox talk."
CAPA needed?
No — severity Minor; rationale recorded.
Behind the scenes · CAPA gate
root_cause_summary required before close (21 CFR 211.192). CAPA-required rule applied: severity in (major,critical) ⇒ capa_required=true. For Minor with no CAPA, rationale stored in investigation_conclusion_text. Annex 22: no genAI in severity/closure decisions.
18
Click
Click Sign Conclusion
Controlled Approval Modal opens.
19
Fill
Sign in the approval modal
20
Sign
Click Sign
Substrate-verified e-sig captured · timestamp 30-May 14:22 stamped.
Sign ▶
Behind the scenes · substrate-verified e-sig (WP-4)
Server calls validateEsignatureForRecord() (hitl/esig.service.ts:650) — same canonical verifier OOS uses. Verifies password + MFA + binds signature row to investigation_signature_id in same DB transaction. Forged/invalid esig_id → rejected, no transition. SoD-16-01 re-verified server-side (Ravi ≠ Sai).
21
See
Confirmation
"Investigation conclusion signed. Record is ready for closure. Anita has been notified."
Behind the scenes · downstream lock + closure routing
Triage section now hard-locked — Amend Triage button hidden (DEC-16-08 immutability cascade). Audit: DEVIATION_INVESTIGATION_SIGNED. URS-30 routes closure notification to deviation_closure_authority pool at site Chennai (broadcast pattern, same as Sai→QA).
DONE · Ravi hands off to Anita.
A
Anita Sharma — Closure Authority
quality_lead deviation_closure_authority site=Chennai
Part 4 · Closure
22
See
Notification in her inbox
Bell icon shows a count. She opens it.
🔔 "DEV-2026-000412 is ready for closure approval."
Behind the scenes
Broadcast pool — all deviation_closure_authority holders at site Chennai pinged. First-claimer-wins (same pattern as Sai→QA at step 7).
23
Click
Click the notification
Opens directly to the deviation record at /deviations/DEV-2026-000412.
Behind the scenes
Route guard: withAuthority({key:'deviation_closure_authority', scopeSource:'context', requiresEsign:true}). Closure tab opens — pre-closure gate evaluation triggers now.
24
See
Reviews the pre-closure checklist
All green; if any blocker existed, the Close button would be disabled.
Pre-closure checklist
Root cause documented — "Paperwork lag during shift handover"
Investigation conclusion signed by Ravi · 30-May 14:22
Severity = Minor → no CAPA required
No batch impact (batch is an optional reference selected from the seeded-batch typeahead — URS-23 reference rows; batch hold/disposition control is Phase-3)
All review comments resolved
SoD pass — Anita ≠ Sai (reporter), Anita ≠ Ravi (investigator)
Behind the scenes · checklist is SERVER-COMPUTED, not human-ticked
Anita reads — does NOT tick. Server runs evaluate_closure_gate(deviation_id, actor) on page open, returns structured result; UI paints it. Each check evaluated against DB state in real time:
  • Root causeroot_cause_summary IS NOT NULL
  • Conclusion signed → join electronic_signatures + validateEsignatureForRecord()
  • CAPA gate → severity-based rule eval
  • Batch reference → optional, selected from the seeded-batch typeahead (URS-23 reference rows, search component); no batch hold/disposition control in Phase-1 (deferred to Phase-3)
  • Commentscount(comments WHERE status='open') = 0
  • SoDactor.id ≠ discoverer_id AND ≠ investigator_id
Any check fails → Close button disabled with tooltip naming the failing rule. Anita cannot override; she must fix the underlying condition (re-route to Ravi etc.). Audit: DEVIATION_CLOSURE_GATE_EVALUATED on each open (forensic trail).
25
See
Reads the investigation summary
Scannable summary: symptom, immediate containment, evidence, root cause, conclusion. Anita reads to confirm she agrees with the closure rationale.
Behind the scenes · human judgment
This is the only purely manual step. Green checks confirm rules pass; whether root cause is good is Anita's call. If she disagrees: add a comment (creates open comment → blocks closure) or reassign back to Ravi.
26
Click
Click Close Deviation
Controlled Approval Modal opens.
Behind the scenes
Button only enabled if gate evaluation returned all_pass=true. Defense-in-depth — server will re-evaluate when she signs (see step 28).
27
Fill
Fill the Controlled Approval Modal
28
Sign
Click Sign & Close
Modal confirms; spinner; few seconds.
Sign & Close ▶
Behind the scenes · defense in depth
Server re-runs evaluate_closure_gate() before applying the signature. If anything changed since step 24 (new comment, signature invalidated, SoD changed) → 409 CLOSURE_GATE_CHANGED; UI refreshes checklist. Otherwise: validateEsignatureForRecord() binds e-sig to closed_e_sig_id in same transaction; state machine transitions investigating → closed.
29
See
Sees confirmation — state flips to CLOSED
INVESTIGATINGCLOSED
"DEV-2026-000412 closed. Record is now immutable."
Behind the scenes · immutability cascade
Audit: DEVIATION_CLOSED + bound electronic_signatures row. Parent + child rows all immutable (DEC-16-08) — header, impact_areas, affected_documents, comments. Any mutation attempt → DEVIATION_IMMUTABLE_FINAL_STATE. URS-06 hash chain seals record. URS-30 fires single closure notification to Sai (reporter loop-closure).
DONE · Anita's part is over. Total time: ~3–5 minutes.
Loop-closure · Sai gets pinged
S
Sai — Discoverer (notification only)
quality_lead deviation_discoverer site=Chennai
Part 5 · Notice
30
See
One notification — closure
Sai gets a single notification: "Your report DEV-2026-000412 has been closed by Anita Sharma."
🔔 "DEV-2026-000412 closed."
What Sai is NOT pinged for during the workflow: QA triage in progress · investigator assigned · conclusion signed · CAPA opened · reportability clock. Those route to action-takers via URS-30; SoD-16-01 keeps Sai out of the investigation lane.
Behind the scenes · routing rule
URS-30 closure notification routes ONLY to discoverer_user_id (named recipient). No pool, no broadcast. Other events (TRIAGED, INVESTIGATION_SIGNED, CAPA_LINKED, REPORTABILITY_DEADLINE_SET) explicitly exclude the discoverer from their recipient lists — by URS-30 routing config, not by accident.
END · Full lifecycle complete.