architecture.mdfeat: replace monolithic cron handler with Step Functions orchestrator The hourly cron handler previously ran 15 tasks sequentially in a single setInterval loop with a DynamoDB lock for single-instance election. If any task failed, all subsequent tasks were skipped for that cycle. This replaces that approach with an AWS-native orchestration pattern: - EventBridge hourly rule triggers a Step Function state machine - The state machine runs 14 parallel branches (one per cron task) - Each branch sends an SQS message to a new cron-task-queue using the waitForTaskToken integration pattern - The worker service polls the cron-task-queue, executes the named task via a new cronTaskRegistry, and calls SendTaskSuccess/SendTaskFailure to resume the Step Function branch - Each branch has its own timeout and Catch handler so failures are isolated — task #3 failing no longer blocks tasks #4-15 Infrastructure changes: - New SQS queue: cron-task-queue (30min visibility timeout) - New Step Function: {prefix}-cron-orchestrator - New EventBridge rule: {prefix}-hourly-cron - New env var: AWS_SQS_CRON_TASK_QUEUE_URL - New dependency: @aws-sdk/client-sfn for task token callbacks - Removed: DynamoDB lock acquisition, internal setInterval cron - Kept: POST /scheduled endpoint for manual debugging Co-Authored-By: Claude Opus 4.6 (1M context) <<email>>Apr 6, 2026, 6:30 PMenv-cleanup.mdreplace cloudflare runtime with aws local servicesApr 6, 2026, 12:33 AMenvironment-naming.mdAdd AWS migration foundationMar 26, 2026, 11:04 PMmarketing-domain-split.mdMerge remote-tracking branch 'origin/staging' into feat/inbox-client-composed-feed # Conflicts: # apps/mail/app/(full-width)/onboarding/page.tsx # apps/mail/modules/conversations/components/LeftSidebarContent.tsx # apps/mail/modules/inbox/components/InboxList.tsx # apps/mail/modules/inbox/hooks/use-inbox-items.ts # apps/mail/tests/modules/inbox/feedPartialFailure.test.tsxSep 1, 2026, 11:58 AMprod-threads-hotset-copy-receipt-20260327.jsonAdd migration dashboard and stage thread manifestMar 27, 2026, 5:53 PMsecret-inventory-template.mdconsolidate 5 AWS secrets per env into 1 Previously each environment had 5 separate secrets in Secrets Manager (shared/runtime, shared/observability, api-service/runtime, chat-service/runtime, worker-service/runtime). The 3 per-service secrets were identical. This consolidates everything into a single `/cedar/aws-{env}/runtime` secret with all 44 fields, simplifying config, CDK stacks, deploy scripts, and sync tooling. Co-Authored-By: Claude Opus 4.6 (1M context) <<email>>Apr 6, 2026, 6:24 PMsecrets.mdconsolidate 5 AWS secrets per env into 1 Previously each environment had 5 separate secrets in Secrets Manager (shared/runtime, shared/observability, api-service/runtime, chat-service/runtime, worker-service/runtime). The 3 per-service secrets were identical. This consolidates everything into a single `/cedar/aws-{env}/runtime` secret with all 44 fields, simplifying config, CDK stacks, deploy scripts, and sync tooling. Co-Authored-By: Claude Opus 4.6 (1M context) <<email>>Apr 6, 2026, 6:24 PMstaging-deploy.mdfix(deploy): deploy a surface whose last deploy is out of the lookup window The lookup read one page of 30 runs. A surface that had not deployed within them resolved to nothing, and the planner then fell back to the pushed baseline — which is exactly the fallback this PR exists to remove. Every undeployed commit behind that baseline would be diffed away and stranded, silently, in the one situation where the surface most needed deploying. Page through the run history instead (~200 runs, a month of staging pushes), and separate the two ways the lookup can come back empty: never-deployed the job is there in the history but has not succeeded within the window. There is no evidence anything was deployed, so there is no baseline that can be trusted to exclude work — deploy the surface. The next run finds that deploy and resumes scoping from it. nothing the question could not be asked: no token, an API error, or a job name absent from the history entirely. The last is a rename or a typo, not a stale surface, and answering a config mistake by deploying everything on every push forever is worse than leaving the pushed baseline in place. The run cap is now honoured per run rather than per page, so the window is the number it says. Co-Authored-By: Claude Opus 5 (1M context) <<email>>Sep 2, 2026, 9:28 AMstaging-threads-hotset-copy-receipt-20260327.jsonRecord staging thread pass and standalone dashboardMar 27, 2026, 6:44 PMvalidation-checklist.mdreplace cloudflare runtime with aws local servicesApr 6, 2026, 12:33 AM