feat(schedules): B2 — scheduler dispatcher + worker#565
Merged
Conversation
EventBridge rate(5m) -> dispatcher (sweeps the sparse DueScheduleIndex, runaway guard, conditional re-arm before fire-and-forget) -> worker (mints a per-owner Cognito bearer, run_agent_headless(trigger="schedule"), records the outcome, pauses on reauth_required / oauth_required / repeated_failures). Two Docker Lambdas share one image (Dockerfile.scheduled-runs) via ImageConfig.Command; bootstrap-stub + out-of-band image deploy mirroring kb-sync. IAM scoped to sessions-metadata RW, BFF-grant table RW, app-client secret read, and AgentCore vault token + oauth-secret read; no SigV4/InvokeAgentRuntime (the minted bearer is the front door, matching the Run-now path). Delivery flows straight through — governance is role/auth-based. Fixes the failure breaker: added a persistent consecutive_failures counter (model + record_run_result, mirroring sync_policies) so repeated_failures pauses at the production default of 3. The original last-status proxy capped the streak at 2 and could never trip at the default; unified the worker's two error paths on the returned streak and added regression tests at the default threshold. Co-Authored-By: Claude Opus 4.8 <[email protected]>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
The scheduler engine (Phase B, F3): an EventBridge rate rule fires due schedules through the Phase A headless harness. Completes end-to-end scheduled runs behind the
SCHEDULED_RUNS_ENABLEDflag +scheduled-runscapability.backend/src/lambdas/scheduled_runs_dispatcher/):rate(5m)→ sweeps the sparseDueScheduleIndex→ per schedule: runaway guard (runs_todayvsmax_runs_per_day, UTC-rollover-aware) → conditional re-arm before invoking (a double-tick is idempotent) → async-invoke the worker (InvocationType=Event).backend/src/lambdas/scheduled_runs_worker/): mints a per-owner Cognito bearer viaCognitoRefreshBearerAuth→run_agent_headless(trigger="schedule")as the owner → records the outcome. Pauses cleanly (never retry-spams) onreauth_required(expired grant),oauth_required(connector needs consent), orrepeated_failures.scheduled-runs-construct.ts): two Docker Lambdas sharing one image viaImageConfig.Command, bootstrap-stub + out-of-band deploy mirroring KB-sync. EventBridge rule gated byconfig.scheduledRuns.enabled; IAM scoped to sessions-metadata RW + BFF-grant table RW + app-client secret read + AgentCore vault token/oauth-secret read. NoInvokeAgentRuntime/SigV4 — the minted bearer is the front door (matches the Run-now path).Delivery flows straight through — governance is role/auth-based (run-as-user, deliver-to-self), no content gate.
Orchestrator note (this was finished + reviewed on Opus)
The Sonnet build stalled before committing; I finished it and reviewed the code directly. The CDK/IAM is tightly scoped and correct as built. I fixed one real defect I found in review: the failure breaker used a last-run-status proxy that caps the streak at 2, so with the production default
SCHEDULED_RUNS_MAX_FAILURES=3therepeated_failurespause could never fire (the existing test only passed because it set the threshold to 2). Fixed with a persistentconsecutive_failurescounter (model +record_run_result, mirroringsync_policies), unified the worker's two error paths, and added regression tests at the default threshold.Tests
tsc --noEmitclean;jest429 passed.Part of #559.
🤖 Generated with Claude Code