docs(init): validate specification — v1.24 consumer guide accuracy + env-promotion lifecycle
---ci--- project: acdl phase: 0 milestone: v1.24 status: specify ---/ci---
This commit is contained in:
@@ -2028,3 +2028,171 @@ assert 20 main + 1 appendix.
|
||||
| REQ-273 | P5 | complete |
|
||||
| REQ-274 | P5 | complete |
|
||||
| REQ-275 | P5 | complete |
|
||||
|
||||
## v1.24 — Consumer Guide Accuracy & Env-Promotion Lifecycle Enforcement
|
||||
|
||||
> **Feature milestone** (one `feat` phase: env-transition destroy enforcement;
|
||||
> the rest are `fix`/`docs`/`test`). Tags run on the **v1.23.x** line
|
||||
> (milestone v1.24 → tags v1.23.0..v1.23.N). Final patch = milestone release.
|
||||
>
|
||||
> Two problems, one milestone:
|
||||
> 1. **Consumer guide accuracy.** A review of `docs/consumer-guide.md`
|
||||
> found 5 issues: (a) Step 8 tells consumers to change `environment:` in
|
||||
> their contract to promote, which (b) contradicts the same doc's
|
||||
> "Per-environment deployment" section (lines 396-477) that says
|
||||
> "promotion-without-editing," (c) the Step 3 contract-fields table
|
||||
> lists stale fields (`uses`, `module`) that no longer exist in the
|
||||
> schema (real fields: `id`, `name`, `environment`, `infrastructure`),
|
||||
> (d) Step 4 caller example is inconsistent with Step 2, and (e) Step 5
|
||||
> stage 8 says "(dev only)" when higher envs do apply after attestation.
|
||||
> 2. **Environment-promotion lifecycle enforcement.** When a consumer
|
||||
> edits `environment:` on a stable `contract.id` (Shape A promotion),
|
||||
> the Terraform state key
|
||||
> `spike/{stack_name}/{environment}/terraform.tfstate` (adapter.py:129)
|
||||
> changes — creating a fresh state file in the new env while the prior
|
||||
> env's resources remain live in AWS with no destroy ever running. This
|
||||
> **orphans resources** and violates the platform's full-lifecycle-
|
||||
> management mission. The platform must detect the env change and
|
||||
> destroy the prior env's resources before building the new env. There
|
||||
> must be **no path that orphans resources** — fail closed if the
|
||||
> destroy fails.
|
||||
>
|
||||
> The per-environment caller-workflow path (Shape B: one caller workflow
|
||||
> per env, `environment` passed as a workflow input) remains a fully
|
||||
> supported alternative with no destroy needed (each env has its own state
|
||||
> from day one). Both shapes are documented.
|
||||
|
||||
### Category: Consumer Guide Fixes (docs)
|
||||
- **REQ-276:** `docs/consumer-guide.md` Step 3 "Contract fields" table is
|
||||
corrected to list the real schema-enforced fields: `id`, `name`,
|
||||
`environment`, `infrastructure` (matching `schemas/contract.schema.json`
|
||||
`required` and the worked examples). The stale `uses` and `module` rows
|
||||
are removed. The `uses` row's note about versioned tags moves to the
|
||||
Step 2 caller-workflow section (where the version pin actually lives).
|
||||
- **REQ-277:** `docs/consumer-guide.md` Step 4 caller workflow example is
|
||||
made consistent with Step 2 — both show `environment` in `with:` or both
|
||||
omit it with a "dev is the default" note. The two canonical caller
|
||||
snippets no longer disagree.
|
||||
- **REQ-278:** `docs/consumer-guide.md` Step 5 stage 8 "(dev only)" is
|
||||
corrected to "(autonomous in dev; higher environments apply after HITL
|
||||
attestation)" to match `docs/environments/index.md` autonomy table.
|
||||
- **REQ-279:** `docs/consumer-guide.md` Step 8 "Promote to qa / prod" is
|
||||
rewritten. It documents that editing `environment:` on a stable
|
||||
`contract.id` (Shape A) **is a supported promotion path** and that the
|
||||
platform **destroys the prior environment's resources before building
|
||||
the new environment** — there is no orphan path; if the destroy fails,
|
||||
the pipeline fails closed. It includes the worked qa example with a
|
||||
note: "Changing `environment: dev` → `environment: qa` triggers a
|
||||
destroy of the dev stack (state key `spike/{id}/dev/`) then an apply
|
||||
against the qa stack (state key `spike/{id}/qa/`). Both emit evidence
|
||||
events." It cross-references the "Per-environment deployment" section
|
||||
(Shape B) as the alternative.
|
||||
- **REQ-280:** `docs/consumer-guide.md` "Per-environment deployment"
|
||||
section (lines 396-477) gains a lead sentence clarifying it is **Shape
|
||||
B** (the alternative to Shape A's edit-and-destroy path in Step 8), and
|
||||
that it avoids the destroy step because each env has its own state from
|
||||
first deploy. The existing table, interpolation reference, and HITL
|
||||
gate docs are preserved.
|
||||
- **REQ-281:** `docs/consumer-guide.md` Reference table "sample contracts
|
||||
use `@v1.19`" wording is corrected — the sample contracts no longer
|
||||
carry `uses:` (the version pin lives in the caller workflow). Reword to
|
||||
"used with caller workflow `@v1.19`" or the current tag.
|
||||
|
||||
### Category: Env-Transition Detect-and-Destroy (feat)
|
||||
- **REQ-282:** New module `core/env_transition.py` provides:
|
||||
`detect_prior_env(contract_id, consumer_repo, new_env) -> Optional[str]`
|
||||
— queries the `nova-contracts` DynamoDB table (PK `consumerRepo`, SK
|
||||
`contractId#submittedAt`, written by `core/lambda/contract_ingestor.py`)
|
||||
for the last-applied environment for this consumer+contract. Returns
|
||||
the prior env name if it differs from `new_env`, else `None`. Failures
|
||||
to reach DynamoDB log a warning and return `None` (conservative — Shape
|
||||
B legitimately has no prior record). Uses boto3 with the ABAC-scoped
|
||||
deploy role; respects `core/env.py` for config.
|
||||
- **REQ-283:** `core/env_transition.py` provides
|
||||
`record_applied_env(contract_id, consumer_repo, env)` — called after a
|
||||
successful apply to upsert the last-applied env record in the
|
||||
`nova-contracts` table (SK suffix `#LAST_APPLIED`). Idempotent.
|
||||
- **REQ-284:** `scripts/run_platform.sh` gains a new **Step 0b:
|
||||
environment-transition check** (after Step 0 env onboarding, before
|
||||
Step 1 contract validation). It reads `CONTRACT_ID` + `CONSUMER_REPO`
|
||||
(from `GITHUB_REPOSITORY` / `NOVA_CONSUMER_REPO`), calls
|
||||
`env_transition.py detect`, and if a prior env is returned that differs
|
||||
from the new env: (a) re-resolves the contract with
|
||||
`environment_override=$PRIOR_ENV` to emit the prior TF config + state
|
||||
backend; (b) runs `terraform init -reconfigure` + `terraform destroy
|
||||
-auto-approve` against the prior env's state key
|
||||
(`spike/{id}/{prior_env}/terraform.tfstate`); (c) emits a
|
||||
`nova.env.destroyed` evidence event via `core/outbox_writer.py`; (d)
|
||||
**fails closed** — if the destroy exits non-zero, the pipeline exits
|
||||
non-zero and no apply runs (no orphan path). If no prior env exists
|
||||
(first deploy or Shape B), proceeds normally.
|
||||
- **REQ-285:** `scripts/run_platform.sh` records the applied env after a
|
||||
successful apply (calls `env_transition.py record` with the resolved
|
||||
env). This is the source of truth for the next run's detect step.
|
||||
- **REQ-286:** `.github/workflows/deploy.yml` passes
|
||||
`NOVA_CONSUMER_REPO=${{ github.repository }}` to `run_platform.sh` so
|
||||
`env_transition.py` can query DynamoDB with the correct PK.
|
||||
- **REQ-287:** `adapters/terraform/adapter.py` state-key block
|
||||
(lines 127-133) gains a doc comment clarifying the key
|
||||
`spike/{stack_name}/{environment}/terraform.tfstate` is **env-scoped
|
||||
precisely to support destroy-on-env-change** — the env segment lets the
|
||||
detect-and-destroy step target the prior env's state without affecting
|
||||
the new env. No behavior change.
|
||||
|
||||
### Category: Tests (test)
|
||||
- **REQ-288:** `tests/test_env_transition.py` covers:
|
||||
`detect_prior_env` returns `None` when no record exists (first deploy);
|
||||
returns the prior env when a record exists and differs; returns `None`
|
||||
when the record matches `new_env` (re-apply same env);
|
||||
`record_applied_env` writes the record. Uses moto for DynamoDB mocking
|
||||
(pattern from `tests/test_contract_ingestor.py`).
|
||||
- **REQ-289:** `tests/test_run_platform_env_transition.py` asserts:
|
||||
`run_platform.sh` has a "Step 0b: environment-transition check" block;
|
||||
it calls `env_transition.py detect`; it calls `terraform destroy`
|
||||
against the prior env when a transition is detected; it fails closed on
|
||||
destroy failure (no apply runs); it records the applied env after a
|
||||
successful apply. Pattern: `tests/test_pipeline.py:79-95` (read the
|
||||
script text + assert substrings).
|
||||
- **REQ-290:** `tests/test_consumer_guide_per_env_section.py`
|
||||
`test_consumer_guide_states_no_field_editing` is renamed to
|
||||
`test_consumer_guide_documents_both_promotion_shapes` and asserts both
|
||||
shapes are present (Shape A: edit environment with destroy semantics;
|
||||
Shape B: per-environment caller workflows). The other 5 assertions in
|
||||
the file are preserved. A new test
|
||||
`test_consumer_guide_documents_destroy_on_env_change` asserts the guide
|
||||
states the platform destroys the prior env's resources when the
|
||||
environment field is changed and that there is no orphan path.
|
||||
|
||||
### Out of Scope (v1.24)
|
||||
- **Cross-account destroy.** If the prior and new envs are in different
|
||||
AWS accounts (per `docs/environments/index.md`), the destroy step needs
|
||||
the prior env's role credentials. The current scaffold
|
||||
(`core/environments/dev.json`) uses one account. Cross-account destroy
|
||||
is deferred to a future milestone; v1.24 targets the same-account case
|
||||
and documents the cross-account limitation.
|
||||
- **Decommission pipeline integration.** The env-transition destroy is a
|
||||
direct `terraform destroy` (not the 2-step HITL decommission). The
|
||||
decommission pipeline remains for explicit stack teardown with SRE
|
||||
gates; env-transition is an automated lifecycle step.
|
||||
- **Removing Shape B.** Both shapes stay supported. Shape B is not
|
||||
deprecated.
|
||||
|
||||
### v1.24 Traceability
|
||||
|
||||
| REQ | Phase | Status |
|
||||
|-----|-------|--------|
|
||||
| REQ-276 | P1 | pending |
|
||||
| REQ-277 | P1 | pending |
|
||||
| REQ-278 | P1 | pending |
|
||||
| REQ-279 | P1 | pending |
|
||||
| REQ-280 | P1 | pending |
|
||||
| REQ-281 | P1 | pending |
|
||||
| REQ-282 | P2 | pending |
|
||||
| REQ-283 | P2 | pending |
|
||||
| REQ-284 | P2 | pending |
|
||||
| REQ-285 | P2 | pending |
|
||||
| REQ-286 | P2 | pending |
|
||||
| REQ-287 | P2 | pending |
|
||||
| REQ-288 | P3 | pending |
|
||||
| REQ-289 | P3 | pending |
|
||||
| REQ-290 | P3 | pending |
|
||||
|
||||
Reference in New Issue
Block a user