# ACDL — Requirements

## v1

### Category: Repos & Org
- **REQ-01:** All demo code lives under the `continuous-intelligence` Gitea org at `https://git.cloudinit.dev`.
- **REQ-09:** Three repos exist: `acdl` (platform + stubs + reusable workflows), `acdl-contracts` (developer surface), `acdl-evidence` (Pages audit timeline).

### Category: L1 Modules
- **REQ-02:** 8 L1 module folders exist under `acdl/modules/l1/`: `l1-eks-fargate`, `l1-iam-role`, `l1-lambda`, `l1-api-gateway`, `l1-eventbridge`, `l1-sqs`, `l1-s3`, `l1-cloudwatch`.
- **REQ-03:** Each L1 module has a `manifest.yaml` (declaring inputs) and a `mock_apply.sh` that echoes success, sleeps 1s, and exits 0.

### Category: L2 Modules
- **REQ-04:** 4 L2 modules exist under `acdl/modules/l2/`: `l2-invoice-service`, `l2-commodity-price-feed`, `l2-energy-analytics-api`, `l2-regulatory-reporting`, each composing the specified L1s.
- **REQ-05:** L2 modules compose L1 primitives into deployable shapes with a maximum depth of 5.

### Category: Core Scripts
- **REQ-06:** `mock_executor.sh` reads an L2 composition, invokes each L1 `mock_apply.sh`, and writes `state.json`.
- **REQ-07:** `policy_checker.py` reads `contract.yaml` and fails with `POLICY_VIOLATION:PUBLIC_INGRESS` on `public-ingress: true`; otherwise passes.
- **REQ-08:** `confidence_signal.py` returns a base score of 0.90 and drops to 0.40 (with reason code) when policy fails; gate threshold is ≥ 0.50.

### Category: Evidence Stream
- **REQ-11:** `evidence_writer.py` appends events to `audit.json` and links each event to the previous via a SHA-256 hash chain (`prev_hash` + own `hash`).
- **REQ-13:** `acdl-evidence` is Pages-enabled and serves `audit.json` plus `index.html`.

### Category: Pipeline
- **REQ-10:** The reusable pipeline runs Dev (autonomous), pauses at QA (manual approval), pauses at Prod (manual approval), then finalizes by committing `audit.json` to `acdl-evidence`.
- **REQ-12:** Opening an Issue in `acdl-contracts` runs `l3b_agent_stub.py`, commits a generated `contract.yaml` to a new branch, closes the Issue, and triggers the main pipeline.

### Category: Demo Acts
- **REQ-14:** `index.html` uses vanilla JS to fetch `audit.json` from the Pages URL and render events as a timeline.
- **REQ-15:** All four demo acts (Friction, Developer Self-Service, Citizen Developer, Safety Net) reproduce deterministically in a dry run.

## v2

(None — v1 covers the complete demo.)

## v1.1 (Prior milestone — architecture finalization + v1 spike, complete)

### Category: Architecture Finalization
- **REQ-16:** Architecture reaches v1.0 — all 11 open decisions in `docs/architecture.md` §13 are resolved and recorded in `PROJECT.md` (W1.A, W1.B, W2.A, W3.D, W3.E, BA.A–F, OpenTofu timing).
- **REQ-17:** Target Stack IR is defined as a JSON Schema under `schemas/ir.schema.json`; engine-agnostic (resources, relationships, composition max-depth-5, policy hooks).
- **REQ-18:** `PolicyCheckResult` normalized schema is defined under `schemas/policy_check_result.schema.json`; a Checkov adapter translates Checkov JSON to this schema.
- **REQ-19:** Six-input confidence signal is specified under `platform/confidence_signal.py` with per-env thresholds (dev 0.50 / qa 0.75 / prod 0.90 / dr 0.95) and severity→penalty mapping (critical=hard override, high=-0.2, medium=-0.05, low=-0.01, info=0.0).
- **REQ-20:** Tiered audit ledger design is authored: S3 Object Lock (compliance mode, 7-yr) + DynamoDB outbox (RPO=0, JWS detached signatures, `prev_event_hash` chain, daily checkpoints).
- **REQ-21:** Full 8-concern HITL matrix + separation-of-duties design is authored (CODEOWNERS routing + DynamoDB identity-distinctness check; pre-execution gate model; 1d warn / 2d freeze timeout).
- **REQ-22:** Contract schema (JSON Schema draft 2020-12) is defined under `schemas/contract.schema.json` with per-env mandatory/optional inputs (W3.E) and `profile: agentic` marker for L3B fields.

### Category: AWS OIDC Bootstrap
- **REQ-23:** AWS auth bootstrap + state backend for the spike: an S3 state bucket + DynamoDB lock/outbox table + an IAM user with a minimal scoped policy (S3 + DynamoDB + plan-only). The temporary long-lived key is used once (waiver D-034) then rotated via `scripts/rotate_spike_key.sh` after each spike run (D-039). **Real OIDC federation is deferred to v1.2** — Gitea Actions does not support `id-token: write` (RESEARCH TARGET 1, conf 0.95), blocked on go-gitea/gitea#36988.

### Category: v1 Spike — IR, L1, Adapter
- **REQ-24:** One real L1 module `l1-s3` exists under `modules-ir/l1/l1-s3/` with an IR-typed interface (typed inputs/outputs/NFRs) registered in the L1 registry.
- **REQ-25:** One real L2 thin-composition `l2-static-assets` exists under `modules-ir/l2/l2-static-assets/` referencing `l1-s3` only (depth 1, within max-depth-5).
- **REQ-26:** The Terraform adapter (`adapters/terraform/`) compiles the IR-typed L1 interface to Terraform `variable`/`output` blocks and the L2 thin-composition tree to a Terraform root module; it emits a real `terraform plan` against AWS via OIDC; state is stored in S3 + DynamoDB.

### Category: v1 Spike — End-to-End
- **REQ-27:** One end-to-end contract submission (`contracts/spike.yaml` for `l2-static-assets`) flows through: contract schema validation → contract→IR resolution → `terraform plan` (real AWS) → Checkov `PolicyCheckResult` → confidence signal → evidence event written to the DynamoDB outbox.
- **REQ-28:** Spike verification (`scripts/verify_phase10.sh`) proves the IR-shaped commitments hold: the adapter is the only engine-specific code; no polyglot mess; the L1 content, contract YML, and thin-composition tree are engine-agnostic.

## Out of Scope (v1.1)

| Feature | Reason |
|---------|--------|
| Full HITL matrix wiring (qa/prod/dr) | Spike is dev-only (`terraform plan`); HITL wiring is v1.2. |
| Kyverno + OPA policy engines | Spike uses Checkov only; Kyverno/OPA are v1.2. |
| MCP skill catalog + real L3B agent | L3B spike = a single stub contract submission; the 5-skill catalog is v1.2. |
| GitOps reconciler (ArgoCD/Flux) | v1.2. |
| Multi-region state / outbox | Single-region in v1 (§9, §12.3). |
| Prod/dr environments | v1.2. |
| Terraform `apply` (real provisioning) | Spike runs `plan` only; `apply` is gated by HITL in v1.2. |

## v1.2 (Prior milestone — platform hardening + first real consumer deployment, complete, tag `v1.3.0`)

### Category: Documentation & Simplification
- **REQ-29:** `README.md` is fully rewritten to reflect the v1.1-complete platform: the actual spike flow (contract → IR → `terraform plan` → Checkov → confidence signal → outbox), how to run it (`scripts/run_platform.sh`), the real repo layout (`acdl_platform/`, `schemas/`, `adapters/`, `terraform/`, `modules-ir/`, `contracts/`, `demo/`), and the v1.2 objective. No stale "v1.1 (active)" framing.
- **REQ-30:** NFR hardening of the v1.1 spike: (a) `terraform/bootstrap/spike_runner_policy.json` audited to least-privilege (S3 + DynamoDB + ECS + ECR + ELB + IAM plan-only, no wildcards beyond the documented exceptions); (b) `create_state_backend.py` and `create_iam_user.py` are idempotent (re-running exits 0 without duplicating resources); (c) `run_spike_plan.sh` + `run_spike_e2e.sh` consolidated into a single `scripts/run_platform.sh` with proper exit codes and error handling; (d) P1-1 carried forward from the v1.1 audit — the two AWS access key IDs in `.ciagent/VERIFY.md` Phase 09 narrative are redacted to placeholders; (e) any remaining stale `platform/` paths in `.ciagent/` are corrected to `acdl_platform/`.

### Category: L1 Catalog Expansion (ECS Fargate)
- **REQ-31:** Six new IR-typed L1 modules exist under `modules-ir/l1/` and are registered in `modules-ir/registry.json`: `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` (application load balancer + listener + target group), `l1-ecr` (ECR repository). Each has an `interface.json` valid against `schemas/ir.schema.json` and produces a valid `terraform plan` fragment via the Terraform adapter. The adapter `TYPE_MAP` is expanded to cover all six IR resource types.

