# Nova — Roadmap

## Overview

- **v1.0 (demo):** complete — tag `v1.1.0`, 2026-07-21. All 5 phases shipped + audited PASS.
- **v1.1 (complete):** architecture finalization + v1 spike. 5 phases (06–10). Tag `v1.2.0`, 2026-07-21. All 5 phases shipped + verified; review READY TO SHIP (0 P0); audit CLEAN. Gitea release id 202.
- **v1.2 (complete):** platform hardening + first real consumer deployment. 6 phases (11–16). Tag `v1.3.0`, 2026-07-21. All 6 phases shipped + verified; review READY TO SHIP (1 P0 operator action, 1 P1 deferred); audit CLEAN.
- **v1.3 (complete):** module documentation + thin-composition removal. The L2 composition layer is removed; module READMEs are built out. Tag `v1.3.2`.
- **v1.4 (complete):** central pipeline contract + shell reproducibility + output streaming. A declarative pipeline contract (`schemas/pipeline.schema.json` + `pipelines/ci.yaml`) binds the Gitea and GitHub workflows to a single source of truth. `scripts/run_ci.sh` mirrors the CI pipeline locally. `scripts/run_platform.sh` streams terraform/checkov output by default.
- **v1.5 (complete, tag `v1.5.0`):** consumer happy path + zero-trust docs + reusable deploy workflow. README rewritten so the consumer model is unambiguous (consumer owns only contract + app code; the rest is the platform source). Platform-flow + consumer-guide diagrams converted to mermaid. Legacy surface + implementation nomenclature removed from docs. Credentials section rewritten for zero-trust OIDC + ABAC (with a static-key override + daily rotation). A generic `docs/CONSUMER_GUIDE.md` (all L2 modules, versioned `uses:`, consumer-scoped prereqs, run-time platform fetch) replaces the module-specific guide. A byte-identical reusable `deploy.yml` workflow (Gitea + GitHub) implements `pipelines/deploy.yaml` and is invoked by consumer repos via a versioned tag.
- **v1.6 (complete, tag `v1.6.0`):** consumer-facing docs restructure + terminology normalization + environments concept. `docs/` becomes a Jekyll-style GitHub Pages site. `acdl_platform/` is renamed to `core/`. L2 → "modules", L1 → "primitives", "composition" → "pattern" in prose. README restructured: Features + Roadmap (no internal status), repository roles restated (consumer = app code + contracts + CI definitions), mermaid fixed (visible text, security-checks + infrastructure-apply stages, no tool names), credentials section minus go-gitea/waivers. Platform-managed environments concept + a minimal onboarding scaffold. `.ciagent/` + `.gitea/` references removed from all consumer-facing docs.
- **v1.7 (complete, tag `v1.7.0`):** production platform + contract ingestion + pipeline maturation. Rename `static-assets` → `static-assets` (D-048 — incl. `.ciagent/` historical narrative). Author `cloudfront` + `waf` primitives; augment `static-assets` to a production-ready S3 + CloudFront (OAC) + WAF stack (D-049). Tagging-standard enforcement (Checkov custom rule, D-043 closure, D-054). Wiz adapter stub (D-052) + Kyverno K8s-native adapter (D-053). Platform Lambda + DynamoDB `acdl-contracts` table for contract ingestion (D-051) + cross-account IAM. Deploy outputs via SSM SecureString + GitHub PR comment (D-050). Uniform error reporting via the Lambda `report_error` action → GitHub issue on the platform repo (D-055); Gitea excluded. Stage comments after every successful pipeline stage. Three platform pipelines (platform-test unit+integration, primitives-plan, patterns-plan). Release job with semver + MAJOR.MINOR/MAJOR tag maintenance (D-057). `uses:`/`ref:` bumped to `@v1.6`; floating `v1.6` + `v1` tags created in Phase 22. Remove the legacy consumer-repos directory (a v1.2 artifact, removed in v1.7); add validated per-module examples (`modules/<name>/examples/`, D-058) including a new RDS primitive demonstrating multi-engine variation (D-059).
- **v1.8 (complete, tag `v1.8.0`):** P1 remediation + uptime monitoring + engineering standards + encryption/deletion-protection by default + decommission alias + path documentation. Clears 8 pending P1 issues (P1-3..P1-9 + S1). Adds per-stack CMK + encryption-by-default for all primitives. Adds deletion-protection-by-default + L2 feature flag. Adds uptime-kuma primitive (ECS Fargate, deployed by default after L2, separate state, feature flag, alert channels). Adds decommission mode (2-step pipeline with HITL SRE gates + CMDB-validated change request). Adds `modules/STANDARDS.md` (L1+L2 authoring + review standards). Adds `schemas/README.md`, `pipelines/README.md`, `adapters/README.md`.
- **v1.9.1 (complete, tag `v1.9.1`):** leadership presentation decks. Two leadership-facing presentation decks (How the Platform Works + The Developer Experience) for senior leadership (CTO, Head of Cloud, Head of Infrastructure, Head of DevOps). Each deck has a full markdown source of truth (with speaker notes + mermaid diagrams) and a lean Marp deck (no speaker notes, embedded PNG diagrams). A README documents the 3-step slide creation process (full markdown → Marp synthesis → PPTX export). Docs-only NFR patch.
- **v1.9.2 (complete, tag `v1.9.2`):** S&P Global Energy theme for presentation decks. Applies the S&P Global Energy brand visual identity (red-core #D6002A, grey-90 #1B1B1B, Akkurat Pro font) to both Marp decks. Title headers changed to full platform name. Footer 'Confidential' → 'Internal'. Title slide subtitle removed. Last DX slide renamed to 'The Desired Outcomes'. Docs-only NFR patch.
- **v1.9.3 (complete, tag `v1.9.3`):** rendered presentation decks. HTML renderings of both Marp decks committed to docs/presentations/ (self-contained, base64-embedded images, S&P Global Energy theme). PPTX files uploaded to the Gitea release as downloadable attachments. README updated to document HTML as committed artifacts and PPTX as release attachments. Docs-only NFR patch.
- **v1.9.4 (complete, tag `v1.9.4`):** presentation slide updates + complete removal of a specific compliance framework from all docs. Title slide redesigned (deck title as H1, 'Agentic Cloud Delivery Platform' as subtitle). DX deck: removed Local Reproducibility slide, redesigned Safe Promotion Path with side-by-side layout, 'an agent' → 'an AI agent', What a Developer Does diagram floated right. All references to that framework removed from 25 files (presentations, module READMEs, docs). Compliance lists now: GDPR, SOX, SOC2, DORA. HTML re-rendered. PPTX uploaded to release. Docs-only NFR patch.
- **v1.9.5 (complete, tag `v1.9.5`):** vision gaps + Testing badge + engine terminology + agentic tags + CR format. 9 requirements: (1) DX closing slide strengthened with 'infrastructure as a utility' vision bullet; (2) 'moving' → 'promoting'; (3) added red tape + scalability bullets to Problem slide; (4) Roadmap slide redesigned side-by-side; (5) new 'What This Platform Is — and Isn't' slide (PW deck 16 slides); (6) 'shipped'/'Available today' → 'Testing' (0 consumer adoption); (7) global 'substrate' → 'engine' (88 matches, 30+ files); (8) 'forge' → 'VCS' in presentation files only; (9) new Agentic badge (purple) on agentic features. CR format changed to CHG0678912. HTML re-rendered. PPTX uploaded to release. Docs-only NFR patch.
- **v1.9.6 (complete, tag `v1.9.6`):** consolidate both Marp decks to 10 high-impact slides. PW deck 16 → 10 (merged Problem+North Star+Anti-goals, merged Policy+Secure by Default, merged Audit+HITL, folded Observability/Environments/Portability into existing slides, added Vision Realized closing). DX deck 15 → 10 (merged What Dev Does+Contract+No Platform Code, merged Feedback+Deploy Outputs, merged Promotion+Rising Bar, cut Citizen Developer standalone, kept Versioned Releases/Onboarding/Decommission). Removed '5-line YAML' claim from both decks. Source markdown unchanged. Docs-only NFR patch.
- **v1.9.7 (complete, tag `v1.9.7`):** talking points files + 4-step process. Created two talking points markdown files (one per deck) distilling the source of truth into presenter-ready cues indexed by the Marp deck's 10-slide structure. Each file has 3-6 talking point bullets + key takeaway per slide. README updated from 3-step to 4-step process (added Step 4: talking points). Directory layout, checklist, and decks table updated. Docs-only NFR patch.
- **v1.9.8 (complete, tag `v1.9.8`):** full presentation rework — scope, story arc, visuals, appendix. 6 new mermaid diagrams (scope boundary x2, confidence signal, attestation flow, promotion journey, road to north star). Both decks restructured to 10 main + 6 appendix slides. NEW scope slide clarifying ACDL is infrastructure only. Story beat lines on every slide. Contract examples fixed (image: removed, infra inputs instead). QA attestation reclassified (Design tested → Planned). Confidence signal + attestation flow + promotion journey visuals added. Road to the North Star phased timeline in appendix. Full Testing vs. Planned inventory + glossary in appendix. Source markdown + talking points + README all updated. Docs-only NFR patch. **Last deck-polish phase before the v1.10 deck-freeze.**
- **v1.10 (complete, tag `v1.10.0`):** pipeline regression fix + capability re-verification + verified-reality rewrite. The v1.9.1–v1.9.8 deck work is **superseded-by-reverification**: the decks presented advertised capability as current without disclosing that the platform had decayed (7 adapter defects prevented `terraform init/validate/plan` against live AWS). v1.10 re-verified every advertised capability, fixed all 7 defects in-sweep (D-090: no cap), and rewrote PROJECT/ROADMAP/decks to match verified reality. Decks unfrozen only after Phase 55 lands. See the v1.10 section below for the 4-phase breakdown.
- **v1.10.1 (complete, tag `v1.10.1`):** post-v1.10 NFR patch — adversarial grill review (12 challenges, 10 binding decisions, 2 escalations: G-005 risks, G-008 budget), 4-layer verify gate (PASS), multi-persona code review (1 P1 auto-fixed: mis-citation PROJECT.md:6 → PROJECT.md:487). ACDL reclassified as OSS reference implementation (G-003). Docs-only; 518 tests pass; regression gate 16/16 Verified. Gitea release id 236.
- **v1.10.2 (complete, tag `v1.10.2`):** contract surface redesign + rename + .yml repo-wide + deck polish. Breaking contract schema change: new top-level fields `id`/`name`/`infrastructure`; dropped `uses:`/`module:`/`inputs:`. All 44 `.yaml` → `.yml`. Code review: 3 P0 auto-fixed, 2 P1+ flagged. 494 tests pass. Gitea release id 237.
- **v1.11 (complete, tag `v1.11.0`):** RESTART — stateless adapter + pipeline-driven module lifecycle testing. Closes G-005 (CAP-017..022 deploy-unverified) and G-008 (no cost docs) via a corrected architecture, not the failed v1.11 first attempt (which produced 4 drifted VPCs, ran terraform apply from Python, and had no module lifecycle tests). The restart branches off `v1.10.2` and rebuilds v1.11 on three corrections: (1) the terraform adapter becomes a stateless assembler — each L1 module ships a real `terraform/` module dir (variables/locals/main/outputs) owning its resource shape, nested blocks, and defaults; the adapter deletes `TYPE_MAP`/`INPUT_MAP`/`OUTPUT_MAP` and all 39 type-specific branches, becoming a ~80-line assembler that emits `module "x" { source = ... }` blocks; (2) lifecycle is owned by terraform via the shell orchestrator (`run_platform.sh --apply`/`--destroy`), never by Python — `verify_deploy_microservice.py` is deleted; (3) testing is pipeline-driven — a `modules-lifecycle` pipeline (Gitea + GitHub, byte-identical) matrix-runs each L1 module's `examples/{simple,complex}.yml` contracts through apply→modify→destroy against live AWS; no per-module Python. A single platform VPC (`terraform/platform`) is shared by all stacks via `data` source — no per-contract VPC. State keys are deterministic and env-aware (`spike/{id}/{env}/terraform.tfstate`), stable across lifecycle changes. 13 phases (P56a–P65). See the v1.11 section below for the phase breakdown.
- **v1.12 (complete, tag `v1.12.0`):** Presentation Refinement — the leadership decks synced to the v1.11-verified reality (22/22 Verified, stateless adapter, lifecycle pipeline, cost figures, pre-mortem). Includes the CAP-013 adapter dedup fix + 2 probe fixes (required to make the deck claims true) + the ACDL_LIFECYCLE_MODE CI flag (lifecycle tests default to plan-only, full on override). 6 phases (P66–P70). See the v1.12 section below.
- **v1.13 (complete, tag `v1.13.0`):** Presentation Polish — both leadership decks polished across all 4 pipeline layers (source .md → -marp.md → .html → -talking-points.md). Action headlines replace category names; story-arc restructure (Intro ~10% / Body ~80% / Conclusion ~10%); removed all transition story lines; bullets ≤12 words, 3–4 per main slide; larger fonts (body 26px, h1 40px, h2 32px); 6 new mermaid diagrams (frictions 2×2, north-star before/after, zero-trust flow, catalog primitives→modules, decommission gates, semver timeline). Code review: 0 P0, 2 P1 auto-fixed (slide-count metadata + README directory layout). 522 tests pass. Docs-only NFR patch. 1 phase (P71). See the v1.13 section below.
- **v1.13.1 (complete, tag `v1.13.1`):** config.json schema migration — regenerate `.ciagent/config.json` to the updated CIAgent v2 config structure (drop removed fields, migrate `gitea`→`release.gitea`, add `secrets`/`ship`/`backend`/`ideation`/`personas`/`logging`/`telemetry` sections). Code review: 0 P0, 2 P1/P2 auto-fixed. Docs-only NFR patch (no code changes). Gitea release id 253.
- **v1.13.2 (complete, tag `v1.13.2`):** presentation badge cleanup + platform architecture diagram — removed all `testing`/`agentic` maturity badges from both decks (only `planned` retained); added a new Slide 3 "The platform at a glance" with a shared high-level logical architecture diagram (consumer surfaces → contract → central pipeline → cross-cutting components → AWS) to both decks; renumbered subsequent slides 4–11; synced talking points + README. Docs-only NFR patch (no code changes).
- **v1.0 demo URL:** https://git.cloudinit.dev/continuous-intelligence/acdl-evidence/raw/branch/main/index.html
- **v1.23 (complete, tag `v1.22.6`):** Nova Deck Cleanup & Python PPTX — consolidated the deck to a single source-of-truth `*-marp.md` (deleted the plain `.md`; speaker notes + talking points embedded as Marp HTML comments); restored the clean S&P visual style (Marp `default` theme + inline `style:` block, matching the old `the-developer-experience.html`); retired `nova-sp-theme.css` from the render path (kept as reference); base64-inlined all images in the HTML for redistribution (`scripts/inline_images.py`); built a parallel structured editable S&P-themed PPTX generator (`scripts/render_pptx.py` via `python-pptx`); restyled benefit callouts (`<div class="benefit">`); targeted ~20-30% word-count trim on 8 verbose slides; removed the term "penetrate" repo-wide. 13 requirements (REQ-263..275), 6 phases. 43 tests pass.
- **v1.24 (active):** Consumer Guide Accuracy & Env-Promotion Lifecycle Enforcement — fixes 5 consumer-guide accuracy issues (stale contract-fields table, inconsistent caller examples, misleading "dev only" apply phrasing, Step 8 promotion contradicts the per-env section, stale `@v1.19` reference wording) and adds platform-enforced destroy-on-environment-change: when a consumer edits `environment:` on a stable `contract.id` (Shape A promotion), the platform detects the change via the `nova-contracts` DynamoDB table, destroys the prior env's Terraform state (`spike/{id}/{prior_env}/`) before building the new env, and fails closed if the destroy fails (no orphan path). The per-environment caller-workflow path (Shape B) remains supported. New `core/env_transition.py` module. 15 requirements (REQ-276..290), 4 phases. Feature milestone; tags on v1.23.x line.

---

## v1.0 (Prior — the demo, complete)

Five-phase breakdown that took ACDL from empty repo to a reproducible 4-act
executive demo. Milestone `v1.0-initial` covered the full demo build. Each
phase produced a runnable increment and ended with a phase-completion commit
+ tag. All phases complete; demo archived to `demo/` in v1.1 Phase 06.

## Phases

### Phase 01 — repo-scaffolding
- **Description:** Create the three repos under `continuous-intelligence` (`acdl-contracts`, `acdl-evidence`; `acdl` already exists), seed directory layouts, configure Pages on `acdl-evidence`, add environment protection for `qa` and `prod` on `acdl-contracts`.
- **Status:** complete (v1.0.1)
- **Depends on:** —
- **Requirements:** REQ-01, REQ-09, REQ-10
- **Success Criteria:**
  - `acdl-contracts` and `acdl-evidence` exist and are pushable.
  - `acdl-evidence` Pages returns 200 with placeholder `index.html`.
  - `qa` and `prod` environments exist on `acdl-contracts`.

### Phase 02 — l1-modules
- **Description:** Create all 8 L1 module folders under `acdl/modules/l1/`, each with `manifest.yaml` (declared inputs) and `mock_apply.sh` (uniform echo + 1s sleep + exit 0).
- **Status:** complete (v1.0.2)
- **Depends on:** [1]
- **Requirements:** REQ-02, REQ-03
- **Success Criteria:**
  - All 8 L1s present; `mock_apply.sh` runs and exits 0 for each.
  - `manifest.yaml` validates against the L1 schema.

### Phase 03 — l2-modules-and-core-scripts
- **Description:** Create the 4 L2 compositions under `acdl/modules/l2/` referencing L1s, plus the 5 core scripts in `acdl/scripts/` (`mock_executor.sh`, `policy_checker.py`, `confidence_signal.py`, `evidence_writer.py`, `l3b_agent_stub.py`).
- **Status:** complete (v1.0.3)
- **Depends on:** [2]
- **Requirements:** REQ-04, REQ-05, REQ-06, REQ-07
- **Success Criteria:**
  - `mock_executor.sh` applies each L1 in an L2 and writes `state.json`.
  - `policy_checker.py` fails on `public-ingress: true` with `POLICY_VIOLATION:PUBLIC_INGRESS`.
  - `confidence_signal.py` returns 0.90 (pass) / 0.40 (fail).
  - `evidence_writer.py` appends an event with a valid hash chain.
  - `l3b_agent_stub.py` maps the Act 3 example issue to `l2-commodity-price-feed`.

### Phase 04 — pipeline-and-approval-gates
- **Description:** Build the reusable pipeline workflow in `acdl/.gitea/workflows/` (Dev → QA → Prod → Finalize) plus the issue-triggered L3B workflow in `acdl-contracts/.gitea/workflows/`. Wire environment protection for QA and Prod.
- **Status:** complete (v1.0.4)
- **Depends on:** [3]
- **Requirements:** REQ-08, REQ-09, REQ-10, REQ-12
- **Success Criteria:**
  - Pushing a valid `contract.yaml` runs Dev automatically and pauses at QA.
  - Approving QA moves to Prod; approving Prod finalizes.
  - Opening an Issue with the Act 3 text generates a `contract.yaml` commit and triggers the pipeline.

### Phase 05 — evidence-ui-and-demo-dry-run
- **Description:** Build `index.html` (vanilla JS, fetches `audit.json`, renders timeline) and run all four acts end-to-end as a dry run.
- **Status:** complete (v1.0.5)
- **Depends on:** [4]
- **Requirements:** REQ-11, REQ-13, REQ-14, REQ-15
- **Success Criteria:**
  - Pages timeline renders events from `audit.json`.
  - Act 2: valid contract passes through all gates; timeline shows the full flow.
  - Act 3: Issue text produces the expected `l2-commodity-price-feed` contract and triggers the pipeline.
  - Act 4: malicious `public-ingress: true` contract halts in Dev with confidence < 0.50 and a visible rejection reason on the timeline.

---

## v1.1 (Complete — architecture finalization + v1 spike, 2026-07-21, tag `v1.2.0`)

Five-phase breakdown to finalize the architecture to v1.0 and prove the
locked commitments with one end-to-end implementation spike. Milestone
`v1.1-spike` covered the real platform's first materialization. Ship tag
at milestone COMPLETE: **`v1.2.0`** (feature milestone, next minor per
ship.md). **Status: COMPLETE — all 5 phases shipped (v1.1.1..v1.1.5) +
verified; review READY TO SHIP (0 P0); audit CLEAN; Gitea release id 202.
D-034 closed (root key deactivated by user).**

### Phase 06 — archive-demo-and-reorient
- **Description:** Move the v1.0 demo (`modules/`, `scripts/`, `evidence-ui/`, `contracts/`, demo `.gitea/workflows/`) to `demo/`. Establish the new repo layout (`platform/`, `schemas/`, `adapters/`, `terraform/`, `modules-ir/`). Rewrite README to reflect the real platform. Verify the demo still runs from `demo/` (regression check).
- **Status:** complete (v1.1.1)
- **Depends on:** —
- **Requirements:** (no new REQ; repo hygiene)
- **Success Criteria:**
  - `demo/` contains the full v1.0 demo; `demo/scripts/run_demo.sh --no-upload` still exits 0.
  - New top-level dirs exist and are empty-but-scaffolded: `platform/`, `schemas/`, `adapters/`, `terraform/`, `modules-ir/`.
  - README reflects the real platform (vision + architecture links, new layout).

### Phase 07 — architecture-v1-finalization
- **Description:** Resolve the 11 open decisions in `docs/architecture.md` §13 (already recorded in `PROJECT.md`). Author the locked schemas + designs: `schemas/ir.schema.json` (REQ-17), `schemas/policy_check_result.schema.json` (REQ-18), `schemas/contract.schema.json` (REQ-22), `platform/confidence_signal.py` spec (REQ-19), `platform/audit_ledger_design.md` (REQ-20), `platform/hitl_matrix_design.md` (REQ-21). Mark architecture v1.0.
- **Status:** complete (v1.1.2)
- **Depends on:** [06]
- **Requirements:** REQ-16, REQ-17, REQ-18, REQ-19, REQ-20, REQ-21, REQ-22
- **Success Criteria:**
  - All 11 open decisions resolved and recorded in `PROJECT.md`.
  - All 6 schema/design files exist and validate (`ajv` / `python -m jsonschema`).
  - `docs/architecture.md` status note updated to v1.0 (or a `docs/architecture-v1.0.md` snapshot).

### Phase 08 — aws-oidc-bootstrap
- **Description:** **Re-scoped per RESEARCH TARGET 1 + D-039.** Gitea Actions does not support `id-token: write` (conf 0.95), so real OIDC is deferred to v1.2. This phase instead: uses the temporary long-lived key (waiver D-034) once to create an S3 state bucket, a DynamoDB lock/outbox table, and an IAM user with a minimal scoped policy (S3 + DynamoDB + plan-only); stores the key as a Gitea Actions secret; implements `scripts/rotate_spike_key.sh` to rotate the key after each spike run. Real OIDC federation is tracked via go-gitea/gitea#36988 for v1.2.
- **Status:** complete (v1.1.3)
- **Depends on:** [07]
- **Requirements:** REQ-23 (re-interpreted: AWS auth bootstrap + state backend; OIDC deferred to v1.2 per D-039)
- **Success Criteria:**
  - S3 state bucket + DynamoDB lock/outbox table exist.
  - An IAM user with a minimal scoped policy exists; its access key is stored as a Gitea Actions secret.
  - `scripts/rotate_spike_key.sh` rotates the key (deactivates old, creates new, updates the secret) and is idempotent.
  - A workflow step authenticates to AWS with the rotated secret and runs `aws sts get-caller-identity` successfully.
  - D-034 is closed: the bootstrap long-lived key is rotated/deactivated (logged in `PROJECT.md`).

### Phase 09 — v1-spike-ir-and-l1-and-adapter
- **Description:** Implement the Target Stack IR, one real L1 `l1-s3` (IR-typed interface, registered), and the Terraform adapter that compiles the IR → Terraform `variable`/`output` + root module and emits a real `terraform plan` against AWS (via the rotated-key secret per D-039; OIDC is v1.2). State in S3 + DynamoDB.
- **Status:** complete (v1.1.4)
- **Depends on:** [08]
- **Requirements:** REQ-24, REQ-26
- **Success Criteria:**
  - `schemas/ir.schema.json` is satisfied by `modules-ir/l1/l1-s3/` interface.
  - The Terraform adapter translates `l1-s3` to a valid `terraform plan` (real AWS).
  - `terraform validate` + `terraform plan` succeed; no long-lived credential in the workflow.

### Phase 10 — v1-spike-l2-and-contract-e2e
- **Description:** Implement `l2-static-assets` (thin-composition referencing `l1-s3`), the contract schema + contract→IR resolution, and one end-to-end contract submission (`contracts/spike.yaml` for `l2-static-assets`) flowing through schema validation → IR resolution → `terraform plan` → Checkov `PolicyCheckResult` → confidence signal → evidence event to the DynamoDB outbox. Verify the IR commitments hold (no polyglot mess).
- **Status:** complete (v1.1.5)
- **Depends on:** [09]
- **Requirements:** REQ-25, REQ-27, REQ-28
- **Success Criteria:**
  - `l2-static-assets` references `l1-s3` only (depth 1).
  - One contract submission completes the full pipeline end-to-end.
  - `scripts/verify_phase10.sh` proves the adapter is the only engine-specific code.
  - Evidence event is written to the DynamoDB outbox.

After Phase 10: COMPLETE gate — review → ship `v1.2.0` → audit. **DONE.**

---

## v1.2 (Complete — platform hardening + first real consumer deployment, 2026-07-21, tag `v1.3.0`)

Six-phase breakdown to harden the v1.1 spike, simplify the setup, update
the docs, and prove the platform delivers real value by deploying a basic
microservice to AWS ECS Fargate end-to-end. Ship tag at milestone COMPLETE:
**`v1.3.0`** (feature milestone, next minor per ship.md — v1.1 shipped
`v1.2.0`). Phase patches `v1.2.1`..`v1.2.6`. **Status: COMPLETE — all 6
phases shipped (v1.2.1..v1.2.6) + verified; review READY TO SHIP (1 P0
operator action, 1 P1 deferred to v1.3); audit CLEAN. The terraform apply
is blocked by the live IAM policy (P0-IAM, operator action); the platform
flow is verified end-to-end up to terraform plan (13 to add).**

### Phase 11 — v1.2-research-and-readme
- **Description:** Re-evaluate go-gitea/gitea#36988 (OIDC for Gitea Actions) — confirm still open (re-checked 2026-07-21: open, last updated 2026-05-27, not merged) and record the decision to extend D-039 as D-047. Audit the v1.1 spike for NFR gaps (least-privilege IAM, idempotency, error handling, rotation hygiene) and simplification opportunities (script consolidation, dead code, stale paths). Rewrite `README.md` to reflect v1.1 complete + the actual spike flow + how to run + the real repo layout + the v1.2 objective.
- **Status:** complete (v1.2.1)
- **Depends on:** —
- **Requirements:** REQ-29
- **Success Criteria:**
  - `RESEARCH.md` has a v1.2 addendum with the #36988 re-check + NFR audit + simplification findings.
  - `README.md` reflects v1.1 complete; documents the spike flow, `scripts/run_platform.sh`, the repo layout, and the v1.2 objective; no stale "v1.1 (active)" framing.
  - D-047 is recorded in `PROJECT.md`.

### Phase 12 — nfr-harden-and-simplify
- **Description:** Apply Phase 11's findings. Tighten `terraform/bootstrap/spike_runner_policy.json` to least-privilege (add ECS + ECR + ELB + IAM plan-only permissions for v1.2; audit for wildcards). Make `create_state_backend.py` and `create_iam_user.py` idempotent. Consolidate `run_spike_plan.sh` + `run_spike_e2e.sh` into a single `scripts/run_platform.sh` with proper exit codes and error handling. Redact P1-1 (the two AWS access key IDs in `.ciagent/VERIFY.md` Phase 09 narrative). Fix any remaining stale `platform/` paths in `.ciagent/`. The v1.1 spike still runs e2e after the refactor.
- **Status:** complete (v1.2.2)
- **Depends on:** [11]
- **Requirements:** REQ-30
- **Success Criteria:**
  - `scripts/run_platform.sh` runs the full v1.1 spike e2e and exits 0.
  - `create_state_backend.py` / `create_iam_user.py` re-runs are idempotent (no duplicate resources; exit 0).
  - `spike_runner_policy.json` passes a least-privilege audit (no `*` actions beyond documented exceptions).
  - `.ciagent/VERIFY.md` Phase 09 narrative has no live AWS access key IDs.
  - No stale `platform/` paths remain in `.ciagent/`.

### Phase 13 — l1-catalog-for-ecs
- **Description:** Author six IR-typed L1 modules for an ECS Fargate microservice: `l1-vpc` (VPC + subnets + route tables), `l1-ecs-cluster` (ECS Fargate cluster), `l1-ecs-service` (ECS service + task definition), `l1-iam-role` (task execution + task role), `l1-alb` (ALB + listener + target group), `l1-ecr` (ECR repository). Each has an `interface.json` valid against `schemas/ir.schema.json`. Register all six in `modules-ir/registry.json`. Expand the Terraform adapter `TYPE_MAP` to cover the new IR resource types. Each L1 produces a valid `terraform plan` fragment.
- **Status:** complete (v1.2.3)
- **Depends on:** [12]
- **Requirements:** REQ-31
- **Success Criteria:**
  - All six L1s exist under `modules-ir/l1/` with `interface.json` valid against `schemas/ir.schema.json`.
  - `modules-ir/registry.json` lists all six.
  - The adapter `TYPE_MAP` covers all six IR resource types.
  - Each L1 produces a valid `terraform plan` fragment.

### Phase 14 — l2-microservice-and-contract-schema
- **Description:** Author `l2-microservice` thin-composition under `modules-ir/l2/l2-microservice/` referencing the six ECS L1s (depth ≤ 5). Extend `schemas/contract.schema.json` with microservice inputs (`image: string`, `port: integer`, `env: map`, `healthcheck: object`). Verify contract→IR resolution yields a complete target stack.
- **Status:** complete (v1.2.4)
- **Depends on:** [13]
- **Requirements:** REQ-32
- **Success Criteria:**
  - `l2-microservice` references the six ECS L1s only (depth ≤ 5).
  - `schemas/contract.schema.json` validates a `contracts/microservice.yaml` with the new inputs.
  - Contract→IR resolution yields a complete target stack (all six L1 instances + relationships).

### Phase 15 — consumer-repo-and-terraform-apply
- **Description:** Create a new Gitea repo `acdl-consumer-microservice` under the `continuous-intelligence` org containing a basic HTTP microservice (tiny Python/Go server returning 200), a `Dockerfile`, an ECR push step, and a `contracts/microservice.yaml` submission for `l2-microservice` (dev environment). Lift the platform from `plan` to **`apply`** for the `dev` environment (autonomous per §10, confidence ≥ 0.50, no HITL). Submit the contract → pipeline → IR → plan → apply → a real ECS Fargate service running.
- **Status:** complete (v1.2.5, PARTIAL — terraform apply blocked by IAM P0)
- **Depends on:** [14]
- **Requirements:** REQ-33 (partial), REQ-34
- **Success Criteria:**
  - `acdl-consumer-microservice` repo exists under `continuous-intelligence`.
  - The microservice builds into a Docker image and is pushed to ECR.
  - `terraform apply` (dev) creates real AWS resources (VPC, ECS cluster, ECR repo, ALB, ECS service).
  - The apply result is captured in the evidence stream.

### Phase 16 — v1.2-capstone-e2e
- **Description:** End-to-end verification: consumer commit to `acdl-consumer-microservice` triggers the pipeline → contract→IR resolution → `terraform plan` → `terraform apply` (dev) → a live ECS Fargate service serving HTTP 200 on its ALB → evidence event written to the DynamoDB outbox → the event renders on the `acdl-evidence` timeline. Verify the NFR improvements from Phase 12 hold, the setup is simpler (one `scripts/run_platform.sh`), and the README is accurate. `scripts/verify_phase16.sh` proves the full flow green.
- **Status:** complete (v1.2.6, capstone — terraform apply blocked by IAM P0, verified up to plan)
- **Depends on:** [15]
- **Requirements:** REQ-35 (partial — IAM-blocked)
- **Success Criteria:**
  - One consumer commit produces a live ECS service serving HTTP 200.
  - An evidence event for the apply is in the DynamoDB outbox and renders on the timeline.
  - `scripts/verify_phase16.sh` exits 0.
  - README accurately documents the v1.2 platform flow.

After Phase 16: COMPLETE gate — review → ship `v1.3.0` → audit.

---

## v1.3 (Complete — module documentation + thin-composition removal)

The v1.3 milestone starts with simplification: removing the unsatisfactory
thin-composition layer and building out proper module documentation. The
L2 composition mechanism will be redesigned in a later phase.

### Phase 17 — remove-thin-composition-and-module-readmes
- **Description:** Remove the L2 thin-composition layer completely (composition.json files, contract_resolver.py, contract schema, sample contracts) and build out proper module READMEs. Create a README template for both L1 and L2 modules, rewrite all 7 L1 module READMEs in plain language (no jargon, with Resources/Inputs/Outputs/Usage/Compliance-extension-points/Versioning sections), write 2 L2 placeholder READMEs noting the composition is under redesign, create a catalog index, and patch run_platform.sh to load a pre-existing IR instance instead of resolving a contract. Prune L2 entries from the registry.
- **Status:** complete (v1.3.1)
- **Depends on:** —
- **Requirements:** REQ-36, REQ-37, REQ-38
- **Success Criteria:**
  - The thin-composition layer is fully removed (composition.json, contract_resolver.py, contract schema, contracts/).
  - run_platform.sh loads a pre-existing IR instance; the downstream adapter/checkov/confidence/outbox pipeline still works.
  - A README-TEMPLATE.md exists for both L1 and L2 modules.
  - Every L1 module has a README.md with Resources/Inputs/Outputs/Usage/Compliance-extension-points/Versioning.
  - Every L2 module has a placeholder README.md noting the composition is under redesign.
  - A modules-ir/README.md catalog index exists.

### Phase 18 — testing-and-cicd-pipelines
- **Description:** Create a pytest test suite that reproduces the platform pipeline offline (adapter, confidence_signal, checkov_adapter, outbox_writer). Add an offline `--check-only` mode to `run_platform.sh` that runs the pipeline up to adapter emission without AWS/Checkov/outbox. Create identical CI/CD pipelines for both Gitea Actions (`.gitea/workflows/ci.yml`, dev environment) and GitHub Actions (`.github/workflows/ci.yml`, production) that run: lint, pytest, `run_platform.sh --check-only`. Add `pyproject.toml` + `requirements-test.txt` for dependency pinning.
- **Status:** complete (v1.3.2)
- **Depends on:** [17]
- **Requirements:** REQ-39, REQ-40, REQ-41, REQ-42
- **Success Criteria:**
  - `pytest` runs and passes offline (no AWS, no Checkov, no DynamoDB).
  - `run_platform.sh --check-only` runs offline and exits 0.
  - `.gitea/workflows/ci.yml` and `.github/workflows/ci.yml` exist with identical job stages (lint, test, check-only).
  - `pyproject.toml` + `requirements-test.txt` pin test dependencies.

After Phase 18: COMPLETE gate — review → ship `v1.3.2` → audit.

---

## v1.4 (Active — central pipeline contract + shell reproducibility + streaming)

The v1.4 milestone makes the CI/CD pipeline a declarative contract rather
than duplicated workflow copies, enables full shell reproducibility of the
CI pipeline, and streams terraform/checkov output so users can see what
the platform is doing.

### Phase 19 — central-pipeline-contract-and-shell-reproducibility
- **Description:** Create a central pipeline contract (`schemas/pipeline.schema.json` JSON Schema + `pipelines/ci.yaml` YAML instance) that both `.gitea/workflows/ci.yml` (Gitea Actions, dev) and `.github/workflows/ci.yml` (GitHub Actions, production) implement. Create `scripts/run_ci.sh` that mirrors the CI pipeline locally (lint → test → check-only). Update `scripts/run_platform.sh` to stream terraform init/validate/plan output, Checkov compliance results, and PolicyCheckResult records to stdout by default (with `--quiet` for log-only mode). Add `tests/test_pipeline_contract.py` validating the contract schema, workflow conformance, and run_ci.sh. Update both workflow YAMLs with contract reference headers (staying byte-identical).
- **Status:** complete (v1.4.1)
- **Depends on:** [18]
- **Requirements:** REQ-43, REQ-44, REQ-45
- **Success Criteria:**
  - `pipelines/ci.yaml` validates against `schemas/pipeline.schema.json`.
  - Both `.gitea/workflows/ci.yml` and `.github/workflows/ci.yml` are byte-identical.
  - A test parses both workflows and asserts their stages/commands match the contract.
  - `scripts/run_ci.sh` exits 0 and outputs "CI PIPELINE OK".
  - `scripts/run_platform.sh --check-only` streams the emitted Terraform to stdout.
  - `scripts/run_platform.sh --check-only --quiet` suppresses the Terraform stream.
  - `pytest` total count increases from 90 to 122 (32 new contract/streaming tests).

After Phase 19: COMPLETE gate — review → ship `v1.4.1` → audit.

---

## v1.5 (Complete — consumer happy path + zero-trust docs + reusable deploy workflow, tag `v1.5.0`)

The v1.5 milestone makes the consumer happy path self-evident, documents the
zero-trust credential model, and provides a reusable deploy workflow so
consumer repos never need to clone the platform repo or invoke its scripts
locally.

### Phase 20 — consumer-happy-path-and-reusable-deploy-workflow
- **Description:** Rewrite `README.md` so the consumer model is unambiguous (this repo is the platform source; a consumer owns only `contract.yaml` + app code). Convert the platform-flow diagram to a mermaid `flowchart TD`. Remove "L3A"/"L3B" + "spike" nomenclature from README prose. Rewrite the Credentials section for zero-trust OIDC + ABAC (with a static-key override + daily rotation; consumer rotates out of band when using `.env.secrets` locally). Replace `docs/consumer-guide-static-assets.md` with a generic `docs/CONSUMER_GUIDE.md` (all L2 modules, mermaid diagrams, versioned `uses:` floating MAJOR+MINOR, consumer-scoped prerequisites, run-time platform fetch via a reusable workflow). Create byte-identical `.gitea/workflows/deploy.yml` + `.github/workflows/deploy.yml` implementing `pipelines/deploy.yaml` — a reusable workflow invoked by consumer repos via `uses: acdl/.gitea/workflows/deploy.yml@v1.4` that checks out the consumer repo + the ACDL platform repo and runs `scripts/run_platform.sh`. Update `contracts/static-assets.yaml` to `uses: acdl/pipelines/deploy.yaml@v1.4`. Extend `tests/test_pipeline_contract.py` to validate the new deploy workflows (byte-identical, schema-conformant).
- **Status:** complete (v1.5.0)
- **Depends on:** [19]
- **Requirements:** REQ-46, REQ-47, REQ-48, REQ-49, REQ-50, REQ-51
- **Success Criteria:**
  - `README.md` states the platform-source vs consumer-repo distinction up front; platform flow is a mermaid `flowchart TD`; `grep L3B README.md` returns 0 hits; `grep -i spike README.md` returns 0 prose hits (code paths in bash blocks allowed).
  - `docs/CONSUMER_GUIDE.md` exists; `docs/consumer-guide-static-assets.md` is deleted; `grep -R consumer-guide-static-assets` returns 0 dangling references; guide is generic (static-assets is the worked example, not the scope); diagrams are mermaid; `uses:` references use `@v1.4`.
  - `README.md` Credentials section describes OIDC + ABAC zero-trust as the default and the static-key override + daily rotation + consumer out-of-band rotation duty for local `.env.secrets`.
  - `.gitea/workflows/deploy.yml` and `.github/workflows/deploy.yml` exist, are byte-identical, conform to `schemas/deploy-pipeline.schema.json`, and are reusable (`on: workflow_call` with a `contract` input).
  - `contracts/static-assets.yaml` uses `uses: acdl/pipelines/deploy.yaml@v1.4`.
  - `tests/test_pipeline_contract.py` validates the deploy workflows (exist, byte-identical, schema-conformant); the extended test suite passes; `bash scripts/run_ci.sh` exits 0.

After Phase 20: COMPLETE gate — review → ship `v1.5.0` → audit.

---

## v1.6 (Active — consumer-facing docs restructure + terminology normalization + environments concept)

The v1.6 milestone restructures the consumer-facing documentation into a real
GitHub Pages site, normalizes the terminology (L2 → "modules", L1 →
"primitives", "composition" → "pattern", "forge" → "platform runners"), renames
`acdl_platform/` to `core/` (platform/ shadows stdlib), rewrites the README (Features + Roadmap,
restated repository roles, fixed mermaid, cleaned credentials section), removes
all `.ciagent/` + `.gitea/` references from consumer surfaces, and introduces
the concept of platform-managed environments with a minimal first-run onboarding
scaffold.

### Phase 21 — docs-restructure-and-terminology-normalization
- **Description:** Rename `acdl_platform/` → `core/` (directory + all code/test/script/pipeline/workflow references; tests green — `platform/` was the original target but shadows Python's stdlib `platform` module, so `core/` was chosen). Restructure `docs/` into a Jekyll-style GitHub Pages site (`_config.yml`, `index.md`, `modules/`, `contracts/`, `pipeline/`, `environments/`, `consumer-guide.md`, consolidated `architecture.md`, `vision.md`). Rewrite `README.md`: remove `.ciagent/` + `.gitea/workflows/` rows; restate consumer repo model (app code + 1+ contracts + CI definitions `uses:`-ing the central workflow); replace Status with Features + Roadmap (planned only); fix the mermaid (visible text, add security-checks stage before policy, no tool names, add infrastructure-apply stage); remove the environments table; clean the credentials section (no go-gitea/waivers, keep daily/out-of-band rotation); forge → platform runners/platform-managed. Update `docs/consumer-guide.md`: drop L2 (→ modules), composition → pattern (prose), remove `.gitea/` (GitHub only), forge → platform runners, mermaid updated. Update `modules/` READMEs: L1 → primitives, L2 → modules, composition → pattern (prose only, files kept); bump stale `@v1` → `@v1.4`. Consolidate `docs/architecture.md` + `docs/architecture-v1.0.md` into a single current-architecture `docs/architecture.md`. Add `docs/environments/index.md` (platform-managed AWS account/network/state/runner; consumer provides none). Add a minimal onboarding scaffold: `core/environments/` dir + sample `dev.json` + README, `core/environment_check.py`, wire-in at the top of `scripts/run_platform.sh`, friendly onboarding message when no environment is defined, `tests/test_environment_check.py`. Add a roadmap entry: "composition" will later describe the thin orchestration where consumers dynamically create a module directly from the contract file (future implementation, not this phase).
- **Status:** complete (v1.6.0)
- **Depends on:** [20]
- **Requirements:** REQ-52, REQ-53, REQ-54, REQ-55, REQ-56, REQ-57, REQ-58, REQ-59, REQ-60, REQ-61
- **Success Criteria:**
  - `grep -R "\.ciagent" docs/ README.md` returns 0 hits; `grep -R "\.gitea" docs/ README.md modules/ contracts/` returns 0 hits.
  - `grep -R "acdl_platform" .` (excluding `.ciagent/`, `demo/`, `.git/`) returns 0 hits; the test suite passes after the rename.
  - `docs/` has the Jekyll structure (`_config.yml`, `index.md`, `modules/`, `contracts/`, `pipeline/`, `environments/`); no `.ciagent/` links in `docs/`.
  - Consumer-facing docs have no "L2"/"L1" labels (modules/primitives) and no "forge" term; "composition" → "pattern" in prose.
  - README.md has Features + Roadmap (no version changelog); repository roles restated; mermaid visible + security-checks + infrastructure-apply stages + no tool names; no environments table; credentials section has no go-gitea/waivers.
  - `docs/environments/index.md` exists; `core/environments/` + `dev.json` + `environment_check.py` + `run_platform.sh` wire-in + `tests/test_environment_check.py` exist and pass.
  - `bash scripts/run_ci.sh` exits 0; `python3 -m pytest tests/ -v` passes (154 + new environment-check tests).

After Phase 21: COMPLETE gate — review → ship `v1.6.0` → audit. **DONE.**

---

## v1.7 (Complete — production platform + contract ingestion + pipeline maturation, tag `v1.7.0`)

The v1.7 milestone takes the platform from a documented, environments-aware
foundation to a production-grade platform with a production-ready
`static-assets` stack (CloudFront + WAF), a contract-ingestion Lambda + DynamoDB
store for historical/impact analysis, a uniform error-reporting pathway via the
same Lambda, DX-friendly deploy outputs (SSM + PR comments), three dedicated
platform pipelines (unit+integration, primitives plan, patterns plan), a
release job with MAJOR.MINOR/MAJOR tag maintenance, new security adapters
(Wiz, Kyverno), real tagging-standard enforcement (closing D-043), removal of
the legacy consumer-repos directory (removed in v1.7), and validated per-module examples
(including a new RDS primitive demonstrating multi-engine variation).

The `uses:`/`ref:` tag advances from `@v1.4` to `@v1.6`; the floating `v1.6` +
`v1` tags are created in Phase 22 (pointing at the v1.6.0 release) so the
reference is never broken, and the release job (Phase 26) owns ongoing updates.

### Phase 22 — rename-and-production-static-assets-stack
- **Description:** Rename `static-assets` → `static-assets` everywhere (D-048 — including `.ciagent/` historical narrative, overriding the v1.6 preservation precedent). Author two new primitives: `cloudfront` (distribution + OAC, stack types `aws:cloudfront:distribution` + `aws:cloudfront:originaccesscontrol`) and `waf` (WAFv2 web ACL, stack type `aws:wafv2:webacl`). Augment the `static-assets` module to a production-ready stack referencing s3 + cloudfront + waf (depth 1, D-049). Expand the Terraform adapter `TYPE_MAP`/`INPUT_MAP`/`OUTPUT_MAP` for the new stack types. Bump `uses:`/`ref:` from `@v1.4` to `@v1.6` (D-056/D-057); create the floating `v1.6` + `v1` git tags pointing at `v1.6.0` so the reference resolves immediately.
- **Status:** complete (v1.7.0)
- **Depends on:** [21]
- **Requirements:** REQ-62, REQ-63, REQ-64
- **Success Criteria:**
  - `grep -R "static-assets[^s]" .` (excluding `.git/`) returns 0 hits; `modules/l2/static-assets/` is renamed to `modules/l2/static-assets/`; `contracts/static-assets.yaml` → `contracts/static-assets.yaml`; registry key renamed; all `.ciagent/` references (incl. verbatim phase descriptions, REQ-25/27/50 text, D-036) rewritten to `static-assets`.
  - `modules/l1/cloudfront/` + `modules/l1/waf/` exist with `interface.json` valid against `schemas/stack.schema.json`; registered in `modules/registry.json`.
  - `modules/l2/static-assets/composition.json` references s3 + cloudfront + waf (depth 1).
  - `adapters/terraform/adapter.py` `TYPE_MAP` covers `aws:cloudfront:distribution`, `aws:cloudfront:originaccesscontrol`, `aws:wafv2:webacl`.
  - `contracts/static-assets.yaml` + `.github/workflows/deploy.yml` + `.gitea/workflows/deploy.yml` use `@v1.6`; git tags `v1.6` + `v1` exist pointing at `v1.6.0`.
  - `bash scripts/run_ci.sh` exits 0; `python3 -m pytest tests/ -v` passes; `bash scripts/run_platform.sh --check-only` exits 0.

### Phase 23 — tagging-standards-and-security-adapters
- **Description:** Define a required-tag set (`acdl:owner`, `acdl:contract`, `acdl:environment`, `acdl:cost-center`) in `schemas/tagging-standard.json` (D-054). Author a Checkov custom YAML rule at `adapters/terraform/policy/custom_rules/acdl_tagging.yaml` that fails when required tags are missing on taggable resources. Remove the `_emit_tag_naming_skipped()` placeholder in `checkov_adapter.py` (D-043 closure) and add `ACDL_TAG_NAMING` to `RULE_MAP` as a real rule. Author a Wiz adapter stub (`adapters/wiz/wiz_adapter.py`) translating Wiz API issues → `PolicyCheckResult` records (`engine: "wiz"`), degrading gracefully when unconfigured (D-052). Author a Kyverno K8s-native adapter (`adapters/kyverno/kyverno_adapter.py`) translating Kyverno `PolicyReport` results → `PolicyCheckResult` records (`engine: "kyverno"`), with sample policies as documentation; inactive for Terraform-only stacks, ready for the GitOps reconciler roadmap item (D-053). Add `wiz` + `kyverno` to the `schemas/policy_check_result.schema.json` engine enum.
- **Status:** complete (v1.7.0)
- **Depends on:** [22]
- **Requirements:** REQ-65, REQ-66, REQ-67
- **Success Criteria:**
  - `adapters/terraform/policy/custom_rules/acdl-tagging.yaml` exists; Checkov loads it; `checkov_adapter.py` no longer emits a SKIPPED `ACDL_TAG_NAMING` placeholder (D-043 closed).
  - `adapters/wiz/wiz_adapter.py` + `tests/test_wiz_adapter.py` exist; tests pass offline (not-configured graceful degradation).
  - `adapters/kyverno/kyverno_adapter.py` + sample policies + `tests/test_kyverno_adapter.py` exist; tests pass offline.
  - `schemas/policy_check_result.schema.json` engine enum includes `checkov | kyverno | opa | wiz`.
  - `bash scripts/run_ci.sh` exits 0; `python3 -m pytest tests/ -v` passes.

### Phase 24 — platform-lambda-and-contract-ingestion
- **Description:** Author a platform Lambda (`core/lambda/contract_ingestor.py`) invoked via a Function URL (IAM auth) that accepts `{ consumerRepo, contractId, contract, environment, action }` and writes contracts to a DynamoDB table `acdl-contracts` (PK `consumerRepo`, SK `contractId#submittedAt`, SSE via a customer-managed CMK) (D-051). Define the Terraform (`terraform/platform/main.tf`) for the table, Lambda, Function URL, KMS key, Secrets Manager secret (`acdl/github-token`), and Lambda execution role. Define the cross-account consumer-invoke IAM policy (`terraform/platform/consumer_invoke_policy.json`) granting the consumer's deploy role `lambda:InvokeFunctionUrl` on the Lambda ARN, scoped via ABAC. The `report_error` action (Phase 25) is prepared but not yet implemented. Update `docs/environments/index.md` to document that onboarding now also grants Lambda-invoke permission.
- **Status:** complete (v1.7.0)
- **Depends on:** [23]
- **Requirements:** REQ-68
- **Success Criteria:**
  - `core/lambda/contract_ingestor.py` exists; handler writes contracts to DynamoDB (tested offline with moto).
  - `terraform/platform/main.tf` defines `acdl-contracts` DynamoDB table, `acdl-contract-ingestor` Lambda, Function URL (IAM auth), KMS CMK, Secrets Manager secret, Lambda execution role.
  - `terraform/platform/consumer_invoke_policy.json` exists (cross-account invoke policy template).
  - `tests/test_contract_ingestor.py` passes offline.
  - `bash scripts/run_ci.sh` exits 0.

### Phase 25 — deploy-pipeline-dx-outputs-and-error-reporting
- **Description:** Add a `publish-outputs` step to `scripts/run_platform.sh` (after apply) that writes deploy outputs to SSM Parameter Store as `SecureString` (KMS-encrypted, namespaced `/acdl/{env}/{contractId}/{output_name}`) for runtime-injectable values, and a `comment-outputs` step that posts a structured GitHub PR comment / job summary with human-readable connection strings (D-050). Implement `core/output_publisher.py` (SSM write + GitHub comment formatting). Implement the Lambda `report_error` action (`core/lambda/contract_ingestor.py`) that creates a GitHub issue on the platform repo (`acdl/acdl`) via the GitHub API using a token from Secrets Manager; idempotent (comments on existing open issue rather than duplicating) (D-055). Add an `if: failure()` error-report step to `.github/workflows/deploy.yml` that invokes the Lambda via `aws lambda invoke-function-url` (SigV4-signed). Add a PR comment after every successful pipeline stage (D-055 extension) via `scripts/post_stage_comment.sh` (uses `GITHUB_TOKEN` + `gh api`; no-op when not in a PR context). Update `pipelines/deploy.yaml` + both deploy workflow YAMLs with the new stages (byte-identical).
- **Status:** complete (v1.7.0)
- **Depends on:** [24]
- **Requirements:** REQ-69, REQ-70, REQ-71
- **Success Criteria:**
  - `scripts/run_platform.sh` has a `publish-outputs` step (SSM SecureString, tested offline with moto) + a `comment-outputs` step (GitHub PR comment formatting, tested offline).
  - `core/lambda/contract_ingestor.py` `report_error` action creates a GitHub issue (tested with mocked API); idempotent.
  - `.github/workflows/deploy.yml` + `.gitea/workflows/deploy.yml` (byte-identical) have an `if: failure()` error-report step invoking the Lambda + stage comments after each successful stage (PR context).
  - `pipelines/deploy.yaml` declares the new stages.
  - `bash scripts/run_ci.sh` exits 0; `python3 -m pytest tests/ -v` passes.

### Phase 26 — platform-pipelines-and-release-automation
- **Description:** Author three platform pipelines (D-057): (1) `.github/workflows/platform-test.yml` (PR, lint + unit + integration + schema-validation — replaces `ci.yml` for PRs); (2) `.github/workflows/primitives-plan.yml` (PR, plan-only for all L1 primitives via matrix); (3) `.github/workflows/patterns-plan.yml` (PR, plan-only for all L2 modules via matrix). Author `scripts/run_primitive_plan.sh` + `scripts/run_pattern_plan.sh` (with `--check-only` mode for CI). Author the release job (`.github/workflows/release.yml`) that runs on merge to `main`, computes the next semver (PATCH per phase, MINOR on milestone COMPLETE), creates the MAJOR.MINOR.PATCH tag, force-moves the MAJOR.MINOR + MAJOR floating tags, creates a GitHub release with an auto-generated body. This is the mechanism that lets consumers on `@v1` or `@v1.7` receive updates.
- **Status:** complete (v1.7.0)
- **Depends on:** [25]
- **Requirements:** REQ-72, REQ-73
- **Success Criteria:**
  - `.github/workflows/platform-test.yml` exists, runs lint + unit + integration + schema-validation on PR.
  - `.github/workflows/primitives-plan.yml` + `.github/workflows/patterns-plan.yml` exist, run plan-only (matrix) on PR.
  - `.github/workflows/release.yml` exists, computes next semver, creates + updates MAJOR.MINOR.PATCH / MAJOR.MINOR / MAJOR tags on merge.
  - `scripts/run_primitive_plan.sh` + `scripts/run_pattern_plan.sh` exit 0 in `--check-only` mode.
  - `bash scripts/run_ci.sh` exits 0; `python3 -m pytest tests/ -v` passes.

### Phase 27 — remove-legacy-consumer-repos-and-module-documentation-examples
- **Description:** Delete the legacy consumer-repos directory entirely (a v1.2 artifact removed in v1.7; references in `.ciagent/` historical narrative are rewritten per D-048). Author a new RDS primitive (`modules/l1/rds/`) with an `engine` input (enum: postgres, mysql, etc.) demonstrating multi-engine variation (D-059). Expand the adapter `TYPE_MAP` for `aws:rds:instance` → `aws_db_instance`. For **each** module (primitives + patterns), add a `modules/<name>/examples/` directory with `simple.yaml` + `complex.yaml` (+ variation files) validated against `schemas/contract.schema.json` in the platform-test pipeline (Phase 26 schema-validation stage) (D-058). Each module's `README.md` `## Examples` section references + excerpts the validated files. Update `docs/modules/index.md` + `docs/consumer-guide.md` + `docs/contracts/index.md` with the new module names + examples.
- **Status:** complete (v1.7.0)
- **Depends on:** [26]
- **Requirements:** REQ-74, REQ-75
- **Success Criteria:**
  - The legacy consumer-repos directory does not exist; a recursive grep for the legacy directory name (excluding `.git/`) returns 0 hits.
  - `modules/l1/rds/` exists with `interface.json` (`engine` enum) + `examples/`; registered; adapter emits `aws_db_instance`.
  - Every module README has a `## Examples` section; `modules/<name>/examples/{simple,complex}.yaml` exist and validate against `schemas/contract.schema.json`.
  - `docs/modules/index.md` links to all module READMEs (including cloudfront, waf, rds).
  - `bash scripts/run_ci.sh` exits 0; `python3 -m pytest tests/ -v` passes.

After Phase 27: COMPLETE gate — review → ship `v1.7.0` → audit. **DONE.**

---

## v1.8 (Complete — P1 remediation + uptime + engineering standards + encryption/deletion-protection by default + decommission + docs)

The v1.8 milestone clears all pending P1 issues from v1.5–v1.7 verify
reviews AND delivers three user-directed tracks: encryption + deletion
protection by default (with a decommission alias), uptime monitoring
(uptime-kuma primitive deployed by default after L2 modules), and
engineering standards + path documentation. Ship tag at milestone
COMPLETE: **`v1.8.0`** (feature milestone, next minor per run.md — v1.7
shipped `v1.7.0`). Phase patches `v1.7.1`..`v1.7.9`.

### Phase 28 — adapter-waf-and-resolver-outputs
- **Description:** Fix WAF HCL emission: custom `rules` input emits nested `rules { ... }` blocks (not `rules = [...]` attribute syntax — P1-4). Honor `default_action` input (allow/block) instead of hardcoding `allow {}` (P1-5). Implement L2 composition `outputs[]` processing in `resolve_l2()` — build `stack.outputs` dict + adapter emits `output` blocks (P1-7). Tests for all three fixes.
- **Status:** complete (v1.8.0)
- **Depends on:** —
- **Requirements:** REQ-76, REQ-77
- **Success Criteria:**
  - WAF with custom rules emits nested `rules {` blocks, not `rules = [`.
  - WAF with `default_action: block` emits `block {}`; default (absent) emits `allow {}`.
  - L2 resolution of `static-assets` yields `stack.outputs.distribution_domain_name`, `bucket_arn`, `web_acl_arn`.
  - Adapter emits `output "distribution_domain_name" { value = ... }` blocks.
  - `pytest` passes; `run_platform.sh --check-only` exits 0.

### Phase 29 — ssm-kms-and-invoke-policy
- **Description:** SSM publisher fails loud (`RuntimeError`) when `ACDL_KMS_KEY_ID` unset; `ACDL_ALLOW_DEFAULT_KMS=1` escape hatch for local testing (P1-3). Convert `consumer_invoke_policy.json` to a Terraform-rendered template using `data.aws_caller_identity` + `templatestring` — no `000000000000` placeholder (P1-6). Tests for both.
- **Status:** complete (v1.8.0)
- **Depends on:** [28]
- **Requirements:** REQ-78, REQ-79
- **Success Criteria:**
  - SSM publisher raises `RuntimeError` when `ACDL_KMS_KEY_ID` unset; succeeds with `ACDL_ALLOW_DEFAULT_KMS=1`.
  - Rendered invoke policy contains the caller's live account ID, not `000000000000`.
  - `pytest` passes; `run_ci.sh` exits 0.

### Phase 30 — run-platform-isolation-and-api-portability
- **Description:** `run_platform.sh` emits adapter output to `$WORK/tf` (per-run temp dir), not `terraform/spike/`; remove committed `terraform/spike/*.tf` (P1-8). `contract_ingestor.py` reads `GITHUB_API_BASE` env for forge-agnostic API URLs (GitHub + Gitea); `_forge_type()` branches search URL (P1-9). Deploy workflow `configure-aws-credentials` step restructured as single conditional step: OIDC when no static key, `access-key`/`secret-key` inputs when static key present (S1). Both deploy workflows remain byte-identical.
- **Status:** complete (v1.8.0)
- **Depends on:** [29]
- **Requirements:** REQ-80, REQ-81, REQ-82
- **Success Criteria:**
  - `run_platform.sh --check-only` writes to a temp dir; no `terraform/spike/*.tf` committed.
  - `contract_ingestor.py` uses `GITHUB_API_BASE`; Gitea base URL produces correct API paths.
  - Deploy workflow static-key override wired to `configure-aws-credentials` inputs.
  - Both deploy workflows byte-identical; `pytest` + `run_ci.sh` green.

### Phase 31 — encryption-by-default-and-per-stack-cmk
- **Description:** Create `kms-key` L1 primitive (type `aws:kms:key`, inputs: description/region/deletion_window_days, outputs: kms_key_arn/kms_key_id, NFRs: enable_rotation default true, deletion_protection default true). Adapter emits `aws_kms_key` + `aws_kms_alias` + `enable_key_rotation = true`. Add `encryption_enabled` NFR (default true) + `kms_key_arn` input to all primitives. L2 modules wire a `kms-key` child + connect its output to all children. Managed KMS fallback when no CMK provided (with stderr warning).
- **Status:** complete (v1.8.0)
- **Depends on:** [30]
- **Requirements:** REQ-83, REQ-84, REQ-85
- **Success Criteria:**
  - Every primitive has `encryption_enabled` NFR (default true) + optional `kms_key_arn` input.
  - L2 resolution wires per-stack CMK to all children.
  - Adapter emits encryption blocks (SSE, storage_encrypted, encryption_configuration) referencing the CMK.
  - `enable_key_rotation = true` on the CMK; no shared keys across stacks.
  - `pytest` + `run_ci.sh` green.

### Phase 32 — deletion-protection-by-default-and-l2-feature-flag
- **Description:** Add `deletion_protection` NFR (boolean, default true) to every L1 primitive. Adapter emits `lifecycle { prevent_destroy = true }` when true; omits it when false. L2 modules expose `features.deletion_protection` flag (default true); resolver propagates to each child's NFR. Consumers can set `inputs.deletion_protection: false` in contract. Update contract schema.
- **Status:** complete (v1.8.0)
- **Depends on:** [31]
- **Requirements:** REQ-86, REQ-87
- **Success Criteria:**
  - Every primitive has `deletion_protection` NFR defaulting to true.
  - Adapter emits `prevent_destroy = true` when true; omits when false.
  - L2 feature flag propagates to all children.
  - `pytest` + `run_ci.sh` green.

### Phase 33 — uptime-kuma-primitive
- **Description:** Create `uptime` L1 primitive (ECS Fargate running `louislam/uptime-kuma:1`). Inputs: container_image, region, monitored_endpoints (array of {name, url, type, interval, timeout}), static_checks, alert_channels ({teams_webhook, email_addresses, sms_numbers, github_issue_repo}), feature_flag_enabled (default true), cpu, memory. Outputs: uptime_url, service_arn, task_definition_arn. NFRs: deletion_protection, encryption_enabled. Adapter emits ECS service + ALB + log group; no resources when feature_flag_enabled=false. Register in registry. Add `deploy-uptime` pipeline stage (separate state, after publish-outputs) to `pipelines/deploy.yaml` + both deploy workflows. `run_platform.sh` constructs synthetic uptime contract from L2 outputs + runs second terraform apply. Uptime URL published via PR comment. Feature flag from `inputs.uptime_enabled` (default true).
- **Status:** complete (v1.8.0)
- **Depends on:** [32]
- **Requirements:** REQ-88, REQ-89, REQ-90, REQ-91
- **Success Criteria:**
  - Uptime primitive exists with feature flag, monitored endpoints, alert channels.
  - Deployed by default after L2 module (separate state); endpoints passed from L2 outputs.
  - Uptime URL published via PR comment.
  - Feature flag disables deployment (no resources emitted).
  - `deploy-uptime` stage in deploy contract + byte-identical workflows.
  - `pytest` + `run_ci.sh` green.

### Phase 34 — decommission-alias-and-cmdb-validation
- **Description:** Add `mode: decommission` to deploy pipeline. Stages: validate-change-request (Lambda `validate_change_request` action queries DynamoDB `acdl-change-requests` table, asserts status=approved) → disable-deletion-protection (resolve contract with deletion_protection=false, terraform plan/apply, HITL SRE gate) → zero-counts (resolver `decommission_transform` zeroes all counts, terraform plan/apply, second HITL SRE gate) → confirm-decommission. Add `acdl-change-requests` DynamoDB table to terraform/platform/main.tf. Add `validate_change_request` to contract_ingestor.py. Document in `docs/CONSUMER_GUIDE.md`.
- **Status:** complete (v1.8.0)
- **Depends on:** [33]
- **Requirements:** REQ-92, REQ-93, REQ-94
- **Success Criteria:**
  - Decommission mode works via existing deploy pipeline with 2-step HITL SRE gates.
  - CR ID validated against DynamoDB CMDB (status must be approved).
  - `decommission_transform` zeroes all counts.
  - Documented in consumer guide.
  - `pytest` + `run_ci.sh` green.

### Phase 35 — module-engineering-standards
- **Description:** Scan all current modules to generate `modules/STANDARDS.md` — comprehensive L1+L2 authoring + code review standards: required files, interface schema, input/output/NFR conventions, encryption + deletion protection as mandatory NFRs, naming, multi-resource pattern, adapter extension pattern (TYPE_MAP + INPUT_MAP + OUTPUT_MAP + specialized branches), code review checklist. Fix `modules/README.md` catalog index (add rds + uptime + kms-key). Update `modules/README-TEMPLATE.md` with `## NFRs` section. Add `tests/test_module_standards.py` for automated enforcement.
- **Status:** complete (v1.8.0)
- **Depends on:** [34]
- **Requirements:** REQ-95, REQ-96
- **Success Criteria:**
  - `modules/STANDARDS.md` exists with L1+L2 authoring + review standards.
  - Catalog index includes all primitives; template has NFRs section.
  - Automated standards test passes for all modules.
  - `pytest` + `run_ci.sh` green.

### Phase 36 — schemas-adapters-pipelines-readmes
- **Description:** Author `schemas/README.md` (how to write schemas, wire into platform, test in CI, dependencies, existing catalog), `pipelines/README.md` (how to write pipeline contracts, wire into workflows, test, dependencies, catalog), `adapters/README.md` (how to write adapters, wire into platform, test, dependencies, catalog). Add `tests/test_docs_coverage.py` to validate presence + required sections.
- **Status:** complete (v1.8.0)
- **Depends on:** [35]
- **Requirements:** REQ-97, REQ-98, REQ-99
- **Success Criteria:**
  - All 3 READMEs exist with comprehensive documentation.
  - CI validates their presence.
  - `pytest` + `run_ci.sh` green.

### Phase 37 — verify
- **Description:** 4-layer verification (structural, behavioral, security, quality) of all v1.8 phases. Re-verify each P1 (P1-3..P1-9 + S1) is resolved. Verify all new features (encryption, deletion protection, uptime, decommission, standards, docs) have dedicated tests.
- **Status:** complete (v1.8.0)
- **Depends on:** [36]
- **Requirements:** —
- **Success Criteria:**
  - All 4 layers pass; each P1 fix + each new feature has a dedicated test.
  - `pytest` passes (~358 tests); `run_ci.sh` exits 0; `run_platform.sh --check-only` exits 0.

### Phase 38 — review-audit-complete
- **Description:** Multi-persona code review across the full v1.8 diff. Audit (reconstruction, file discipline, branch hygiene, commit discipline). Complete: update REQUIREMENTS.md (REQ-76..99), ROADMAP.md (v1.8 complete), PROJECT.md. Tag `v1.8.0`. Update floating `v1.8` + `v1` tags. Bump `uses:`/`ref:` from `@v1.6` to `@v1.8`.
- **Status:** complete (v1.8.0)
- **Depends on:** [37]
- **Requirements:** —
- **Success Criteria:**
  - Review: 0 new P0/P1; all P1-3..P1-9 + S1 resolved; 3 new requirements delivered.
  - Audit: clean; 0 outstanding issues.
  - Tag `v1.8.0` created; floating tags updated.

After Phase 38: COMPLETE gate — review → ship `v1.8.0` → audit.

---

## v1.9 (complete — design doc refresh + contract interpolation + per-env CI jobs + stub implementation + P1-1 remediation, tag `v1.9.0`)

The v1.9 milestone closes four gaps left by v1.8 (user-directed,
2026-07-23): stale design docs, no contract interpolation, promotion
requires editing the `environment` field, and unimplemented stubs. It
also closes P1-1 (adapter hardcoded defaults, deferred from v1.2).

### Phase 39 — design-doc-refresh-and-p1-1-parameterization
- **Description:** Refresh `core/hitl_matrix_design.md` (no stale "dev-only spike"/"v1.2 wires the gates" framing; v1.9 wiring section; 8-concern matrix marked implemented offline-testable subset) + `core/audit_ledger_design.md` (outbox marked shipped+production since v1.8; S3 Object Lock + JWS + worker + DLQ + checkpoints deferred D-083). P1-1: move adapter ECS/ALB/VPC hardcoded defaults (`desired_count`, `launch_type`, `family`, `target_type`, `load_balancer_type`, `Name` tags) into L1 `interface.json` inputs with defaults; the adapter reads from inputs; the resolver routes wires to the sub-resource that declares the input.
- **Status:** complete (v1.8.1)
- **Depends on:** —
- **Requirements:** REQ-100, REQ-101, REQ-102
- **Success Criteria:**
  - Both design docs refreshed; no stale framing; `test_design_docs_current.py` passes.
  - Adapter has no hardcoded ECS/ALB/VPC defaults; overrides flow through; `test_p1_1_adapter_parameterization.py` passes.
  - v1.1 S3 regression passes; `pytest` 371 (was 350, +21); `run_ci.sh` exits 0; `run_platform.sh --check-only` exits 0.

### Phase 40 — contract-interpolation
- **Description:** `${env.<field>}` + `${contract.<field>}` resolver expansion from environment onboarding JSON (D-081). Environment JSON schema (`schemas/environment.schema.json`) + qa/prod/dr placeholder bindings. `core/environment_check.py` gains `load()`. Sample contracts use naming patterns that include region, account id, environment (e.g. `acdl-${env.environment}-${contract.module}-${env.account_id}-${env.region}`). Expansion is recursive (D-087), post-schema-validation, pre-IR-resolution; unknown tokens raise `ValueError`. `resolve()` accepts `environment_override` (D-088).
- **Status:** complete (v1.8.2)
- **Depends on:** [39]
- **Requirements:** REQ-103, REQ-104
- **Success Criteria:**
  - `schemas/environment.schema.json` exists; 4 env files validate; `load()` works.
  - `_expand_vars` in resolver; unknown tokens raise; recursive over dicts/lists/strings.
  - Sample contracts use `${env.*}` + `${contract.*}` naming patterns; resolve to concrete values.
  - `tests/test_environment_schema.py` + `tests/test_interpolation.py` + `tests/test_sample_contracts_interpolate.py` pass.
  - `pytest` 406 (was 371, +35); `run_ci.sh` exits 0; `run_platform.sh --check-only` exits 0.
### Phase 41 — per-environment-ci-jobs
- **Description:** Per-env contract files (static-assets + microservice × dev/qa/prod/dr, REQ-105) using interpolation. Deploy workflow (`.github` + `.gitea`, byte-identical) declares an `environment` `workflow_call` input (REQ-106); `run_platform.sh --environment <name>` overrides the contract's environment at load time (D-088, before schema validation + interpolation). `resolve()` accepts `environment_override`. Consumer guide documents the per-env caller-workflow pattern (4 jobs, one per environment) + HITL gate structure (approve_qa/approve_prod/approve_dr, D-042) + interpolation reference table. Promotion = running the matching job; no environment field editing.
- **Status:** complete (v1.8.3)
- **Depends on:** [40]
- **Requirements:** REQ-105, REQ-106
- **Success Criteria:**
  - 8 per-env contract files exist + validate + resolve to correct env.
  - Deploy workflow has `environment` input (byte-identical Gitea + GitHub); `run_platform.sh --environment` overrides; resolver supports `environment_override`.
  - Consumer guide documents per-env caller workflows + promotion-without-editing + HITL gates + interpolation reference.
  - `tests/test_per_env_contracts.py` + `tests/test_deploy_workflow_env_input.py` + `tests/test_consumer_guide_per_env_section.py` pass.
  - `pytest` 446 (was 406, +40); `run_ci.sh` exits 0; both deploy workflows byte-identical.

### Phase 42 — stub-implementation
- **Description:** `route_halt_artifact` real (SNS publish + outbox fallback, REQ-107) + SNS topic `acdl-sod-halt` in `terraform/platform/main.tf`. HITL attestation gates (`core/hitl_gates.py`, REQ-108) — records approver to outbox, runs SoD on prod, invokes the attestation matrix; `run_platform.sh` calls `attest` before apply for qa/prod/dr (dev skips). 8-concern attestation matrix (`core/attestation_matrix.py`, REQ-109, D-084) — offline-testable concerns run for real; operator-supplied concerns accept signed evidence artifacts validated for freshness + schema; signature skip when `ACDL_ATTESTATION_SIGNING_KEY_ID` unset (D-089). Wiz real API client (`WizClient`, REQ-110) — GraphQL queries + pagination + graceful degrade. Kyverno translator fleshed out (REQ-111) — full PolicyReport mapping + skip-with-reason + inactive-for-TF guard + `--kube-version` stub.
- **Status:** complete (v1.8.4)
- **Depends on:** [41]
- **Requirements:** REQ-107, REQ-108, REQ-109, REQ-110, REQ-111
- **Success Criteria:**
  - `route_halt_artifact` publishes to SNS when ARN set; outbox fallback when unset; SNS topic in Terraform.
  - `hitl_gates.attest` records approver; SoD blocks on identity equality; dev skips; `run_platform.sh` has the HITL step.
  - `attestation_matrix.check` runs 8 concerns; offline concerns pass; operator-supplied missing → block for prod; expired → block; signature skip when key unset.
  - Wiz `WizClient` real client + pagination + graceful degrade; `fetch_and_adapt` translates.
  - Kyverno full mapping (pass/fail/skip/warn + severity + skip-with-reason + resource construction); inactive guard preserved; `--kube-version` parsed.
  - `tests/test_route_halt_artifact.py` + `test_hitl_gates.py` + `test_attestation_matrix.py` + `test_wiz_adapter_real_client.py` + expanded `test_kyverno_adapter.py` pass.
  - `pytest` 493 (was 446, +47); `run_ci.sh` exits 0; `run_platform.sh --check-only` exits 0.

### Phase 43 — verify-review-audit-complete
- **Description:** 4-layer verify (structural, behavioral, security, quality) of all v1.9 phases. Multi-persona review (0 P0, 0 P1). Audit (reconstruction, file discipline, branch hygiene, commit discipline — all clean). REVIEW.md reconstructed (D-086). Complete: update REQUIREMENTS.md (REQ-100..111), ROADMAP.md, PROJECT.md. Tag `v1.9.0`; update floating `v1.9` + `v1` tags. Bump `uses:`/`ref:` from `@v1.6` → `@v1.9`.
- **Status:** complete (v1.9.0)
- **Depends on:** [42]
- **Requirements:** —
- **Success Criteria:**
  - 4-layer verify PASS; 493 tests; `run_ci.sh` + `run_platform.sh --check-only` green.
  - Review: 0 P0, 0 P1; REVIEW.md reconstructed with v1.9 content (D-086).
  - Audit: clean; all 12 v1.9 commits have `---ci---` blocks.
  - Tag `v1.9.0` created; floating tags updated; `uses:` bumped to `@v1.9`.

After Phase 43: COMPLETE gate — review → ship `v1.9.0` → audit. **DONE.**

---

## v1.10 (complete — pipeline regression fix + capability re-verification + verified-reality rewrite, tag `v1.10.0`)

The v1.10 milestone corrects a structural defect and a credibility gap
surfaced in the 2026-07-27 CLARIFY/RESEARCH stages:

1. **VERIFY is diff-scoped** — it checks the phase diff only, never
   re-runs underlying capability. 8 NFR-patch phases (v1.9.1→v1.9.8)
   passed VERIFY while the platform decayed underneath.
2. **Advertised capability is not currently reproducible** — v1.2 ECS
   E2E and v1.7 pipelines ran once historically but decayed; decks
   presented them as current without disclosing the decay.
3. **Deck work was sequenced backwards** — re-verify → rewrite → polish
   is the honest order; v1.9.x did it backwards for 8 phases.

User decisions: D-090 (no cap on sweep; fix everything; unbounded risk
accepted), D-091 (regression-class VERIFY), D-092 (local emulating
adapters), D-093 (re-verify v1.1→v1.8; v1.0 demo excluded), D-094
(rewrite docs/decks to verified reality; unfreeze decks).

### Phase 52 — pipeline-regression-verify-fix
- **Description:** Add a regression-class VERIFY that re-runs capability checks (not just diff checks), at minimum on milestone completion. Regression run executes the local-emulator tier for every capability marked Verified in prior milestones; any failure blocks milestone completion. Records `regression: { capability, status }` in `---ci---` blocks.
- **Status:** complete (v1.9.9)
- **Depends on:** —
- **Requirements:** REQ-112
- **Success Criteria:**
  - VERIFY supports `regression` mode; milestone completion requires a clean regression run.
  - A regression run against current code surfaces decay (fails closed).
  - `tests/test_verify_regression_mode.py` passes.

### Phase 53 — local-emulating-adapters
- **Description:** Build local emulating adapters so the platform is fully locally testable without cloud credentials: flat-file DynamoDB outbox, local ECS emulator (synthetic HTTP 200 from local shell), local S3 state backend (flat-file tfstate), local Lambda stub (in-process handler invocation). Same interfaces as the live adapters.
- **Status:** complete (v1.9.10)
- **Depends on:** [52]
- **Requirements:** REQ-113
- **Success Criteria:**
  - All local adapters exist; headline E2E runs end-to-end against the local tier with no cloud credentials.
  - `tests/test_local_emulating_adapters.py` passes.
  - `run_platform.sh --local` runs the full pipeline locally.

### Phase 54 — v1.1-v1.8 capability-reverification-sweep
- **Description:** Enumerate every capability advertised in v1.1→v1.8 PROJECT/ROADMAP to `.ciagent/CAPABILITY_INVENTORY.md`. Re-verify each: headline E2E at both tiers (live AWS + local emulator, both must pass); all other capabilities at the local tier via emulating adapters. Tag each Verified/Decayed/Broken. Fix every Decayed/Broken capability in-sweep (D-090: no cap; all must end Verified) until Verified. v1.0 demo excluded as archived/superseded.
- **Status:** complete (v1.9.11)
- **Depends on:** [53]
- **Requirements:** REQ-114
- **Success Criteria:**
  - Every v1.1→v1.8 advertised capability is tagged Verified in `CAPABILITY_INVENTORY.md`.
  - Headline E2E passes at both tiers.
  - Regression run (Phase 52) is clean against the re-verified state.

### Phase 55 — rewrite-to-verified-reality
- **Description:** Rewrite PROJECT.md (add "Capability Status (Re-Verified 2026-07-27)" section + decay disclosure), ROADMAP.md (v1.9.x entries noted as deck-freeze / superseded-by-reverification), and both leadership decks so every capability claim reflects the re-verified status. Remove any claim that cannot be demonstrated live. Re-render HTML; upload PPTX to the v1.10.0 release. Decks unfrozen only after this lands.
- **Status:** complete (v1.9.12)
- **Depends on:** [54]
- **Requirements:** REQ-115
- **Success Criteria:**
  - PROJECT/ROADMAP/decks match `CAPABILITY_INVENTORY.md` exactly.
  - `ci-doc-verifier` confirms no stale capability claims remain.
  - Decks unfrozen; v1.10.0 tagged; Gitea release published.

After Phase 55: COMPLETE gate — review → ship `v1.10.0` (next minor;
fix/test/docs, not a breaking schema change) → audit. **DONE.**

---

## v1.11 (complete — RESTART: stateless adapter + pipeline-driven module lifecycle testing, tag `v1.11.0`)

The v1.11 milestone closes the two GRILL escalations blocking the leadership
pitch: G-005 (6 IAM-gated cloud capabilities CAP-017..022 deploy-unverified)
and G-008 (no cost documentation despite live AWS resources).

**Why a restart.** The first v1.11 attempt (P56 IAM re-bootstrap + P57
live-deploy-microservice, branches `phase/56-iam-re-bootstrap` +
`phase/57-live-deploy-microservice`, now abandoned) produced five defects:
(1) 4 VPCs created when 1 should have — the adapter emitted per-contract
state keys with no VPC sharing; (2) Python scripts made lifecycle changes
directly to the cloud (`verify_deploy_microservice.py` ran `terraform apply
-auto-approve`); (3) no L1 module lifecycle testing — `tests/test_adapter.py`
only string-validated HCL, never ran terraform apply/modify/destroy; (4) no
L2 integration testing; (5) lifecycle was managed by Python, not terraform.
The restart branches off `v1.10.2` and rebuilds v1.11 on three corrections.

**The three corrections.**
1. **Stateless adapter.** `adapters/terraform/adapter.py` (918 lines, 3
   hardcoded constant tables `TYPE_MAP`/`INPUT_MAP`/`OUTPUT_MAP`, 39
   type-specific branches) is rewritten to a ~80-line stateless assembler.
   Each L1 module ships a real `terraform/` module dir
   (`versions.tf`/`variables.tf`/`locals.tf`/`main.tf`/`outputs.tf`) owning
   its resource shape, nested HCL blocks, and defaults. The adapter reads
   the registry, emits a root `main.tf` instantiating each L1 as
   `module "x" { source = "..." ... }` with resolved inputs and wired refs.
   `interface.json` stays engine-agnostic; the terraform dir is the engine
   binding. Defaults move into `locals.tf` (heavy interpolation of vars
   against sensible defaults).
2. **Terraform owns lifecycle.** `scripts/run_platform.sh` gains `--apply`
   and `--destroy` modes. Python never runs terraform.
   `scripts/verify_deploy_microservice.py` is deleted. The shell owns all
   apply/modify/destroy; Python only orchestrates the shell (and may use
   boto3 for read-only verify probes in a future QA milestone, not this one).
3. **Pipeline-driven testing.** A `modules-lifecycle` pipeline (Gitea +
   GitHub, byte-identical) matrix-runs each L1 module's
   `examples/{simple,complex}.yml` contracts through apply→modify→destroy
   against live AWS. No per-module Python/pytest. The "test" = the pipeline
   cell going green (terraform apply exit 0 → modify exit 0 → destroy exit 0).

**Single platform VPC.** `terraform/platform/main.tf` owns ONE VPC; the
microservice composition drops its `vpc` child and references the platform
VPC via `data` source. The standalone `vpc` L1 module stays (consumers
deploy their own VPCs). State keys are deterministic and env-aware
(`spike/{contract.id}/{contract.environment}/terraform.tfstate`), stable
across apply/modify/destroy — the same contract+env always hits the same
state key, so terraform modifies rather than duplicates.

**L2 = composition only.** L2 modules keep `composition.json` only (no L2
terraform files). The composition must be deterministic: same contract →
same resolved stack → same state key, every time.

**Versioning.** Feature milestone (P56a/P56b/P57/P58/P59/P60/P61/P62 are
feat). Ship tag at milestone COMPLETE: `v1.11.0` (v1.10.2 → v1.11.0).

**Wave ordering.** Wave 1 (P56a → P56b → P57 → P58) is sequential — the
stateless adapter, shell lifecycle modes, and platform VPC are prerequisites
for all testing. Wave 2 (P59 → P60) authors then runs the L1 lifecycle
pipeline. Wave 3 (P61 → P62) authors then runs the L2 lifecycle pipeline.
Wave 4 (P63 → P64 → P65) closes G-005/G-008 + teardown + deck rewrite.

### Phase P56a — stateless-adapter-rewrite (Wave 1)
- **Description:** Rewrite `adapters/terraform/adapter.py` from a 918-line monolith (3 constant tables + 39 type-specific branches) to a ~80-line stateless assembler. Author `modules/l1/s3/terraform/` (`versions.tf`/`variables.tf`/`locals.tf`/`main.tf`/`outputs.tf`) as the reference module proving the assembly path end-to-end. Extend `modules/registry.json` with a `terraform_dir` field. Rewrite `modules/STANDARDS.md` §8 from "three tables + specialized branches" to "stateless assembler + per-module terraform dir". Rewrite `tests/test_adapter.py` to assert module-instantiation assembly (root `main.tf` contains `module "x" { source = ... }` blocks with correct inputs + refs), not HCL string matching.
- **Status:** active
- **Depends on:** —
- **Requirements:** REQ-123
- **Success Criteria:**
  - `grep -n "TYPE_MAP\|INPUT_MAP\|OUTPUT_MAP\|rtype ==" adapters/terraform/adapter.py` returns nothing.
  - `wc -l adapters/terraform/adapter.py` < 100.
  - `modules/l1/s3/terraform/` passes `terraform init + validate` standalone.
  - Adapter, given the s3 instance, emits a root `main.tf` that `terraform init + validate` accepts.

### Phase P56b — l1-module-terraform-authoring (Wave 1)
- **Description:** Author the remaining 11 L1 module terraform subdirs (`vpc`, `ecs-cluster`, `ecs-service`, `iam-role`, `alb`, `ecr`, `cloudfront`, `waf`, `rds`, `kms-key`, `uptime`) with the full `versions.tf`/`variables.tf`/`locals.tf`/`main.tf`/`outputs.tf` split. Defaults currently hardcoded in the adapter (CIDR blocks, assume_role_policy JSON, ECR/logs inline policy, Fargate requires_compatibilities, assign_public_ip, listener/target ports) move into `locals.tf` as heavy interpolation of vars against sensible defaults. Multi-resource modules get the full split; trivial single-resource modules (kms-key, ecr) may inline locals in main.tf. Each module's `interface.json` stays engine-agnostic. Add `terraform_dir` to each registry entry.
- **Status:** pending
- **Depends on:** [P56a]
- **Requirements:** REQ-124
- **Success Criteria:**
  - All 12 `terraform/` subdirs pass `terraform init + validate` standalone.
  - No defaults remain in the adapter.
  - Each registry entry has a `terraform_dir` field.

### Phase P57 — shell-orchestrator-lifecycle-modes (Wave 1)
- **Description:** `scripts/run_platform.sh` gains `--apply <contract.yml>` and `--destroy <contract.yml>` modes. `--apply` runs resolve → adapter → `terraform init` → `terraform apply -auto-approve` (HITL gate for qa/prod/dr). `--destroy` runs resolve → adapter → `terraform destroy -auto-approve` (gated behind `--decommission` + CR validation, D-070 two-step). `--modify` is implicit (a second `--apply` with a changed contract produces a terraform diff). Delete `scripts/verify_deploy_microservice.py` (the offending script that ran `terraform apply` from Python).
- **Status:** pending
- **Depends on:** [P56b]
- **Requirements:** REQ-125
- **Success Criteria:**
  - `run_platform.sh --apply` and `--destroy` modes exist and are the ONLY path to terraform apply/destroy.
  - `grep -rn "terraform apply\|terraform destroy" scripts/*.py` returns nothing.
  - `verify_deploy_microservice.py` no longer exists.

### Phase P58 — single-platform-vpc-deterministic-state (Wave 1)
- **Description:** Add a single VPC (`aws_vpc.acdl_shared` 10.0.0.0/16, 2 public subnets, IGW, route table, ECS security group) to `terraform/platform/main.tf`; output `vpc_id`, `public_subnet_ids`, `ecs_security_group_id`. `modules/l2/microservice/composition.json` drops the `vpc` child and references the platform VPC via a `data_sources` block. `core/contract_resolver.py` resolves `data:platform/vpc` references. The adapter emits `data "terraform_remote_state" "platform"` + `data "aws_vpc"`/`data "aws_subnets"` blocks, never an inline `aws_vpc` for the microservice stack. State key fix: `spike/{contract.id}/{contract.environment}/terraform.tfstate` (deterministic, env-aware, stable across lifecycle). Add `state_key` derivation to `schemas/contract.schema.json`.
- **Status:** pending
- **Depends on:** [P57]
- **Requirements:** REQ-126
- **Success Criteria:**
  - `terraform/platform` apply creates exactly ONE VPC.
  - `contracts/microservice.yml` resolution produces NO `aws:ec2:vpc` resource.
  - Two contract applies (dev + prod) → ONE VPC, two state keys, two ECS services.
  - Same contract+env re-applied → same state key → terraform modifies, never duplicates.

### Phase P59 — l1-lifecycle-pipeline-author (Wave 2)
- **Description:** Author `pipelines/modules-lifecycle.yml` (declarative contract: validate → resolve → apply → modify → destroy) + byte-identical `.gitea/workflows/modules-lifecycle.yml` + `.github/workflows/modules-lifecycle.yml`. Matrix over 12 L1 modules × {simple, complex} example contracts. Each cell: `run_platform.sh --apply examples/simple.yml` → `run_platform.sh --apply examples/complex.yml` (same state key → terraform modifies) → `run_platform.sh --destroy examples/complex.yml`. VPC-dependent L1s (alb, ecs-service, rds, uptime) reference the platform VPC applied by a prerequisite job; standalone `vpc` L1 applies its own. Trigger: `pull_request: [main]` + `workflow_dispatch`. Author `schemas/modules-lifecycle-pipeline.schema.json`. Extend `tests/test_pipeline_contract.py` (offline: validate schema + byte-identical).
- **Status:** pending
- **Depends on:** [P58]
- **Requirements:** REQ-127
- **Success Criteria:**
  - Pipeline YAML validates against its schema.
  - Gitea + GitHub workflows are byte-identical.
  - `test_pipeline_contract.py` passes (offline).
  - Matrix lists all 12 L1 modules × 2 examples.

### Phase P60 — l1-lifecycle-pipeline-live-run (Wave 2)
- **Description:** Run the P59 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`, and rarely the adapter assembler. No new Python files.
- **Status:** pending
- **Depends on:** [P59]
- **Requirements:** REQ-127
- **Success Criteria:**
  - Full L1 lifecycle matrix green: 12 modules × 2 examples = 24 cells, each apply→modify→destroy exit 0.
  - No live resources remain after the run (destroy enforced).
  - `primitives-plan.yml` (plan-only) still passes.

### Phase P61 — l2-lifecycle-pipeline-author (Wave 3)
- **Description:** Extend `pipelines/modules-lifecycle.yml` + 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). Author `modules/l2/static-assets/examples/complex.yml` + `modules/l2/microservice/examples/complex.yml` (modify variants, defined within the modules). L2 = composition only (no L2 terraform files); the composition must be deterministic (same contract → same resolved stack → same state key, every time).
- **Status:** pending
- **Depends on:** [P60]
- **Requirements:** REQ-128
- **Success Criteria:**
  - L2 matrix lists both modules with apply→modify→destroy cells.
  - Composition resolution is deterministic (same contract → same stack, byte-identical).

### Phase P62 — l2-lifecycle-pipeline-live-run (Wave 3)
- **Description:** Run the L2 lifecycle pipeline live; fix composition wiring + adapter assembly until green. This replaces the deleted `verify_deploy_microservice.py` — the pipeline IS the verify. CAP-017..022 boto3 probes are deferred to a future QA milestone. Fixes land in `modules/l2/<module>/composition.json`, `modules/l2/<module>/examples/*.yml`, `core/contract_resolver.py`, and rarely the adapter. No new Python files.
- **Status:** pending
- **Depends on:** [P61]
- **Requirements:** REQ-128
- **Success Criteria:**
  - L2 matrix green: static-assets + microservice, each apply→modify→destroy exit 0.
  - Microservice apply creates NO inline VPC (references platform VPC).
  - Same state key across apply/modify/destroy (deterministic).
  - `patterns-plan.yml` (plan-only) still passes.

### Phase P63 — regression-registry-cost-docs (Wave 4)
- **Description:** Add CAP-017..022 to `core/regression_verify.py` registry (evidence = lifecycle pipeline green, not boto3 probes). Author `.ciagent/COST.md` (AWS Cost Explorer 6-day window query: v1.0 ship 2026-07-21 → v1.10 complete 2026-07-27; document monthly + per-day if available). Closes G-008.
- **Status:** pending
- **Depends on:** [P62]
- **Requirements:** REQ-119, REQ-121
- **Success Criteria:**
  - Regression registry includes CAP-017..022 with "lifecycle pipeline green" evidence.
  - `COST.md` documents the v1.0→v1.10 spend window.

### Phase P64 — pre-mortem-teardown (Wave 4)
- **Description:** Author `.ciagent/PRE_MORTEM.md` (v1.10 decay root cause + forward pre-mortem for the OSS reference + leadership pitch). `run_platform.sh --decommission` with CR CHG0680001 — tears down ALL deployed stacks INCLUDING the 4 drifted VPCs from the failed first attempt. HITL SRE gates (D-070 two-step). D-096 enforced (live resources do not persist past v1.11).
- **Status:** pending
- **Depends on:** [P63]
- **Requirements:** REQ-120, REQ-122
- **Success Criteria:**
  - `PRE_MORTEM.md` documents the decay root cause + forward pre-mortem.
  - All deployed stacks torn down; zero live ACDL resources remain.

### Phase P65 — rewrite-caps-decks (Wave 4)
- **Description:** Rewrite `CAPABILITY_INVENTORY.md`, `PROJECT.md` §Capability Status, and both leadership decks: CAP-017..022 → "Verified live-aws via lifecycle pipeline <date>; torn down to zero-cost steady state." Remove the IAM-drift framing. Add the cost appendix slide (P63) + pre-mortem reference (P64). Re-render HTML; upload PPTX to the v1.11.0 release. `ci-doc-verifier` confirms no stale "deploy-unverified" claims remain.
- **Status:** pending
- **Depends on:** [P64]
- **Requirements:** REQ-116, REQ-118
- **Success Criteria:**
  - CAPABILITY_INVENTORY + PROJECT + decks all reflect "Verified live-aws via lifecycle pipeline; torn down to zero-cost."
  - `ci-doc-verifier` confirms no stale "deploy-unverified" claims.
  - HTML re-rendered; PPTX uploaded to v1.11.0 release.

After Phase P65: COMPLETE gate — review → ship `v1.11.0` (next minor;
feature milestone) → audit. **DONE.**

## v1.12 (complete — Presentation Refinement, tag `v1.12.0`)

The v1.12 milestone refines the leadership presentation decks to reflect
the verified reality after v1.11. The v1.11 P65 deck-rewrite task did not
fully land on the deck artifacts: the rendered HTML still claimed 6 cloud
capabilities were "deploy-unverified (IAM drift)", the road-to-north-star
diagram still showed v1.10 as "NEXT", and the v1.11 architecture stories
(stateless adapter, pipeline-driven lifecycle testing) were absent. The
v1.10 decay lesson (PRE_MORTEM.md FM-3) requires decks to match verified
reality exactly, not outrun it.

**Scope:** leadership decks only (`docs/presentations/`) across all four
layers (source markdown, Marp deck, rendered HTML, talking points) +
diagrams + README. Plus one real adapter fix + two probe fixes required to
make the deck claims true, and a CI lifecycle-mode flag (REQ-134) that
defaults the lifecycle tests to plan-only.

**Why the code fixes.** The v1.12 regression gate run (Phase 66) found
CAP-013 (a real adapter dedup defect — multi-resource L1s produced invalid
Terraform) + CAP-017/018 (two regression-probe bugs). The inventory's
"22/22 Verified" claim was overstated until CAP-013 was fixed. The pre-
mortem mitigation (verified-only claims) made the fixes mandatory.

**Why the lifecycle flag.** The lifecycle pipeline defaulted to full
apply→modify→destroy, which requires AWS credentials + cost on every PR.
REQ-134 adds the `ACDL_LIFECYCLE_MODE` CI variable (workflow_dispatch
input `lifecycle_mode`, default `plan`) so the pipeline runs plan-only on
every PR (fast, no AWS mutation, no cost) and full only on override.

### Phase breakdown (6 phases)

- **P66 — init + research.** Drift audit (9 items) comparing
  `docs/presentations/*` against v1.11-verified reality. Regression gate
  (D-091) re-run surfaced 3 Broken capabilities; root-cause analysis
  classified CAP-013 (real adapter defect), CAP-017/018 (probe bugs).
  REQ-129..133 added.
- **P67 — fix adapter dedup + 2 probe bugs (REQ-129/130).** The adapter's
  module-call dedup logic rewritten: multi-resource L1s (ecs-service, alb)
  now emit ONE module block named by the composition child id, with
  expanded sub-ids rewritten via id_remap (stack outputs + cross-module
  ref: inputs). terraform validate succeeds for the microservice; adapter
  192 lines (< 200 gate). CAP-017 probe: locals.tf conditional on local.*
  usage. CAP-018 probe: LocalLambdaStub constructed with the required
  outbox arg. Regression gate: 22/22 Verified.
- **P67b — lifecycle tests plan-only default + ACDL_LIFECYCLE_MODE flag
  (REQ-134).** The four lifecycle scripts dispatch to --plan-only (plan
  mode, default) or --apply/--destroy (full mode). Both workflows
  (byte-identical) expose lifecycle_mode as a workflow_dispatch input;
  CI VPC jobs skipped in plan mode. 24 new tests.
- **P68 — sync decks source .md (REQ-131/132).** Re-verification headers
  → 22/22 Verified. De-duplicated story-beat lines. Version refs
  @v1.10 → @v1.11. road-to-north-star.mmd re-rendered. New A6 (real
  COST.md figures + pre-mortem) + A7 (stateless adapter + lifecycle
  pipeline + plan-only default) in both decks.
- **P69 — Marp + talking points + HTML render (REQ-133).** Re-synthesized
  both Marp decks (10 main + 7 appendix = 17 slides). Re-distilled both
  talking-points files (added the missing A6/A7 sections). Re-rendered
  both HTML (committed). Exported both PPTX (held for the v1.12.0
  release).
- **P70 — verify + review + complete.** Multi-persona code review: no P0;
  2 P1 testing gaps remediated (6 CAP-013 regression tests added —
  terraform validate + dedup unit assertions). Audit clean. 522 tests
  pass. Ship v1.12.0.

**Outcome:** the decks now honestly claim 22/22 Verified (backed by the
regression gate), reflect the v1.11 architecture (stateless adapter +
lifecycle pipeline), carry the real cost figures + pre-mortem, and the
lifecycle pipeline defaults to plan-only. The v1.10 "deploy-unverified
(IAM drift)" framing is disclosed as closed, not hidden.

Ship tag at milestone COMPLETE: `v1.12.0` (v1.11.0 → v1.12.0; feature
milestone — includes the adapter fix + lifecycle flag). **DONE.**

## v1.13 (complete — Presentation Polish, tag `v1.13.0`)

The v1.13 milestone polishes both leadership decks for clarity, impact, and
storytelling best practices. It is a docs-only NFR patch — no code, no
capability claims changed, no schema changes.

**Why.** The v1.12 decks were factually correct but rhetorically flat:
category-name headlines ("The Contract-Driven Model", "Zero-Trust by
Default"), dense 5–6 bullet slides, formulaic italic transition lines on
every slide, and body fonts at 22px (below the 24pt readability floor).
v1.13 applies the storytelling + design best practices the leadership
audience requires.

**What changed.** Both decks (`how-the-platform-works` + `the-developer-experience`)
across all 4 pipeline layers (source `.md` → `-marp.md` → `.html` →
`-talking-points.md`):

- **Action headlines** replace category names — each slide title states the
  takeaway, not the topic ("One YAML file. The platform owns everything
  else." not "The Contract-Driven Model").
- **Story-arc restructure** — Intro ~10% / Body ~80% (3–5 points) /
  Conclusion ~10%. Before/after 2-slide sequence on Problem → North Star.
- **Removed all `<em class="story">` transition lines** — flow now carried
  by action headlines + talking points (saved ~15–20 words × 17 slides).
- **Bullets ≤12 words, 3–4 per main slide** (was 5–6). Cut parentheticals,
  sub-clauses, redundant restatements.
- **Larger fonts** — body 22→26px, h1 34→40px, h2 26→32px, blockquote
  20→24px. High contrast preserved, no animations, maturity badges intact.
- **6 new mermaid diagrams** → PNGs:
  - `platform-works-02-frictions` (2×2 quadrant of the four frictions)
  - `platform-works-03-north-star` (before/after: today's queue vs. ACDL)
  - `platform-works-07-zero-trust` (OIDC + ABAC flow)
  - `developer-experience-05-catalog` (primitives → composed modules)
  - `developer-experience-07-decommission` (6-node gate flow with 2 SRE gates)
  - `developer-experience-08-semver` (floating/pinned/major-only timeline)
- **Appendix expanded** — "Security by Construction" (formerly a dense main
  slide) moved to appendix A3; platform deck now 10 main + TOC + 8 appendix
  (19 slides); developer deck 10 main + TOC + 7 appendix (18 slides).

**Phases:**

- **P71 — deck polish (single phase).** Restructured both decks across all
  4 layers. Created 6 new `.mmd` + 6 new `.png` files. Re-rendered both
  HTML. Re-distilled both talking-points to match the new slide structure.
  Multi-persona code review: 0 P0, 2 P1 auto-fixed (slide-count metadata in
  source `.md` headers + `README.md` directory layout drifted — both
  synced). 522 tests pass. No code files touched.

**Outcome:** the decks now read as a story (problem → solution → evidence),
each slide has one idea with an action headline, the body is scannable in
2 seconds, and the visuals carry the mechanism where bullets undersold it.
22/22 Verified framing, badge statuses, and capability claims preserved
verbatim from v1.12. No factual drift.

Ship tag at milestone COMPLETE: `v1.13.0` (v1.12.0 → v1.13.0; docs-only NFR
patch — final patch IS the deliverable, no separate milestone tag).
**DONE.**

### v1.13.1 (complete, tag `v1.13.1`): config.json schema migration

NFR patch: regenerated `.ciagent/config.json` to the updated CIAgent v2 config
structure. The old config used the pre-v2 schema (`mode` field,
`projects[].milestone/status/branch/tag`, top-level `gitea` block, missing
`secrets`/`release`/`ship`/`backend`/`ideation`/`personas`/`logging`/`telemetry`
sections). The new config conforms to `CIAgentConfig` (config.ts:156) and passes
`validateConfig()` + `loadConfig()` deep-merge.

Multi-persona code review: 0 P0, 2 P1/P2 auto-fixed (`.ciagent/logs/` gitignored,
trailing newline restored). Gitea release id 253. Docs-only NFR patch (no code
changes).

### v1.13.2 (complete, tag `v1.13.2`): presentation badge cleanup + platform architecture diagram

NFR patch (docs-only). Two presentation changes across both leadership decks
(`how-the-platform-works` + `the-developer-experience`):

1. **Badge cleanup** — removed all `testing` and `agentic` maturity badges from
   every deck layer (source `.md`, Marp `-marp.md`, rendered `.html`,
   talking-points). Only the `planned` badges are retained where relevant. The
   Marp inline `style:` CSS dropped the `.testing` / `.agentic` rules (kept
   `.planned`). The README maturity-framing section updated to describe only the
   `Planned` badge. Empty table cells (dev environment Maturity row) normalized
   to `—`.
2. **Platform architecture diagram** — added a new Slide 3 "The platform at a
   glance" to both decks, right after the problem statement. A shared mermaid
   source (`assets/mmd/platform-architecture.mmd`) renders to
   `assets/png/platform-architecture.png` and is embedded in both Marp decks.
   The diagram shows the full logical topology: consumer surfaces (technical dev
   + citizen dev) → contract schema → central pipeline (8 fixed stages) →
   cross-cutting components (module catalog, stateless engine adapter,
   platform-managed environments, HITL gates, hash-chained evidence stream) →
   downstream AWS resources. All subsequent slides renumbered 4–11; talking
   points + README directory layout + slide counts (10→11 main, 19→20 / 18→19
   total) synced. Both HTML decks re-rendered via Marp.

Docs-only NFR patch (no code changes).

---

## v1.14 (complete — NFR Refinement: bug fixes, security, stubs, tests, docs, tag `v1.13.24`)

The v1.14 milestone is a 20-phase NFR sweep — no new features. It clears
the open P1/P2 backlog from the v1.11 review, hardens the security
posture (swallowed errors, hardcoded account ID, IAM wildcards, schema
validation, credential hygiene), resolves stub/missing functionality
(Kyverno `--kube-version`, orphan artifacts), adds test coverage for 7
untested scripts, and refines all documentation (ARCHITECTURE.md
v1.11–v1.14 addenda, stale `@v1.6–1.9` → `@v1.13` refs, COST.md/GRILL/
IAM_POLICY.md sync, STANDARDS.md reconciliation).

**Milestone type:** NFR (all phases fix/test/docs/chore/refactor). The
final phase's patch IS the release — no separate milestone tag. Tags run
on the v1.13.x line: `v1.13.3` (P0) → `v1.13.4..v1.13.23` (P1–P20) →
`v1.13.24` (P21 final = milestone release).

**Wave ordering:**
- Wave 1 (P1–P6): bug fixes — P1 before P2 (composition depends on dedup
  correctness); P3–P6 independent.
- Wave 2 (P7–P12): security — P8 before P9 (externalized account ID for
  IAM ARNs); rest independent.
- Wave 3 (P13–P17): stub/test/CI/hygiene — P15 benefits from P7 landing
  first; P17 after P14 (both touch config.json).
- Wave 4 (P18–P20): standards/docs/VPC — P19 last (reflects all prior
  phases).

### Phase P1 — adapter-dedup-diagnostic (Wave 1)
- **Description:** Fix P1-1 from the v1.11 review. The adapter dedup loop
  (`adapters/terraform/adapter.py:159-170`) silently drops resources whose
  module is not in the registry — a typo'd `module` field vanishes without
  diagnostic. Raise `ValueError` (preserving the pre-dedup contract) so the
  misconfiguration surfaces instead of being silently omitted.
- **Status:** pending
- **Depends on:** —
- **Requirements:** REQ-135
- **Success Criteria:**
  - A resource with `module: nonexistent@1.0.0` raises `ValueError` with a
    descriptive message, not a silent drop.
  - Existing registered-module dedup behavior preserved (multi-resource L1s
    still merge into one `module "x" { ... }` block).
  - `pytest` passes; `run_ci.sh` exits 0.

### Phase P2 — static-assets-wiring-fix (Wave 1)
- **Description:** Fix P1-2. `modules/l2/static-assets/composition.json`
  drops `default_ttl`/`max_ttl`/`price_class`/`viewer_protocol_policy`
  (accepted by `cloudfront/interface.json` but never wired) and WAF is
  unconditionally present (no `features`/conditional). Wire the cloudfront
  inputs; make WAF conditional via a `waf_enabled` feature flag so
  `examples/complex.yml` is a real modify (adds CDN + WAF), not a no-op
  re-apply.
- **Status:** pending
- **Depends on:** [P1]
- **Requirements:** REQ-136
- **Success Criteria:**
  - `complex.yml` resolves to a resource set that differs from `simple.yml`
    (WAF + CDN TTLs present when `waf_enabled: true`, absent when false).
  - The L2 static-assets lifecycle cell's "modify" step exercises a real
    terraform diff, not idempotent re-apply.
  - `pytest` passes; `run_ci.sh` exits 0.

### Phase P3 — lifecycle-script-arg-cleanup (Wave 1)
- **Description:** Fix P1-3. `scripts/run_l2_lifecycle_test.sh` and
  `run_l2_lifecycle_destroy.sh` advertise `[ci-vpc-outputs.json]` ($3) in
  their usage strings but never read it (the L2 path uses
  `terraform_remote_state`, not the file). Remove the vestigial arg or
  document that the L2 path uses remote state and the arg is
  accepted-but-ignored for workflow-argument parity with the L1 scripts.
- **Status:** pending
- **Depends on:** —
- **Requirements:** REQ-137
- **Success Criteria:**
  - Usage strings no longer advertise a feature the scripts don't provide,
    OR a comment explains the L2-uses-remote-state design + parity reason.
  - `pytest` passes; `run_ci.sh` exits 0.

### Phase P4 — regression-gate-evidence-hardening (Wave 1)
- **Description:** Fix P1-5. `core/regression_verify.py:432-519`
  CAP-017..022 checks are offline proxies (files exist + contracts
  resolve) — a module with broken HCL would pass as long as files exist.
  Add a `terraform validate` step to
  `_check_lifecycle_module_terraform` so at least HCL syntax is verified
  at the gate. Tighten the CAPABILITY_INVENTORY wording to "offline proxy;
  live apply/modify/destroy verified by the modules-lifecycle workflow
  run, not by this gate."
- **Status:** pending
- **Depends on:** —
- **Requirements:** REQ-138
- **Success Criteria:**
  - `_check_lifecycle_module_terraform` runs `terraform validate` (or
    documents why it's too slow + falls back to a syntax probe).
  - CAPABILITY_INVENTORY + docstrings reflect the offline-proxy caveat
    honestly.
  - `pytest` passes; `run_ci.sh` exits 0.

### Phase P5 — adapter-behavior-tests (Wave 1)
- **Description:** Fix P2-2. Add `test_adapter_dedup_merges_same_module`
  (two resources with the same `module` collapse to one
  `module "<first_id>" { ... }` block with merged inputs) and
  `test_adapter_remote_state_key_override` (`ACDL_REMOTE_STATE_KEY`
  overrides the default `platform/terraform.tfstate` key in the emitted
  `data terraform_remote_state` block).
- **Status:** pending
- **Depends on:** [P1]
- **Requirements:** REQ-139
- **Success Criteria:**
  - Both unit tests exist in `tests/test_adapter.py` and pass.
  - `pytest` count increases; `run_ci.sh` exits 0.

### Phase P6 — alb-name-prefix-fix (Wave 1)
- **Description:** Fix P2-1. `modules/l1/alb/terraform/main.tf:9` uses
  `name_prefix = "tg-ci-"` (hardcoded literal) which discards `var.name`
  entirely — the target group name is non-configurable and inconsistent
  with the LB name. Change to `name_prefix = "${var.name}-"` so the
  consumer's name prefixes the target group while preserving uniqueness.
- **Status:** pending
- **Depends on:** —
- **Requirements:** REQ-140
- **Success Criteria:**
  - Target group `name_prefix` derives from `var.name`.
  - `terraform validate` passes for the alb module standalone.
  - `pytest` passes; `run_ci.sh` exits 0.

### Phase P7 — swallowed-error-hardening (Wave 2)
- **Description:** Narrow 6 over-broad `except ...: pass`/`except
  Exception:` sites: `core/local_emulators.py:374` (fake_urlopen swallow
  → network egress risk if patching fails), `core/lambda/contract_ingestor.py:157`
  (GitHub search failure → duplicate issues),
  `terraform/bootstrap/create_state_backend.py:51` (over-broad → spurious
  create_bucket), `core/output_publisher.py:100,168`,
  `terraform/bootstrap/apply_iam_baseline.py:78`. Catch specific
  `ClientError`/`NoSuch*` exceptions; log + re-raise where silent failure
  masks a real defect.
- **Status:** pending
- **Depends on:** —
- **Requirements:** REQ-141
- **Success Criteria:**
  - No bare `except Exception: pass` remains in the targeted files (grep
    clean for the 6 sites).
  - Specific exception types caught; errors logged with context.
  - `pytest` passes; `run_ci.sh` exits 0.

### Phase P8 — account-id-externalization (Wave 2)
- **Description:** Externalize the hardcoded account ID `581513795199`
  from 15+ sites: `adapters/terraform/adapter.py:125,140`,
  `terraform/bootstrap/apply_iam_baseline.py:33`,
  `terraform/bootstrap/create_state_backend.py:33,35`,
  `scripts/push_consumer_image.py:32`, terraform state-bucket names, ECR
  image refs. Read from `ACDL_AWS_ACCOUNT_ID` env (code) /
  `data.aws_caller_identity` (terraform); fall back to env for offline.
  Keep the COST.md account ID (accepted exposure per P2-4) but centralize
  the code-side.
- **Status:** pending
- **Depends on:** —
- **Requirements:** REQ-142
- **Success Criteria:**
  - `grep -rn "581513795199" adapters/ scripts/ terraform/ core/` returns
    0 hits (excluding tests + docs).
  - `ACDL_AWS_ACCOUNT_ID` env read with a clear default/fallback.
  - `pytest` passes; `run_ci.sh` exits 0.

### Phase P9 — iam-policy-least-privilege (Wave 2)
- **Description:** Scope 6 `Resource: "*"` statements in
  `terraform/bootstrap/spike_runner_policy.json` (cloudfront, wafv2, kms,
  iam) to `acdl-*` ARNs. Scope `iam:CreateRole` etc. to
  `arn:aws:iam::...:role/acdl-*`; scope KMS to
  `arn:aws:kms:...:key/acdl-*`; narrow CloudFront/WAF where possible.
  Add a regression test asserting no new `Resource:"*"` on non-global
  actions.
- **Status:** pending
- **Depends on:** [P8]
- **Requirements:** REQ-143
- **Success Criteria:**
  - `Resource: "*"` remains only on actions that require it (sts, ce).
  - IAM/KMS/CloudFront/WAF scoped to `acdl-*` ARNs.
  - Regression test in `tests/test_iam_policy_baseline.py` asserts the
    scoping.
  - `pytest` passes; `run_ci.sh` exits 0.

### Phase P10 — contract-ingestor-identity-validation (Wave 2)
- **Description:** Harden `core/lambda/contract_ingestor.py:221-245`
  `_validate_caller_identity` — currently best-effort (validates
  `consumerRepo` format only, doesn't verify the caller owns the repo).
  Add `contractId` format validation, `environment` enum validation,
  `error` length cap. Document the ABAC reliance explicitly. Add a
  spoofing-resistance test.
- **Status:** pending
- **Depends on:** —
- **Requirements:** REQ-144
- **Success Criteria:**
  - `contractId`, `environment`, `error` validated; malformed input
    rejected with 400.
  - ABAC reliance documented in the function docstring + ARCHITECTURE.md.
  - Spoofing-resistance test in `tests/test_contract_ingestor.py` passes.
  - `pytest` passes; `run_ci.sh` exits 0.

### Phase P11 — schema-input-validation-hardening (Wave 2)
- **Description:** Add `additionalProperties: false` to
  `schemas/contract.schema.json` + `schemas/environment.schema.json`
  (currently allows undocumented fields silently). Add `maxItems`/
  `maxProperties` bounds. Validate `state_backend.bucket` S3 naming
  rules, `runner_role_arn` ARN format, `vpc_cidr` CIDR format. Add tests
  asserting rejection of malformed input.
- **Status:** pending
- **Depends on:** —
- **Requirements:** REQ-145
- **Success Criteria:**
  - Both schemas reject undocumented top-level fields.
  - Format validation (bucket/ARN/CIDR) rejects malformed values.
  - New tests in `tests/test_environment_schema.py` +
    `tests/test_contract_schema.py` pass.
  - `pytest` passes; `run_ci.sh` exits 0.

### Phase P12 — gitignore-credential-hygiene (Wave 2)
- **Description:** `.gitignore` covers `.env*`/`*.tfstate*` but lacks a
  credential-pattern catch-all (`*.pem`/`*.key`/`*.p12`/`*.pfx`). Add
  credential patterns. Add `tests/test_no_secrets_tracked.py` asserting no
  credential-looking file is tracked by git.
- **Status:** pending
- **Depends on:** —
- **Requirements:** REQ-146
- **Success Criteria:**
  - `.gitignore` has credential-pattern catch-all.
  - `test_no_secrets_tracked.py` passes (grep `git ls-files` for
    credential patterns → 0 hits).
  - `pytest` passes; `run_ci.sh` exits 0.

### Phase P13 — kyverno-kube-version-resolution (Wave 3)
- **Description:** Resolve the discarded `--kube-version` flag in
  `adapters/kyverno/kyverno_adapter.py:11,115-116` (`_ = kube_version`).
  Either implement version-aware policy selection (select policies by k8s
  version) or remove the flag and document why it's deferred to the
  GitOps reconciler roadmap. Resolve the ambiguity either way.
- **Status:** pending
- **Depends on:** —
- **Requirements:** REQ-147
- **Success Criteria:**
  - `--kube-version` is either used (version-aware policy selection) or
    removed with a documented deferral rationale.
  - `tests/test_kyverno_adapter.py` updated to match.
  - `pytest` passes; `run_ci.sh` exits 0.

### Phase P14 — orphan-artifact-and-dead-config-cleanup (Wave 3)
- **Description:** Clean up orphan artifacts + dead config: the orphan
  `scripts/__pycache__/verify_deploy_microservice.cpython-312.pyc` (source
  deleted in v1.11); stale `pyproject.toml` coverage source
  `acdl_platform` → `core` (renamed in v1.6); `pyproject.toml` version
  `1.3.0` → current; dead JS allowlist entries in `config.json`
  (npm/node/jest/eslint/tsc — no package.json).
- **Status:** pending
- **Depends on:** —
- **Requirements:** REQ-148
- **Success Criteria:**
  - No orphan `.pyc` for a deleted source file.
  - `pyproject.toml` coverage source = `core`; version = current.
  - `config.json` bash_allowlist has no JS-only entries.
  - `pytest` passes; `run_ci.sh` exits 0.

### Phase P15 — untested-scripts-coverage (Wave 3)
- **Description:** Add unit tests for 7 scripts with no test coverage:
  `scripts/seed_uptime_monitors.py`, `scripts/push_consumer_image.py`,
  `scripts/sync_to_gl.sh`, `scripts/post_stage_comment.sh`,
  `scripts/rotate_spike_key.sh`, `terraform/bootstrap/create_state_backend.py`,
  `terraform/bootstrap/create_iam_user.py`. Mock boto3/subprocess for
  offline-testable coverage. Add `--check-only`/dry-run modes where
  missing.
- **Status:** pending
- **Depends on:** [P7]
- **Requirements:** REQ-149
- **Success Criteria:**
  - Each of the 7 scripts has a corresponding test file with ≥1 passing
    test.
  - `pytest` count increases by ≥7; `run_ci.sh` exits 0.

### Phase P16 — workflow-parity-and-script-flags (Wave 3)
- **Description:** 4 GitHub-only workflows (patterns-plan, platform-test,
  primitives-plan, release) have no Gitea mirror — either mirror them or
  document the Gitea limitation. Fix `scripts/rotate_spike_key.sh` (only
  `set -u`, no `-e`/`pipefail`) and `scripts/sync_to_gl.sh` (no `set`
  flags at all) — add `set -euo pipefail`.
- **Status:** pending
- **Depends on:** —
- **Requirements:** REQ-150
- **Success Criteria:**
  - Gitea workflow parity resolved (mirrored or documented).
  - `rotate_spike_key.sh` + `sync_to_gl.sh` have `set -euo pipefail`.
  - `pytest` passes; `run_ci.sh` exits 0.

### Phase P17 — config-and-persona-hygiene (Wave 3)
- **Description:** Fix `config.json` hygiene: `branching_strategy: "phase"`
  mismatch with flat-workflow practice; empty `ollama-cloud` base_url (no
  `glm` model configured); `frontend-engineer` persona `active: false` in
  config.json (PERSONAS.md:80 already says inactive). Align config.json
  with PERSONAS.md + actual runtime.
- **Status:** pending
- **Depends on:** [P14]
- **Requirements:** REQ-151
- **Success Criteria:**
  - `config.json` persona block matches PERSONAS.md (frontend-engineer
    inactive).
  - `branching_strategy` reflects actual practice (or documented).
  - `ollama-cloud` backend configured or documented as intentionally
    unset.
  - `pytest` passes; `run_ci.sh` exits 0.

### Phase P18 — module-standards-consistency (Wave 4)
- **Description:** 3 L1 modules (`ecr`, `ecs-cluster`, `rds`) lack
  `locals.tf`; `modules/STANDARDS.md` §9.4 requires the full 5-file split
  but §489-492 allows inlining — internally inconsistent. Either add
  `locals.tf` to all 3 or reconcile STANDARDS §9.4 with the inline
  allowance. Remove the stale `TYPE_MAP` reference in §8 (deleted in the
  v1.11 stateless rewrite).
- **Status:** pending
- **Depends on:** —
- **Requirements:** REQ-152
- **Success Criteria:**
  - STANDARDS.md internally consistent (§8 + §9.4 agree).
  - No stale `TYPE_MAP` reference.
  - `pytest` passes; `run_ci.sh` exits 0.

### Phase P19 — documentation-sync-v1.14 (Wave 4)
- **Description:** ARCHITECTURE.md: add v1.11/v1.12/v1.13/v1.14 addenda
  (stateless adapter, platform VPC, ACDL_LIFECYCLE_MODE, all v1.14
  changes; record D-083 deferral explicitly). Bump stale `@v1.6–1.9` →
  `@v1.13` across `README.md`, `docs/consumer-guide.md` (12 sites),
  `docs/architecture.md`, `docs/pipeline/`. Sync decks to v1.13.2 reality.
  Update COST.md window to v1.11–v1.14. Resolve G-005/G-008 in GRILL.md
  (CAP-017..022 now Verified via lifecycle pipeline; COST.md now exists +
  covers v1.11+). Update IAM_POLICY.md for v1.12/v1.13/v1.14.
- **Status:** pending
- **Depends on:** [P1-P18]
- **Requirements:** REQ-153
- **Success Criteria:**
  - ARCHITECTURE.md has v1.11–v1.14 addenda; D-083 deferral recorded.
  - `grep -rn "@v1\.[6-9]" docs/ README.md` returns 0 hits (bumped to
    @v1.13).
  - GRILL G-005/G-008 marked resolved with evidence.
  - COST.md window covers v1.11–v1.14.
  - `pytest` passes; `run_ci.sh` exits 0.

### Phase P20 — platform-vpc-parameterization (Wave 4)
- **Description:** `terraform/platform/main.tf:255` hardcodes
  `cidr_block = "10.0.0.0/16"` (not `var.vpc_cidr`); `count = 2` subnets
  hardcoded (not data-driven AZs). Parameterize; document the
  `0.0.0.0/0` ingress on port 80 (ALB-fronted, acceptable but should be
  explicit).
- **Status:** pending
- **Depends on:** —
- **Requirements:** REQ-154
- **Success Criteria:**
  - VPC CIDR is a variable (default `10.0.0.0/16`); subnet count is
    data-driven (`length(data.aws_availability_zones.available)`).
  - `0.0.0.0/0` ingress documented.
  - `terraform validate` passes; `pytest` passes; `run_ci.sh` exits 0.

### Phase P21 — final-review-ship (Final Phase)
- **Description:** Multi-persona code review across all v1.14 phases.
  Audit (reconstruction test, file discipline, branch hygiene, commit
  discipline). Complete: update REQUIREMENTS.md (REQ-135..154 marked
  complete), ROADMAP.md (v1.14 complete), PROJECT.md. Tag final patch
  `v1.13.24` (IS the milestone release). Merge `milestone/v1.14` → `main`.
- **Status:** pending
- **Depends on:** [P1-P20]
- **Requirements:** —
- **Success Criteria:**
  - Review: 0 new P0; all P1-1..P1-5 + P2-1..P2-4 resolved.
  - Audit: clean; reconstruction test passes.
  - Tag `v1.13.24` created; milestone merged to main.

After Phase P21: milestone COMPLETE — `v1.13.24` IS the v1.14 release.

---

## v1.15 (complete — Nova Rebrand, tag `v1.15.4`)

A full rebrand from **ACDL** / "Agentic Cloud Delivery Platform" →
**Nova** / "The New Dawn of DevSecOps — security as a seamless enabler
of fast deployments." The rebrand applies across docs, decks, code,
configs, CI, env var prefixes, the consumer contract path, SSM
parameter paths, AWS tag keys, and AWS resource names — with a staged
infrastructure migration to avoid breakage. The Nova tagline is added
alongside (not replacing) the existing "North Star" / "consumers
declare intent" framing; the S&P Global Energy visual theme
(`sp-theme.json`) is a client brand and is **not** touched.

**Milestone type:** Major (breaking — consumer-facing path, env var
prefixes, SSM path, AWS tag keys, and AWS resource names all change).
Per the branch-strategy precedent (breaking/feature milestones tag on
their OWN minor line), v1.15 tags run on the **v1.15.x minor line**:
`v1.15.0` (P0) → `v1.15.1..v1.15.4` (P1–P4) → `v1.15.4` (P5 final =
milestone release). (G-104 binding.)

**Brand mapping:**
- Name: `ACDL` / `Agentic Cloud Delivery Platform` → `Nova`
- Tagline (added): "The New Dawn of DevSecOps — security as a seamless
  enabler of fast deployments"
- Env var prefix: `ACDL_*` → `NOVA_*` (dual-read fallback in P2;
  removed in P5)
- Consumer path: `.acdl/contract.yml` → `.nova/contract.yml`
- SSM path: `/acdl/{env}/{contractId}/{output}` →
  `/nova/{env}/{contractId}/{output}`
- AWS tag keys: `acdl:owner|environment|contract|cost-center|ref` →
  `nova:*`
- AWS resource names: `acdl-*` → `nova-*` (with migration, P4)
- Illustrative URLs in docs: `github.com/acdl/...` →
  `github.com/nova/...` (prose only; real repo name unchanged)
- Gitea release titles going forward: `ACDL vX.Y.Z` → `Nova vX.Y.Z`
- S&P visual theme: unchanged (client branding)

**Wave ordering:**
- Wave 1 (P1): docs/decks/prose — no runtime impact; establishes new
  vocabulary. REQ-155, REQ-156, REQ-157.
- Wave 2 (P2): code + env vars + consumer path — rename in code with a
  dual-read env fallback so deployments don't break during the
  transition window. REQ-158, REQ-159, REQ-160.
- Wave 3 (P3): SSM path + tag keys — SSM: copy `/acdl/...` →
  `/nova/...`, update readers, delete old. Tag keys: parallel-tag
  period (`nova:*` added, ABAC policy swapped, `acdl:*` removed).
  REQ-161, REQ-162.
- Wave 4 (P4): AWS resource names — the big migration (KMS alias, SNS,
  SG, Lambda, DynamoDB data migration, ECR re-push, IAM re-bootstrap,
  state bucket migration, ALB recreate). Maintenance window + rollback
  runbook. REQ-163.
- Wave 5 (P5): final-review-ship — remove dual-read fallback, consumer
  migration guide finalized, review + audit + milestone ship. REQ-164.

### Phase P1 — docs-decks-prose (Wave 1)
- **Description:** Rebrand all prose, titles, headers, comments,
  deck markdown sources, mermaid `.mmd` sources, `pyproject.toml`
  name/description, and `release.yml` release-title prefix from
  `ACDL`/`Agentic Cloud Delivery Platform` → `Nova`. Add the Nova
  tagline ("The New Dawn of DevSecOps — security as a seamless enabler
  of fast deployments") to the README header, both deck title slides,
  and `docs/vision.md` — alongside the existing "North Star" framing.
  Re-export the mermaid PNG diagrams so committed PNGs match new
  labels. Re-render the deck HTML. Update illustrative URLs in docs
  (`github.com/acdl/...` → `github.com/nova/...`,
  `git.cloudinit.dev/continuous-intelligence/acdl*` → `.../nova*` for
  prose). Ship a consumer migration guide (`docs/NOVA_MIGRATION.md`)
  announcing the `.acdl/`→`.nova/` path, `ACDL_*`→`NOVA_*` env vars,
  `/acdl/`→`/nova/` SSM path, `acdl:*`→`nova:*` tag keys, and
  `acdl-*`→`nova-*` AWS resource names changes coming in P2–P4.
- **Status:** complete (v1.15.1)
- **Depends on:** —
- **Requirements:** REQ-155, REQ-156, REQ-157
- **Success Criteria:**
  - `grep -rni "ACDL\|Agentic Cloud Delivery" README.md docs/ .ciagent/*.md`
    returns 0 hits (except historical narrative marked as historical).
  - `pyproject.toml` `name` = `nova`; `description` mentions Nova.
  - `release.yml` release title prefix is `Nova `.
  - Both decks' title-slide subtitle is
    `Nova — The New Dawn of DevSecOps`; mermaid `.mmd` sources use
    `Nova`; PNGs re-exported; HTML re-rendered.
  - `docs/vision.md` and README header carry the Nova tagline
    alongside the North Star.
  - `docs/NOVA_MIGRATION.md` exists and lists the 5 breaking changes.
  - `pytest` passes; `run_ci.sh` exits 0.

### Phase P2 — code-envvars-consumer-path (Wave 2)
- **Description:** Rename
  `adapters/terraform/policy/custom_rules/acdl_tagging.py` →
  `nova_tagging.py` (+ Checkov custom-rule registration in
  `schemas/tagging-standard.json` + adapter config). Rename all 21
  `ACDL_*` env var prefixes → `NOVA_*` across `scripts/`, `core/`,
  `adapters/`, `tests/`, workflows (`.gitea/`, `.github/`), `.env`,
  `.env.secrets` (key names only — values stay), and consumer docs.
  Implement a **dual-read fallback** (`NOVA_X` preferred, fall back to
  `ACDL_X`) in the env/config loader so deployments don't break during
  the transition window. Rename the consumer on-disk contract path
  `.acdl/contract.yml` → `.nova/contract.yml` (and
  `.acdl/static-assets.*.yml`, `.acdl/contract.yaml`) across the
  contract resolver, deploy workflow checkout path, consumer docs, and
  the contract schema description. Rotate Gitea repo secrets via API
  (rename keys `ACDL_*` → `NOVA_*`, values stay).
- **Status:** complete (v1.15.2)
- **Depends on:** [P1]
- **Requirements:** REQ-158, REQ-159, REQ-160
- **Success Criteria:**
  - `nova_tagging.py` exists; `acdl_tagging.py` removed; Checkov
    registration updated; rule enforces `nova:*` tag keys (tag-key
    enforcement of `nova:*` lands here; existing resources still carry
    `acdl:*` until P3 parallel-tag — rule warns during P2).
  - No `ACDL_` env var references remain in code/scripts/workflows/tests
    except the dual-read fallback in the loader + `.env.secrets` legacy
    comment.
  - Dual-read fallback implemented and unit-tested.
  - Contract resolver reads `.nova/contract.yml`; deploy workflow
    checks out `.nova/`; docs updated.
  - `pytest` passes; `run_ci.sh` exits 0.

### Phase P3 — ssm-tagkeys (Wave 3)
- **Description:** SSM path migration: rename the parameter path prefix
  `/acdl/{env}/{contractId}/{output}` →
  `/nova/{env}/{contractId}/{output}` across `core/output_publisher`,
  the contract resolver, and consumer docs. Add a migration script
  (`scripts/migrate_ssm_paths.py`) that copies existing `/acdl/...`
  parameters → `/nova/...`, then readers are updated, then old
  parameters are deleted. Tag key migration: add `nova:*` tags to all
  AWS resources (parallel-tag period), update the ABAC session policies
  to match `nova:*`, update `nova_tagging.py` to enforce `nova:*`
  (hard, no warn), then remove `acdl:*` tags once consumers are
  verified. Terraform tagging updated to emit `nova:*`.
- **Status:** complete (v1.15.3)
- **Depends on:** [P2]
- **Requirements:** REQ-161, REQ-162
- **Success Criteria:**
  - SSM readers use `/nova/...`; migration script copies + deletes;
    test asserts new path.
  - `nova_tagging.py` enforces `nova:*` (hard fail on `acdl:*`).
  - ABAC session policies match `nova:*`; terraform emits `nova:*` tags.
  - `acdl:*` tags removed from all resources (verified via `aws` CLI or
    documented deferred if no live AWS access).
  - `pytest` passes; `run_ci.sh` exits 0.

### Phase P4 — aws-resource-migration (Wave 4)
- **Description:** Rename all `acdl-*` AWS resources → `nova-*` via
  terraform with a staged migration: KMS alias `alias/acdl-platform` →
  `alias/nova-platform` (repoint), SNS `acdl-sod-halt` →
  `nova-sod-halt` (recreate), SG `acdl-ecs-sg` → `nova-ecs-sg`
  (recreate), Lambda `acdl-contract-ingestor` →
  `nova-contract-ingestor` (recreate), DynamoDB `acdl-contracts`/
  `acdl-change-requests` → `nova-contracts`/`nova-change-requests`
  (scan+copy data migration, verify row counts, keep old tables until
  verified), ECR `acdl-microservice` → `nova-microservice` (re-push
  images), IAM user/policy `acdl-spike-runner` → `nova-spike-runner`
  (re-bootstrap with new key), state bucket `acdl-tfstate-...` →
  `nova-tfstate-...` (`terraform init -migrate-state` to new backend,
  state JSON backed up first), ALB name prefix `acdl-alb` → `nova-alb`
  (recreate, brief downtime). Publish a maintenance window + rollback
  runbook (`docs/NOVA_AWS_MIGRATION.md`). For the offline/local tier,
  the terraform `name`/`resource` labels change so `terraform validate`
  passes; live apply/modify/destroy is exercised by the
  modules-lifecycle workflow when `ACDL_LIFECYCLE_MODE` (now
  `NOVA_LIFECYCLE_MODE`) is set to full.
- **Status:** complete (v1.15.4)
- **Depends on:** [P3]
- **Requirements:** REQ-163
- **Success Criteria:**
  - All terraform resource names/labels use `nova-*`; `terraform
    validate` passes for platform/microservice/ci-vpc.
  - State bucket name → `nova-tfstate-...`; `terraform init
    -migrate-state` documented + tested offline.
  - DynamoDB data-migration script exists (scan+copy, row-count
    verify).
  - `docs/NOVA_AWS_MIGRATION.md` runbook exists (maintenance window,
    rollback steps).
  - `grep -rn "acdl-" terraform/` returns 0 hits.
  - `pytest` passes; `run_ci.sh` exits 0.

### Phase P5 — final-review-ship (Final Phase)
- **Description:** Multi-persona code review across all v1.15 phases.
  Audit (reconstruction test, file discipline, branch hygiene, commit
  discipline). Remove the dual-read env var fallback (`ACDL_*`→`NOVA_*`)
  once all consumers are migrated; finalize the consumer migration
  guide; `nova_tagging.py` no longer accepts `acdl:*` tag keys. Complete:
  update REQUIREMENTS.md (REQ-155..164 marked complete), ROADMAP.md
  (v1.15 complete), PROJECT.md. Tag final patch `v1.14.5` (IS the
  milestone release). Merge `milestone/v1.15-nova` → `main`.
- **Status:** complete (v1.15.4, milestone release)
- **Depends on:** [P1-P4]
- **Requirements:** REQ-164
- **Success Criteria:**
  - Review: 0 new P0; all P1+ flagged or auto-fixed.
  - Audit: clean; reconstruction test passes.
  - Dual-read fallback removed; `nova_tagging.py` hard-fails `acdl:*`.
  - Tag `v1.15.4` created; milestone merged to main.

After Phase P5: milestone COMPLETE — `v1.15.4` IS the v1.15 release.

---

## v1.16 (complete — Nova Simplification, tag `v1.15.26`)

A 20-phase NFR sweep (no new features) themed around five user-directed
axes: **Simplify without regressions**, **Security**, **Maintainability**,
**User/Developer Experience**, **No Humans Onboarding Flow**. The v1.15
rebrand left a fresh debt layer (stale brand strings, a state-bucket
drift, a Kyverno policy contradicting the Nova tagging standard, dead
code) that this milestone cleared, alongside genuine simplification
(dedup helpers, a workflow generator, file splits) and the first
self-service onboarding request path (request-path only; real AWS
provisioning deferred, D-113).

**Milestone type:** NFR (all phases fix/chore/docs/refactor/test). The
final phase's patch IS the deliverable. Tags on the v1.15.x line:
`v1.15.5` (P0) → `v1.15.6..v1.15.25` (P1–P20) → `v1.15.26` (P21 final =
milestone release).

**Regression gate (D-118, G-111):** 18 Verified + 4 Skipped (CAP-013..016
live-AWS caps are the post-v1.11-teardown steady state, D-096; re-
provisioning is a future feature). 0 Decayed/Broken at P9 + P21.

**Grill:** PASS-with-binding (G-111..G-113, E-002 deferred to P21).
G-111: gate criterion restated 18V+4S + Skipped logic. G-112: P9 source
model pinned. G-113: P12/P13 import direction documented.

**Wave outcomes:**
- Wave 1 (P1–P4): state-bucket + Kyverno rebrand fix (correctness
  regression), user-facing ACDL→Nova sweep, dead-code cleanup, except
  narrowing.
- Wave 2 (P5–P9): regression-verify dedup (~70 lines), run-platform
  HITL fn + config, contract-resolver envloader + registry kind, workflow
  generator (sync_workflows.py + workflows-src/), run-platform split
  (decommission + uptime helpers). Gate PASS at P9.
- Wave 3 (P10–P14): ingestor defense-in-depth (fail closed on missing
  IAM), payload validation (size cap + schema), split contract-resolver
  (decommission + CLI modules), split regression-verify (CLI module),
  schema-driven outputs + schema cache. Mid-milestone checkpoint clean.
- Wave 4 (P15–P17): run-platform --help + flags doc, workflows README
  catalog (7 workflows), getting-started consolidation (offline-first).
- Wave 5 (P18–P20): onboarding schema + onboard_consumer Lambda action,
  env-file autogen (core/onboarding.py), cross-account role Terraform
  (offline-proven, D-114).

**Outcome:** 20 requirements (REQ-165..184) satisfied; ~630 tests pass;
regression gate 18V+4S; the onboarding request path is self-service (no
"contact the platform team" handoff); real AWS provisioning explicitly
deferred (D-113/D-114).

Ship tag at milestone COMPLETE: `v1.15.26` (NFR milestone; final patch IS
the release). **DONE.**

## v1.18 (complete — Citizen Developer & Production-Grade Guidance, tag line `v1.17.x`)

Nova advances from a platform that governs infrastructure delivery to one
that **instructs the citizen developer on production-grade engineering**
and defines a **clear, machine-checkable contract for what is acceptable
to start**. Five user-directed inputs drive the milestone:

1. **S&P Global theme restoration** (P1) — the v1.17 P5 deck rebuild lost
   the S&P Global Energy brand visual identity (introduced v1.9.2 / P45).
   The Marp `style:` block (`#D6002A` red, `#1B1B1B` grey-90, Akkurat Pro,
   8px accent bar) is restored to the unified deck.
2. **PDLC-upstream scope** (P2) — promotes Core Tenet #2 + Anti-Goal #1
   from buried tenets to a dedicated, unmissable scope statement: the PDLC
   is upstream of Nova; Nova governs infra + delivery only.
3. **RACI matrix** (P2) — three-role responsibility matrix (Citizen
   Developer / Platform / Release Management co-owned) clarifies who owns
   what, with the compliance-standard-equivalence note.
4. **Nova input contract** (P3) — `schemas/submission-readiness.schema.json`
   + `core/submission_readiness.py` validator define "what is acceptable to
   start" as a superset gate above contract-schema validity.
5. **Atelier integration** (P4+P5) — skills (markdown, extending BA.A) + an
   MCP server (plugin-registry, vendored Atelier, agentic validation
   beyond Wiz/Checkmarx/Mend).

**Milestone type:** Feature (P1 theme restoration + P3 schema/validator +
P5 MCP server are new code). Tags run on the v1.17.x patch line:
`v1.17.0` (P0) → `v1.17.1..v1.17.6` (P1–P6) → `v1.17.7` (P7 final =
milestone release).

**Deck automation (cross-cutting, REQ-228):** any phase modifying
`docs/presentations/*-marp.md` or `docs/presentations/assets/` re-renders
HTML + PPTX, commits the PPTX binary to git, and attaches it to the
phase's Gitea release.

**Phase count:** 8 (P0 pre-execution + 6 execution + 1 final).

**Phases:**
- **P1 — sp-theme-restoration** (feat): restore S&P Global Marp theme to
  unified deck + HTML re-render + PPTX commit + release attach. REQ-214,228.
- **P2 — pdlc-scope-raci** (docs): PDLC-upstream scope + RACI matrix +
  2 deck slides + HTML/PPTX re-render. REQ-215,216,228.
- **P3 — submission-readiness** (feat): JSON Schema + validator + docs +
  tests. REQ-217,218,219,220.
- **P4 — atelier-skills** (docs): 9 Atelier-derived skill files + index +
  BA.A extension. REQ-221,222.
- **P5 — atelier-mcp** (feat): plugin-registry MCP server + vendored
  Atelier + 4 tools + tests. REQ-223,224,225.
- **P6 — deck-slides-atelier** (docs): 3 new deck slides (scope/RACI/atelier)
  → 21 slides + talking points + HTML/PPTX re-render + README. REQ-226,227,228.
- **P7 — final-review-ship** (final): review + audit + milestone ship.

**Requirements:** REQ-214..228 (15 requirements). See
`.ciagent/REQUIREMENTS.md` §v1.18.

**Open decisions to lock (CLARIFY/GRILL):** D-133 (validator location),
D-134 (deck slide budget), D-135 (MCP transport), D-136 (Atelier vendoring),
D-137 (MCP server language), D-138 (skill format), D-139 (RACI roles),
D-140 (MCP plugin-registry), D-141 (PPTX storage), D-142 (deck render trigger).

**Outcome:** 15 requirements (REQ-214..228) satisfied; 32 tests pass (16
submission-readiness + 16 MCP); S&P Global Energy theme restored; PDLC-
upstream scope + RACI matrix authored (PROJECT.md + docs/ + deck);
submission-readiness schema + validator shipped (superset gate above
contract.schema.json); 9 Atelier-derived skills + docs/skills.md; MCP
server (plugin-registry, stdio, vendored Atelier v0.3.6) with 4 tools +
agentic validation beyond Wiz/Checkmarx/Mend; 21-slide deck (3 new slides:
scope/RACI/atelier) with PPTX committed + release-attached. 10 decisions
locked (D-133..D-142).

Ship tag at milestone COMPLETE: `v1.17.7` (feature milestone; final patch
IS the release). **DONE.**

## v1.19 (complete — Nova 2nd-Release Sync, tag line `v1.18.x`)

> **NFR-only chore milestone.** Single execution phase. Establishes the
> manual-only "2nd release" pipeline `~/acdl → ~/nova` (GitLab
> `jonathanchery/nova`, separate repo + history, consumer/platform-team
> audience). Replaces the old `~/gl/acdl` mirror sync.

### Phase P1 — nova-sync-script (Wave 1)
- **Description:** Replace `scripts/sync_to_gl.sh` (kitchen-sink mirror sync
  into `~/gl/acdl`) with `scripts/sync_to_nova.sh` — a manual-only,
  consumer-subset, domain-committed 2nd-release pipeline into `~/nova`.
  Excludes `.ciagent/`, `terraform/`, `demo/`, runtime metrics, and
  internal-only scripts. Protects `~/nova/.git`. Commits per domain in a fixed
  order using positional `-m` conventional-commit messages. Validates
  conventional format. Never triggerable by CI (`--release` gate).
- **Status:** complete
- **Depends on:** —
- **Requirements:** REQ-229
- **Success Criteria:**
  - `scripts/sync_to_nova.sh` exists with `set -euo pipefail`.
  - Refuses without `--release` (exit 2); `--list-domains` prints 13 domains.
  - rsync excludes `.ciagent`, `terraform`, `demo`, internal scripts, runtime
    metrics; protects destination `.git`.
  - Domain commits in fixed order; positional `-m` mapping; conventional
    format validated.
  - `scripts/sync_to_gl.sh` removed.
  - `pytest` passes; `run_ci.sh` exits 0.

### Phase P2 — final-review-ship (Final Phase)
- **Description:** Final review + audit + milestone ship. Merge to main, tag
  `v1.18.0` (first patch on the v1.18.x line), create Gitea release.
- **Status:** complete
- **Depends on:** [P1]
- **Requirements:** REQ-229
- **Success Criteria:**
  - Review + audit clean (no P0).
  - `phase/02-final-review-ship` merged to `milestone/v1.19-nova-sync` then to
    `main`.
  - Tag `v1.18.0` created; release notes summarize REQ-229.
  - Milestone branches deleted; CHECKPOINT cleared.

Ship tag at milestone COMPLETE: `v1.18.1` (NFR milestone; final patch IS the
release). **DONE.**

---

## v1.20 — Consumer Cleanup + Transparent Terraform + Slide Pipeline

> **Multi-concern milestone.** Four user-directed inputs: (1) remove all
> gitea/gitlab from synced files — the platform team must never know about
> the dev forge; (2) radically simplify documentation for the Platform Team
> audience; (3) make terraform runs transparent in workflows with feature-flag
> client differentiation; (4) dedicated S&P-themed slide render pipeline +
> 12-month product roadmap slides.
>
> Tags run on the v1.19.x line (milestone v1.20 → tags v1.19.x).

### Phase P0 — pre-execution
- **Description:** Specify → clarify → research → plan. Validate v1.20
  requirements (REQ-230..244). Establish milestone version in config.json.
- **Status:** complete
- **Requirements:** REQ-230..244
- **Success Criteria:**
  - `.ciagent/REQUIREMENTS.md` has v1.20 section with all 15 requirements.
  - `.ciagent/config.json` has `active_milestone: "v1.20"`.
  - Checkpoint written.

### Phase P1 — consumer-cleanup (gitea removal + doc simplification)
- **Description:** Remove all gitea/gitlab mentions from synced files.
  Genericize forge-detection code. Drop `.gitea/` byte-identity test
  assertions. Add `test_no_forge_mentions.py` guard test. Simplify
  documentation: delete completed migration docs, move thesis to `.ciagent/`,
  strip ciagent-internal provenance from synced docs.
- **Status:** complete
- **Requirements:** REQ-230, REQ-231, REQ-232
- **Success Criteria:**
  - `tests/test_no_forge_mentions.py` passes — zero gitea/gitlab mentions in
    synced subset.
  - `pytest` passes — all existing tests green after genericization.
  - Synced docs stripped of REQ-/D-/P- IDs, milestone headers, `.ciagent/`
    citations.
  - `docs/NOVA_MIGRATION.md` + `docs/NOVA_AWS_MIGRATION.md` deleted.
  - `docs/NO_HUMANS_THESIS.md` moved to `.ciagent/`.

### Phase P2 — slide-pipeline (S&P theme + render automation)
- **Description:** Create dedicated S&P theme CSS, render_slides.sh pipeline,
  CI workflow, tests. Update Marp frontmatter to use dedicated theme. Fix
  README directory layout.
- **Status:** complete
- **Requirements:** REQ-239, REQ-240, REQ-241, REQ-242, REQ-243
- **Success Criteria:**
  - `docs/presentations/assets/nova-sp-theme.css` exists with S&P colors.
  - Marp deck frontmatter references the theme CSS.
  - `scripts/render_slides.sh` renders mermaid PNGs + HTML + PPTX.
  - `workflows-src/slides.yml` + `.github/workflows/slides.yml` exist.
  - `tests/test_slides_pipeline.py` passes.
  - `docs/presentations/README.md` updated (no retired decks).

### Phase P3 — product-roadmap (12-month slides)
- **Description:** Add 12-month product roadmap as Slide 20 + Slide 21 to the
  deck. Add matching talking-points sections. Render via new pipeline.
- **Status:** complete
- **Requirements:** REQ-244
- **Success Criteria:**
  - Slide 20 + 21 in `nova-no-humans-platform-marp.md` + source-of-truth +
    talking-points.
  - HTML + PPTX re-rendered via `render_slides.sh`.
  - 4-quarter product arc grounded in NORTH_STAR + deferred metrics.

### Phase P4 — transparent-terraform (workflow refactor + feature flags)
- **Description:** Split run_platform.sh → run_codegen.sh + run_postapply.sh.
  Rewrite deploy.yml with native terraform steps. Add var.enabled to all L1
  modules + L2 composition toggles. Wire forge repo variables as feature
  flags. Fix stale artifact path.
- **Status:** complete
- **Requirements:** REQ-233, REQ-234, REQ-235, REQ-236, REQ-237, REQ-238
- **Success Criteria:**
  - `scripts/run_codegen.sh` + `scripts/run_postapply.sh` exist.
  - `deploy.yml` has native terraform init/validate/plan/apply steps.
  - Every L1 module has `variable "enabled"` + `count = var.enabled ? 1 : 0`.
  - L2 `composition.json` supports per-child `enabled`.
  - `deploy.yml` reads `vars.ENABLE_*` as `-var` flags.
  - Stale `/tmp/acdl_platform_run_v18` path fixed to `NOVA_WORK_DIR`.
  - `pytest` passes; `run_platform.sh` shim backward-compat verified.

### Phase P5 — final-review-ship (Final Phase)
- **Description:** Final review + audit + milestone ship. Merge to main,
  tag `v1.19.4` (final patch = milestone release), create release.
- **Status:** complete
- **Depends on:** [P1, P2, P3, P4]
- **Requirements:** REQ-230..244
- **Success Criteria:**
  - Review + audit clean (no P0).
  - Milestone branches merged to main.
  - Tag `v1.19.4` created; release notes summarize all 15 requirements.
  - CHECKPOINT cleared; milestone branches deleted.

## v1.21 — Nova Deck Refinement & Pipeline Hardening (complete)

> Leadership-deck refinement based on 33 review notes on the v1.20 deck.
> Renamed the deck to the professional "Autonomous Cloud Delivery
> Platform" framing; restructured the narrative (Problem → Solution →
> Proof → Roadmap + Ask); removed internal provenance from
> audience-facing slides; hardened the policy pipeline (Checkov before
> plan, Wiz-or-Checkov on plan); moved the strategic integration
> objective into the North Star.
>
> Tags run on the v1.20.x line (milestone v1.21 → tags v1.20.0..v1.20.6).
> Flat workflow: commits on main, tags per phase.

### Phase P0 — pre-execution (complete, tag v1.20.0)
- SPECIFY → CLARIFY → RESEARCH → PLAN. Validated v1.21 requirements
  (REQ-245..253). Established `active_milestone: "v1.21"`. Synced
  PROJECT.md strategic-direction pillar.

### Phase P1 — strategic-docs (complete, tag v1.20.1)
- `git mv .ciagent/NO_HUMANS_THESIS.md .ciagent/AUTONOMY_THESIS.md` +
  reframe content (autonomy in operations, not "removing humans").
- `NORTH_STAR.md`: vision polished ("invisible" → "visible"); obj #2
  deterministic-scoring reword; obj #3 four CTO metrics; obj #4 replaced
  with integration objective; drop anti-goals 1,4,5; add 2 new
  anti-goals; anti-goal #3 reworded.
- `docs/raci.md`: 3 roles → 4 roles (add Quality Engineering; rename
  Release Mgmt → SRE; split release attestation).
- `docs/scope.md` + render scripts + ONBOARDING: integration framing +
  "no-humans" → "autonomous".

### Phase P2 — slides source-of-truth (complete, tag v1.20.2)
- `git mv` all 5 deck files `nova-no-humans-platform*` →
  `nova-autonomous-cloud-delivery*`.
- Rewrote source of truth to 18 main + 1 appendix slides, 4-beat arc.
  All 33 review notes applied. Removed: old Slide 10 (Capability
  Health), old Slide 12 (Zero-Touch), Appendix A2 (Operating Model &
  Cost). Global: tech-leadership benefits; no D-###/REQ-###/.py paths in
  audience slides; no badges; no version in footer.

### Phase P3 — marp deck + talking points + README (complete, tag v1.20.3)
- Synthesized Marp deck from updated source; frontmatter — title
  "Nova — The Autonomous Cloud Delivery Platform", footer without
  version + without "Act N/5", title-slide subtitle "Product Development
  & Citizen Developer Overview"; no badges.
- Re-distilled talking points to 18-slide + A1 structure.
- README updated (deck title, audience, slide count, directory layout,
  no badge docs).
- Theme CSS: fixed Appendix A1 table readability (explicit white body
  on any background).
- Tests: added v1.21 assertions (no badges, no version, 18+1 slides, no
  D-###/REQ-###/.py paths, old files removed, default deck renamed).

### Phase P4 — pipeline hardening (complete, tag v1.20.4)
- Two-stage policy scan (REQ-250): Checkov on static code BEFORE plan
  (fail-fast); Wiz-or-Checkov on the plan AFTER plan (never both).
  Implemented in run_platform.sh + run_codegen.sh + run_postapply.sh.
- `adapters/wiz/wiz_adapter.py`: added --plan mode CLI.
- `pipelines/contract.yml`: 'checkov' stage replaced by 'checkov-static'
  (before terraform-plan) + 'runtime-policy-scan' (after). 9 → 10 stages.
- Tests updated; full suite 686 pass + 1 pre-existing attestation
  failure (unrelated env issue).

### Phase P5 — render + verify (complete, tag v1.20.5)
- New mermaid diagrams: platform-pipeline.mmd/.png (slide 6),
  telemetry-live-ops.mmd/.png (slide 9).
- Re-rendered HTML + PPTX (20 slides, 21 media files).
- Verify: 101 v1.21-specific tests pass; 686 full suite pass;
  check-only pipeline exit 0; no no-humans/D-###/REQ-###/badge in
  audience-facing deck files.

### Phase P6 — final-review-ship (Final Phase, complete, tag v1.20.6)
- Multi-file audit: git log matches `.ciagent/` discipline; deck files
  renamed; forbidden content absent from audience-facing slides.
- Ship: tag `v1.20.6` (final patch = milestone release). Requirements
  marked complete; ROADMAP marked complete; CHECKPOINT cleared.
- **Requirements:** REQ-245..253 (9 requirements, all complete).

## v1.22 — Nova Deck Layout Fix (complete)

> Fixes the systemic layout/formatting problems in the Nova presentation
> deck that made every slide look "out of whack" after the v1.21 P5
> re-render. Root cause (per investigation): `nova-sp-theme.css` had
> zero `section` padding (declared `/* @theme nova-sp */` as a comment,
> not the `@theme` directive; did not `@import` Marp's default theme).
> Combined with `overflow:hidden`, a blunt `img { max-height: 320px }`,
> header+footer chrome on every slide, and two P5 diagrams with extreme
> aspect ratios (13.52× and 0.63×), 8 of 19 slides overflowed.
>
> Tags run on the v1.21.x line (milestone v1.22 → tags v1.21.0..v1.21.6).

### Phase P0 — pre-execution (complete, tag v1.21.0)
- SPECIFY → CLARIFY → RESEARCH → PLAN → GRILL. Validated v1.22
  requirements (REQ-254..262). 8 research findings persisted to
  RESEARCH.md. 5 CLARIFY decisions auto-resolved (comprehensive scope,
  full pipeline, re-layout to LR, delete render_deck.sh, split slides
  3+8). Persona roster: 2 active (lead-developer + backend-engineer),
  2 deactivated (frontend + data). Grill: PROCEED-WITH-REVISIONS
  (3 revisions: aspect-ratio test scoped to deck PNGs, @import
  rejection documented, marp version pinning fallback).

### Phase P1 — theme-css (complete, tag v1.21.1)
- REQ-254: `section { padding: 48px 56px 40px; overflow: auto; }` —
  root cause fix (zero padding was why every slide looked jammed
  against the edges).
- REQ-255: `img { max-width: 100%; max-height: 380px; object-fit:
  contain; }` + `.wide`/`.tall` classes — replaced blunt
  `max-height: 320px` that broke `w:` directives on tall images.
- REQ-256: `section.title header/footer { display: none; }` — title
  chrome suppression. `h2 + p { margin-top: 0.2em; }`, `p { margin:
  0.4em 0; }` — spacing tightening. `ol` styling. `table.dense`
  class. `@media print { section { overflow: hidden; } }` for PPTX.

### Phase P2 — render-scripts (complete, tag v1.21.2)
- REQ-257: deleted `scripts/render_deck.sh` (omitted `--theme`,
  produced unthemed output). Pinned marp-cli@4.5.0 + mermaid-cli@
  11.16.0 in `render_slides.sh`. Removed references from README,
  sync_to_nova.sh, test_no_forge_mentions.py.
- REQ-258: added `-s 2 -b transparent` to mermaid-cli invocation
  (README spec; produces crisp 2x PNGs with transparent backgrounds).

### Phase P3 — mermaid-relayout (complete, tag v1.21.3)
- REQ-259: `telemetry-live-ops.mmd` kept as `flowchart TB` (the 3-way
  branch makes LR too wide at 4.22 aspect; TB gives 0.63 which is
  legible at h:480 with img.tall class). Re-rendered at 2x transparent
  (1024x1628).
- REQ-260: `platform-pipeline.mmd` restructured from 10-node LR chain
  (aspect 13.52, illegible 1000x74 strip) to 4-node TB with combined
  nodes. Re-rendered at 2x transparent (552x1116, aspect 0.49).
- Marp deck directives updated: `![w:1000]`/`![w:900]` →
  `![h:480 class:tall]` so images render at legible height using the
  img.tall class budget (480px).
- Aspect-ratio bounds revised from [1.2, 2.5] to [0.4, 4.0] (accepts
  both tall and wide diagrams; still catches original outliers).

### Phase P4 — deck-content (complete, tag v1.21.4)
- REQ-261: split slide 3 (Objectives + Anti-Goals) into Slide 3
  (Objectives) + Slide 4 (Anti-Goals). Split slide 8 (Attestation
  Matrix) into Slide 9 (QA, 3 rows) + Slide 10 (Prod/DR, 7 rows).
  Main slide count 18 → 20.
- Trimmed: slide 7 (Pipeline) to 3 bullets. slide 11 (Telemetry) to
  3 bullets. slide 14 (Deferred) merged 3 Live-AWS rows into 1 (8→6
  rows). slide 17 (Quarter-by-Quarter) dropped Grounding column
  (5→4 cols). Global table cell padding reduced (6px 10px → 4px 8px).
- Removed `header:` from frontmatter (keep `footer:` + `paginate`
  only). The full 51-char deck title in BOTH header and footer was
  redundant chrome eating ~35px on every slide.
- Source `.md` and talking-points re-synced to 20-slide structure.
- Updated `test_marp_deck_slide_count` (18→20 main + 1 appendix).
  Updated README slide-count convention (all 6 references).

### Phase P5 — render-and-test (complete, tag v1.21.5)
- REQ-262: re-rendered HTML + PPTX via `render_slides.sh` (pinned
  marp-cli@4.5.0, mermaid-cli@11.16.0, 2x transparent PNGs). 22
  slides (title + 20 main + 1 appendix), 23 media files embedded.
  Theme embedded in HTML (--sp-red + padding confirmed).
- Added 9 tests to `test_slides_pipeline.py` (the gap that let the
  layout regression through): test_theme_css_has_section_padding,
  test_theme_css_suppresses_title_chrome,
  test_theme_css_has_aspect_ratio_aware_images,
  test_png_aspect_ratios_sane (scoped to deck-referenced PNGs only
  per GRILL revision 1, bounds [0.4, 4.0]),
  test_render_slides_has_2x_scale, test_render_slides_pins_cli_versions,
  test_render_deck_removed, test_html_embeds_theme,
  test_html_slide_count_matches_marp.
- 32 slide tests pass (23 original + 9 new). 94 key-file tests pass.
  `run_platform.sh --check-only` exit 0.

### Phase P6 — final-review-ship (Final Phase, complete, tag v1.21.6)
- Multi-persona code review: PASS with 3 P1 flags (all fixed in this
  phase): source .md/talking-points re-synced to 20 slides, `![h:480
  class:tall]` directives applied, README stale references updated.
- Audit: git log matches `.ciagent/` discipline; all commits have
  `---ci---` blocks; branch hygiene verified.
- Ship: tag `v1.21.6` (final patch = milestone release). Merge
  `milestone/v1.22-deck-layout-fix` → `main`. Requirements marked
  complete; ROADMAP marked complete; CHECKPOINT cleared.
- **Requirements:** REQ-254..262 (9 requirements, all complete).

## v1.23 — Nova Deck Cleanup & Python PPTX (complete)

> **NFR milestone** (docs/render/test only; no features). Tags run on the
> **v1.22.x** line (milestone v1.23 → tags v1.22.0..v1.22.6). Final patch
> `v1.22.6` = milestone release. Branch: `milestone/v1.23-deck-cleanup-python-pptx`.
>
> Driven by the user's feedback that the deck looked "out of whack" and
> the desire to return to the clean, well-formatted style of the old
> `the-developer-experience.html` (which used Marp's built-in `default`
> theme + an inline `style:` block). That investigation revealed:
> (1) the "clean" reference was itself MARP output — MARP is not the
> problem; (2) the current deck uses a standalone `nova-sp-theme.css`
> that re-derives all base spacing from scratch and had a zero-padding
> bug (fixed in v1.22 but the standalone approach is fragile);
> (3) there are two markdown documents (a plain source-of-truth `.md`
> and a manually-synthesized `-marp.md`) that should be consolidated;
> (4) images are referenced as file paths in the HTML, so the HTML
> breaks when redistributed without the `assets/` folder; (5) the deck
> is verbose in places and uses the term "penetrate" which the user
> wants removed.
>
> The milestone delivers: single-document consolidation, clean style
> restoration (Marp `default` + inline `style:`), self-contained HTML
> (base64 images), a parallel structured python-pptx PPTX generator,
> targeted word-count trim, and "penetrate" removal. `nova-sp-theme.css`
> is retained as a styling reference but retired from the render path.

### Phase P0 — pre-execution (active)
- SPECIFY → CLARIFY → RESEARCH → PLAN → GRILL. Establishes v1.23
  requirements (REQ-263..275). Tag `v1.22.0`. Grill PROCEED-WITH-
  REVISIONS (0.78): 4 binding revisions applied (G-001 repo-wide
  "penetrate" purge; G-002 P3→P4 serialized; G-003 P3 split P3a+P3b;
  G-004 P5+P6 merged).

### Phase P1 — consolidate-docs (planned, tag v1.22.1)
- REQ-263: fold speaker notes + talking points into `*-marp.md` as Marp
  HTML comments; delete the plain `.md`. `-marp.md` becomes the sole
  source of truth.
- REQ-264: keep `*-talking-points.md` as a standalone presenter aid,
  synced from the deck's `<!-- Talking points: -->` comments.

### Phase P2 — restore-clean-style (planned, tag v1.22.2)
- REQ-265: revert frontmatter to `theme: default` + inline `style:`
  block (S&P palette). Keep H2 + bold-lead structure, no header, no
  badges.
- REQ-266: retain `nova-sp-theme.css` as a styling reference; drop
  `--theme` from `render_slides.sh`.
- REQ-267: restyle benefit callouts — remove `**Benefit:**` prefix; use
  `.benefit` class (red top-rule + black italic; white on title slides).

### Phase P3a — inline-images (planned, tag v1.22.3)
- REQ-268: new `scripts/inline_images.py` — base64-embeds all images in
  the rendered HTML for redistribution. Invoked after the MARP HTML
  render. Low-risk, mechanical (G-003 isolation).

### Phase P3b — python-pptx-generator (planned, tag v1.22.4)
- REQ-269: new `scripts/render_pptx.py` — structured, editable, S&P-themed
  PPTX via `python-pptx`. 16:9; native tables; embedded PNGs; benefit
  callouts. Add `python-pptx` to `pyproject.toml`. High-risk, isolated
  (G-003).
- REQ-270: `render_slides.sh` produces both PPTX outputs; CI installs
  `python-pptx`; both attached to release.

### Phase P4 — trim-wordcount + repo-wide "penetrate" purge (planned, tag v1.22.5)
- REQ-271: targeted ~20-30% word-count trim on verbose slides (1, 5, 7,
  8, 13, 14, 20, appendix). Tables untouched. Spirit preserved.
- REQ-272: remove "penetrate" (and derivatives) repo-wide (G-001) —
  `docs/` + `.ciagent/PROJECT.md`/`CLARIFY.md`; RESEARCH.md/PLAN.md/
  GRILL.md exempt as decision-history. Slide 5's phrase removed with no
  replacement (slide 4 already excludes the PDLC).

### Phase P5 — ci-tests-readme + review + audit + ship (Final Phase, tag v1.22.6)
- REQ-273: CI workflows install `python-pptx`, run `render_slides.sh`,
  commit HTML + both PPTX + inlined images.
- REQ-274: update `test_slides_pipeline.py` (consolidated doc, inline
  style assertions, image inlining, python-pptx, benefit class,
  "penetrate" absence). New `test_pptx_generator.py`.
- REQ-275: rewrite `README.md` for the single-document + dual-PPTX +
  image-inlining pipeline.
- Review + audit + milestone ship (merged P5+P6 per G-004 — NFR docs
  milestone). Tag `v1.22.6` (final patch = milestone release). Merge
  `milestone/v1.23-deck-cleanup-python-pptx` → `main`.
- **Requirements:** REQ-263..275 (13 requirements).
