docs(P1): consumer guide accuracy fixes — REQ-276..281,290
- 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---
This commit is contained in:
+47
-11
@@ -140,10 +140,10 @@ name: microservice
|
|||||||
|
|
||||||
| Field | Type | Required | Description |
|
| 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). |
|
| `id` | string | yes | Short operational acronym (3-6 chars, lowercase + digits + hyphens). Becomes `stack.name`: the Terraform state key (`spike/<id>/<env>/terraform.tfstate`), the outbox event identity, and the resource naming prefix. Stable across deploys and environment promotions. |
|
||||||
| `module` | string | yes | Module name from the registry — any primitive or module (e.g. `static-assets`, `microservice`, `s3`). See the [module catalog](modules/). |
|
| `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 (e.g. `dev`). See [Environments](environments/). |
|
| `environment` | string | yes | The platform-managed environment to deploy to (`dev`, `qa`, `prod`, or `dr`). See [Environments](environments/). |
|
||||||
| `inputs` | object | yes | Module-specific inputs (see the module's README). |
|
| `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
|
### Module inputs
|
||||||
|
|
||||||
@@ -180,6 +180,7 @@ jobs:
|
|||||||
uses: nova/.github/workflows/deploy.yml@v1.19
|
uses: nova/.github/workflows/deploy.yml@v1.19
|
||||||
with:
|
with:
|
||||||
contract: .nova/contract.yml
|
contract: .nova/contract.yml
|
||||||
|
environment: dev
|
||||||
```
|
```
|
||||||
|
|
||||||
That is the entire consumer-side workflow. When you push to `main`:
|
That is the entire consumer-side workflow. When you push to `main`:
|
||||||
@@ -229,7 +230,7 @@ flowchart TD
|
|||||||
S5["policy checks<br/>(adapter -> PolicyCheckResult)"] --> S6
|
S5["policy checks<br/>(adapter -> PolicyCheckResult)"] --> S6
|
||||||
S6["confidence<br/>score + band (dev >= 0.50)"] --> S7
|
S6["confidence<br/>score + band (dev >= 0.50)"] --> S7
|
||||||
S7["evidence event<br/>to the audit outbox"] --> S8
|
S7["evidence event<br/>to the audit outbox"] --> S8
|
||||||
S8["infrastructure apply<br/>(dev only)"]
|
S8["infrastructure apply<br/>(autonomous in dev;<br/>higher envs apply after HITL)"]
|
||||||
```
|
```
|
||||||
|
|
||||||
1. **validate-contract** — validates your contract YAML against the contract
|
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.
|
threshold is ≥ 0.50. If the band is `pass`, the pipeline proceeds.
|
||||||
7. **evidence event** — a hash-chained evidence event is written to the
|
7. **evidence event** — a hash-chained evidence event is written to the
|
||||||
audit outbox.
|
audit outbox.
|
||||||
8. **infrastructure apply** (dev only) — the infrastructure plan is applied,
|
8. **infrastructure apply** (autonomous in dev; higher environments apply
|
||||||
creating the resources in your AWS account. An evidence event for the
|
after HITL attestation) — the infrastructure plan is applied, creating
|
||||||
apply is recorded.
|
the resources in your AWS account. An evidence event for the apply is
|
||||||
|
recorded.
|
||||||
|
|
||||||
## Step 6 — What gets created
|
## 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
|
## 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
|
```yaml
|
||||||
id: assets
|
id: assets
|
||||||
@@ -301,10 +310,32 @@ infrastructure:
|
|||||||
inputs: { ... }
|
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
|
Higher environments require human attestation (a platform-runner deployment
|
||||||
approval) and higher confidence thresholds. See [Environments](environments/)
|
approval) and higher confidence thresholds. See [Environments](environments/)
|
||||||
for the full table.
|
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
|
## Step 9 — Compliance extensions
|
||||||
|
|
||||||
Each module lists compliance extension points for the future compliance
|
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. |
|
| Contract schema | `schemas/contract.schema.json` | JSON Schema for consumer contracts. |
|
||||||
| Stack schema | `schemas/stack.schema.json` | JSON Schema for the resolved stack instance. |
|
| Stack schema | `schemas/stack.schema.json` | JSON Schema for the resolved stack instance. |
|
||||||
| Module catalog | [modules/](modules/) | All primitives and modules. |
|
| Module catalog | [modules/](modules/) | All primitives and modules. |
|
||||||
| Sample contract | `contracts/static-assets.yaml` | The reference 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.yaml` | The microservice example contract (uses `@v1.19`). |
|
| Sample contract | `contracts/microservice.yml` | The microservice example contract (used with caller workflow `@v1.19`). |
|
||||||
| Module examples | `modules/<name>/examples/` | Validated per-module example contracts (`simple.yaml` + `complex.yaml`). |
|
| Module examples | `modules/<name>/examples/` | Validated per-module example contracts (`simple.yaml` + `complex.yaml`). |
|
||||||
| Contract resolver | `core/contract_resolver.py` | Resolves contracts to stack instances. |
|
| Contract resolver | `core/contract_resolver.py` | Resolves contracts to stack instances. |
|
||||||
| Angine adapter | `adapters/terraform/adapter.py` | Compiles stack instances to infrastructure. |
|
| 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).
|
endpoints going dark).
|
||||||
## Per-environment deployment
|
## 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
|
Nova supports a **promotion-without-editing** model: you do not edit the
|
||||||
`environment:` field in a contract to promote dev → qa → prod → dr.
|
`environment:` field in a contract to promote dev → qa → prod → dr.
|
||||||
Instead, there is **one CI job per environment**, each pointing at its
|
Instead, there is **one CI job per environment**, each pointing at its
|
||||||
|
|||||||
@@ -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
|
from pathlib import Path
|
||||||
|
|
||||||
ROOT = Path(__file__).resolve().parent.parent
|
ROOT = Path(__file__).resolve().parent.parent
|
||||||
@@ -43,6 +49,28 @@ def test_consumer_guide_has_interpolation_reference():
|
|||||||
assert "${contract.module}" not in text
|
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()
|
text = GUIDE.read_text()
|
||||||
assert "no" in text.lower() and "environment" in text.lower() and "editing" in text.lower()
|
# 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
|
||||||
Reference in New Issue
Block a user