Conversation
Grant only the exact execution role to CloudFormation. Correct recursive policy hashing and include the generated inline policy; bump bundle to 1.7.0.
…ands" This reverts commit 62cce37d8f942560be2bbee08e2a046acfb0fd8d.
ayushtr-aws
left a comment
There was a problem hiding this comment.
Review at head 548bdf48 — Request changes
The engineering here is unusually rigorous, and the security boundary work (trusted approval writer, Linear consent read-back, payload bootstrap) survived an adversarial security pass with no findings above threshold. But the PR ships two verified correctness defects on the new wake path, one unbounded storage leak, an unsafe-by-default layout change with unfinished migration tooling, and a governance gap.
Vision alignment
- Tenet 1 / Tenet 5 — advanced. Approvals survive worker retirement, decisions stay owner-attributed, workers lose direct approval-write access, and the session role can only invoke
/tasks/${aws:PrincipalTag/task_id}. - Tenet 6 — traded.
approval_timeout_sdefault 300 → 0 applies to all backends, so an unanswered gate on AgentCore/ECS bills to the worker lifetime ceiling. Documented only in ADR-023, which isproposed. - Tenet 4 — traded.
microvm_nested_stackdefaults totruewith an advisory warning (see B2). - ADR-003 — violated. See B1. ADR-021 and ADR-023 both remain
proposedand need maintainer acceptance as part of merging.
Blocking
B1 — Scope and governance (ADR-003). #645's acceptance criteria cover a MicroVM ComputeStrategy. This PR also delivers a new IAM-authenticated approval-writer API, native Linear approve/deny replies (~1,000 lines across 8 modules), a repo-wide change to the default approval expiry, a ConcurrencyMaintenance nested stack that affects every deployment including default AgentCore, and a nested-by-default MicroVM layout. None has an approved issue (#793 separately wants the ADR-023 number). #645 is assigned to @dreamorosi, not the author. Your own first self-review flagged this. Please file approved issues for the approval-writer API, Linear reply approvals, and the maintenance-stack split (or re-approve #645 with widened deliverables), and get assigned.
B2 — cdk/src/stacks/agent.ts:361 nests MicroVM by default while migration is unfinished. The checklist item "Complete reusable flat-to-nested migration commands and independently verify upgrading a current-main deployment" is unchecked. cdk/src/migration/permissions.ts and template.ts are referenced only by tests, with no script, mise task, or doc entry point. The synth warning at :364-372 does not block a cdk deploy that can delete payload/artifact buckets. Fix: when compute_type=lambda-microvm and microvm_nested_stack is unset, throw until migration tooling lands and is verified; then downgrade to a warning.
B3 — cdk/src/constructs/task-api.ts:1460-1471 never sets USER_CONCURRENCY_TABLE_NAME on the approve/deny Lambdas. enableMicrovmContinuations adds only CONTINUATION_BUCKET_NAME and ORCHESTRATOR_FUNCTION_ARN, but approve-task.ts:267 / deny-task.ts:241 → wakeMicrovmAfterApproval → dispatchMicrovmContinuation:63 → admitContinuation, which reads process.env.USER_CONCURRENCY_TABLE_NAME! (microvm-continuation-start.ts:32). A CLI/API decision on a PARKED task runs a TransactWrite with TableName: undefined, is caught as wake-reconciliation-failed, and the replacement launches only when the 5-minute reconciler sweep reaches the row. linear-integration.ts:330 sets the variable, which is why the six Linear live flows passed. Fix: set USER_CONCURRENCY_TABLE_NAME and MAX_CONCURRENT_TASKS_PER_USER in enableMicrovmContinuations, and add a CDK assertion on both decision functions' environment.
B4 — cdk/src/handlers/reconcile-microvm-continuations.ts:72-85 throws forever for a replacement that failed before launching. admitContinuation writes the lease ACTIVE (microvm-continuation-start.ts:101); the runner can throw MICROVM_CONTINUATION_VERSION_CHANGED (microvm-continuation-runner.ts:181) before any start receipt exists; the catch at :262-269 calls failContinuationAttempt, which moves the lease to FENCED (:85-92). For a terminal task with no microvm_start, the reconciler gate accepts only PARKED/CLOSED or ACTIVE-with-slot-held, so a FENCED lease throws MICROVM_CONTINUATION_LEASE_INVALID every sweep and deleteClosedTaskContinuations at :108 never runs. continuation-bucket.ts:39-41 has no object expiration, so up to 1 GiB per affected task is retained indefinitely behind a "will retry" warning. Fix: treat FENCED with no lease_microvm_id as never-launched (or have failContinuationAttempt write CLOSED when no handle exists), and add a lifecycle expiration to the bucket as defence in depth.
B5 — agent/src/hooks.py:1978-1979 leaks tool registrations when a second PreToolUse denier fires. _pre calls lifecycle.tool_finished only on the non-allow path. Repo tasks load setting_sources including project (runner.py:428), so a target repo's .claude/settings.json can register a PreToolUse hook or deny rule that blocks a tool the Cedar hook allowed. If the CLI then returns an error tool result without emitting PostToolUse/PostToolUseFailure, the id stays in MicrovmLifecycle._tools, and suspend() (microvm_lifecycle.py:355) / continuation_checkpoint() (:262) refuse for the rest of the task. Confidence note: the code path is verified; the CLI behaviour (no Post* event when a later hook denies) was checked by an automated pass against CLI 2.1.258 but not reproduced by me. If the CLI does emit a failure event there, this drops to a hardening nit. Either way, evicting stale ids at the Stop hook plus a test for the "no Post* hook arrives" case is cheap.
Non-blocking
Correctness / operations
task-concurrency.ts:172-178+reconcile-microvm-continuations.ts:63-67: a knownRunMicrovm4xx rejection after receipt and lease exist holds the seat for the full 8-hour lifetime with no log. Replacement launches only. Record the rejection on the receipt so release is immediate.request-approval.ts:95-99: the leaseConditionCheckis unconditional forlambda-microvm; a task RUNNING before this deploy has no lease row and fails closed at its first gate. Mitigated by the documented drain step; say so in the error string.aws_session.py:388-389: advisoryrefresh_needed()escalates to_protected_refresh(is_mandatory=True)on a 1-attempt/2 s STS client, so a transient STS stall becomes a broker 503 while cached keys are still valid.continuation_workspace.py:222: rejectingst_nlink != 1will fail every checkpoint ifuvhardlinks site-packages into the workspace. SetUV_LINK_MODE=copyin the image or relax the check.lambda-microvm-strategy.ts:918-943:ConflictExceptionon Terminate is nowunconfirmedand emits ERROR + a task event on the routine cancel/finalize race; confirm viaGetMicrovmfirst.continuation_runtime.py:239-242:approval_scopesare seeded from the worker-written manifest without cross-checking APPROVED rows. Not a new boundary crossing, but validate against the approvals table on restore.server.py:536still discards thewrite_terminaloutcome on the outermost crash path (noting your decline for the record).
Efficiency
continuation_session.py:258-270: eager append deep-copies and re-encodes the whole conversation per frame — O(n²) on the SDK loop.continuation_storage.py:405: re-downloads the tar it just uploaded to learnVersionId; useHeadObjectwithChecksumMode=ENABLED, as the coordinator already does atmicrovm-continuation-storage.ts:229.
Design / clarity
microvm-start.ts:36-42StartReceiptis private and re-declared inline in three modules;TaskRecordlacksmicrovm_start. Export and share.microvm-continuation-types.ts:42-53ContinuationRecordhas state-dependent optionals; make it a discriminated union likeApprovalRecordin this same PR.approve-task.ts:231-290anddeny-task.ts:208-265are a ~60-line duplicate.contracts/constants.jsonapproval_timeout_shasdefault: 0outsidemin: 30, and the sync script was relaxed to allow it. Ano_deadline_sentinelfield would make the schema self-describing.approval-request-service.ts:89cdk-nag reason still reads as semantic validation.- New dependency
@aws-sdk/client-ssmand edits to.github/workflows/build.ymlare "ask first" items per AGENTS.md; both look justified but should be called out in the body.
Documentation
- Starlight mirror regenerated locally with
mise //docs:sync: zero drift. ✅ - Bootstrap 1.6.0 → 1.9.0 with rationale; artifacts regenerated locally with zero drift; template body 46,725 bytes.
DEPLOYMENT_ROLES.mdgolden baseline updated. ✅ - Stale:
docs/guides/QUICK_START.mdx:483still says "300 s by default".CEDAR_HITL_GATES.md:199-213, :485, :2129-2133, :2515still describe built-in rules carrying 300/600 s timeouts thatbuiltin-policies.ts:141-179no longer declares.types.ts:1394-1395says the stranded reconciler leaves approval rows PENDING, butreconcile-stranded-tasks.ts:297cancels them. - Missing: the six checkpoint
codevalues; a documented runner forcdk/test/live/*.live.ts; any entry point forcdk/src/migration/.
Tests & CI
- All four checks green at head.
test/bootstraprun locally: 140 tests pass;synth-coveragecovers both MicroVM layouts and assertsAWS::SSM::ParameterandAWS::Lambda::MicrovmImage. Types/constants sync scripts pass; the only relaxation is the deliberatedefault === 0sentinel. - #366: no
bundling-stacksre-enable; large synths inbeforeAll. Env-gated DynamoDB Local / SDK probe tests are fail-closed underCI=trueand wired inbuild.yml. ✅ - R1/R2 from your third pass are closed:
_persist_finished_taskraises only on FAILED, andtest_terminal_persistence_through_task_entry_pointdrivesrun_taskend to end. - Gaps:
agent-heartbeat.tshas zero direct test references for its boundary arithmetic;approval-request-service.test.tshas no negative case; nothing asserts approve/deny Lambda environment (would have caught B3); nothing covers the B5 "no Post* hook" path.
Human heuristics
- Proportionality — concern. Core lifecycle work is proportionate.
cdk/src/migration/*(test-only) and the approve/deny duplication add surface without payoff yet. - Coherence — concern. Two vocabularies for one lifecycle (
lease_statevsContinuationRecord.state), dualattempt_idmeanings,TaskStatusliterals inmicrovm-worker-lease.ts:35-36. Acknowledged where wire-format bound. - Clarity — concern. B4's perpetual "will retry" warning and the silent seat hold both hide a permanent state behind a transient-looking message.
- Appropriateness — pass. Integration code verified against real AWS and Linear; real-DynamoDB tests in CI; ADR-023 names residual risks honestly.
Suggested order
B3 and B4 are small, mechanical fixes. B5 is a Stop-hook eviction plus one test. B2 is a one-line throw. B1 is the maintainers' call and should be settled before re-review.
Reverts 4ea7e88. Suffixing the CloudWatch dashboard and CloudFront OAC names with the Region fixes a real multi-Region collision, but it is unrelated to the MicroVM P3 lifecycle and renames (replaces) live resources for every existing deployment, including AgentCore-only ones. Keeping it out of this PR narrows the change to aws-samples#645; it should ship on its own with its own upgrade note. Refs aws-samples#645 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
cdk/src/migration/{template,permissions}.ts were imported only by their own
tests and had no script, mise task or documented entry point; the explicit
microvm_nested_stack requirement means a migration command is a follow-up,
not part of this PR. The three agent/scripts probes are one-off diagnostics
that nothing in the repo, CI or docs references. verify_microvm_credentials.py
stays because agent/tests/test_continuation_sdk_probe.py imports it.
Refs aws-samples#645
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
approve-task.ts and deny-task.ts carried identical ~60-line blocks for the approval_decision_recorded audit write and the best-effort MicroVM wake that this PR added. Move them to recordDecisionPostCommit in a separate module so the handler tests' module mock of wakeMicrovmAfterApproval still applies. Behaviour, event shapes and error handling are unchanged. Refs aws-samples#645 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
P3 lets an agent wait for human approval while its MicroVM sleeps or retires with a verified checkpoint. A later decision wakes the original worker or admits one replacement with the saved conversation and workspace. Unanswered approvals remain available by default; explicit deadlines remain supported.
Linear users can reply
approveordenyunder the approval comment. The service verifies the actual comment through Linear’s API, rejects bots and the saved OAuth token’s own identity, and applies the task owner’s decision to that request. CLI decisions work on all backends; sleep and replacement are MicroVM-specific.Area
cdk— infrastructure, handlers and permissionsagent— runtime and imagecli— task and approval settingsdocs— guides and design sourcestooling— contracts, packaging and CIRelated
ADR-021 P3 for #645, including remaining P2 prerequisites. Related follow-ups: #817, #841 and #857. AgentCore remains the default compute backend.
Changes
truefor new/already-nested installations,falsefor existing flat installations. Omission fails synthesis. Group stateless scheduled maintenance in a separate nested stack while retaining existing tables and MicroVM identities. Both layouts are covered across all image, gateway and vault configurations by the 490-resource budget.Latest review fixes (
bf7c271d)uvinstalls to copies after reproducing Linux cache hardlinks. Confirm termination conflicts withGetMicrovmbefore treating cleanup as successful.Local full build passed with DynamoDB Local and real SDK continuation probes enabled: 5,758 CDK tests (261 suites), 2,254 agent tests (86.84% coverage), and 1,005 CLI tests, plus lint, type checks, synthesis and documentation. The final agent diagnostic change passed another complete 2,254-test quality run. Final layout compatibility and heartbeat checks passed 25 tests, with CDK lint/compile passing. The documentation build and internal links passed across 82 pages; all three live-harness inspection commands ran without AWS calls.
Deployed
bf7c271dto the retained nested fixture inus-east-1: CloudFormationUPDATE_COMPLETE, MicroVM image 3.0, AgentCore runtime 4, and coordinator version 5. Four deployed Lambda code hashes match the reviewed assets; approve/deny functions have the concurrency table and shared limit. Verified 26 baseline stateful resource identities were preserved. Latest-head GitHub checks passed.Both live Cognito-authenticated API decision cases passed after a saved checkpoint, sleep, source-worker loss and
PARKED: approval and denial started replacement workers in 11.7s and 12.3s. Approval performed exactly one Read; denial performed none and acknowledged the decision. Both tasks completed, their workers terminated, and scheduled cleanup closed leases and returned capacity to zero. These are the API routes used by CLI decisions; no interactive CLI command was used. Tests used an explicit 30-second sleep delay with a test-only policy, leaving the ten-minute default unchanged. The fixture configuration was restored exactly; final checks found no active tasks, open leases or held slots. Cleanup follows the five-minute reconciliation schedule. This already-nested update does not verify flat-to-nested migration. Earlier results below retain their recorded revisions.Scope and remaining tradeoffs
This PR includes the trusted approval writer, Linear replies, retained-request default, shared maintenance stack and MicroVM layout selection. Maintainer agreement on this expanded scope and the proposed ADRs remains open. The
@aws-sdk/client-ssmdependency supports the runtime suspend setting; build-workflow changes make DynamoDB Local and the pinned SDK recovery probes required in CI. These are intentional dependency/CI changes, not incidental cleanup.Nonblocking optimization remains: eager transcript append repeatedly copies/encodes history (a local synthetic 1,000-frame run took about 5.5 seconds cumulatively). Replacing checkpoint readback with
HeadObjectmust preserve checksum/version validation and recovery after a lost upload reply. Broad type/handler refactors and restored-scope revalidation are deferred rather than mixed into lifecycle corrections. Known launch rejections still share the conservative unknown-outcome capacity hold; earlier release needs a durable, operation-specific rejection classification.Validation
Review validation at
159576e6(eight focused commits after the third review):worker_attempt_idvalidator; 22 broker unit tests passed. Source tracing shows this field carries the platform launch/lease token, not AWS’s physical MicroVM identifier.denycomment. The human body and author stayed unchanged. This verifies the current app-token installation; no historical approval threads were modified.159576e6. The silent-failure scan and its rule tests pass. Lint, type checks, contracts, documentation and infrastructure synth pass. The full build and all PR checks passed at that revision.159576e6successfully to the retained isolated fixture after pausing submissions and draining tasks, leases and capacity. Verified the new approval API, AgentCore runtime version 3, and MicroVM image 2.0 against the built image/artifact references. Seven live IAM simulation checks passed: own-task invocation/read allowed; other-task or untagged invocation and direct approval writes denied. The borrowed Linear vault and shared logging settings remain unchanged and functional.Live stress validation and capacity fix at
7384f9b0:TransactionConflict. Bounded retries preserve the exact transaction, idempotency token and worker-lease conditions. An isolated live reproduction went from 6/12 successful releases to 12/12; concurrent admission still accepted exactly 10/12. All 71 focused unit/transaction tests and the full local build passed: 5,723 CDK, 2,231 agent, 1,005 CLI tests.The later main merge at
2a79c0eretains this fix; its CI is separate from the live deployment above.Earlier live approval-boundary validation used the actual approval service and Python signing client: 34 AWS checks covered creation, field rejection, task/tag isolation, denied direct writes, timeout races and MicroVM lease fencing; nine IAM simulation checks also passed. These were isolated API tests with task records for all three backends, without coding-agent images.
Earlier coding-agent tests additionally covered explicit expiry/cancellation and AWS access after credential expiry. Those earlier configurations remain separate from the latest-image cases above. Retained Linear fixtures and reproducible integration harnesses stay outside this public PR.
Before marking ready
159576e6); full local build and live stress validation passed for capacity fix7384f9b0.2a79c0e).35f05bbb).bf7c271d).Review decisions and limits
ADR-023 records the approval writer, rollout and retained-request decision. No default approval expiry is deliberate; zero is the no-expiry sentinel and the positive timeout range starts at 30 seconds. Worker/resource lifetime remains separate. A public SigV4 endpoint is intentional, with task-path IAM authorization.
This protects approval-record writes, not complete isolation from compromised ambient compute credentials: those credentials can still select session tags, and legacy channel bundles still include the webhook signing key. Action descriptions and policy metadata are worker assertions.
TIMED_OUTcurrently also includes worker polling failures and is not proof that a human deadline elapsed. These limits are documented rather than presented as solved by the writer API.The normal pipeline propagates failed terminal writes, while a superseding cancellation is benign and does not produce a false failure reaction. Actual pipeline entry points and the outer runtime's unhealthy response after a failed backup write are tested. Pure transaction conflicts get bounded retries; failed ownership checks do not. The continuation scan cursor uses compare-and-set to preserve progress when old and new schedules overlap during upgrade. Existing orchestrator heartbeat/backend monitoring handles lost running workers; a second broad
RUNNINGreaper was not added. Checkpoint field renames or hash-encoding consolidation require a versioned migration, not a style-only edit.Deployment notes
Re-bootstrap to bundle 1.9.0 or later. For the approval permission change, pause submissions and drain existing tasks, including suspended workers and retained continuations; deploy CDK and the matching rebuilt image together. Old workers lose direct approval-write access and fail closed at their next gate. See the upgrade procedure.
Automatic suspension remains behind the deployment compatibility gate, which defaults off. Once enabled, the default sleep delay is ten minutes. MicroVM deployments must now explicitly select
microvm_nested_stack: usetruefor new/already-nested installations and retainfalsefor existing flat installations. Omission fails synthesis. Explicitly selectingtruedoes not migrate a flat deployment. Regenerate old cloud assemblies before deploying. Portable migration and independent upgrade acceptance remain open.Acknowledgment
By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution under the terms of the project license.