### Category: L2 Composition & Contract Schema
- **REQ-32:** `l2-microservice` thin-composition exists under `modules-ir/l2/l2-microservice/` referencing the six ECS L1s (depth ≤ 5, within max-depth-5). `schemas/contract.schema.json` is extended with microservice inputs (`image: string`, `port: integer`, `env: map`, `healthcheck: object`) and validates a `contracts/microservice.yaml` submission. Contract→IR resolution (`acdl_platform/contract_resolver.py`) yields a complete target stack for `l2-microservice`.

### Category: Real Provisioning
- **REQ-33:** The platform runs `terraform apply` (not just `plan`) for the `dev` environment, autonomous per §10 (confidence ≥ 0.50, no HITL). The apply creates real AWS resources (VPC, ECS cluster, ECR repo, ALB, ECS service) and the result is captured in the evidence stream. `apply` for qa/prod/dr remains HITL-gated and out of scope for v1.2.

### Category: Consumer Repo
- **REQ-34:** A new Gitea repo `acdl-consumer-microservice` exists under the `continuous-intelligence` org, containing: a basic HTTP microservice (e.g., a tiny Python/Go server returning 200), a `Dockerfile`, an ECR push step, and a `contracts/microservice.yaml` submission for `l2-microservice` (dev environment).

### Category: End-to-End Verification
- **REQ-35:** One end-to-end flow: consumer commit to `acdl-consumer-microservice` → pipeline triggered → 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. `scripts/verify_phase16.sh` proves the full flow green.

## v1.3 (Prior — module documentation + thin-composition removal, complete)

### Category: Thin-Composition Removal
- **REQ-36:** The L2 thin-composition layer is removed completely: `composition.json` files, `acdl_platform/contract_resolver.py`, `schemas/contract.schema.json`, `contracts/spike.yaml`, `contracts/microservice.yaml`, and L2 entries in `modules-ir/registry.json` are deleted. The L2 directories are kept as placeholders with READMEs. The downstream pipeline (adapter → checkov → confidence → outbox) is patched to load a pre-existing IR instance instead of resolving a contract.
- **REQ-37:** A `modules-ir/README-TEMPLATE.md` exists that works for both L1 and L2 modules, written in plain language (no jargon), with sections for Overview, Resources, Inputs, Outputs, Usage, Compliance extension points, and Versioning.
- **REQ-38:** Every module has a `README.md`: the 7 L1 modules have full READMEs with Resources/Inputs/Outputs/Usage/Compliance-extension-points/Versioning sections derived from their `interface.json`; the 2 L2 modules have placeholder READMEs noting the composition is under redesign. A `modules-ir/README.md` catalog index lists all modules with one-line descriptions and links.

### Category: Testing
- **REQ-39:** A pytest test suite exists under `tests/` covering the platform components offline (no AWS, no Checkov, no DynamoDB): the Terraform adapter (`adapters/terraform/adapter.py`), the confidence signal (`acdl_platform/confidence_signal.py`), the Checkov adapter (`adapters/terraform/policy/checkov_adapter.py`), and the outbox writer (`acdl_platform/outbox_writer.py`). The suite validates the IR schema, registry, spike_instance, and adapter output structure. `pyproject.toml` + `requirements-test.txt` pin test dependencies (pytest, jsonschema, pyyaml, boto3-stubs or moto for outbox mocking).

### Category: Shell Reproducibility
- **REQ-40:** `scripts/run_platform.sh` has a `--check-only` mode that runs offline: loads the pre-existing IR instance, runs the adapter to emit Terraform, validates the JSON structure — without AWS credentials, Checkov, or DynamoDB. The existing `--plan-only` and full modes continue to require AWS. The `--check-only` mode is what CI pipelines run.

### Category: CI/CD Pipelines
- **REQ-41:** Identical CI/CD pipelines exist for both Gitea Actions (`.gitea/workflows/ci.yml`, dev environment) and GitHub Actions (`.github/workflows/ci.yml`, production). Both run the same three stages: (1) lint — `py_compile` all Python files, (2) test — `pytest`, (3) check-only — `bash scripts/run_platform.sh --check-only`. Both trigger on push to main + pull request. Both use `ubuntu-latest`. Identical outcomes — the only difference is the runner environment.

- **REQ-42:** `pyproject.toml` exists at the repo root with pytest configuration (testpaths, markers) and the project metadata. `requirements-test.txt` pins test-only dependencies separate from runtime dependencies.

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

### Category: Central Pipeline Contract
- **REQ-43:** A central pipeline contract exists as `schemas/pipeline.schema.json` (JSON Schema draft 2020-12) + `pipelines/ci.yaml` (YAML instance). The contract declares the pipeline name, triggers (push/PR branches), runner, Python version, and stages (name + command + required + install + description). Both `.gitea/workflows/ci.yml` (Gitea Actions, dev) and `.github/workflows/ci.yml` (GitHub Actions, production) implement the same stages, commands, triggers, and runner as declared in the contract. A test (`tests/test_pipeline_contract.py`) validates the contract against the schema and asserts both workflows conform (same jobs, same commands, same triggers, same runner, byte-identical).

### Category: Shell Reproducibility
- **REQ-44:** `scripts/run_ci.sh` reproduces the CI pipeline locally — runs the same 3 stages (lint, test, check-only) in sequence with proper exit codes, failing on first error. The script exits 0 with "CI PIPELINE OK" on success. A `--quiet` flag suppresses per-stage banners. The script mirrors the central pipeline contract (`pipelines/ci.yaml`) so the shell and CI environments produce identical outcomes.

### Category: Pipeline Streaming
- **REQ-45:** `scripts/run_platform.sh` streams output by default: terraform init/validate/plan output is piped to stdout via `tee` (visible to the user and logged), Checkov results are printed in human-readable form, and PolicyCheckResult records are displayed with severity, rule ID, and pass/fail status per record. The `--check-only` mode streams the emitted Terraform file content. A `--quiet` flag suppresses streaming (output to log files only) for backwards compatibility. Both gitea and github workflows are byte-identical (identical outcomes — the only difference is the forge runtime).

## v1.5 (Prior — consumer happy path + zero-trust docs + reusable deploy workflow, complete)

### Category: Consumer Happy Path Documentation
- **REQ-46:** `README.md` is rewritten so the consumer model is unambiguous: this repo is the platform source; a consumer never clones it. A consumer repo contains only app code + `contract.yaml` referencing the central pipeline + contract. The platform-flow diagram is a mermaid `flowchart TD` (replacing the ASCII art). "L3A"/"L3B" nomenclature is removed from README (single-surface model). "spike" nomenclature is removed from prose (code paths in bash blocks are kept verbatim).
- **REQ-47:** `docs/CONSUMER_GUIDE.md` (all-caps) replaces `docs/consumer-guide-static-assets.md`. It is generic across all L2 modules (`static-assets` as the worked example), uses mermaid diagrams (model + pipeline flow), documents versioned `uses:` references (floating MAJOR+MINOR tags — bare/`@main` discouraged), scopes prerequisites to consumer-repo bootstrap only (no Terraform/Checkov/boto3/runner-key — those are platform-repo concerns), and documents that the pipeline fetches the ACDL repo at run time via a reusable workflow (consumers never invoke `scripts/run_platform.sh` locally for the happy path).
- **REQ-48:** `README.md` Credentials section is rewritten to express the zero-trust target model: consumer repos use OIDC federation (no long-lived keys) with attribute-based authorization (ABAC) — IAM roles + session policies scoped by repository identity and resource-creation tags so a consumer can only view/update resources it created (blast-radius containment). A documented override allows a static key in GitHub Secrets (consumer repo) or `.env.secrets` (local testing), rotated by a platform-managed scheduled pipeline on a daily cadence; when `.env.secrets` is used locally, rotating out of band is the consumer's responsibility.

### Category: Reusable Deploy Workflow
- **REQ-49:** A reusable deploy workflow exists as byte-identical `.gitea/workflows/deploy.yml` (Gitea, dev) and `.github/workflows/deploy.yml` (GitHub, production), implementing the central deployment pipeline contract (`pipelines/deploy.yaml` validated against `schemas/deploy-pipeline.schema.json`). It is invoked by consumer repos via `uses: acdl/.gitea/workflows/deploy.yml@vMAJOR.MINOR` (versioned tag). The workflow checks out the consumer repo, checks out the ACDL platform repo into the runner workspace, installs runtime deps (Python, Terraform, Checkov), and invokes `scripts/run_platform.sh` against the consumer's contract path (passed as a workflow input). OIDC is the default auth (`permissions: id-token: write`); a static-key override reads from repository secrets.
- **REQ-50:** `contracts/static-assets.yaml` uses a versioned `uses:` reference (`@v1.4`, MAJOR+MINOR) — not bare `@v1` or `@main` — as the canonical example the consumer guide points at.
- **REQ-51:** `tests/test_pipeline_contract.py` is extended to validate the new deploy workflows: both files exist, are byte-identical, and conform to `schemas/deploy-pipeline.schema.json` (stages present, names match `pipelines/deploy.yaml` stage names). The existing CI-workflow conformance tests continue to pass unchanged.

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

