CAP workflow abstraction
CAP workflow abstraction
Section titled “CAP workflow abstraction”Audience: backend, product
Status: specced
Owns: backend
Depends on: CAP overview, RPL lifecycle, NSQ lifecycle, Data model, Architecture handover
Protects against changing stage order and ownership without pretending every stage behavior is a JSON parameter.
Two problems (keep separate)
Section titled “Two problems (keep separate)”| Problem | Configurable? | Approach |
|---|---|---|
| A — Stage sequencing | Yes | Which stages exist, order, assigning authority, entry conditions |
| B — Stage behavior | No (closed set of strategies in code) | Single approval vs panel+signatures vs evidence review vs form submission are different interaction patterns |
Do not build a full BPMN engine. Platform admin may later reorder/add stages or swap role ownership; how a panel completes stays in code.
Data model
Section titled “Data model”workflow_templates (id, name, application_type, version)stage_definitions (workflow_template_id, key, order, handler_strategy, -- see closed set below actor_role, -- reviewer | facilitator | panel | iv | ev | unit_assessor | ... assigning_authority,-- centre | awarding_body | platform entry_conditions) -- e.g. payment_completeapplication_workflow_state (application_id, workflow_template_id, current_stage_key, entered_at)stage_history (application_id, stage_key, entered_at, exited_at, outcome)Cross-cutting (any stage):
feedback (application_id, stage_key, version_no, author_id, content, created_at)appeals (application_id, stage_key, raised_by, against_stage_history_id, status)resolutions (appeal_id, resolved_by, decision, comment, resulting_stage_key)Appeals reference (application_id, stage_key) / stage_history — they do not need to know the handler strategy. Resolver derives from that stage’s assigning_authority.
Version-lock: application_workflow_state.workflow_template_id points at the template version active when the application started so editing future templates does not break in-flight apps.
Default workflows
Section titled “Default workflows”Seed RPL and NSQ as rows via migration (no platform-admin config UI yet).
| Path | Mid-pipeline (simplified) | Shared tail |
|---|---|---|
| RPL | application form → payment → folder arrangement → interview (panel_evaluation) |
IV → EV → certification |
| NSQ | application form → payment → induction (form_submission) → regular assessment (evidence_review) |
IV → EV → certification |
RPL seven-stage display (§28)
Section titled “RPL seven-stage display (§28)”For the single-application view, Application Form and Payment become real stage_definitions rows (not only derived UI state), so GET /applications/{id}/stages reads one uniform stage_history source for:
- Application Form
- Payment
- Folder Arrangement
- Interview
- IV
- EV
- Certification
Relative-time labels (“3 days left”) are client-computed from raw status + dates — not returned by the API.
Handler strategies (closed set in code)
Section titled “Handler strategies (closed set in code)”interface StageHandlerStrategy { readonly key: string; assign(ctx: StageContext, actors: Actor[]): Promise<void>; submitEvaluation(ctx: StageContext, input: EvaluationInput): Promise<void>; evaluate(ctx: StageContext): Promise<StageOutcome>;}
const strategyRegistry: Record<string, StageHandlerStrategy> = { single_approval: /* reviewer, facilitator final, IV, EV */, panel_evaluation: /* 3 panelists + lead; signatures; candidate sign-off — RPL */, evidence_review: /* NSQ NOS evidence / DO+PRF; persists AssessorAssignment */, form_submission: /* NSQ induction — candidate self-service, no external approver */, // TODO (§28): self_service — Application Form draft/submit as a real stage // TODO (§28): external_gate — Payment stage whose "approval" comes from webhook, not an actor};Engine: look up handler_strategy from stage_definitions → dispatch → persist outcome to stage_history → advance current_stage_key on approve, or write feedback / bump application_versions on reject.
Planned strategies (TODO — not implemented)
Section titled “Planned strategies (TODO — not implemented)”| Strategy | Intended use | Status |
|---|---|---|
self_service |
Application Form as a tracked stage (draft/submit) | TODO — need described; seeded stage_definitions rows not written yet |
external_gate |
Payment stage completed by payment.completed webhook, not an actor |
TODO — same |
Until these land, payment unlock and application submit continue to work via existing entry conditions / consumers; the seven-stage API surface expects the fuller model once CAP-36 (implementation checklist) completes.
UNIT_ASSESSOR / AssessorAssignment (NSQ-only)
Section titled “UNIT_ASSESSOR / AssessorAssignment (NSQ-only)”- NOS form sign-off roles:
LEARNER | UNIT_ASSESSOR | IQA | EQA.UNIT_ASSESSORis a contextual role (not identity-levelAssessorProfile). AssessorAssignmentis structurally parallel toIvAssignment/EvAssignment;EvidenceReviewStrategy.assign()persists to it.- For NSQ DO, the UNIT_ASSESSOR role is filled by someone holding QAA.
- RPL has no unit sign-off. RPL uses interview panel evaluation only.
UnitAssessmentRecord,PerformanceCriteriaEvidence,UnitSignoff, andAssessorAssignmentare never created for RPL (templates never useevidence_reviewfor that path). - RPL Folder Arrangement uses the Evidence Vault models instead — see Data model.
LEARNERneeds no assignment table — always viaapplication.candidateId.ApplicationUnitremains shared by both application types.
Build now vs later
Section titled “Build now vs later”| Now | Later |
|---|---|
| Tables + four strategies + seeded RPL/NSQ templates | Platform-admin workflow editor UI |
| Candidate self-submit induction | Optional centre reviewer on induction (product open) |
Document need for self_service / external_gate |
Implement + seed those strategies (§28 TODO) |
See also
Section titled “See also”- Tables → Data model
- Process SLAs on stage dwell time → Observability
- Authz → RBAC matrix