Add ALARM-only CloudWatch alarms routed to the shared site-alerts SNS
topic (imported once via Topic.fromTopicArn and injected into both
constructs via props). All alarms use treatMissingData NOT_BREACHING and
have no OK / InsufficientData actions, mirroring the proposal-system
alarm construct.
Lambda (fetch-classify, daily-digest, conversation):
- Errors (Sum >= 1, eval 1)
- Throttles (Sum >= 1, eval 1)
- Duration (p99, eval 3 / datapoints 2, ~80% of timeout:
96000ms for the 120s fns, 144000ms for conversation's 180s)
-- thresholds pending Adam sign-off.
DynamoDB exec-aide table:
- ThrottledRequests and SystemErrors. These metrics are NOT published at
the bare TableName dimension (CDK's metricThrottledRequests /
metricSystemErrors are deprecated as invalid); they are keyed by the
Operation dimension. Used the *ForOperations math helpers scoped to the
6 operations this single-table app issues (GetItem/PutItem/Query/Scan/
UpdateItem/DeleteItem) to stay within the 10-metric math-expr cap.
ECS exec-aide-listener Fargate service (AWS/ECS, no Container Insights):
- CPU and Memory utilization (Average > 80%, eval 3 / datapoints 2).
- Service assigned to a const (logical id 'Service' unchanged) so metrics
can reference it.
The RunningTaskCount alarm (requires Container Insights) is intentionally
deferred to a separate sign-off-gated commit.
The task runs ARM64, but the image asset had no explicit platform, so
the amd64 CD runner built an amd64 image (QEMU alone does not change
the default target). Revision :8 crash-looped with "exec format
error" (50 failed task starts); CloudFormation hung on service
stabilization until cancelled. Local arm64 builds masked this -
deploys from this Mac always produced the right image.
Same fix pattern as the org-wide QEMU+platform rule for arm64 Lambda
bundling.
Listener posts "Thinking..." placeholder and async-invokes a new
exec-aide-conversation Lambda that runs a Bedrock Sonnet tool-use loop
over 7 email tools, then updates the Slack message with the response.
Bedrock Haiku now classifies marketing/promotional emails as MARKETING
and they are filtered out before saving or alerting. This replaces
Gmail label-based filtering for more accurate classification.
Also: enable Fargate listener (desiredCount=1), update README for CDK,
update Notion AWS Architecture Map and Slack Apps Inventory.
SAM is for simple serverless stacks; this project has ECS, VPC, and
multi-service composition which requires CDK. Migration brings
ContainerImage.fromAsset() for automatic Docker builds on deploy,
matching the seahaven-slack-bot pattern.
Also fixes: Bedrock model ID (add version suffix), Gmail history API
parameter (labelId not labelIds), classify JSON extraction (handle
markdown fences), and digest block limit (cap sections at 5 items
to stay under Slack's 50-block limit).