From 25427250ad99c07151a9d78d387d8df312a564c1 Mon Sep 17 00:00:00 2001 From: Jon Chery Date: Wed, 12 Aug 2026 14:26:22 +0000 Subject: [PATCH] =?UTF-8?q?docs(P1):=20consumer=20guide=20accuracy=20fixes?= =?UTF-8?q?=20=E2=80=94=20REQ-276..281,290?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Step 3 contract fields table: stale uses/module → real id/name/environment/infrastructure (REQ-276) - Step 4 caller: add environment: dev to match Step 2 (REQ-277) - Step 5 stage 8: (dev only) → (autonomous in dev; higher envs apply after HITL) (REQ-278) - Step 8: rewrite with Shape A destroy-then-rebuild + Shape B cross-ref (REQ-279) - Per-env section: add Shape B lead sentence (REQ-280) - Reference table: @v1.19 wording + .yaml→.yml extension fix (REQ-281) - Tests: rename no-field-editing → both-promotion-shapes + new destroy-on-env-change test (REQ-290) ---ci--- project: acdl phase: 1 milestone: v1.24 status: execute requirements: [REQ-276,REQ-277,REQ-278,REQ-279,REQ-280,REQ-281,REQ-290] ---/ci--- --- docs/consumer-guide.md | 58 ++++++++++++++++---- tests/test_consumer_guide_per_env_section.py | 34 +++++++++++- 2 files changed, 78 insertions(+), 14 deletions(-) diff --git a/docs/consumer-guide.md b/docs/consumer-guide.md index 6b425a2..53896c0 100644 --- a/docs/consumer-guide.md +++ b/docs/consumer-guide.md @@ -140,10 +140,10 @@ name: microservice | Field | Type | Required | Description | |-------|------|----------|-------------| -| `uses` | string | yes | Reference to the central deployment pipeline, **versioned** with a floating MAJOR+MINOR tag (e.g. `nova/pipelines/contract.yml@v1.19`). Bare or `@main` references are discouraged. See [Versioning](pipeline/versioning). | -| `module` | string | yes | Module name from the registry — any primitive or module (e.g. `static-assets`, `microservice`, `s3`). See the [module catalog](modules/). | -| `environment` | string | yes | The platform-managed environment to deploy to (e.g. `dev`). See [Environments](environments/). | -| `inputs` | object | yes | Module-specific inputs (see the module's README). | +| `id` | string | yes | Short operational acronym (3-6 chars, lowercase + digits + hyphens). Becomes `stack.name`: the Terraform state key (`spike///terraform.tfstate`), the outbox event identity, and the resource naming prefix. Stable across deploys and environment promotions. | +| `name` | string | yes | Full human-readable stack name. Becomes `stack.title`: the display name in PR comments, evidence records, and dashboards. | +| `environment` | string | yes | The platform-managed environment to deploy to (`dev`, `qa`, `prod`, or `dr`). See [Environments](environments/). | +| `infrastructure` | object | yes | Map of modules to deploy, keyed by module name (matching a registry key in `modules/registry.json`). Each entry carries an optional `version` (defaults to latest published) and per-module `inputs`. One entry = single-module deploy; N entries = multi-module manifest. | ### Module inputs @@ -180,6 +180,7 @@ jobs: uses: nova/.github/workflows/deploy.yml@v1.19 with: contract: .nova/contract.yml + environment: dev ``` That is the entire consumer-side workflow. When you push to `main`: @@ -229,7 +230,7 @@ flowchart TD S5["policy checks
(adapter -> PolicyCheckResult)"] --> S6 S6["confidence
score + band (dev >= 0.50)"] --> S7 S7["evidence event
to the audit outbox"] --> S8 - S8["infrastructure apply
(dev only)"] + S8["infrastructure apply
(autonomous in dev;
higher envs apply after HITL)"] ``` 1. **validate-contract** — validates your contract YAML against the contract @@ -250,9 +251,10 @@ flowchart TD threshold is ≥ 0.50. If the band is `pass`, the pipeline proceeds. 7. **evidence event** — a hash-chained evidence event is written to the audit outbox. -8. **infrastructure apply** (dev only) — the infrastructure plan is applied, - creating the resources in your AWS account. An evidence event for the - apply is recorded. +8. **infrastructure apply** (autonomous in dev; higher environments apply + after HITL attestation) — the infrastructure plan is applied, creating + the resources in your AWS account. An evidence event for the apply is + recorded. ## Step 6 — What gets created @@ -289,7 +291,14 @@ push your container image to the ECR repo the platform created. ## Step 8 — Promote to qa / prod -Change `environment` in your contract (the infrastructure stays the same): +There are **two supported promotion shapes**. Both are valid; pick the one +that fits your repo's workflow. + +### Shape A — edit the environment field (destroy-then-rebuild) + +Change `environment` in your contract (the infrastructure stays the same). +The contract `id` stays stable, so the platform knows this is the same +stack moving to a new environment: ```yaml id: assets @@ -301,10 +310,32 @@ infrastructure: inputs: { ... } ``` +**What happens when you change `environment: dev` → `environment: qa`:** +the platform detects that the environment changed on a known contract `id`. +Before building the new environment, it **destroys the prior environment's +resources** (Terraform state key `spike/{id}/dev/`) and records an evidence +event for the destroy. Only then does it apply the new environment (state +key `spike/{id}/qa/`). **There is no orphan path** — if the destroy fails, +the pipeline fails closed (no apply runs, no resources are left behind). +This is full lifecycle management: the platform never creates a state +where prior-environment resources are abandoned. + Higher environments require human attestation (a platform-runner deployment approval) and higher confidence thresholds. See [Environments](environments/) for the full table. +> **Note:** the destroy-then-rebuild runs within the same AWS account (the +> current platform scaffold uses one account). Cross-account promotion +> (separate accounts per env) is a future milestone. + +### Shape B — per-environment caller workflows (no editing) + +Alternatively, keep one contract per environment (or one contract + the +`environment` workflow input) and run the matching CI job to promote. This +avoids the destroy step because each environment has its own state from the +first deploy. See [Per-environment deployment](#per-environment-deployment) +below for the full pattern. + ## Step 9 — Compliance extensions Each module lists compliance extension points for the future compliance @@ -326,8 +357,8 @@ per-module extension points. Common examples: | Contract schema | `schemas/contract.schema.json` | JSON Schema for consumer contracts. | | Stack schema | `schemas/stack.schema.json` | JSON Schema for the resolved stack instance. | | Module catalog | [modules/](modules/) | All primitives and modules. | -| Sample contract | `contracts/static-assets.yaml` | The reference example contract (uses `@v1.19`). | -| Sample contract | `contracts/microservice.yaml` | The microservice example contract (uses `@v1.19`). | +| Sample contract | `contracts/static-assets.yml` | The reference example contract (used with caller workflow `@v1.19`). | +| Sample contract | `contracts/microservice.yml` | The microservice example contract (used with caller workflow `@v1.19`). | | Module examples | `modules//examples/` | Validated per-module example contracts (`simple.yaml` + `complex.yaml`). | | Contract resolver | `core/contract_resolver.py` | Resolves contracts to stack instances. | | Angine adapter | `adapters/terraform/adapter.py` | Compiles stack instances to infrastructure. | @@ -395,6 +426,11 @@ separately (or left running to monitor the decommissioned stack's endpoints going dark). ## Per-environment deployment +> **This is Shape B** (the alternative to [Shape A's edit-and-destroy +> path](#step-8--promote-to-qa--prod) in Step 8). Shape B avoids the +> destroy step because each environment has its own state from the first +> deploy — no prior environment to tear down. + Nova supports a **promotion-without-editing** model: you do not edit the `environment:` field in a contract to promote dev → qa → prod → dr. Instead, there is **one CI job per environment**, each pointing at its diff --git a/tests/test_consumer_guide_per_env_section.py b/tests/test_consumer_guide_per_env_section.py index 7300895..f4d8df8 100644 --- a/tests/test_consumer_guide_per_env_section.py +++ b/tests/test_consumer_guide_per_env_section.py @@ -1,4 +1,10 @@ -"""REQ-106: consumer guide documents per-env caller workflows.""" +"""REQ-106 + REQ-290: consumer guide documents both promotion shapes. + +Shape A (Step 8): edit the environment field → platform destroys the prior +env before building the new env (no orphan path). +Shape B (Per-environment deployment): per-env caller workflows, no field +editing, promotion = running the matching job. +""" from pathlib import Path ROOT = Path(__file__).resolve().parent.parent @@ -43,6 +49,28 @@ def test_consumer_guide_has_interpolation_reference(): assert "${contract.module}" not in text -def test_consumer_guide_states_no_field_editing(): +def test_consumer_guide_documents_both_promotion_shapes(): + """REQ-290: the guide documents both Shape A (edit + destroy) and + Shape B (per-env caller workflows). Replaces the old + test_consumer_guide_states_no_field_editing which asserted only + Shape B.""" text = GUIDE.read_text() - assert "no" in text.lower() and "environment" in text.lower() and "editing" in text.lower() \ No newline at end of file + # Shape B: per-env caller workflows, no field editing + assert "Per-environment deployment" in text + assert "promotion-without-editing" in text.lower() or "promotion = running the matching job" in text.lower() + # Shape A: edit environment field (Step 8 documents this as a valid path) + assert "Shape A" in text or "Shape B" in text + assert "edit the environment field" in text.lower() or "change `environment`" in text.lower() or "change \"environment\"" in text.lower() + + +def test_consumer_guide_documents_destroy_on_env_change(): + """REQ-290: the guide states the platform destroys the prior env's + resources when the environment field is changed, and that there is no + orphan path.""" + text = GUIDE.read_text() + text_lower = text.lower() + # The guide must state the platform destroys the prior environment + assert "destroy" in text_lower and ("prior environment" in text_lower or "prior env" in text_lower) + # The guide must state there is no orphan path / fail closed + assert "no orphan path" in text_lower or "orphan" in text_lower + assert "fail closed" in text_lower or "fails closed" in text_lower \ No newline at end of file