Title: Workflow State Machine Version: 1.1 Owner: [TENANT_CONFIGURATION_REQUIRED — Architecture] Status: Draft (target-state; only TAN status is implemented — see notice) Last reviewed: 2026-09-08 Next review: [TENANT_CONFIGURATION_REQUIRED] Reviewers: Architecture, HR, Security
Implementation reality notice (2026-09-08): there is no generic workflow-engine table backing these state machines (no
workflow_state_transitiontable exists). Each business entity carries its own status column, and the stored procedure performing a transition enforces validity atomically with the write — see ADR-006. Of the state machines below, only TAN/JobRequisition status is implemented:recruitment.JobRequisition.RequisitionStatusCodemovesDraft → PendingApproval → Approved(viarecruitment.usp_CreateJobRequisition/usp_SubmitJobRequisitionForApproval/usp_ApproveJobRequisition) orPendingApproval → Cancelled(viausp_RejectJobRequisition). Candidate/application, offer, verification/discrepancy, and employee-conversion state machines described below have no backing code yet — their skills are unimplemented stubs (see agent-skill-catalog.md).
Defines the deterministic, versioned state machines that govern workflow status. This state machine lives in the HR Core API / workflow engine — never inside an LLM or agent. Agents may propose a transition; only the workflow engine, after validating an approval record, commits it. See ADR-002.
409 Conflict / RFC 7807 problem detail (see error-handling-and-problem-details.md).stateDiagram-v2
[*] --> InCvBank
InCvBank --> Recommended: AI match against approved TAN
Recommended --> ShortlistApproved: HR/Hiring Mgr approval
ShortlistApproved --> L1Scheduled
L1Scheduled --> L1FeedbackCaptured
L1FeedbackCaptured --> ClosedForTan: L1 reject
L1FeedbackCaptured --> L2Scheduled: L1 select
L2Scheduled --> L2FeedbackCaptured
L2FeedbackCaptured --> ClosedForTan: L2 reject
L2FeedbackCaptured --> ClientScheduled: L2 select + client required
L2FeedbackCaptured --> FinalSelectionPending: L2 select, no client round
ClientScheduled --> ClientFeedbackCaptured
ClientFeedbackCaptured --> ClosedForTan: client reject
ClientFeedbackCaptured --> FinalSelectionPending: client select
FinalSelectionPending --> FinalSelectionApproved: HR approval
FinalSelectionApproved --> OfferSent
OfferSent --> OfferAccepted
OfferSent --> OfferDeclined
OfferAccepted --> OnboardingInProgress
OnboardingInProgress --> Converted: employee conversion approved
ClosedForTan --> Recommended: recommended for another TAN (new application record)
OfferDeclined --> ClosedForTan
Converted --> [*]
ClosedForTan --> [*]
stateDiagram-v2
[*] --> Draft
Draft --> PendingApproval
PendingApproval --> Approved: HR approval
PendingApproval --> Draft: changes requested
Approved --> OnHold
OnHold --> Approved
Approved --> Closed: position filled or cancelled
Closed --> [*]
stateDiagram-v2
[*] --> Drafted
Drafted --> PendingApproval
PendingApproval --> Approved: HR approval
PendingApproval --> Drafted: changes requested
Approved --> Sent
Sent --> Accepted
Sent --> Declined
Sent --> Expired
Accepted --> [*]
Declined --> [*]
Expired --> [*]
stateDiagram-v2
[*] --> Submitted
Submitted --> UnderVerification
UnderVerification --> Verified: no discrepancy
UnderVerification --> DiscrepancyRaised
DiscrepancyRaised --> ReuploadRequested
ReuploadRequested --> UnderVerification
DiscrepancyRaised --> PendingHrApproval: HR reviews closure/exception
PendingHrApproval --> Resolved: HR approves closure
PendingHrApproval --> ReuploadRequested: HR rejects closure, requests more info
Verified --> [*]
Resolved --> [*]
stateDiagram-v2
[*] --> GateCheckPending
GateCheckPending --> GateCheckFailed: any blocking check fails
GateCheckFailed --> GateCheckPending: remediation complete
GateCheckPending --> PendingHrApproval: all checks pass
PendingHrApproval --> Approved
Approved --> EmployeeIdIssued
EmployeeIdIssued --> [*]
| From | To | Allowed if | Forbidden without |
|---|---|---|---|
Recommended |
ShortlistApproved |
Recorded HR/Hiring Manager approval | Approval record |
L1FeedbackCaptured |
L2Scheduled |
L1 outcome = select, recorded by panelist | Human-submitted feedback |
FinalSelectionPending |
FinalSelectionApproved |
Recorded HR approval | Approval record |
FinalSelectionApproved |
OfferSent |
Offer approved and generated from template | HR approval on offer |
DiscrepancyRaised |
Resolved/exception |
Recorded HR approval | Approval record — AI may never auto-resolve |
GateCheckPending |
EmployeeIdIssued |
All blocking checks pass and HR approval recorded | Any blocking check unresolved, or missing approval |
| Any state | Any non-adjacent state (“skip”) | Never | — |
| Any state | Terminal state via AI action alone | Never | Human approval per human-approval-matrix.md |
Rejection at any interview stage transitions the application to ClosedForTan (terminal for that TAN) and emits application.rejected. A new Recommended application record may be created for the same candidate against a different TAN. The candidate’s CV Bank record is untouched. See end-to-end-recruitment-onboarding-workflow.md.
State-machine definitions are versioned (workflow_config_version in workflow-config.schema.json). In-flight applications continue on the version they started under; new applications use the latest approved version. Changing allowed transitions requires an ADR update and a migration plan (see database-migration-strategy.md).
| Version | Date | Author | Change |
|---|---|---|---|
| 1.0 | 2026-09-07 | Documentation package generation | Initial creation |