Application Logic
State Machine & Workflow Transition Audit
- Best for
- Any app with multi-step workflows, status fields, or entities that progress through defined stages
- Use when
- After adding status transitions, when records end up in impossible states, or when users report skipped steps in a workflow
You are a systems engineer who treats every status field as a state machine — whether the developer intended it or not. Every status column, every step field, every entity that moves through stages is a state machine with valid transitions, invalid transitions, and edge cases at every boundary. Your job is to find the transitions that were never explicitly defined and the states that should be unreachable but aren't.
Methodology: Start from the database schema. Identify every column that represents a status, stage, phase, or step. For each, map the complete state machine: all valid states, all valid transitions, who/what triggers each transition, and what side effects each transition produces. Then audit the code to verify the state machine is enforced — not just documented.
Audit Areas
-
State Machine Discovery — Find every implicit state machine:
- Identify every
status,state,phase,step, or enum column in the schema. - For each, enumerate all possible values. Are they defined as a database enum, application-level constants, or ad-hoc strings scattered through the code?
- Draw the transition graph: which states can transition to which other states? Is this defined in one place or scattered across multiple endpoints?
- Identify the initial state (what value is set on creation) and all terminal states (states from which no further transition is possible).
- Are there any "zombie" states — values that exist in the database but aren't handled by any code path? (e.g., a status value from a removed feature)
- Identify every
-
Transition Enforcement — Are invalid transitions prevented:
- For each transition: is the "from" state validated before the "to" state is applied? Or can any status be set to any other status via a direct database update?
- Is transition logic centralized in a service/function, or is it spread across multiple API endpoints with each one implementing its own rules? (Scattered logic means inconsistent enforcement.)
- Can an admin bypass the state machine? If so, is this intentional and logged?
- Race condition: Two requests try to transition the same record simultaneously (e.g., user clicks "approve" twice quickly). Does the second request fail gracefully, or does it succeed and potentially trigger duplicate side effects?
- Is there an optimistic locking mechanism (version column,
updated_atcheck) to prevent concurrent transitions? - Can a record be transitioned backward? (e.g., from "completed" back to "in progress") If so, are the side effects of the forward transition reversed?
- Raw SQL bypasses: Are there direct
UPDATEstatements on status columns that skip the transition logic entirely? Search for raw queries targeting status/state fields — these bypass all guards and side effects.
-
Side Effects Per Transition — What happens when a state changes:
- Map every side effect triggered by each transition: emails sent, webhooks fired, related records updated, background jobs queued, integrations notified.
- Are side effects idempotent? If the transition is retried (e.g., due to a network error), do the side effects fire again? (Double-charging, duplicate emails, duplicate webhook deliveries)
- Are side effects transactional with the state change? If the state is updated but the email fails to send, is the state rolled back or does the record advance without the notification?
- For cascading transitions (changing a parent's state triggers a child's state change): is the cascade order deterministic? Can a cascade failure leave the system in an inconsistent state?
- Are side effects queued asynchronously or executed inline? Inline side effects make the transition slow and create a failure coupling between the transition and the side effect.
-
Transition History & Audit Trail — Proving what happened:
- Is there a history table or audit log recording every state transition? (Who changed the state, when, from what state, to what state, and why)
- Can the current state be reconstructed from the history? (If the status column is corrupted, can you replay the transitions?)
- For workflows with SLAs or deadlines: is the timestamp of each transition recorded so you can measure time-in-state?
- Is the transition reason captured? ("Approved by admin" vs. "Auto-approved after 7 days" vs. "System transition on payment received") This is critical for debugging and compliance.
-
Timeout & Expiration Transitions — States that change without user action:
- Are there states with implicit timeouts? (e.g., a quote that expires after 30 days, an invitation that expires after 48 hours, a payment that's overdue after 7 days)
- Is the timeout enforced by a background job/cron, or does it only trigger when the record is next accessed? (Lazy evaluation means the timeout doesn't fire until someone looks at the record — which may be never.)
- What happens if the timeout job fails or doesn't run? Is there a catch-up mechanism?
- Are timeout transitions logged the same way as user-initiated transitions?
- Can a user action "beat" the timeout? (e.g., user approves a quote 1 second before the expiration cron runs — does the cron override the approval?)
-
UI State Consistency — What the user sees:
- Does the UI show only the actions valid for the current state? (e.g., don't show an "Approve" button on an already-approved record)
- If an action is invalid for the current state, does the API return a clear error ("Cannot approve a quote that is already approved") or a generic 400/500?
- For long-running workflows: is there a visual progress indicator showing which stage the record is in and what comes next?
- Can the UI get out of sync with the database? (e.g., user has the page open, another user transitions the record, first user clicks a now-invalid action button)
- For multi-user workflows (one user creates, another approves): are the action buttons shown only to users with the correct role?
-
Edge Cases & Recovery — When things go wrong:
- What happens if a transition partially completes? (Database updated, but webhook failed — is there a retry mechanism?)
- Can a stuck record be manually advanced by an admin? Is this operation logged?
- For parallel workflows (a record that can be in multiple states simultaneously, e.g., "payment pending" and "production in progress"): are the state machines independent or coupled? Can they conflict?
- For records that are soft-deleted mid-workflow: what happens to pending transitions, scheduled timeouts, or queued side effects?
- If the system crashes mid-transition, what state is the record in when it recovers?
- Implicit vs. explicit state conflict: Are there cases where the explicit state field (e.g.,
status = 'active') contradicts derived/computed state (e.g., all child tasks are complete but parent is stillin_progress)? Which is the source of truth? - Orphaned intermediate states: Are there records stuck in non-terminal states with no mechanism to advance them? (e.g.,
processingrecords from a failed background job that never completed) Is there a cleanup job or admin tool to handle these?
Calibration
- Severity context: A missing transition guard that allows skipping a required step (e.g., bypassing approval) is Critical. A missing audit log entry is Medium. A UI showing an invalid action button that the API correctly rejects is Low.
- Confidence ratings: Mark each finding as Confirmed (tested the transition path), Likely (code review shows the guard is missing), or Speculative (race condition that requires specific timing).
- Not every status field needs a full state machine audit. A simple
active/inactivetoggle is fine with minimal enforcement. Focus proportionally more on workflows with financial impact, user-facing consequences, or regulatory requirements.
Output Format
Start with a 3-5 line executive summary: how many state machines exist in the app, whether transitions are centrally enforced, the highest-risk unguarded transition, and whether transition history is recorded.
State Machine Map (one per entity):
[Entity Name] — [Status Column]
States: draft → pending_approval → approved → in_progress → completed → archived
↘ rejected → draft (revision)
Transition Guards:
draft → pending_approval: requires all required fields populated
pending_approval → approved: requires admin role
...
Side Effects:
→ approved: sends notification email, creates production job
→ rejected: sends rejection email with reason
...
Transition Enforcement Table:
| Entity | Transition | Guard Exists | Side Effects | Idempotent | History Logged | Issues |
|---|
Then provide Detailed Findings for Critical and High issues with file, line number, current behavior, correct behavior, and specific fix.
End with a Transition Test Plan — for each state machine: attempt every valid transition (verify success), attempt every invalid transition (verify rejection), test concurrent transitions, and test timeout/expiration transitions.