Fable r2
Vote¶
APPROVE WITH NOTES — every round-1 P1 is addressed by a concrete gate or edit, and the mechanics I checked against AWS docs, the Copilot source and the provider source hold; the one factual error r2 introduced (VPC connector security groups) is a bounded P2 edit, not a safety regression.
Round-1 items status¶
| Item | Status | Note |
|---|---|---|
| Astra 1 (Copilot gone) | addressed | §4.1 verify-handoff four checks + 24 h freshness; handoff_stacks exists in the v0.1 manifest (src/ecsodus/emit/terraform.py:208) |
| Astra 2 (desired_count interlock, Express) | addressed | §4.3 target at 0/0, §4.8 #3–5 live counts, §4.6 Express fails closed; RegisterScalableTarget only moves capacity "inside of this range" (https://docs.aws.amazon.com/autoscaling/application/APIReference/API_RegisterScalableTarget.html) so 0/0 holds 0 |
| Astra 3 (double-run, rollback order) | addressed | §4.10 off-first with lock/idle-check, boot migrations blocked; §4.11 reverse order |
| Astra 4 (missing transitions, vacuous stage 0) | addressed | §4.8 full table; ready requires running ≥ MinSize, COMPLETED, TLS/SNI, probe |
| Astra 5 (unsafe weight states) | addressed | §4.9 exact live before-pair, no (0,0) on any path incl. intermediates; provider waits INSYNC (waitChangeInsync, record.go) |
| Astra 6 (retire evidence) | addressed | §5.2 CloudTrail us-east-1 (https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/logging-using-cloudtrail.html), 2xxStatusResponses Sum, late/missing data, bound file; §5.4 consumes it |
| Astra 7 (shared resources in retire set) | addressed | §5.3 deletes only service, association, apprunner record |
| Astra 8 (deployed digest) | addressed | §4.1 digest or synchronized deployment + SUCCEEDED; ECR Public path; amd64 child pin |
| Astra 9 (networking/auth) | partial | §4.3 task SG + §4.5 checks and simulation are right, but the connector-SG fact is wrong (Note 1) |
| Astra 10 (shared ALB) | addressed | §4.7 opt-in, listener-certificate resource, bounded prepare, WAF block |
| Astra 11 (ack flag ≠ validation) | addressed | §5.1 refuses, no flag; §9 "plan validation" vs e2e |
| Opus 1 (scalable target) | addressed | as Astra 2; live task definition read via DescribeServices→DescribeTaskDefinition |
| Opus 2 (TTL+24 h unverifiable) | addressed | §5.2 item 1, 90-day fail-closed, wording fixed |
| Opus 3 (validation record, rollback gate) | addressed | §4.3 collision compare, §4.11 rollback-rebuild, own root/state, §5.3 never-touch rule |
| Opus 4 (retire unverified, eligibility) | addressed | §4.12 ends at weight 0; §5.1 separate artifact; sentinel service |
| Fable 1 (scaling bypasses gate) | addressed | as Opus 1 |
Fable 2 (Requests service-wide) |
addressed | §5.2 2xxStatusResponses + attested host sample; ActiveInstances zero-means-no-requests matches https://docs.aws.amazon.com/apprunner/latest/dg/monitor-cw.html |
| Fable 3 (ACM CNAME collision) | addressed | §4.3 compare at generate and pre-create; never deleted (https://docs.aws.amazon.com/acm/latest/userguide/dns-validation.html) |
| Fable 4 (cutover gate) | addressed | §4.9 pair rules incl. --rollback direction |
Fable 5 (enable_www_subdomain) |
addressed | §3.3 read EnableWWWSubdomain from DescribeCustomDomains (required field, https://docs.aws.amazon.com/apprunner/latest/api/API_CustomDomain.html); handler omits it (custom-domain-app-runner.js) |
Blocking issues (only if REJECT)¶
None.
Notes (non-blocking)¶
- P2 — §3.1
VpcConnectorrow, §4.3 "Security groups", §15 "ALB ingress and SG membership": r2's correction is itself wrong. Copilot's connector carries bothServiceSecurityGroupand the importedEnvironmentSecurityGroup: fixturetests/fixtures/copilot/rendered/workloads/rdws-test.stack.yml(VpcConnectorSecurityGroupshas theFn::ImportValue ... EnvironmentSecurityGroupentry) and mainlineworkloads/partials/cf/vpc-connector.yml(https://raw.githubusercontent.com/aws/copilot-cli/mainline/internal/pkg/template/templates/workloads/partials/cf/vpc-connector.yml). r1 was right. Consequence: any SG rule whose source isEnvironmentSecurityGroup(user-authored; Copilot's own RDWS addons trustServiceSecurityGroupId, seetemplates/addons/aurora/rdws/cf.yml) stops matching ECS tasks after cutover, and neither §4.5 nor the safe-path probe sees it. Edit: read liveDescribeVpcConnector.SecurityGroups; if it holds more thanServiceSecurityGroup,decisions.ymlmust answerenv_sg: join | omit(the report lists rules in the closure whose source is each extra SG and states the inbound exposure of joining). Delete the §15 sentence "r1 wrongly listed …". - P2 — §4.8 #9
worker-onconflicts with the §4.8 rule "unknown values on a gated attribute fail". A task-definition change is a replace, soaws_ecs_service.task_definitionis "known after apply". Edit: in this phase gate on the plannedaws_ecs_task_definitioncontainer_definitionsdiff (only the declared env var) and accept an unknowntask_definitionon the service, withverify-cutover --step worker-onproving the live value. Also emitskip_destroy = trueon the task definition (https://raw.githubusercontent.com/hashicorp/terraform-provider-aws/main/website/docs/r/ecs_task_definition.html.markdown) so the previous revision stays ACTIVE for rollback; an INACTIVE revision cannot be re-referenced byUpdateService(https://docs.aws.amazon.com/AmazonECS/latest/developerguide/deregister-task-definition-v2.html). - P2 — §4.8 #7a and §7:
check --phase importtoday rejectsforget.src/ecsodus/check/plan.py:103accepts["forget"]only in the steady phase and:74only foraws_ecs_task_definition. State in §7 that the import phase accepts the manifest-named record address asforgetalongside the two weighted imports, otherwise step 5 of §4.9 cannot pass. - P3 — UNCONFIRMED tags that can be dropped from §4.9/§15: the provider sends a single UPSERT when name/type/set_identifier are unchanged ("If neither type nor set_identifier changed we use UPSERT") and otherwise a DELETE+CREATE within one transactional batch, and both create and update wait for INSYNC (https://raw.githubusercontent.com/hashicorp/terraform-provider-aws/main/internal/service/route53/record.go). Reword 7c's "delete-then-create gap": the gate still rejects the replace, but for identity, not for a gap.
- P3 — §3.3 health check: provider block defaults are protocol TCP, path
/, interval 5, timeout 2, healthy 1, unhealthy 5 (https://raw.githubusercontent.com/hashicorp/terraform-provider-aws/main/website/docs/r/apprunner_service.html.markdown), equal to App Runner's default; keep UNCONFIRMED only for the real plan. - P3 — §4.3 scaling:
MinCapacity0 is documented for ECS;MaxCapacity = 0is not documented either way. Add to the UNCONFIRMED list and to the §8 moto/stand-in checks. - P3 — §4.1 synchronized deployment vs §4.10 "state equals live": after
update-serviceto the digest, the M5a HCL still holds the tag;ignore_changesplus refresh carries the live digest into state, soworker-offsends the digest. Makeverify-handoffcheck 3 assert stateimage_identifier== live, so the assumption is tested rather than implied. - P3 — §4.3 execution role: SSM-backed
RuntimeEnvironmentSecretsneedssm:GetParameters, not only Secrets Manager read;kms:Decryptonly for customer-managed keys. ECS confirms the execution role fetches them (https://docs.aws.amazon.com/AmazonECS/latest/developerguide/secrets-envvar-secrets-manager.html). - P3 — §14 numbering: ADR-0016 (hatchling) exists only on branch
build-hatchling;docs/adr/onmainends at 0015. Renumber if that branch does not merge first. - P3 — confirmed, no change: Copilot end of support 2026-06-12 (https://aws.amazon.com/blogs/containers/announcing-the-end-of-support-for-the-aws-copilot-cli); App Runner closed to new customers, existing customers may create services (https://docs.aws.amazon.com/apprunner/latest/dg/apprunner-availability-change.html); Delete handler disassociates and DELETEs the domain CNAME and validation CNAMEs,
HostedZones[0], TTL 60, viaAppDNSRole(https://raw.githubusercontent.com/aws/copilot-cli/mainline/cf-custom-resources/lib/custom-domain-app-runner.js); all-zero weights route equally, range 0–255 (https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/resource-record-sets-values-weighted.html); trust policy withaws:SourceAccount/aws:SourceArn(https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task-iam-roles.html); every §4.4 CPU/memory pair is a valid Fargate size (https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task-cpu-memory-error.html);ListOperationsstatuses includeSUCCEEDED/ROLLBACK_SUCCEEDED— treatROLLBACK_SUCCEEDEDas failure in §4.1 and §4.8 #8 (https://docs.aws.amazon.com/apprunner/latest/api/API_OperationSummary.html); fixture has exactly 14 resources as §3.1 lists.