### Category: Internal-surface scrub
- **REQ-52:** No consumer-facing documentation (README.md, docs/**, modules/**/README.md, contracts/**) references `.ciagent/` — it is local CIAgent metadata, never visible to platform engineers or consumers. The README repository-layout table has no `.ciagent/` row. No `.gitea/` references appear in consumer-facing docs (consumers use GitHub only); the README repository-layout table has no `.gitea/workflows/` row.
- **REQ-53:** `acdl_platform/` is renamed to `core/` across the directory, all imports in tests/scripts/pipelines/workflows, and all doc references. (`platform/` was the original target but shadows Python's stdlib `platform` module — `core/` was chosen to stay importable.) `grep -R "acdl_platform" .` (excluding `.ciagent/`, `demo/`, `.git/`) returns 0 hits. The test suite passes after the rename.

### Category: Docs site restructure
- **REQ-54:** `docs/` is restructured into a Jekyll-style GitHub Pages site: `docs/_config.yml`, `docs/index.md` (landing), `docs/modules/` (catalog + per-module Pages-friendly copies), `docs/contracts/index.md`, `docs/pipeline/index.md` + `docs/pipeline/versioning.md`, `docs/environments/index.md`, `docs/consumer-guide.md`, `docs/architecture.md` (consolidated from architecture.md + architecture-v1.0.md, current-architecture only), `docs/vision.md`. No `.ciagent/` links anywhere in `docs/`. Consumer-facing content (modules, contracts, pipeline, versioning) lives in Pages.

### Category: Terminology normalization
- **REQ-55:** Consumer-facing docs drop the "L2" nomenclature — L2 modules are referred to as "modules". "L1" label is dropped in consumer-facing docs — L1 primitives are referred to as "primitives". The "composition" terminology is changed to "pattern" for modules in prose (the on-disk `composition.json` files and code references are unchanged this phase). A roadmap entry records that "composition" will later describe the thin orchestration where consumers dynamically create a module directly from the contract file (future implementation, not implemented now).
- **REQ-56:** The term "forge" is replaced in consumer-facing docs with "platform runners" / "platform-managed" as appropriate. The term "forge" remains only in internal architecture docs.

### Category: README rewrite
- **REQ-57:** README.md repository-roles section is restated to match reality: a consumer repo contains (a) its application code, (b) one or more contracts (`.acdl/contract.yaml`), and (c) one or more CI definitions (a thin `.github/workflows/deploy.yml` that `uses:` the central reusable workflow, pointing at the appropriate environment + contract). The platform repo (this one) owns modules/adapters/schemas/pipelines/scripts/workflows. A consumer never clones the platform repo.
- **REQ-58:** README.md Status section is replaced with a Features list (referenceable by consumers and platform engineers) and a Roadmap subsection listing only planned future features (no internal CIAgent status, no version-by-version changelog).
- **REQ-59:** README.md "How the platform works" mermaid diagram is revised so all node text is visible (no overflow): labels are split with `<br/>`, boxes widened as needed. A security-checks stage is added before the policy-checks stage. Specific tools (Checkov, Terraform) are not named — they are "security checks (adapter)", "policy checks (adapter)", "infrastructure plan". An "infrastructure apply" stage is added at the appropriate level (dev only, after confidence).
- **REQ-60:** README.md Credentials & zero-trust section removes the "go-gitea/gitea#36988 blocked" mention and the "waivers D-039/D-047" language (not consumer/platform-engineer facing). It states: default OIDC + ABAC; alternative is a static AWS key (GitHub Secrets for platform-runner runs, or `.env.secrets` locally) with the expectation of daily rotation (platform-managed for runner runs) or out-of-band rotation (consumer-managed for local `.env.secrets`).

### Category: Environments concept + onboarding
- **REQ-61:** The concept of platform-managed environments is introduced: consumers are not required to provide an AWS account, VPC, subnet, S3 state bucket, or runner key. `docs/environments/index.md` documents that a named environment is a platform-owned AWS account + network + state backend + IAM role surfaced to the consumer via ABAC, selected by name in the contract. The old README environments table (dev/qa/prod/dr) is removed completely. A minimal onboarding scaffold exists: `platform/environments/` with a sample `dev.json` + README, `platform/environment_check.py`, a wire-in at the top of `scripts/run_platform.sh`, a friendly first-run onboarding message when no environment is defined for the repo, and `tests/test_environment_check.py` covering the missing-env and present-env cases.

## v1.7 (Active — production platform + contract ingestion + pipeline maturation)

### Category: Rename + production-ready stack
- **REQ-62:** `static-assets` is renamed to `static-assets` everywhere (D-048 — including `.ciagent/` historical narrative: verbatim phase descriptions, REQ-25/27/50 text, D-036, RESEARCH.md). `grep -R "static-assets[^s]" .` (excluding `.git/`) returns 0 hits. The module dir `modules/l2/static-assets/` → `modules/l2/static-assets/`; `contracts/static-assets.yaml` → `contracts/static-assets.yaml`; the registry key is renamed; all scripts, tests, docs, and `.ciagent/` files use `static-assets`. The reconstruction test is updated to expect `static-assets` throughout.
- **REQ-63:** Two new primitives exist: `cloudfront` (distribution + OAC, stack types `aws:cloudfront:distribution` + `aws:cloudfront:originaccesscontrol`) and `waf` (WAFv2 web ACL, stack type `aws:wafv2:webacl`), each with an `interface.json` valid against `schemas/stack.schema.json` and a full README (Resources/Inputs/Outputs/Usage/Compliance/Versioning). Both are registered in `modules/registry.json`. The Terraform adapter `TYPE_MAP`/`INPUT_MAP`/`OUTPUT_MAP` covers the new stack types.
- **REQ-64:** The `static-assets` module is augmented to a production-ready stack referencing s3 + cloudfront + waf (depth 1, D-049). `composition.json` wires the s3 bucket regional domain name to the CloudFront origin, and the WAF web ACL ARN to the CloudFront distribution. `schemas/contract.schema.json` is extended for the new module inputs (`price_class`, `viewer_protocol_policy`, `waf_enabled`, `default_ttl`, `max_ttl`). The `uses:`/`ref:` tag advances from `@v1.4` to `@v1.6` (D-056/D-057); floating git tags `v1.6` + `v1` are created pointing at `v1.6.0`.

### Category: Tagging standards + security adapters
- **REQ-65:** A required-tag set is defined in `schemas/tagging-standard.json` (`acdl:owner`, `acdl:contract`, `acdl:environment`, `acdl:cost-center`). A Checkov custom YAML rule at `adapters/terraform/policy/custom_rules/acdl_tagging.yaml` fails (severity `medium`) when required tags are missing on taggable resources. `checkov_adapter.py` removes the `_emit_tag_naming_skipped()` placeholder (D-043 closure) and maps `ACDL_TAG_NAMING` as a real rule. `scripts/run_platform.sh` Step 5 passes `--external-checks-dir` to load the custom rule.
- **REQ-66:** A Wiz adapter stub exists at `adapters/wiz/wiz_adapter.py` translating Wiz API issues → `PolicyCheckResult` records (`engine: "wiz"`, D-052). It degrades gracefully when unconfigured (emits a single `SKIPPED` `WIZ_NOT_CONFIGURED` record). `tests/test_wiz_adapter.py` passes offline with a fixture response. The pipeline invokes it optionally (Step 5b) when `WIZ_API_TOKEN` is set.
- **REQ-67:** A Kyverno K8s-native adapter exists at `adapters/kyverno/kyverno_adapter.py` translating Kyverno `PolicyReport` results → `PolicyCheckResult` records (`engine: "kyverno"`, D-053). Sample policies exist at `adapters/kyverno/policies/` (disallow-privileged, require-labels, require-image-digests). `tests/test_kyverno_adapter.py` passes offline. The adapter is inactive for Terraform-only stacks (the platform emits Terraform, not K8s manifests); it is ready for the GitOps reconciler roadmap item. `schemas/policy_check_result.schema.json` engine enum includes `checkov | kyverno | opa | wiz`.

### Category: Platform Lambda + contract ingestion
- **REQ-68:** A platform Lambda (`core/lambda/contract_ingestor.py`) is invoked via a Function URL (IAM auth) and accepts `{ consumerRepo, contractId, contract, environment, action }`. It writes contracts to a DynamoDB table `acdl-contracts` (PK `consumerRepo`, SK `contractId#submittedAt`, SSE via a customer-managed CMK, point-in-time recovery) (D-051). `terraform/platform/main.tf` defines the table, Lambda, Function URL, KMS key, Secrets Manager secret (`acdl/github-token`), and Lambda execution role. `terraform/platform/consumer_invoke_policy.json` grants the consumer's deploy role `lambda:InvokeFunctionUrl` on the Lambda ARN, scoped via ABAC (cross-account). Onboarding grants the Lambda-invoke permission; `docs/environments/index.md` documents this. `tests/test_contract_ingestor.py` passes offline (moto-mocked DynamoDB).

### Category: Deploy outputs + error reporting + stage comments
- **REQ-69:** `scripts/run_platform.sh` has a `publish-outputs` step (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). `core/output_publisher.py` implements the SSM write + GitHub comment formatting. `tests/test_output_publisher.py` passes offline (moto + mocked GitHub API). `pipelines/deploy.yaml` + both deploy workflow YAMLs declare the new stages (byte-identical).
- **REQ-70:** The Lambda `report_error` action (`core/lambda/contract_ingestor.py`) creates a GitHub issue on the platform repo (`acdl/acdl`) via the GitHub API using a token from Secrets Manager (D-055). Idempotent (comments on an existing open issue rather than duplicating). `.github/workflows/deploy.yml` + `.gitea/workflows/deploy.yml` (byte-identical) have an `if: failure()` error-report step invoking the Lambda via `aws lambda invoke-function-url` (SigV4-signed). Gitea is excluded (only the CIAgent uses it; platform engineers and consumers use GitHub).
- **REQ-71:** `.github/workflows/deploy.yml` + `.gitea/workflows/deploy.yml` (byte-identical) post a PR comment after every successful pipeline stage (validate-contract, resolve-stack, plan, checkov, confidence, apply, publish-outputs) via `scripts/post_stage_comment.sh` (uses `GITHUB_TOKEN` + `gh api`; no-op when not in a PR context). The comment includes the stage name, status (pass), and key metrics (plan counts, confidence score, outputs published).

### Category: Platform pipelines + release automation
- **REQ-72:** Three platform pipelines exist: (1) `.github/workflows/platform-test.yml` (PR, stages: lint, unit-test, integration-test — runs `run_platform.sh --check-only` for every sample contract, schema-validation — validates all `schemas/*.json` + `modules/**/interface.json` + `modules/**/composition.json` + `modules/<name>/examples/*.yaml` against their schemas); (2) `.github/workflows/primitives-plan.yml` (PR, plan-only for all L1 primitives via matrix, `scripts/run_primitive_plan.sh`); (3) `.github/workflows/patterns-plan.yml` (PR, plan-only for all L2 modules via matrix, `scripts/run_pattern_plan.sh`).
- **REQ-73:** `.github/workflows/release.yml` 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, and creates a GitHub release with an auto-generated body (D-057). `tests/test_release_logic.py` passes (unit test the semver computation + tag-update logic with a mocked `git describe`).

### Category: Remove legacy consumer-repos + module examples + RDS primitive
- **REQ-74:** The legacy consumer-repos directory is deleted entirely (a v1.2 artifact removed in v1.7; references in `.ciagent/` historical narrative are rewritten per D-048). A recursive grep for the legacy directory name (excluding `.git/`) returns 0 hits.
- **REQ-75:** A new RDS primitive (`modules/l1/rds/`) with an `engine` input (enum: postgres, mysql, etc.) demonstrates multi-engine variation (D-059). Every module (primitives + patterns) has a `modules/<name>/examples/` directory with `simple.yaml` + `complex.yaml` (+ variation files) validated against `schemas/contract.schema.json` in the platform-test pipeline schema-validation stage (D-058). Each module's `README.md` `## Examples` section references + excerpts the validated files. `docs/modules/index.md` + `docs/consumer-guide.md` + `docs/contracts/index.md` are updated with the new module names + examples.

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

### Category: P1 Fixes
- **REQ-76:** WAF adapter emits custom `rules` as nested HCL blocks (not attribute syntax) and honors `default_action` input (allow/block) — P1-4, P1-5 closed.
- **REQ-77:** L2 composition `outputs[]` array is resolved by `contract_resolver.py` into `stack.outputs`; the adapter emits corresponding `output` blocks — P1-7 closed.
- **REQ-78:** SSM publisher fails loud when `ACDL_KMS_KEY_ID` is unset (no silent AWS-managed-key fallback); `ACDL_ALLOW_DEFAULT_KMS=1` escape hatch for local testing — P1-3 closed.
- **REQ-79:** `consumer_invoke_policy` is rendered via Terraform with the caller's live account ID (no `000000000000` placeholder) — P1-6 closed.
- **REQ-80:** `run_platform.sh` emits adapter output to a per-run temp dir, not committed `terraform/spike/*.tf`; the committed files are removed — P1-8 closed.
- **REQ-81:** `contract_ingestor.py` reads `GITHUB_API_BASE` env for forge-agnostic API URLs (GitHub + Gitea) — P1-9 closed.
- **REQ-82:** Deploy workflow static-key override is wired to `configure-aws-credentials` inputs (`access-key`/`secret-key`), not inert env vars — S1 closed.

### Category: Encryption by Default
- **REQ-83:** A per-stack CMK primitive (`kms-key`) exists with 90-day rotation enabled at creation; one key per L2 deployment; no shared keys across stacks.
- **REQ-84:** All primitives have encryption by default (`encryption_enabled` NFR, default true) + optional `kms_key_arn` input. CMK is prioritized; managed KMS is the fallback when no CMK is provided.
- **REQ-85:** L2 modules wire a per-stack CMK child + connect its `kms_key_arn` output to each child's `kms_key_arn` input.

### Category: Deletion Protection by Default
- **REQ-86:** `deletion_protection` NFR (boolean, default true) on every L1 primitive; the adapter emits `prevent_destroy` lifecycle meta-arg when true.
- **REQ-87:** L2 modules expose a `features.deletion_protection` flag (default true); consumers can disable via contract `inputs.deletion_protection: false`.

### Category: Uptime Monitoring
- **REQ-88:** An uptime-kuma L1 primitive exists (ECS Fargate) with: `feature_flag_enabled` (boolean, default true), `monitored_endpoints` (array of HTTP/DNS/TCP checks), `static_checks` (pre-defined health checks), `alert_channels` (Teams webhook, email, SMS, GitHub issues).
- **REQ-89:** Uptime is deployed by default after any L2 module deploy (separate terraform state, separate terraform run); L2 module outputs (endpoints) are passed to the uptime deployment as `monitored_endpoints`. The uptime URL is published to the consumer via PR comment.
- **REQ-90:** The `feature_flag_enabled` input (set from consumer contract `inputs.uptime_enabled`, default true) disables the uptime deployment entirely (no resources emitted).
- **REQ-91:** A `deploy-uptime` pipeline stage is declared in `pipelines/deploy.yaml` + both deploy workflow YAMLs (byte-identical).

### Category: Decommission + CMDB
- **REQ-92:** A decommission mode on the deploy pipeline (`mode: decommission`) implements a 2-step pipeline: (1) plan/apply to disable deletion protection with an HITL SRE gate, (2) plan/apply with all counts set to 0 with a second HITL SRE gate. Uses the existing deploy pipeline with different behavior.
- **REQ-93:** A DynamoDB `acdl-change-requests` table serves as the CMDB. The decommission alias accepts a `changeRequestId` input validated via a `validate_change_request` Lambda action (CR status must be `approved`).
- **REQ-94:** The decommission flow is documented in `docs/CONSUMER_GUIDE.md` (how to request a CR, trigger decommission, HITL gates, what happens).

### Category: Engineering Standards
- **REQ-95:** `modules/STANDARDS.md` exists with comprehensive L1 + L2 authoring + code review standards (scanned from current modules): required files, interface schema, input/output/NFR conventions, encryption + deletion protection as mandatory NFRs, naming, adapter extension pattern, code review checklist.
- **REQ-96:** `modules/README.md` catalog index includes all primitives (rds + uptime + kms-key added); `modules/README-TEMPLATE.md` updated with `## NFRs` section.

### Category: Path Documentation
- **REQ-97:** `schemas/README.md` documents how to write a schema, wire it into the platform, test it in CI, where to write tests, dependencies, and the existing schema catalog.
- **REQ-98:** `pipelines/README.md` documents how to write a pipeline contract, wire it into workflows, test it, dependencies, and the existing pipeline catalog.
- **REQ-99:** `adapters/README.md` documents how to write an adapter, wire it into the platform, test it, dependencies, and the existing adapter catalog.

## Out of Scope (v1.2)

| REQ | Original criterion | Clarified criterion (effective) | Decision |
|-----|--------------------|----------------------------------|----------|
| REQ-09 | Three repos exist | Three repos exist (`acdl`, `acdl-contracts`, `acdl-evidence`) under `continuous-intelligence`; new repos use `default_branch: "main"`, `auto_init: true` | D-015 |
| REQ-10 | "Pages returns 200 with placeholder `index.html`" on `acdl-evidence` | Gitea has no Pages; substitute: an HTTP GET against the raw file URL `https://git.cloudinit.dev/continuous-intelligence/acdl-evidence/raw/branch/main/index.html` returns 200 with the placeholder HTML body | D-012, D-016 |
| REQ-10 | "`qa` and `prod` environments exist on `acdl-contracts`" | Gitea has no environments API and ignores `environment:` blocks; substitute: the reusable workflow defines `qa-gate` and `prod-gate` jobs gated by `workflow_dispatch` approval inputs (D-004 fallback); a `qa` and `prod` branch may be created on `acdl-contracts` as a visible stand-in for environments | D-013 |

## Out of Scope (v1.0 demo — retained for history)

| Feature | Reason |
|---------|--------|
| Real cloud provisioning (AWS/GCP/Azure) | Demo explicitly stubs all infrastructure; no cloud access available. |
| Real LLM inference / external AI APIs | Spec forbids external AI; L3B is a keyword parser. |
| Production-grade infrastructure | Demo target is a 30-minute executive show, not a production system. |
| Adversarial tamper-proofing of evidence | Hash chain is demonstrative; not cryptographically secure against a determined attacker. |
| Multi-tenant isolation | Out of demo scope. |

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

### Category: Design Doc Refresh
- **REQ-100:** `core/hitl_matrix_design.md` is up to date: the "dev-only spike" framing is replaced with the v1.9 wired-gates reality (qa/prod/dr `workflow_dispatch` approval gates + CODEOWNERS routing + outbox-based SoD); the 8-concern attestation matrix is marked implemented (offline-testable subset) with operator-supplied concerns noted; the spike-scope note is updated. No stale "v1.2 wires the gates" language remains.
- **REQ-101:** `core/audit_ledger_design.md` is up to date: the hash-chain + DynamoDB-outbox path is marked shipped + production (since v1.8); the S3 Object Lock + JWS + async worker + DLQ + daily checkpoints build-out is clearly labeled "Deferred to a future milestone" (D-083); the RPO/RTO table reflects the v1.9 state.

### Category: P1-1 Remediation
- **REQ-102:** The adapter (`adapters/terraform/adapter.py`) contains no resource-type-specific hardcoded defaults for ECS/ALB/VPC resources — `desired_count`, `launch_type`, `target_type`, `load_balancer_type`, `family`, and `Name` tag values are read from L1 `interface.json` inputs (with defaults declared in the interface). The adapter is a thin translator. An L1 with an overridden `desired_count: 3` emits `desired_count = 3`; the default emits `desired_count = 1` via the interface default, not an adapter hardcode (P1-1 closed).

### Category: Contract Interpolation
- **REQ-103:** The contract resolver (`core/contract_resolver.py`) expands `${env.<field>}` and `${contract.<field>}` tokens in contract string values (including dotted paths like `${env.state_backend.bucket}`) after schema validation and before IR resolution. The `env` context is the loaded `core/environments/<contract.environment>.json`; the `contract` context is the contract dict. Unresolved tokens raise `ValueError` (fail loud). Sample contracts use naming patterns that include region, account id, and environment (e.g. `acdl-${env.environment}-${contract.module}-${env.account_id}-${env.region}`).
- **REQ-104:** An environment JSON schema `schemas/environment.schema.json` (draft 2020-12) defines the environment file shape (`name`, `account_id`, `region`, `state_backend`, `network`, `runner_role_arn`, `autonomy`, `confidence_threshold`). `core/environments/dev.json` validates against it. `qa.json`, `prod.json`, `dr.json` placeholder bindings exist (autonomy `attested`, thresholds 0.75/0.90/0.95).

### Category: Per-Environment CI Jobs
- **REQ-105:** Per-environment contract files exist for each sample module (`contracts/static-assets.{dev,qa,prod,dr}.yaml` and `contracts/microservice.{dev,qa,prod,dr}.yaml`), each setting `environment:` to its own name and using interpolation for env-specific values. The existing `contracts/static-assets.yaml` + `contracts/microservice.yaml` remain as the dev default for backwards compatibility.
- **REQ-106:** The reusable deploy workflow (`.github/workflows/deploy.yml` + `.gitea/workflows/deploy.yml`, byte-identical) declares an `environment` `workflow_call` input (enum dev/qa/prod/dr, default empty). When non-empty, `scripts/run_platform.sh --environment <name>` overrides the contract's `environment` field at load time (before interpolation). A consumer repo's caller workflow has one job per environment, each pointing at its respective contract (or the same contract + the env input). Promotion = running the matching job; no `environment:` field editing. `docs/CONSUMER_GUIDE.md` documents the per-env caller workflow pattern.

### Category: Stub Implementation
- **REQ-107:** `core/separation_of_duties.py` `route_halt_artifact` is a real implementation: publishes to an SNS topic `acdl-sod-halt` (ARN from `ACDL_SOD_HALT_TOPIC_ARN`); when unset, falls back to a structured stderr emission + a `SEPARATION_OF_DUTIES_VIOLATION` event write to the DynamoDB outbox via `outbox_writer.write_event`. No silent print-only stub. The SNS topic is defined in `terraform/platform/main.tf`.
- **REQ-108:** HITL qa/prod/dr pre-execution attestation gates are wired via `core/hitl_gates.py` (`attest(contract_id, env, approver, evidence)`). The gate records the approver (`gitea.actor` / `github.actor`) to the outbox (`approver_qa` / `approver_prod` / `approver_dr` attributes per `audit_ledger_design.md`), runs the separation-of-duties check on prod, and returns `(ok, reason)`. `scripts/run_platform.sh` calls `hitl_gates.attest` before apply for qa/prod/dr (dev skips). The workflow's `workflow_dispatch` approval input is the trigger.
- **REQ-109:** The full 8-concern attestation matrix from `hitl_matrix_design.md` §10.4 is implemented in `core/attestation_matrix.py`. Offline-testable concerns (contract NFRs, schema validity, policy pass) run for real; operator-supplied concerns (k6 load test, DR drill, FinOps forecast) accept an uploaded signed evidence artifact validated for freshness + schema, failing loud if missing/expired for prod/dr. `hitl_gates.attest` invokes the matrix for the target env and blocks on any failing concern.
- **REQ-110:** The Wiz adapter (`adapters/wiz/wiz_adapter.py`) is a real API client: a `WizClient` queries the Wiz GraphQL API (`WIZ_API_TOKEN` + `WIZ_API_URL`) and translates issues → `PolicyCheckResult` records. It degrades gracefully (existing `WIZ_NOT_CONFIGURED` SKIPPED record) when env unset. Offline tests use a recorded GraphQL fixture.
- **REQ-111:** The Kyverno adapter (`adapters/kyverno/kyverno_adapter.py`) translator is fleshed out: full `PolicyReport` → `PolicyCheckResult` mapping with severity + skip handling. It remains inactive for Terraform-only stacks (guard preserved); a `--kube-version` stub is added for future GitOps. Sample policies already exist.

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

### Category: Pipeline Regression Fix
- **REQ-112:** The CIAgent VERIFY stage supports a `regression` mode that re-runs capability checks (not just diff checks), triggered at minimum on milestone completion. The regression run executes the local-emulator tier (REQ-113) for every capability marked Verified in prior milestones; any capability that fails the regression run blocks milestone completion. Regression results are recorded in `---ci---` blocks as `regression: { capability: <id>, status: Verified|Decayed|Broken }`. Existing diff-scoped VERIFY behavior is preserved for non-regression invocations. A regression run against the current codebase surfaces at least one Decayed/Broken capability (proving the gate catches decay, not just passes). `tests/test_verify_regression_mode.py` passes.

### Category: Local Emulating Adapters
- **REQ-113:** Local emulating adapters exist so the platform is fully locally testable without cloud credentials: (a) a flat-file DynamoDB outbox adapter that writes evidence events to flat files in a temp folder with a valid hash chain, same write/read interface as the live DynamoDB outbox adapter; (b) a local ECS Fargate emulator that records the service definition and returns a synthetic HTTP 200 from a local shell process, same interface as the live ECS adapter; (c) a local S3 state backend (flat-file tfstate in a temp folder); (d) a local Lambda stub that invokes the handler in-process with no AWS Lambda call. The headline E2E (contract submission → service live → evidence event) runs end-to-end against the local tier with no cloud credentials. `tests/test_local_emulating_adapters.py` passes. `run_platform.sh --local` (or equivalent) runs the full pipeline locally.

### Category: Capability Re-Verification Sweep
- **REQ-114:** Every capability advertised in v1.1→v1.8 PROJECT/ROADMAP is enumerated in `.ciagent/CAPABILITY_INVENTORY.md` with a unique ID per capability (v1.0 demo excluded as archived/superseded). Each capability is re-verified: the headline E2E (contract → ECS Fargate → evidence event) runs both live-AWS and local-emulator tiers, both must pass; all other capabilities run the local tier via emulating adapters (REQ-113). Each capability is tagged Verified / Decayed / Broken in `CAPABILITY_INVENTORY.md`. Every Decayed/Broken capability is fixed in-sweep (D-090: no cap) until Verified, with per-capability commits `verify(P54): <id> — <status>` and `fix(P54): <id> — <summary>`. All v1.1→v1.8 advertised capabilities end Verified. The regression run (REQ-112) is clean against the re-verified state.

### Category: Verified-Reality Rewrite
- **REQ-115:** PROJECT.md, ROADMAP.md, and both leadership decks are rewritten to match `CAPABILITY_INVENTORY.md` exactly. PROJECT.md gains a "Capability Status (Re-Verified 2026-07-27)" section listing every v1.1→v1.8 capability with its Verified tag and the tier(s) tested, plus a decay disclosure: capabilities marked complete in v1.1–v1.8 ran at the time of tagging; as of 2026-07-27 they were not reproducible and were re-verified in v1.10. ROADMAP.md v1.9.x entries note deck-freeze and superseded-by-reverification status. Both leadership decks reflect the re-verified status; any claim that cannot be demonstrated live is removed. HTML is re-rendered; PPTX is uploaded to the v1.10.0 release. Decks are unfrozen only after this lands. `ci-doc-verifier` confirms no stale capability claims remain. v1.10.0 is tagged; the Gitea release is published.

## Out of Scope (v1.9)

| Feature | Reason |
|---------|--------|
| S3 Object Lock + JWS + async worker + DLQ + daily checkpoints (audit ledger build-out) | Requires non-offline-testable AWS infra (Object Lock bucket, KMS signing key, SQS DLQ, Lambda worker). Deferred to a future milestone (D-083). The hash-chain + DynamoDB-outbox path remains the v1.9 production audit record. |
| Live k6/Gatling load test execution, live DR drill, live FinOps forecast | Operator-supplied evidence artifacts (signed blobs) are accepted + validated; the platform does not run these inline. |
| Self-service environment provisioning | Adding an environment remains a platform-team action (per `core/environments/README.md`). v1.9 adds the env files + schema, not self-service provisioning. |

## Traceability

### v1.0 (prior — demo)

| Requirement | Phase | Status |
|-------------|-------|--------|
| REQ-01 | 1 | complete (v1.0.1) |
| REQ-02 | 2 | complete (v1.0.2) |
| REQ-03 | 2 | complete (v1.0.2) |
| REQ-04 | 3 | complete (v1.0.3) |
| REQ-05 | 3 | complete (v1.0.3) |
| REQ-06 | 3 | complete (v1.0.3) |
| REQ-07 | 3 | complete (v1.0.3) |
| REQ-08 | 3 | complete (v1.0.3) |
| REQ-09 | 1 | complete (v1.0.1) |
| REQ-10 | 4 | complete (v1.0.4) |
| REQ-11 | 3 | complete (v1.0.3) |
| REQ-12 | 4 | complete (v1.0.4) |
| REQ-13 | 5 | complete (v1.0.5) |
| REQ-14 | 5 | complete (v1.0.5) |
| REQ-15 | 5 | complete (v1.0.5) |

### v1.1 (prior — architecture finalization + v1 spike, complete)

| Requirement | Phase | Status |
|-------------|-------|--------|
| REQ-16 | 07 | complete (v1.1.2) |
| REQ-17 | 07 | complete (v1.1.2) |
| REQ-18 | 07 | complete (v1.1.2) |
| REQ-19 | 07 | complete (v1.1.2) |
| REQ-20 | 07 | complete (v1.1.2) |
| REQ-21 | 07 | complete (v1.1.2) |
| REQ-22 | 07 | complete (v1.1.2) |
| REQ-23 | 08 | complete (v1.1.3) |
| REQ-24 | 09 | complete (v1.1.4) |
| REQ-25 | 10 | complete (v1.1.5) |
| REQ-26 | 09 | complete (v1.1.4) |
| REQ-27 | 10 | complete (v1.1.5) |
| REQ-28 | 10 | complete (v1.1.5) |

### v1.2 (prior — platform hardening + first real consumer deployment, complete)

| Requirement | Phase | Status |
|-------------|-------|--------|
| REQ-29 | 11 | complete (v1.2.1) |
| REQ-30 | 12 | complete (v1.2.2) |
| REQ-31 | 13 | complete (v1.2.3) |
| REQ-32 | 14 | complete (v1.2.4) |
| REQ-33 | 15 | partial (v1.2.5, IAM-blocked) |
| REQ-34 | 15 | complete (v1.2.5) |
| REQ-35 | 16 | partial (v1.2.6, IAM-blocked) |

### v1.3 (prior — module documentation + thin-composition removal, complete)

| Requirement | Phase | Status |
|-------------|-------|--------|
| REQ-36 | 17 | complete (v1.3.1) |
| REQ-37 | 17 | complete (v1.3.1) |
| REQ-38 | 17 | complete (v1.3.1) |
| REQ-39 | 18 | complete (v1.3.2) |
| REQ-40 | 18 | complete (v1.3.2) |
| REQ-41 | 18 | complete (v1.3.2) |
| REQ-42 | 18 | complete (v1.3.2) |

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

| Requirement | Phase | Status |
|-------------|-------|--------|
| REQ-43 | 19 | complete (v1.4.1) |
| REQ-44 | 19 | complete (v1.4.1) |
| REQ-45 | 19 | complete (v1.4.1) |

### v1.5 (prior — consumer happy path + zero-trust docs + reusable deploy workflow, complete)

| Requirement | Phase | Status |
|-------------|-------|--------|
| REQ-46 | 20 | complete (v1.5.0) |
| REQ-47 | 20 | complete (v1.5.0) |
| REQ-48 | 20 | complete (v1.5.0) |
| REQ-49 | 20 | complete (v1.5.0) |
| REQ-50 | 20 | complete (v1.5.0) |
| REQ-51 | 20 | complete (v1.5.0) |

### v1.6 (complete — consumer-facing docs restructure + terminology normalization + environments concept, tag `v1.6.0`)

| Requirement | Phase | Status |
|-------------|-------|--------|
| REQ-52 | 21 | complete (v1.6.0) |
| REQ-53 | 21 | complete (v1.6.0) |
| REQ-54 | 21 | complete (v1.6.0) |
| REQ-55 | 21 | complete (v1.6.0) |
| REQ-56 | 21 | complete (v1.6.0) |
| REQ-57 | 21 | complete (v1.6.0) |
| REQ-58 | 21 | complete (v1.6.0) |
| REQ-59 | 21 | complete (v1.6.0) |
| REQ-60 | 21 | complete (v1.6.0) |
| REQ-61 | 21 | complete (v1.6.0) |

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

| Requirement | Phase | Status |
|-------------|-------|--------|
| REQ-62 | 22 | complete (v1.7.0) |
| REQ-63 | 22 | complete (v1.7.0) |
| REQ-64 | 22 | complete (v1.7.0) |
| REQ-65 | 23 | complete (v1.7.0) |
| REQ-66 | 23 | complete (v1.7.0) |
| REQ-67 | 23 | complete (v1.7.0) |
| REQ-68 | 24 | complete (v1.7.0) |
| REQ-69 | 25 | complete (v1.7.0) |
| REQ-70 | 25 | complete (v1.7.0) |
| REQ-71 | 25 | complete (v1.7.0) |
| REQ-72 | 26 | complete (v1.7.0) |
| REQ-73 | 26 | complete (v1.7.0) |
| REQ-74 | 27 | complete (v1.7.0) |
| REQ-75 | 27 | complete (v1.7.0) |

### v1.8 (complete — P1 remediation + uptime + standards + encryption/deletion-protection by default + decommission + docs, tag `v1.8.0`)

| Requirement | Phase | Status |
|-------------|-------|--------|
| REQ-76 | 28 | complete (v1.8.0) |
| REQ-77 | 28 | complete (v1.8.0) |
| REQ-78 | 29 | complete (v1.8.0) |
| REQ-79 | 29 | complete (v1.8.0) |
| REQ-80 | 30 | complete (v1.8.0) |
| REQ-81 | 30 | complete (v1.8.0) |
| REQ-82 | 30 | complete (v1.8.0) |
| REQ-83 | 31 | complete (v1.8.0) |
| REQ-84 | 31 | complete (v1.8.0) |
| REQ-85 | 31 | complete (v1.8.0) |
| REQ-86 | 32 | complete (v1.8.0) |
| REQ-87 | 32 | complete (v1.8.0) |
| REQ-88 | 33 | complete (v1.8.0) |
| REQ-89 | 33 | complete (v1.8.0) |
| REQ-90 | 33 | complete (v1.8.0) |
| REQ-91 | 33 | complete (v1.8.0) |
| REQ-92 | 34 | complete (v1.8.0) |
| REQ-93 | 34 | complete (v1.8.0) |
| REQ-94 | 34 | complete (v1.8.0) |
| REQ-95 | 35 | complete (v1.8.0) |
| REQ-96 | 35 | complete (v1.8.0) |
| REQ-97 | 36 | complete (v1.8.0) |
| REQ-98 | 36 | complete (v1.8.0) |
| REQ-99 | 36 | complete (v1.8.0) |
### v1.9 (complete — design doc refresh + contract interpolation + per-env CI jobs + stub implementation + P1-1 remediation, tag `v1.9.0`)

| Requirement | Phase | Status |
|-------------|-------|--------|
| REQ-100 | 39 | complete (v1.9.0) |
| REQ-101 | 39 | complete (v1.9.0) |
| REQ-102 | 39 | complete (v1.9.0) |
| REQ-103 | 40 | complete (v1.9.0) |
| REQ-104 | 40 | complete (v1.9.0) |
| REQ-105 | 41 | complete (v1.9.0) |
| REQ-106 | 41 | complete (v1.9.0) |
| REQ-107 | 42 | complete (v1.9.0) |
| REQ-108 | 42 | complete (v1.9.0) |
| REQ-109 | 42 | complete (v1.9.0) |
| REQ-110 | 42 | complete (v1.9.0) |
| REQ-111 | 42 | complete (v1.9.0) |
### v1.10 (active — pipeline regression fix + capability re-verification + verified-reality rewrite, tag `v1.10.0`)

| Requirement | Phase | Status |
|-------------|-------|--------|
| REQ-112 | 52 | complete (v1.9.9) |
| REQ-113 | 53 | complete (v1.9.10) |
| REQ-114 | 54 | complete (v1.9.11) |
| REQ-115 | 55 | complete (v1.9.12) |

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

The v1.11 milestone closes G-005 (CAP-017..022 deploy-unverified) and G-008
(no cost docs) via a corrected architecture. The first v1.11 attempt is
abandoned (branches `phase/56-iam-re-bootstrap` + `phase/57-live-deploy-microservice`);
the restart branches off `v1.10.2`.

### Category: Stateless Adapter
- **REQ-123** — The terraform adapter (`adapters/terraform/adapter.py`) is rewritten from a 918-line monolith (3 constant tables `TYPE_MAP`/`INPUT_MAP`/`OUTPUT_MAP` + 39 type-specific branches) to a ~80-line stateless assembler. Each L1 module ships a real `terraform/` module dir owning its resource shape, nested blocks, and defaults. The adapter reads the registry and emits `module "x" { source = ... }` blocks. No type-specific logic in the adapter. (Phase P56a)

### Category: Per-Module Terraform
- **REQ-124** — All 12 L1 modules have a `terraform/` subdir (`versions.tf`/`variables.tf`/`locals.tf`/`main.tf`/`outputs.tf`) with defaults centralized in `locals.tf` (heavy interpolation of vars against sensible defaults). `interface.json` stays engine-agnostic. The registry has a `terraform_dir` field per entry. (Phase P56b)

### Category: Shell Lifecycle Modes
- **REQ-125** — `scripts/run_platform.sh` gains `--apply` and `--destroy` modes; the shell owns all terraform lifecycle. Python never runs terraform. `scripts/verify_deploy_microservice.py` is deleted. (Phase P57)

### Category: Single Platform VPC + Deterministic State
- **REQ-126** — `terraform/platform/main.tf` owns ONE VPC; the microservice composition references it via `data` source (no inline VPC). State keys are deterministic and env-aware (`spike/{id}/{env}/terraform.tfstate`), stable across apply/modify/destroy. (Phase P58)

### Category: L1 Lifecycle Pipeline
- **REQ-127** — 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. The "test" = the pipeline cell going green. (Phases P59–P60)

### Category: L2 Lifecycle Pipeline
- **REQ-128** — The lifecycle pipeline extends to L2 modules (static-assets, microservice). L2 = composition only (no L2 terraform files); the composition is deterministic (same contract → same stack → same state key). (Phases P61–P62)

### Category: Operating Model + G-005/G-008 Closure
- **REQ-116** — CAP-017..022 marked Verified in CAPABILITY_INVENTORY + PROJECT + decks with "Verified live-aws via lifecycle pipeline; torn down to zero-cost" note. (Phase P65)
- **REQ-118** — Both leadership decks rewritten to reflect verified-then-torn-down status; no stale "deploy-unverified" claims. (Phase P65)
- **REQ-119** — `.ciagent/COST.md` documents the v1.0→v1.10 AWS spend window (Cost Explorer query). (Phase P63)
- **REQ-120** — `.ciagent/PRE_MORTEM.md` documents the v1.10 decay root cause + forward pre-mortem. (Phase P64)
- **REQ-121** — CAP-017..022 added to the regression registry (evidence = lifecycle pipeline green). (Phase P63)
- **REQ-122** — All deployed stacks torn down via `--decommission` (D-070 two-step, CR CHG0680001); zero live ACDL resources remain. (Phase P64)

### v1.11 Traceability

| Requirement | Phase | Status |
|-------------|-------|--------|
| REQ-123 | P56a | complete |
| REQ-124 | P56b | complete |
| REQ-125 | P57 | complete |
| REQ-126 | P58 | complete |
| REQ-127 | P59, P60 | complete |
| REQ-128 | P61, P62 | complete |
| REQ-116 | P65 | complete |
| REQ-118 | P65 | complete |
| REQ-119 | P63 | complete |
| REQ-120 | P64 | complete |
| REQ-121 | P63 | complete |
| REQ-122 | P64 | complete |

### Out of Scope (v1.11)
- OIDC act_runner adoption (pending go-gitea/gitea#36988).
- Per-phase regression (G-007: milestone-level regression gate is correct).
- Audit ledger build-out (D-083).
- Operator-supplied evidence.
- Pilot onboarding (G-001).
- Boto3 post-deploy verification probes (CAP-017..022 live-verify via boto3) — deferred to a future QA milestone. The lifecycle pipeline apply→destroy IS the verification for v1.11.

---

## Milestone v1.12 — Presentation Refinement (REQ-129..REQ-133)

**Objective:** Refine the leadership presentation decks to reflect the
verified reality after v1.11 — the stateless adapter, pipeline-driven
lifecycle testing, the cost operating model, the pre-mortem, and the
teardown to zero-cost. The v1.11 P65 deck-rewrite task did not fully land
on the deck artifacts: the rendered HTML still claims 6 cloud
capabilities are "deploy-unverified (IAM drift)", the road-to-north-star
diagram still shows v1.10 as "NEXT", and the v1.11 architecture stories
are absent. The v1.10 decay lesson (PRE_MORTEM.md FM-3) requires decks
to match verified reality exactly, not outrun it. The v1.12 regression
gate run (Phase 66) surfaced 3 Broken capabilities — one real adapter
defect (CAP-013) and two regression-probe bugs (CAP-017, CAP-018) — that
must be fixed before the decks can honestly claim 22/22 Verified.

**Surface:** leadership decks only (`docs/presentations/`) — both decks
across all four layers (source markdown, Marp deck, rendered HTML,
talking points) + diagrams + README. Plus the one real adapter fix and
two probe fixes required to make the deck claims true.

### Requirements

- **REQ-129** — The adapter's module-call dedup logic
  (`adapters/terraform/adapter.py`) is fixed so multi-resource L1s with
  stack outputs (e.g. `ecs-service`, `alb`) produce valid Terraform:
  `terraform validate` succeeds for the microservice stack (CAP-013
  Verified live-aws). The regression gate re-runs and confirms 22/22
  Verified. (Phase 67)
- **REQ-130** — The two regression-probe bugs are fixed: CAP-017's
  probe no longer requires `locals.tf` for modules that legitimately
  omit it (`core/regression_verify.py`); CAP-018's probe instantiates
  `LocalLambdaStub` with the required `outbox` arg. The regression gate
  re-runs clean (19 Verified + 3 fixed → 22/22 Verified). (Phase 67)
- **REQ-131** — Both leadership decks' capability claims match
  `CAPABILITY_INVENTORY.md` exactly: 22/22 Verified, no
  "deploy-unverified" / "IAM drift" / "design-verified" framing. The
  decks reflect "Verified live-aws via lifecycle pipeline; torn down to
  zero-cost." A grep-based doc verification (successor to the planned
  `ci-doc-verifier`) confirms zero stale claims across
  `docs/presentations/`. (Phase 68, Phase 70)
- **REQ-132** — Both decks reflect v1.11's architecture as
  leadership-relevant stories: (a) the stateless adapter
  (918→~80 lines, defaults centralized in per-module `terraform/`
  dirs, the adapter is an assembler); (b) pipeline-driven lifecycle
  testing (a `modules-lifecycle` pipeline matrix-runs each module
  apply→modify→destroy against live AWS — the green cell IS the
  verification). The `road-to-north-star` diagram + both decks' roadmap
  appendix slides reflect v1.11 complete (v1.10 no longer "NEXT").
  Version refs in deck examples bump from `@v1.10` → `@v1.11` (and
  `@v1.12` at Phase 70 complete after the tag exists). (Phase 68)
- **REQ-133** — Both decks' "Operating Model & Cost" appendix slide
  carries the real `COST.md` figures ($0.001883 / 8 days, ~$0.007/mo,
  S3-dominated, zero BAU compute) + the zero-cost-steady-state /
  D-096 teardown claim, and references the pre-mortem
  (`PRE_MORTEM.md`: v1.10 decay root cause + four forward failure modes
  + structural mitigations). Both rendered HTML decks re-rendered and
  committed; both talking-points files re-distilled to match the updated
  Marp structure (including the A6 Operating Model & Cost section that
  was missing from the talking points). PPTX exported to the v1.12.0
  release. (Phase 69, Phase 70)
- **REQ-134** — The `modules-lifecycle` pipeline defaults to **plan-only**
  (fast, no AWS mutation) so it runs on every PR without cost or AWS
  credentials. A CI variable `ACDL_LIFECYCLE_MODE` (workflow input
  `lifecycle_mode`, default `plan`) overrides to `full` for the real
  apply→modify→destroy against live AWS. The four lifecycle scripts
  (`run_lifecycle_test.sh`, `run_lifecycle_destroy.sh`,
  `run_l2_lifecycle_test.sh`, `run_l2_lifecycle_destroy.sh`) read the
  flag and dispatch to `--plan-only` (plan mode) or `--apply`/`--destroy`
  (full mode). Both forge workflows (`.github` + `.gitea`, byte-identical)
  expose `lifecycle_mode` as a `workflow_dispatch` input and pass it via
  `env:` to every lifecycle step; the CI VPC apply/destroy jobs are
  skipped in plan mode. `pipelines/modules-lifecycle.yml` + the schema
  document the `default_mode: plan` field. Tests assert the plan-only
  default, the override path, the byte-identity of both workflows, and
  the CI VPC skip in plan mode. (Phase 67b)

### v1.12 Traceability

| Requirement | Phase | Status |
|-------------|-------|--------|
| REQ-129 | P67 | complete |
| REQ-130 | P67 | complete |
| REQ-134 | P67b | complete |
| REQ-131 | P68, P70 | complete |
| REQ-132 | P68 | complete |
| REQ-133 | P69, P70 | complete |

### Out of Scope (v1.12)
- docs/ site, README.md, consumer-guide, module READMEs (decks only).
- Structural deck rework (re-ordering, adding/removing main slides) —
  v1.12 keeps the 10 main + 6 appendix structure to avoid the
  backwards-sequencing failure mode (PRE_MORTEM.md FM-3).
- New capability claims beyond what v1.11 verified.
- Per-phase regression hardening (G-007, unchanged).

---

## Milestone v1.14 — NFR Refinement (REQ-135..REQ-154)

**Objective:** Bug fixes, security posture improvements, stub/missing-
functionality identification + implementation, and documentation + NFR
refinement across the entire codebase. **No new features.** NFR milestone
— the final phase's patch IS the deliverable.

The v1.11 multi-persona review left 5 P1 + 4 P2 findings open; the
codebase has 6+ swallowed-error sites, 15+ hardcoded account-ID
references, 7 untested scripts, an offline-proxy regression gate,
ARCHITECTURE.md with no v1.11–v1.13 addendum, and consumer-facing docs
referencing stale `@v1.6`–`@v1.9` workflow tags. v1.14 clears all of it
in a 20-phase sweep.

### Requirements

- **REQ-135** — The adapter dedup loop raises `ValueError` for
  unregistered-module resources instead of silently dropping them (P1-1).
  (Phase P1)
- **REQ-136** — The static-assets L2 composition wires `default_ttl`/
  `max_ttl`/`price_class`/`viewer_protocol_policy` and makes WAF
  conditional via `waf_enabled`, so `complex.yml` is a real modify (P1-2).
  (Phase P2)
- **REQ-137** — The L2 lifecycle scripts' usage strings no longer
  advertise the vestigial `[ci-vpc-outputs.json]` arg, or document the
  remote-state design (P1-3). (Phase P3)
- **REQ-138** — The regression gate's CAP-017..022 checks run
  `terraform validate` (not just file-existence + resolver); the
  offline-proxy caveat is documented honestly (P1-5). (Phase P4)
- **REQ-139** — Unit tests for adapter dedup merge behavior +
  `ACDL_REMOTE_STATE_KEY` override exist and pass (P2-2). (Phase P5)
- **REQ-140** — The ALB target group `name_prefix` derives from `var.name`
  (P2-1). (Phase P6)
- **REQ-141** — 6 over-broad `except ...: pass` sites narrowed to specific
  exceptions; errors logged with context. (Phase P7)
- **REQ-142** — The hardcoded account ID `581513795199` is externalized to
  `ACDL_AWS_ACCOUNT_ID` env / `data.aws_caller_identity` across 15+ sites.
  (Phase P8)
- **REQ-143** — 6 `Resource: "*"` IAM statements scoped to `acdl-*` ARNs;
  regression test asserts the scoping. (Phase P9)
- **REQ-144** — The contract ingestor validates `contractId`/`environment`/
  `error`; ABAC reliance documented; spoofing-resistance test passes.
  (Phase P10)
- **REQ-145** — `contract.schema.json` + `environment.schema.json` reject
  undocumented fields (`additionalProperties: false`); format validation
  for bucket/ARN/CIDR. (Phase P11)
- **REQ-146** — `.gitignore` has a credential-pattern catch-all;
  `test_no_secrets_tracked.py` passes. (Phase P12)
- **REQ-147** — The Kyverno `--kube-version` flag is either implemented or
  removed with a documented deferral rationale. (Phase P13)
- **REQ-148** — Orphan bytecode + dead config cleaned (orphan `.pyc`,
  stale coverage source, stale version, dead JS allowlist). (Phase P14)
- **REQ-149** — 7 untested scripts have unit test coverage (≥1 test each).
  (Phase P15)
- **REQ-150** — Gitea workflow parity resolved; `rotate_spike_key.sh` +
  `sync_to_gl.sh` have `set -euo pipefail`. (Phase P16)
- **REQ-151** — `config.json` persona block + branching strategy +
  ollama-cloud backend aligned with PERSONAS.md + actual runtime.
  (Phase P17)
- **REQ-152** — `modules/STANDARDS.md` internally consistent; no stale
  `TYPE_MAP` reference. (Phase P18)
- **REQ-153** — ARCHITECTURE.md has v1.11–v1.14 addenda; stale `@v1.6–1.9`
  → `@v1.13`; GRILL G-005/G-008 resolved; COST.md window covers v1.11–v1.14;
  D-083 deferral recorded. (Phase P19)
- **REQ-154** — Platform VPC CIDR is a variable; subnet count is
  data-driven; `0.0.0.0/0` ingress documented. (Phase P20)

### v1.14 Traceability

| Requirement | Phase | Status |
|-------------|-------|--------|
| REQ-135 | P1 | pending |
| REQ-136 | P2 | pending |
| REQ-137 | P3 | pending |
| REQ-138 | P4 | pending |
| REQ-139 | P5 | pending |
| REQ-140 | P6 | pending |
| REQ-141 | P7 | pending |
| REQ-142 | P8 | pending |
| REQ-143 | P9 | pending |
| REQ-144 | P10 | pending |
| REQ-145 | P11 | pending |
| REQ-146 | P12 | pending |
| REQ-147 | P13 | pending |
| REQ-148 | P14 | pending |
| REQ-149 | P15 | pending |
| REQ-150 | P16 | pending |
| REQ-151 | P17 | pending |
| REQ-152 | P18 | pending |
| REQ-153 | P19 | pending |
| REQ-154 | P20 | pending |

### Out of Scope (v1.14)
- New features (feat phases). v1.14 is NFR-only.
- D-083 audit ledger build-out (S3 Object Lock + JWS + SQS DLQ + async
  worker) — remains deferred; documented explicitly in ARCHITECTURE.md.
- Real OIDC federation (blocked on go-gitea/gitea#36988).
- Per-phase regression hardening (G-007, unchanged).
- Boto3 post-deploy verification probes (deferred to a future QA
  milestone).
