Compare commits

...

9 commits

Author SHA1 Message Date
Adam Moussa
caa28e816a
Merge 5fc749bc0e into 7e1b89306b 2026-07-08 16:49:04 -04:00
Adam Moussa
7e1b89306b
docs: document cross-stack DynamoDB data contracts (INFRA-138) (#91)
Some checks failed
Deploy / deploy (push) Has been cancelled
2026-07-08 16:22:38 -04:00
dependabot[bot]
944b8e7bdd
Update anthropic requirement in /lambdas/wo/email_processor (#89)
Some checks are pending
Deploy / deploy (push) Waiting to run
Updates the requirements on [anthropic](https://github.com/anthropics/anthropic-sdk-python) to permit the latest version.
- [Release notes](https://github.com/anthropics/anthropic-sdk-python/releases)
- [Changelog](https://github.com/anthropics/anthropic-sdk-python/blob/main/CHANGELOG.md)
- [Commits](https://github.com/anthropics/anthropic-sdk-python/compare/v0.113.0...v0.116.0)

---
updated-dependencies:
- dependency-name: anthropic
  dependency-version: 0.116.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-07 18:25:53 +00:00
dependabot[bot]
0eb034f8ee
Bump aws-cdk-lib in /cdk in the minor-and-patch group across 1 directory (#86)
Bumps the minor-and-patch group with 1 update in the /cdk directory: [aws-cdk-lib](https://github.com/aws/aws-cdk).


Updates `aws-cdk-lib` from 2.260.0 to 2.261.0
- [Release notes](https://github.com/aws/aws-cdk/releases)
- [Changelog](https://github.com/aws/aws-cdk/blob/main/CHANGELOG.v2.alpha.md)
- [Commits](https://github.com/aws/aws-cdk/compare/v2.260.0...v2.261.0)

---
updated-dependencies:
- dependency-name: aws-cdk-lib
  dependency-version: 2.261.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: minor-and-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-07 18:20:00 +00:00
dependabot[bot]
5019c2e7b6
Update boto3 requirement in /lambdas/wo/email_processor (#88)
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.38...1.43.41)

---
updated-dependencies:
- dependency-name: boto3
  dependency-version: 1.43.41
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-07 17:59:27 +00:00
dependabot[bot]
e8202a8394
Update boto3 requirement in /lambdas/wo/web_ui (#90)
Some checks are pending
Deploy / deploy (push) Waiting to run
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.39...1.43.41)

---
updated-dependencies:
- dependency-name: boto3
  dependency-version: 1.43.41
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-07 16:36:58 +00:00
dependabot[bot]
995eb0df6d
Update boto3 requirement in /lambdas/po/email_processor (#87)
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.39...1.43.41)

---
updated-dependencies:
- dependency-name: boto3
  dependency-version: 1.43.41
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-07 12:29:50 -04:00
5fc749bc0e
refactor(cdk): encrypt migrated log groups with seahaven-logs CMK (INFRA-114) 2026-07-06 18:09:19 -04:00
62a3a7f0be
refactor(cdk): replace deprecated logRetention with explicit LogGroup (INFRA-114) 2026-07-06 17:59:27 -04:00
7 changed files with 109 additions and 10 deletions

View file

@ -107,6 +107,23 @@ The consumer imports the table via `Table.fromTableName(...)` and is granted rea
**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.
### `WorkOrders` and `WorkOrderComments` tables (owned here)
Both tables are **owned by this repo's `WorkorderIngestStack`** (`cdk/wo_stack.py`, `RemovalPolicy.RETAIN`, shared customer-managed CMK per INFRA-95 / M-3):
- `WorkOrders` — PK `work_order_id` (S).
- `WorkOrderComments` — PK `work_order_id` (S), SK `comment_id` (S).
**Consumer (read-only) — data contract:** `seahaven-slack-bot` imports both tables via `Table.fromTableName(...)` (`grantReadData` plus an explicit `kms:Decrypt` grant on the shared CMK) and reads them from two Lambdas: `workorder-sync` (daily full-table scan into the Bedrock knowledge base) and `wo-po-lookup` (the Bedrock agent's direct WO lookup action group). The bot depends on the PK/SK schema above, the CMK encryption, and these attributes: on `WorkOrders` — `description`, `wo_status`, `customer`, `site_code`, `building`, `address`, `severity`, `priority`, `assigned_to`, `date_reported`, `scheduled_start`, `due_date`, `updated_at`; on `WorkOrderComments` — `created_at` (used to sort comments), `commenter`, `text`. Any change to table name, key schema, these attribute names, or the encryption key must be coordinated with `seahaven-slack-bot` before it ships, or the Bedrock agent breaks at runtime (not at deploy — the tables are imported by name, so there is no compile-time link).
### `verified-sites` table (owned here)
Owned by this repo's `po-ingest` stack (`cdk/po_stack.py`). PK `siteCode` (S); AWS-managed encryption (NOT the shared CMK).
**Consumer (read-only) — data contract:** `seahaven-slack-bot`'s `wo-po-lookup` Lambda imports this table via `Table.fromTableName(...)` for the Bedrock agent's `lookup_site` action. It does point lookups by `siteCode` and reads `address`, `fullAddress`, `city`, `state`, `zip`, `latitude`, `longitude`, `notes`. Coordinate any change to the table name, key schema, or these attribute names with `seahaven-slack-bot`.
> **GSI drift (INFRA-138):** the `by-state` GSI was removed here on 2026-06-03 (audit M-20, "0 reads in 30d"), but `seahaven-slack-bot` still queries `IndexName: 'by-state'` for its state-listing path, so that path fails at runtime today. Restoring the GSI or removing the consumer's state path needs to be reconciled cross-repo. This is the kind of silent owner-side lifecycle change this data-contract note exists to prevent.
## Documentation
The canonical map of Sea Haven's AWS infrastructure lives in Confluence. This project's `po-ingest` and `workorder-ingest` stacks are represented there as Mermaid subgraphs.

View file

@ -118,6 +118,18 @@ class PoIngestStack(Stack):
),
)
# --- Shared customer-managed CMK for CloudWatch log encryption ---
# alias/seahaven-logs. The po-email-processor log group was already
# associated with this CMK out-of-band; declaring encryption_key on the
# migrated LogGroups below reconciles that drift and hardens the remaining
# groups (INFRA-114, per cross-review). The key policy already permits
# logs.us-east-1.amazonaws.com (verified in use on po-email-processor).
logs_cmk = kms.Key.from_key_arn(
self,
"LogsKey",
"arn:aws:kms:us-east-1:328440206208:key/b748750c-3b26-478d-acfb-d0126cc97f56",
)
# --- Purchase-orders DynamoDB table ---
# Owned by this stack. Streams enabled for the site-extractor pipeline.
# Other stacks (seahaven-slack-bot) reference this table via fromTableName().
@ -157,6 +169,22 @@ class PoIngestStack(Stack):
enforce_ssl=True,
)
# --- Explicit log group (INFRA-114) ---
# Replaces the deprecated log_retention prop, which provisioned a
# LogRetention custom resource whose role held logs:PutRetentionPolicy/
# DeleteRetentionPolicy on Resource "*" (checkov CKV_AWS_111). The name
# matches Lambda's default (/aws/lambda/<function-name>) so the function
# keeps writing to the same group; RETAIN matches the repo convention for
# stateful resources.
email_processor_log_group = logs.LogGroup(
self,
"EmailProcessorLogGroup",
log_group_name="/aws/lambda/po-email-processor",
retention=logs.RetentionDays.TWO_MONTHS,
encryption_key=logs_cmk,
removal_policy=RemovalPolicy.RETAIN,
)
# --- Lambda function ---
email_processor = lambda_.Function(
self,
@ -180,7 +208,7 @@ class PoIngestStack(Stack):
),
timeout=Duration.seconds(60),
memory_size=256,
log_retention=logs.RetentionDays.TWO_MONTHS,
log_group=email_processor_log_group,
dead_letter_queue=email_processor_dlq,
environment={
"PO_TABLE": "purchase-orders",
@ -297,6 +325,15 @@ class PoIngestStack(Stack):
)
# --- Web UI Lambda ---
web_ui_log_group = logs.LogGroup(
self,
"WebUILogGroup",
log_group_name="/aws/lambda/po-web-ui",
retention=logs.RetentionDays.TWO_MONTHS,
encryption_key=logs_cmk,
removal_policy=RemovalPolicy.RETAIN,
)
web_ui = lambda_.Function(
self,
"WebUI",
@ -307,7 +344,7 @@ class PoIngestStack(Stack):
code=lambda_.Code.from_asset("../lambdas/po/web_ui"),
timeout=Duration.seconds(60),
memory_size=256,
log_retention=logs.RetentionDays.TWO_MONTHS,
log_group=web_ui_log_group,
environment={
"PO_TABLE": "purchase-orders",
},
@ -369,6 +406,15 @@ class PoIngestStack(Stack):
# 518 WCU of write amplification. Re-add if a state-level query path ships.
# --- Site extractor Lambda (DynamoDB Streams → verified-sites) ---
site_extractor_log_group = logs.LogGroup(
self,
"SiteExtractorLogGroup",
log_group_name="/aws/lambda/po-ingest-site-extractor",
retention=logs.RetentionDays.TWO_MONTHS,
encryption_key=logs_cmk,
removal_policy=RemovalPolicy.RETAIN,
)
site_extractor = lambda_.Function(
self,
"SiteExtractor",
@ -379,7 +425,7 @@ class PoIngestStack(Stack):
code=lambda_.Code.from_asset("../lambdas/po/site_extractor"),
timeout=Duration.seconds(60),
memory_size=256,
log_retention=logs.RetentionDays.TWO_MONTHS,
log_group=site_extractor_log_group,
environment={
"VERIFIED_SITES_TABLE": verified_sites_table.table_name,
"PENDING_REVIEW_TABLE": "pending-site-review",

View file

@ -1,2 +1,2 @@
aws-cdk-lib==2.260.0
aws-cdk-lib==2.261.0
constructs>=10.6.0

View file

@ -156,6 +156,33 @@ class WorkorderIngestStack(Stack):
enforce_ssl=True,
)
# --- Shared customer-managed CMK for CloudWatch log encryption ---
# alias/seahaven-logs. Same CMK the po-email-processor log group was
# already associated with out-of-band; declaring encryption_key on the
# migrated LogGroups below hardens these groups (INFRA-114, per
# cross-review). The key policy already permits logs.us-east-1.amazonaws.com.
logs_cmk = kms.Key.from_key_arn(
self,
"LogsKey",
"arn:aws:kms:us-east-1:328440206208:key/b748750c-3b26-478d-acfb-d0126cc97f56",
)
# --- Explicit log group (INFRA-114) ---
# Replaces the deprecated log_retention prop, which provisioned a
# LogRetention custom resource whose role held logs:PutRetentionPolicy/
# DeleteRetentionPolicy on Resource "*" (checkov CKV_AWS_111). The name
# matches Lambda's default (/aws/lambda/<function-name>) so the function
# keeps writing to the same group; RETAIN matches the repo convention for
# stateful resources.
email_processor_log_group = logs.LogGroup(
self,
"EmailProcessorLogGroup",
log_group_name="/aws/lambda/workorder-email-processor",
retention=logs.RetentionDays.TWO_MONTHS,
encryption_key=logs_cmk,
removal_policy=RemovalPolicy.RETAIN,
)
# --- Lambda function ---
email_processor = lambda_.Function(
self,
@ -179,7 +206,7 @@ class WorkorderIngestStack(Stack):
),
timeout=Duration.seconds(60),
memory_size=256,
log_retention=logs.RetentionDays.TWO_MONTHS,
log_group=email_processor_log_group,
dead_letter_queue=email_processor_dlq,
environment={
"WORK_ORDERS_TABLE": work_orders_table.table_name,
@ -311,6 +338,15 @@ class WorkorderIngestStack(Stack):
)
# --- Web UI Lambda ---
web_ui_log_group = logs.LogGroup(
self,
"WebUILogGroup",
log_group_name="/aws/lambda/workorder-web-ui",
retention=logs.RetentionDays.TWO_MONTHS,
encryption_key=logs_cmk,
removal_policy=RemovalPolicy.RETAIN,
)
web_ui = lambda_.Function(
self,
"WebUI",
@ -321,7 +357,7 @@ class WorkorderIngestStack(Stack):
code=lambda_.Code.from_asset("../lambdas/wo/web_ui"),
timeout=Duration.seconds(15),
memory_size=128,
log_retention=logs.RetentionDays.TWO_MONTHS,
log_group=web_ui_log_group,
environment={
"WORK_ORDERS_TABLE": work_orders_table.table_name,
"COMMENTS_TABLE": comments_table.table_name,

View file

@ -1,2 +1,2 @@
anthropic>=0.116.0
boto3>=1.43.39
boto3>=1.43.41

View file

@ -1,2 +1,2 @@
anthropic>=0.113.0
boto3>=1.43.38
anthropic>=0.116.0
boto3>=1.43.41

View file

@ -1 +1 @@
boto3>=1.43.39
boto3>=1.43.41