docs(P61): create phase plan — L2 lifecycle pipeline author

---ci---
project: acdl
phase: P61
milestone: v1.11
status: plan
---/ci---
This commit is contained in:
Jon Chery
2026-07-28 20:14:20 +00:00
parent bb3ac7c74d
commit 0c5c4d1c40
+66 -84
View File
@@ -1,109 +1,91 @@
---
phase: P60
name: l1-lifecycle-pipeline-live-run
phase: P61
name: l2-lifecycle-pipeline-author
milestone: v1.11
requirements: [REQ-127]
wave: 2
depends_on: [P59]
requirements: [REQ-128]
wave: 3
depends_on: [P60]
---
# P60 — L1 Module Lifecycle Pipeline (Live Run)
# P61 — L2 Lifecycle Pipeline (Author)
**Phase:** P60
**Phase:** P61
**Milestone:** v1.11 (RESTART)
**Requirement:** REQ-127 (shared with P59)
**Wave:** 2 (the testing milestone — P59 authored the pipeline, P60 runs it live)
**Branch:** `milestone/v1.11-restart` (retrofit — see "Deviation note" below)
**Requirement:** REQ-128
**Wave:** 3 (author the L2 lifecycle pipeline, P62 runs it live)
**Branch:** `milestone/v1.11-restart`
## Goal
Run the `modules-lifecycle` pipeline against live AWS; fix every module whose
apply/modify/destroy fails. Each failing cell is a module defect: bad
`terraform/` subdir (resource shape, nested blocks, defaults), bad example
contract, or bad adapter assembly. Fixes land in
`modules/l1/<module>/terraform/*.tf`, `modules/l1/<module>/examples/*.yml`,
`terraform/ci-vpc/`, `scripts/run_lifecycle_*.sh`, and rarely the adapter
assembler. No new Python files.
Extend the `modules-lifecycle` pipeline + both forge workflows with an L2
matrix: `static-assets` × `contracts/static-assets.yml` (apply → modify: add
WAF rule → destroy) and `microservice` × `contracts/microservice.yml` (apply
→ modify: `desired_count` 1→2 → destroy, references platform VPC). L2 =
composition only (no L2 terraform files); the composition must be
deterministic (same contract → same resolved stack → same state key).
## Deviation note (process audit)
## Tasks (single wave)
P60's execute deliverable was produced out-of-band: 13 `fix(...)` commits
landed on `milestone/v1.11-restart` between the P59 verify PASS (`3739037`)
and HEAD (`88ea408`), committed under `phase: P59, status: execute` rather
than a dedicated `phase/p60-*` branch. This retrofit PLAN formalizes that
work as P60's EXECUTE output. The phase branch deviation is documented for
the COMPLETE-stage audit; no commits are reverted (the fixes are correct
and were validated by terraform validate + 24 example contracts `--check-only`).
### Task 1 — Extend pipelines/modules-lifecycle.yml
## Tasks (already executed — retrofit)
Add L2 stages to the declarative pipeline contract:
- `l2-lifecycle-apply`: `run_platform.sh --apply modules/l2/${MODULE}/examples/simple.yml`
- `l2-lifecycle-modify`: `run_platform.sh --apply modules/l2/${MODULE}/examples/complex.yml`
- `l2-lifecycle-destroy`: `run_platform.sh --destroy modules/l2/${MODULE}/examples/complex.yml`
### Task 1 — CI VPC separation (commits 8071d6a, ad3cc5f, a55752e)
Add L2 matrix entries: `l2_modules: [static-assets, microservice]`
Created `terraform/ci-vpc/` — a short-lived VPC for L1 module lifecycle
testing, separate from the long-lived platform VPC. Created before
VPC-dependent modules (alb, ecs-service, rds, uptime), destroyed after.
Outputs (vpc_id, subnet_ids, sg_id, cluster_arn) read from S3 state by
each lifecycle job (no artifact passing — `upload-artifact@v4` unsupported
on Gitea/GHES).
### Task 2 — Extend .gitea/workflows/modules-lifecycle.yml + .github/workflows/modules-lifecycle.yml
- `8071d6a` — target only VPC resources in platform-vpc-apply/destroy
- `ad3cc5f` — separate short-lived CI VPC + fix 8 module lifecycle failures
- `a55752e` — read CI VPC outputs from S3 state instead of artifacts
Add an `l2-lifecycle` job (byte-identical in both forges) that matrix-runs
over `[static-assets, microservice]`. Each cell:
- Checkout + Python 3.12 + Terraform 1.9.* + AWS creds (from secrets).
- `run_platform.sh --apply modules/l2/<module>/examples/simple.yml`
- `run_platform.sh --apply modules/l2/<module>/examples/complex.yml` (same state key → modify)
- `run_platform.sh --destroy modules/l2/<module>/examples/complex.yml`
### Task 2 — Platform terraform fixes (commits 2f8c020, c4e94cf)
The `l2-lifecycle` job needs the CI VPC (for microservice which references
the platform VPC via `terraform_remote_state`). It runs after `ci-vpc-apply`
and before `ci-vpc-destroy`.
- `2f8c020` — quote `acdl:` tags + fix Lambda + replace interpolation
- `c4e94cf` — make Lambda conditional on zip existing
### Task 3 — Author modules/l2/static-assets/examples/complex.yml (modify variant)
### Task 3 — CI workflow fixes (commits 315a86d, 3597cf0, 75b56f5, 3ef3a82, 60f767d)
The complex example already exists and adds CloudFront + WAF. Verify it's a
valid modify variant (same state key, adds resources without forcing
replacement of the simple S3 bucket). If the bucket_name changes between
simple and complex, that forces replacement — fix to keep the same
bucket_name so terraform modifies in-place.
- `315a86d` — replace configure-aws-credentials with direct env vars
- `3597cf0` — install Terraform 1.9.* in test + check-only jobs
- `75b56f5` — recursive .terraform gitignore (covers all module + platform dirs)
- `3ef3a82` — skip terraform validate test when terraform binary not installed
- `60f767d` — 3 pipeline-readiness fixes (resolver id, schema inputs, CI creds)
### Task 4 — Author modules/l2/microservice/examples/complex.yml (modify variant)
### Task 4 — Module lifecycle fixes (commits ad3cc5f, 6795acc, fad6765, 88ea408)
The complex example already exists and adds ALB + env vars. The modify
variant should change `desired_count` (1→2) to demonstrate in-place modify.
Fix the complex example to keep the same `family` and `image` as simple
(so terraform modifies rather than replaces the ECS task definition).
8 module-specific fixes in `ad3cc5f`:
- **s3** — unique bucket names (acdl-ci-s3a-simple/complex) instead of
globally-taken 'my-simple-bucket'
- **kms-key** — alias name with no spaces (locals.tf → alias/acdl-ci-kms)
- **iam-role** — example contract uses role_name (not name, which the
interface doesn't declare)
- **ecs-service** — example contract uses family (not name); VPC inputs
(cluster_arn, subnets, security_group) injected by CI VPC wrapper
- **uptime** — added subnets, security_group, cluster_arn to interface +
module; network_configuration is dynamic (only when subnets provided)
- **rds** — added subnet_ids input + db_subnet_group resource (conditional
on subnet_ids being non-empty)
- **alb** — removed hardcoded placeholder sg/subnet values from examples;
vpc_id + subnets + security_group injected by CI VPC wrapper
- **cloudfront** — removed invalid placeholder WAF ARN from complex example
### Task 5 — Extend schemas/modules-lifecycle-pipeline.schema.json
Follow-up fixes:
- `6795acc` — ALB: `create_before_destroy` on target group + `depends_on`
on listener (fixes ResourceInUse when port changes simple 80 → complex 443)
- `fad6765` — kms-key deletion window 90→30 (AWS max); rds password policy
(added password var, default 'ACdlcI2026!')
- `88ea408` — uptime: add default for container_image variable (interface
declares default 'louislam/uptime-kuma:1' but module had no default)
Add `l2_modules` to the matrix properties. Add L2 stages to the stages array.
### Task 6 — Extend tests/test_pipeline_contract.py
Add a TestL2LifecyclePipeline class:
- L2 matrix lists both modules.
- L2 lifecycle job has apply/modify/destroy steps.
- L2 job depends on ci-vpc-apply.
- Pipeline contract validates against the extended schema.
- Gitea + GitHub workflows remain byte-identical.
### Task 7 — Extend pipelines/README.md
Add L2 lifecycle stages to the pipeline description.
## Success Criteria (phase gate)
1. Full L1 lifecycle matrix green: 12 modules × 2 examples = 24 cells, each
apply→modify→destroy exit 0. **Evidence = green PR check on
`modules-lifecycle.yml`** (PR opened from `milestone/v1.11-restart`
`main` to trigger the `pull_request` event; or `workflow_dispatch`).
2. No live resources remain after the run (`ci-vpc-destroy` always runs,
`lifecycle` destroy step per module).
3. `primitives-plan.yml` (plan-only) still passes.
4. Full offline pytest suite green (479+ tests).
## Verification approach
- **Offline (this session)**: pytest + 24 example contracts `--check-only`.
- **Live-AWS (CI)**: open PR `milestone/v1.11-restart``main` via `tea`;
wait for `acdl-modules-lifecycle` workflow green across all 14 jobs
(1 ci-vpc-apply + 12 lifecycle + 1 ci-vpc-destroy). Read workflow run
status via `tea` or the Gitea API. Green = P60 verify evidence.
1. `pipelines/modules-lifecycle.yml` validates against its schema.
2. `.gitea/workflows/modules-lifecycle.yml` and `.github/workflows/modules-lifecycle.yml` are byte-identical.
3. L2 matrix lists both `static-assets` and `microservice`.
4. L2 lifecycle job has apply→modify→destroy steps.
5. `test_pipeline_contract.py` passes (offline).
6. Composition resolution is deterministic (same contract → same stack).
7. Full offline pytest suite green.