Updates the requirements on [boto3](https://github.com/boto/boto3) to permit the latest version. - [Release notes](https://github.com/boto/boto3/releases) - [Commits](https://github.com/boto/boto3/compare/1.43.23...1.43.25) --- updated-dependencies: - dependency-name: boto3 dependency-version: 1.43.25 dependency-type: direct:production ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> |
||
|---|---|---|
| .github | ||
| cdk | ||
| lambdas | ||
| scripts | ||
| .gitignore | ||
| README.md | ||
| test_local.py | ||
Procurement Ingest
Unified email ingestion pipelines for Amazon procurement data. Two independent pipelines — purchase orders (Coupa) and work orders (APM/Hexagon EAM) — share a single repo and CDK app but deploy as separate CloudFormation stacks.
Pipelines
Purchase Orders (po-ingest stack)
Coupa PO emails are received at amazon_po@int.seahaven.com, parsed by Claude Haiku 4.5, and written to the purchase-orders DynamoDB table.
Flow:
- Coupa sends a PO email (new, revision, or cancellation).
- SES (
INBOUND_MAILrule set) drops the raw MIME intos3://po-ingest-emails-{AccountId}/inbound/. - S3
ObjectCreatedtriggers thepo-email-processorLambda. - Claude extracts structured JSON (PO number, status, supplier, site code, trade classification, line items, fiscal year).
- Conditional write to DynamoDB:
new_po— idempotent insert (no-op if PO exists)revision— unconditional overwritecancellation— marks existing rowCancelled
- DynamoDB Streams feeds downstream consumers:
- LedgerFlow (
seahaven-slack-bot/po-sync) — daily KB sync - Site extractor (
po-ingest-site-extractor) — real-time site address extraction intoverified-sitestable
- LedgerFlow (
Lambdas (lambdas/po/):
| Function | Trigger | Purpose |
|---|---|---|
po-email-processor |
S3 ObjectCreated | Claude extraction + DynamoDB write |
po-ingest-site-extractor |
DynamoDB Streams | Site code/address extraction -> verified-sites |
po-web-ui |
Manual invoke | HTML dashboard (public Function URL removed 2026-06-08, INFRA-74) |
Tables:
purchase-orders(PK:po_number, Streams: NEW_IMAGE) — shared with seahaven-slack-bot (read-only; see Shared Resources)verified-sites(PK:siteCode, GSI:by-state) — ~1,100 unique Amazon facility sitespending-site-review(PK:po_number) — unresolvable POs for manual Payee Central verification
Work Orders (WorkorderIngestStack stack)
Amazon APM work order emails (from Hexagon EAM / HxGN SmartCloud) are received at apm@int.seahaven.com, parsed by Claude Haiku 4.5, and written to the WorkOrders DynamoDB table.
Flow:
- Hexagon EAM sends email notifications (new assignments, comments, updates, cancellations) to
amazon@seahavenind.com. - Gmail filter forwards APM emails to
apm@int.seahaven.com(SES). - SES drops the raw MIME into
s3://workorder-ingest-emails-{AccountId}/inbound/. - S3 triggers the
workorder-email-processorLambda. - Claude extracts structured JSON (work order ID, site code, severity, priority, dates, assigned technician).
- Work order upserted to
WorkOrders, event/comment appended toWorkOrderComments.
Lambdas (lambdas/wo/):
| Function | Trigger | Purpose |
|---|---|---|
workorder-email-processor |
S3 ObjectCreated | Claude extraction + DynamoDB write |
workorder-web-ui |
Manual invoke | HTML dashboard (public Function URL removed 2026-06-08, INFRA-74) |
Tables:
WorkOrders(PK:work_order_id, GSIs:site-code-index,status-index)WorkOrderComments(PK:work_order_id, SK:comment_id)
Architecture
IaC: AWS CDK (Python), two stacks in one app, region us-east-1.
All Lambdas: Python 3.12, ARM64, 60-day log retention.
Secrets:
po-ingest/anthropic-api-key— Anthropic API key for PO parsingworkorder-ingest/anthropic-api-key— Anthropic API key for WO parsing
SES: Both stacks add rules to the shared INBOUND_MAIL receipt rule set on int.seahaven.com.
Failure handling (INFRA-41): Each email-processor is async-invoked (S3 → Lambda). Both have a CDK-managed SQS dead-letter queue (dead_letter_queue=, 14-day retention, SSL-enforced) so a failed parse is captured rather than silently dropped after Lambda's retries, plus an ALARM-only CloudWatch Errors alarm (Sum, threshold > 0) wired to the shared site-alerts SNS topic.
Shared Resources
purchase-orders table (owned here)
The purchase-orders DynamoDB table is owned by this repo's po-ingest stack (defined in cdk/po_stack.py with RemovalPolicy.RETAIN and StreamViewType.NEW_IMAGE). The po-email-processor Lambda is the authoritative writer — it performs the conditional inserts, revision overwrites, and cancellation updates described above.
Consumers (read-only):
| Repo | How it reads | Purpose |
|---|---|---|
seahaven-slack-bot |
po-sync (DynamoDB Streams + daily scan) and wo-po-lookup |
Daily KB sync + Bedrock agent PO lookups |
The consumer imports the table via Table.fromTableName(...) and is granted read-only access (grantReadData); it does not own or define it.
Schema-coordination rule: Any change to the purchase-orders schema (partition key, item shape, attribute names, streams view type) must be coordinated with seahaven-slack-bot. The owner here ships the change; the consumer must be updated in lockstep so its readers do not break. Treat schema changes as a cross-repo migration, not a local edit.
Known exception (INFRA-51): amazon-po-parser currently writes directly to purchase-orders outside this stack (backfill/enrichment scripts). This second writer is being folded into the po-ingest pipeline so this stack is the sole writer; until INFRA-51 closes, coordinate any schema change with amazon-po-parser as well.
CI/CD
GitHub Actions with reusable workflows from Sea-Haven-Industries/.github:
- CI (PR to
main): linting +cdk synthviaci-python-sam.yaml@main - CD (push to
main):cdk deploy --allviacd-cdk.yaml@main(OIDC auth)
Branch protection on main — all changes through PR.
Setup
- Bootstrap CDK:
cdk bootstrap aws://{AccountId}/us-east-1 - Store Anthropic API keys:
aws secretsmanager create-secret --name po-ingest/anthropic-api-key --secret-string "sk-ant-..." aws secretsmanager create-secret --name workorder-ingest/anthropic-api-key --secret-string "sk-ant-..." - Deploy both stacks:
cd cdk pip install -r requirements.txt cdk deploy --all - CloudFormation outputs include
WebUIUrlfor each stack's dashboard.
Scripts
Reprocess PO emails (re-run parser against all emails still in S3):
python scripts/reprocess.py # dry-run
python scripts/reprocess.py --execute # invoke po-email-processor for each
Backfill verified sites (one-time scan of historical POs):
python scripts/backfill_sites.py
Directory Structure
cdk/
app.py # Two stacks: po-ingest + WorkorderIngestStack
po_stack.py # Purchase order pipeline resources
wo_stack.py # Work order pipeline resources
lambdas/
po/ # PO pipeline Lambdas
email_processor/
site_extractor/
web_ui/
wo/ # WO pipeline Lambdas
email_processor/
web_ui/
shared/
models.py # Work order dataclasses/enums
scripts/
reprocess.py
backfill_sites.py