Skip to content

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 VpcConnector row, §4.3 "Security groups", §15 "ALB ingress and SG membership": r2's correction is itself wrong. Copilot's connector carries both ServiceSecurityGroup and the imported EnvironmentSecurityGroup: fixture tests/fixtures/copilot/rendered/workloads/rdws-test.stack.yml (VpcConnector SecurityGroups has the Fn::ImportValue ... EnvironmentSecurityGroup entry) and mainline workloads/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 is EnvironmentSecurityGroup (user-authored; Copilot's own RDWS addons trust ServiceSecurityGroupId, see templates/addons/aurora/rdws/cf.yml) stops matching ECS tasks after cutover, and neither §4.5 nor the safe-path probe sees it. Edit: read live DescribeVpcConnector.SecurityGroups; if it holds more than ServiceSecurityGroup, decisions.yml must answer env_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-on conflicts with the §4.8 rule "unknown values on a gated attribute fail". A task-definition change is a replace, so aws_ecs_service.task_definition is "known after apply". Edit: in this phase gate on the planned aws_ecs_task_definition container_definitions diff (only the declared env var) and accept an unknown task_definition on the service, with verify-cutover --step worker-on proving the live value. Also emit skip_destroy = true on 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 by UpdateService (https://docs.aws.amazon.com/AmazonECS/latest/developerguide/deregister-task-definition-v2.html).
  • P2 — §4.8 #7a and §7: check --phase import today rejects forget. src/ecsodus/check/plan.py:103 accepts ["forget"] only in the steady phase and :74 only for aws_ecs_task_definition. State in §7 that the import phase accepts the manifest-named record address as forget alongside 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: MinCapacity 0 is documented for ECS; MaxCapacity = 0 is 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-service to the digest, the M5a HCL still holds the tag; ignore_changes plus refresh carries the live digest into state, so worker-off sends the digest. Make verify-handoff check 3 assert state image_identifier == live, so the assumption is tested rather than implied.
  • P3 — §4.3 execution role: SSM-backed RuntimeEnvironmentSecrets need ssm:GetParameters, not only Secrets Manager read; kms:Decrypt only 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/ on main ends 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, via AppDNSRole (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 with aws: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); ListOperations statuses include SUCCEEDED/ROLLBACK_SUCCEEDED — treat ROLLBACK_SUCCEEDED as 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.