State machines & business rules β
Legend: π’ Implemented Β· π‘ Partial Β· π΄ Not implemented
1. Interview lifecycle β
1.1 States β
| State | Spec meaning | Implementation |
|---|---|---|
scheduled | Snapshot frozen; credits reserved; invitation queued | π’ Enum + scheduleInterview |
in_progress | Live session; meter/recording active | π’ startInterview |
disconnected | Recovery window (β€3 min, inside original end) | π’ disconnectInterview |
completed | Normal terminal; settlement | π’ β SQS β settlement |
abandoned | Recovery/original end elapsed | π΄ Enum only; no API path |
expired | Sweeper past expiresAt; release | π΄ No expiry sweeper |
cancelled | Org cancel before start; zero charge | π’ + releaseCredits |
failed | Provider/bot fault | π’ β settleFailedInterview (0) |
deleted (soft) | deletedAt; filtered from queries | π’ Not a status value |
Settlement: orthogonal settledAt + ledger, not a ninth status.
1.2 State diagram β
stateDiagram-v2
[*] --> scheduled: create + reserve
scheduled --> in_progress: preflight OK, live starts
scheduled --> cancelled: org/admin cancel
scheduled --> expired: expiresAt sweeper, no session
scheduled --> failed: pre-start fault (allowed by failInterview)
scheduled --> deleted: soft delete
in_progress --> disconnected: connection lost
in_progress --> completed: normal end / booked limit
in_progress --> failed: provider/bot fault
in_progress --> deleted: soft delete (blocked if in_progress)
disconnected --> in_progress: reconnect in window
disconnected --> abandoned: recovery/original end elapses
disconnected --> completed: end while disconnected (allowed)
disconnected --> failed: fault terminal
completed --> [*]: settledAt + ledger settle
abandoned --> [*]: settlement (consumed stand)
failed --> [*]: settledAt + release (0 charge typical)
expired --> [*]: release (0 charge)
cancelled --> [*]: release (0 charge)
note right of scheduled
Side effects: reserve ledger,
join token, invitation
end note
note right of completed
Side effects: settle U,
release unused, report.generate
end note1.3 Valid transitions β
| From | To | Trigger (spec) | Side effects (spec) | Status |
|---|---|---|---|---|
| β | scheduled | Interview created | Snapshot; reserve; join token; invitation | π’ |
scheduled | in_progress | Preflight OK; live begins | Meter; consent; Daily room | π‘ |
in_progress | disconnected | Connection lost | Meter pauses | π’ |
disconnected | in_progress | Reconnect in window | Context retained | π‘ 3-min / original-end not server-side |
disconnected | abandoned | Recovery / original end elapses | Terminal; consumed stand | π΄ |
in_progress | completed | Normal end or limit | Transcript; settlement | π’ |
in_progress | failed | Provider fault | Remedy table | π‘ |
scheduled | cancelled | Org cancel | release; zero | π’ |
scheduled | expired | Sweeper after expiresAt | release | π΄ |
| any non-terminal | deleted | Soft delete | Hidden from queries | π’ |
| terminal | settlement | settledAt claim | Idempotent settle U, release | π’ |
| From | To | Code | Status |
|---|---|---|---|
disconnected | completed | completeInterview | π’ |
scheduled | failed | failInterview | π‘ |
scheduled / expired | deleted | deleteInterview + release | π’ |
1.4 Invalid transitions to guard β
| Guard | Status |
|---|---|
| Settle before terminal | π‘ No status gate in settleCompletedInterview |
| Release after already settled | π’ Idempotent |
| Start expired/cancelled/deleted | π‘ Partial checks |
| Second concurrent active session | π‘ disconnected excluded from hasActiveInterview |
| Stale worker after newer session | π΄ No generation guard |
2. Credit / commitment β
Logical states β lots, org cache (available / reserved), append-only ledger.
| State (spec) | Implementation | Status |
|---|---|---|
| Uncommitted available | Lot remainingCredits + org available | π’ |
| Reserved | Lot reservedCredits; ledger reserve | π’ |
| Consumed | Ledger settle | π’ |
| Restored | β | π΄ |
| Expired | expireLot; forfeit on release from expired lot | π‘ No lot sweeper |
| Deficit / exposure | β | π΄ |
| Transition | Status |
|---|---|
| Available β Reserved (schedule) | π’ |
| Reserved β Consumed + Released (settle) | π’ |
| Reserved β Released (cancel/fail/zero-U) | π’ |
| Reserved β Forfeited on release (lot expired) | π’ |
| Purchase β Available | π’ |
| Available β Expired (lot) | π‘ expireLot only; scheduler π΄ |
3. Report / session / recording β
Session: same as interview status (π’); no separate entity (π΄).
Report: not_started β processing β completed | failed (β€3 attempts).
| State | Status |
|---|---|
not_started / processing / completed / failed | π’ generateReport, MAX_ATTEMPTS = 3 |
Recording: pending β processing β completed | failed on interview.recording β π‘ enum + bot updateRuntime; 25% restoration π΄.
4. Proctoring (FR19) β
| Dimension | Implementation |
|---|---|
Coverage complete | partial | unavailable | π΄ proctoring Mixed only |
| Analysis / reviewer disposition | π΄ |
| Never default 100% integrity | π΄ |
proctoringCredibility snapshot | π‘ partial |
5. User & organization β
| Entity | States | Status |
|---|---|---|
| User | invited (7d TTL), active, disabled | π’ |
| Organization | active, suspended | π‘ suspend API π΄ |
| Position | draft β active β archived | π’ schedule requires active |
6. Credit arithmetic rules β
| Rule | Status |
|---|---|
| 1 credit = 1 minute; reserve D minutes; charge U actual; 0 β€ U β€ reserved | π‘ floor whole minutes; no segment aggregation (WAL07) |
| A = B β H (available vs commitments) | π‘ cache + lots |
Atomicity / idempotency; FIFO lots by expiresAt | π’ |
| Protected commitments through lot expiry until settle | π‘ |
| 30-day commitment cutoff | π΄ |
| Release after source expiry β forfeit | π’ |
| Restored credits max(original expiry, +90d) | π΄ |
| Purchase expiry 12 months | π’ |
Ledger adjustment writer | π΄ |
7. Charge outcomes (remedy table) β
| Outcome (spec) | Status |
|---|---|
| Normal early finish | π‘ Floor minutes; release unused |
| Cancel / no-show / failed preflight β zero | π‘ No-show/expired sweeper π΄ |
| Candidate abandonment β consumed stand | π‘ Abandon path π΄ |
| Recoverable provider outage unbilled | π΄ |
| Partial/unusable evidence restoration | π΄ |
| Recording missing β 25% restore | π΄ |
| Report delayed β€24h | π‘ Retries only |
| Duplicate charge reverse | π΄ |
| Retakes consume credits | π’ |
8. Short-session & abuse β
| Control | Status |
|---|---|
| <60s or <2 substantive β insufficient evidence | π‘ report π’; billing floor 0 |
| 3 under-60s / hour / workspace β block 1h | π΄ |
| Report attempts capped at 3 | π’ |
| Cost alerts $0.15 / $0.25 | π΄ |
9. WAL01βWAL24 β
β₯1,000 randomized sequences β not automated (unit tests on planners only). WAL19 cited Β§16, not in Β§15.1 table.
| ID | Scenario (abbrev.) | Status |
|---|---|---|
| WAL01 | Duplicate Stripe β grant once | π‘ Code π’; concurrency test π΄ |
| WAL02 | Failed/pending checkout | π‘ |
| WAL03 | Race for last funds | π‘ |
| WAL04 | Five 20-min holds block sixth | π‘ |
| WAL05 | Unbooked expiry releases | π‘ Sweeper π΄ |
| WAL06 | Settle 12 of 20 | π’ Unit test |
| WAL07 | Fractional seconds aggregate once | π΄ |
| WAL08 | No-show / preflight zero | π‘ |
| WAL09 | Disconnect/reconnect no double bill | π‘ |
| WAL10 | Abandonment + insufficient threshold | π‘ / π΄ |
| WAL11 | Crash retry idempotency | π‘ |
| WAL12 | Report retry no second charge | π’ |
| WAL13 | Restoration remedies | π΄ |
| WAL14 | Unknown cause β review | π΄ |
| WAL15 | Reschedule swap capacity | π΄ / π‘ |
| WAL16 | Lot expiry + appointments | π‘ Sweeper π΄ |
| WAL17 | 30-day protection | π΄ |
| WAL18 | Mixed-price packs | π΄ |
| WAL19 | Chargeback / abuse | π΄ |
| WAL20 | Reversal + webhook replay | π΄ |
| WAL21 | 100-row batch | π΄ |
| WAL22 | Demotion during export | π‘ |
| WAL23 | Cost pause vs funded interviews | π΄ |
| WAL24 | Stale callbacks | π΄ |
Gaps: abandoned/expired sweeper; adjustment/restoration; 30-day cutoff; proctoring state machines; under-60s booking block; lot expiry consumer.