docs(decisions): ADR-021 propose Lambda MicroVMs compute backend (#645) - #688
Conversation
…-samples#645) Record the design decision for adding AWS Lambda MicroVMs as a third opt-in ComputeStrategy backend (compute_type: lambda-microvm), per the RFC in aws-samples#645. The ADR resolves the lifecycle tensions surfaced during pre-implementation review (traffic-based idle detection vs an outbound-only agent, no self-suspend, snapshot state baking, 60-minute JWE token TTL) and decides: - mandatory suspendSession/resumeSession on ComputeStrategy with typed unsupported results (codebase idiom: no optional interface methods) - orchestrator-owned suspend/resume around HITL approval waits - zip+Dockerfile packaging reusing the existing FastAPI entrypoint - conditional infra behind the bootstrap ComputeTypes parameter - layered regional-availability enforcement (synth-time constant, onboarding/doctor live probes, orchestration-time classification) - phased P1-P3 rollout; AgentCore remains the default Memory parity is in scope (already cross-substrate on ECS); only the Runtime-coupled workload-token injection path is deferred to aws-samples#249. Starlight mirror generated via docs:sync. Refs aws-samples#645 Co-authored-by: Claude <noreply@anthropic.com>
|
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #688 +/- ##
=======================================
Coverage ? 90.13%
=======================================
Files ? 241
Lines ? 59008
Branches ? 5749
=======================================
Hits ? 53188
Misses ? 5820
Partials ? 0 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
krokoko
left a comment
There was a problem hiding this comment.
ADR review — thanks for this
Really solid proposed ADR. The lifecycle diagnosis (inbound idle detection vs an outbound-only agent, no self-suspend, snapshot uniqueness, short JWE TTL) is accurate, the interface widening matches the codebase idiom (pollSession stub / exhaustive never), and the scope corrections vs #645 (Memory in P2, WAT deferred to #249/ADR-016, Gateway orthogonal) are well reasoned. Numbering banner, EARS requirements, layered regional availability, and the P1–P3 rollout all look right for a docs-only decision record.
Verdict: approve-with-changes before moving to accepted — the HITL suspend model needs to be reconciled with today’s agent-owned approval poll, and a few API/IAM details should be pinned so P1/P3 implementers don’t have to rediscover them.
Blocking / please resolve before accepted
1. HITL suspend vs agent-owned approval poll
Today (CEDAR_HITL_GATES.md): soft-deny → agent writes AWAITING_APPROVAL → agent _poll_for_decision on DDB (monotonic timeout, default 300s) → approve-task writes DDB → agent discovers approval and resumes the tool call. The container stays alive; concurrency slot is held for that reason (decision #7).
Sub-decision 2 currently says the orchestrator suspends on HITL wait and resumes “before delivering the approval outcome.” That delivery path doesn’t exist today — the agent discovers approval itself.
Could you make the intended handshake explicit? For example:
| Question | Needs a decision |
|---|---|
| Who triggers suspend? | Orchestrator sees AWAITING_APPROVAL after grace → suspendSession |
| Who triggers resume? | approve/deny Lambda? Orchestrator poll? Event? |
| Timeout while suspended | time.monotonic() freezes → Cedar gate timeout effectively pauses. Intentional? Or does wall-clock / stranded reconciler still win? |
suspendedDurationSeconds |
Must cover the approval window / stranded timeout (2h), or an intentional suspend gets auto-terminated |
| Concurrency slots | Decision #7 rationale (“container consuming memory/CPU”) changes under suspend — slot still held? |
Without that, P3 will fight Cedar semantics.
2. /run is a fast hook, not the agent pipeline
Runtime /run timeout is 1–60s and the lifecycle docs / toolkit skill say not to do long work there. Suggest a normative line that /run validates the payload / kicks off the agent asynchronously and returns 200; clone→PR stays off the hook path.
3. Pin “auto-suspend disabled”
The normative mechanism is omit idlePolicy on RunMicrovm (if the block is present, all three fields are required). “Disabled” alone is ambiguous for implementers and for the disabled-auto-suspend invariant test.
Should-fix (fine for proposed, needed before P1/P3)
4. IAM gaps. lambda:RunMicrovm / Suspend / Resume / Terminate / CreateMicrovmAuthToken look correct per the IAM reference. Please also call out lambda:PassNetworkConnector (required even for default connectors) and typically lambda:GetMicrovm for pollSession.
5. JWE refresh has no decision. Tension #4 notes the ≤60 min TTL; Decision never says who refreshes, for which calls, or that P1 may need no orchestrator→agent HTTP if work is /run-driven + agent egress only.
6. Security-bar deltas. #645’s acceptance criteria ask to meet the same security bar or list deltas. Workload-token and no SHELL_INGRESS are mentioned; a short table (egress VPC, session-role admit, secrets-at-/run, public HTTPS endpoint + JWE, snapshot uniqueness) would close that AC cleanly.
7. pollSession: SUSPENDED → running. Fine for intentional HITL suspend — please note the orchestrator must not treat AWAITING_APPROVAL + SUSPENDED as unhealthy / fail-fast the way a silent AgentCore crash would.
Nits
- Consequence on the 8h cap including suspended time is good; worth adding that
suspendedDurationSecondsis a second kill switch for intentional suspends. /suspend“flushes progress events” — keep that ≤60s; prefer durable writes before returning 200.- Disk “32 GB hard ceiling” — memory/vCPU are well documented; confirm disk against current quotas when COMPUTE.md is updated in the implementing PR.
- Starlight mirror looks sync’d — thanks.
Suggested minimal ADR edits
- Rewrite sub-decision 2 around detect
AWAITING_APPROVAL→ suspend → on approval row terminal → resume → agent poll continues; drop “orchestrator delivers approval outcome.” - Add EARS requirements for omit-
idlePolicy, async/run,suspendedDurationSeconds≥ stranded/approval bound, andPassNetworkConnector. - One paragraph on approval-timeout semantics under freeze (monotonic vs wall/stranded).
- Short security-delta table for the #645 AC.
Happy to re-review once those land. Refs #645.
…echanics (aws-samples#645) Address PR aws-samples#688 review: sub-decision 2 previously assumed the orchestrator delivers approval outcomes, but the agent discovers decisions itself by polling DynamoDB (_poll_for_decision) with a monotonic timeout. Rework the suspend/resume handshake around that: - suspend: orchestrator-owned, on AWAITING_APPROVAL after grace, only when the gate window justifies it - resume: inline best-effort in the approve/deny Lambda after the transactional write, with orchestrator-poll reconciliation as backstop. Precedent: cancel-task.ts invokes StopTask / StopRuntimeSession inline from the API plane, best-effort with a compute-orphan event and conditional IAM in task-api.ts - resume-on- approve is the same shape (user-initiated, latency-sensitive compute side effect), so poll-only purity was already litigated and declined - timeout under freeze: orchestrator owns the wall clock and resumes before the gate deadline so the agent's monotonic timer fires the deny agent-side; the orchestrator never writes decisions - suspendedDurationSeconds is a backstop sized >= gate ceiling; the concurrency slot stays held (AWS counts SUSPENDED MicroVMs toward the account memory quota, so releasing it frees nothing) Also per review: pin auto-suspend-disabled as omit-idlePolicy, add async /run EARS requirement (1-60s hook budget), add lambda:PassNetworkConnector + GetMicrovm to IAM and drop CreateMicrovmAuthToken (no JWE consumer in P1-P3), add the security- delta table for the aws-samples#645 acceptance criterion, map SUSPENDED as healthy iff AWAITING_APPROVAL, and correct the suspend-economics claims to the 1h Cedar gate ceiling (the weekend-wait scenario cannot occur under decision aws-samples#6). Refs aws-samples#645 Co-authored-by: Claude <noreply@anthropic.com>
|
Thanks @krokoko — all points addressed in de599b6. Point-by-point: Blocking 1 (HITL handshake): You were right that "orchestrator delivers the approval outcome" doesn't exist — the agent discovers decisions itself via
On the resume-trigger tiebreak specifically, we went looking for codebase precedent rather than deciding on taste: Blocking 2: EARS requirement added — Blocking 3: Pinned as omit 4 (IAM): Added 5 (JWE): Decided explicitly: P1–P3 have zero orchestrator→agent HTTP (payload via 6: Security-delta table added under sub-decision 4 (egress, session-role admit, secrets-at- 7: Nits: second-kill-switch note added; Ready for re-review. |
Re-review of de599b6 — thanks @dreamorosiReally thorough response to the first review. The HITL handshake rewrite, cancel-task precedent for inline best-effort resume, omit- Happy to approve once the two blockers below are addressed. Blockers1. Timeout-under-freeze accounting Resume-at-wall-deadline does not make the agent deny at that deadline. Monotonic freezes across suspend. After a grace suspend on a 300 s gate (~30 s elapsed), ~270 s remains on the agent timer. Resume at wall The EARS line (“resume so the monotonic timeout can fire”) understates that. Please pick one and write it down, e.g.:
Without that, P3 ships a Cedar timeout hole. 2.
EARS currently requires both omit-
Nits (non-blocking)
Happy to re-review / approve as soon as the two blockers land. Refs #645. |
…ntradiction (aws-samples#645) Address PR aws-samples#688 re-review blockers: 1. Timeout under freeze: resuming at the wall deadline did not make the deny fire there - the frozen monotonic timer keeps its remaining budget, so the deny would land minutes late and race the approval row TTL (created_at + timeout_s + 120s), triggering the row-reaped stranded fallback on a healthy gate. Gate expiry is now min(monotonic budget, created_at + timeout_s), evaluated on each poll iteration and on /resume. This extends two existing patterns rather than inventing one: Cedar decision aws-samples#6 is already min-wins for timeouts, and the section 13.12 late-approval race fix already makes the durable row authoritative over the local timer. The agent writes created_at itself, so there is no cross-clock skew; deny authority stays agent-side and the orchestrator's deadline-minus-margin resume is wake-up only. 2. idlePolicy contradiction: suspendedDurationSeconds lives inside the idlePolicy block the ADR mandates omitting. Resolved by omitting idlePolicy in every phase and dropping the suspendedDurationSeconds requirement: the suspended-state bound is maximumDurationInSeconds (mandatory, task-budget-derived, <= 8h) plus orchestrator termination and the stranded reconciler - consistent with the codebase placing lifecycle policy in the orchestrator and reconcilers (stranded-approval reconciler, cancel orphan events) rather than substrate config, and keeping one invariant across P1/P3. A tighter substrate suspended-TTL stays available later as an additive idlePolicy change. Nits: name DenyTaskFn alongside ApproveTaskFn, annotate create-microvm-auth-token as not called in P1-P3, add a P1 verification item for the manual-suspend default-TTL question (design is safe either way under maximumDurationInSeconds). Refs aws-samples#645 Co-authored-by: Claude <noreply@anthropic.com>
|
Thanks @krokoko — both blockers addressed in ca3e9e6. You were right on both counts: our round-1 timeout fix didn't do the arithmetic (the frozen monotonic budget survives resume), and we mandated omitting the very block Blocker 1 — your first option, grounded in two existing mechanisms: gate expiry is now Blocker 2 — your second option: Nits: Ready for re-review. |
…ARS atomicity (aws-samples#645) Apply findings from an independent adversarial review of the ADR: - pollSession cannot see task state (interface takes only the handle), so the SUSPENDED-iff-AWAITING_APPROVAL mapping was unimplementable as written. SessionStatus gains a 'suspended' variant; the strategy reports substrate state mechanically and the orchestrator does the cross-referencing - the division of labor finalPollState (ECS) and pollTaskStatus (agentcore) already use. - maximumDurationInSeconds "derived from the task budget" was undefined (budgets are max_turns/max_budget_usd; no wall-clock budget exists). Pinned at 28800s: parity with AgentCore's 8h session cap, inside the orchestrator's ~8.5h safety-net window. - Correct the cancel-task precedent description: the orphan event fires only on missing_runtime_handle, not on failed stops; the resume path deliberately goes further (orphan on failed resume) because a failed resume strands a suspended VM. - Specify handle plumbing: startSession persists microvmId/endpoint to compute_metadata (the field cancel-task.ts already reads); approve/deny load it via post-commit consistent GetItem. - Specify the S3-pointer payload path (platform payload bucket, execution-role read-only grant, lifecycle expiry) following the ecs-agent-cluster pattern. - Split compound EARS bullets into atomic single-pattern requirements per ADR-020; move rationale into prose. - Flag the single stack-level compute_type tag (main.ts) as wrong with three backends; require backend cost-allocation tags. - Mark Lambda MicroVMs service facts as externally sourced and expand the P1 empirical verification list. - Split Testing by rollout phase (P1/P2/P3). Refs aws-samples#645 Co-authored-by: Claude <noreply@anthropic.com>
|
Round 3 (85ee85d): we ran an independent adversarial review of the ADR against the codebase before re-review. It found two more unimplementable normative statements that rounds 1–2 (ours and review) missed, plus several tightenings. All applied: Fixed — would have blocked P1/P3 implementers:
Also applied:
Mirror re-synced. Ready for re-review. |
Records the design decision for adding AWS Lambda MicroVMs as a third opt-in
ComputeStrategybackend (compute_type: 'lambda-microvm'), fulfilling the ADR/RFC acceptance criterion of #645. Docs-only: the ADR (statusproposed) plus its generated Starlight mirror; implementation follows in separate PRs per the phased rollout the ADR defines.Lifecycle model resolves real substrate tensions
MicroVM idle detection is inbound-traffic-based while the ABCA agent is outbound-only, and MicroVMs cannot self-suspend — so the ADR mandates auto-suspend disabled at
RunMicrovmand makes suspend/resume an orchestrator-owned decision, targeted at HITL approval waits (compute billing stops while memory/disk state, including the cloned repo and warm caches, is preserved).Interface widening follows the codebase idiom
suspendSession/resumeSessionare added as mandatory methods returning typed unsupported results onagentcore/ecs(no optional interface methods exist in the codebase;AgentCoreComputeStrategy.pollSessionis already a mandatory explicit stub), landing in P3 across all three strategies in one commit.Regional availability is enforced in layers
Synth-time static constant with a context-flag escape hatch, live probes (
list-managed-microvm-images) at onboarding and inbgagent platform doctor, and orchestration-time error classification as defense in depth — live probes wherever possible so the platform self-heals as AWS expands beyond the 5 launch regions.Scope corrections vs the original issue text: AgentCore Memory parity is in scope (P2) since Memory is already consumed cross-substrate on ECS via IAM grant +
MEMORY_ID; only the Runtime-coupled workload-access-token injection path is deferred (#249/ADR-016). Gateway integration is orthogonal (ADR-019/#641, substrate-portable by design). Numbered ADR-021: 018/019 are claimed by open PRs #548/#663 (banner in the ADR covers renumbering).Refs #645