State Machines
Five state machines wired together via EventBridge. ASL JSON definitions in this directory; CDK reads them at synth time. Per-SM diagrams in ./diagrams/.
Pipeline
GuardDuty NO_THREATS_FOUND (native EventBridge)
↓
move-clean-document.asl.json copy upload → clean bucket, delete original
↓ S3 Object Created on clean-documents bucket
classification.asl.json Textract Lending → type + confidence + page breakdown
↓ DocumentProcessing-Classified (EventBridge rule filters on classification + numeric matchers)
extraction.asl.json Bedrock Data Automation, custom blueprint, splitter enabled
↓ DocumentProcessing-Extracted
context-gathering.asl.json applicant profile from DynamoDB
↓ DocumentProcessing-ContextGathered
claude-analysis.asl.json Bedrock managed prompt + forced tool use, persist to DDB
↓ DocumentProcessing-Completed
State machines
2. classification.asl.json
📊 Diagram
Textract Lending Analysis. Reports the dominant document type + numeric confidence. EventBridge routes downstream based on type and the numeric thresholds in config/routing-policy.json.
- Polling strategy: uses
getLendingAnalysisSummary(small response, returnsJobStatus+ Summary) for both polling and final fetch. Deliberately does not callgetLendingAnalysis— that endpoint returns the fullDocuments[]extraction payload which can exceed Step Functions' 256 KB state limit on multi-page docs. - JSONata
ExtractClassification: filters UNCLASSIFIED as noise, picks dominant from classified pages only, confidence =dominantTypePages / classifiedPages. Comparator is boolean ($a.pageCount < $b.pageCount), not numeric — JSONata's$sortquirk. - S3 tag preservation: reads existing tags and appends the
Classificationtag, preservingApplicationNumber,LicenceNumber, etc. - Emits:
DocumentProcessing-Classifiedwith{ classification, classificationConfidence, totalPages, classifiedPages, dominantTypePages, pageBreakdown, documentSizeBytes, textractJobId }. One event regardless of doc type — routing is per-rule at the bus. - Failure mode:
INVALID_IMAGE_TYPE(HEIC, WebP, DOCX) or other Textract job failure → routes to caseworker-review path (currently emits a typed failure event the test harness picks up). - Timeout: 15 minutes.
Common ASL patterns
Retry with exponential backoff
{
"Retry": [
{
"ErrorEquals": ["ThrottlingException"],
"IntervalSeconds": 2,
"MaxAttempts": 3,
"BackoffRate": 2
}
]
}
Catch + route to failure
{
"Catch": [
{
"ErrorEquals": ["States.ALL"],
"ResultPath": "$.error",
"Next": "EmitFailureEvent"
}
]
}
Emit a typed event
{
"Type": "Task",
"Resource": "arn:aws:states:::events:putEvents",
"Parameters": {
"Entries": [
{
"Detail": { "bucket.$": "$.bucket", "key.$": "$.key" },
"DetailType": "DocumentProcessing-Completed",
"Source": "custom.documentProcessing"
}
]
}
}
JSONata (Pass state)
Used in classification.asl.json for the dominant-type calculation. Note JSONata's $sort takes a boolean comparator (true means $a should come after $b), not numeric like JavaScript.
$sort($breakdown, function($a, $b){ $a.pageCount < $b.pageCount })
Modifying a step
# Edit ASL JSON
vi state-machines/classification.asl.json
CDK reads ASL files at synth time and embeds them in the state machine resources. No code change needed for ASL edits.
Validating
Online: ASL Validator or the AWS Step Functions Workflow Studio.
CLI:
aws stepfunctions validate-state-machine-definition \
--definition file://classification.asl.json