# PLAN — Nova Platform v1.0 > Task-level, wave-ordered, persona-assigned plan for the 5 execution > phases (P1..P5) + the final phase (P6). Consumed by the execute > workflow. Derived from ROADMAP.md phase breakdown + REQUIREMENTS.md > REQ coverage + PERSONAS.md assignments + CLARIFY.md D-011..D-035 + > RESEARCH.md reference shapes. ## User-Facing Surface The platform's user-facing surfaces are **entirely offline-runnable** — no CI required, no AWS credentials required for the primary verification path: 1. **`scripts/run_platform.sh --check-only`** — Offline validation entrypoint. `bash scripts/run_platform.sh --check-only contracts/static-assets.yml` → exit 0 + `=== PLATFORM CHECK OK ===` iff contract validates → resolves → stack is schema-valid → adapter compiles to structurally-valid HCL. No AWS calls. 2. **`scripts/run_platform.sh`** (default mode) — Full apply path. Loads `NOVA_AWS_*` → `AWS_*`, runs terraform init/validate/plan/apply, prints `=== PLATFORM APPLY OK ===`. `--plan-only` stops before apply. 3. **`scripts/run_ci.sh`** — Local CI mirror. lint (py_compile) → test (pytest) → check-only. Prints `=== CI PIPELINE OK ===`. 4. **`docs/consumer-guide.md`** — Consumer happy-path walkthrough. 5. **`README.md`** quickstart — operator/dev quickstart. 6. **`.feature`-equivalent scenarios** — `tests/test_run_platform_check_only.py` + `tests/test_run_ci.py` encode the happy path as executable pytest cases. ## Happy Path Two end-to-end scenarios, written BEFORE execute, verified by automated tests in P5: ### Scenario A — Offline contract validation (primary gate) ```bash bash scripts/run_platform.sh --check-only contracts/static-assets.yml # expected: === PLATFORM CHECK OK === ; exit 0 ``` Steps: load `core/environments/dev.json` → parse contract → validate against contract schema → resolve to stack (interpolation) → adapt to HCL → validate output structure → print banner → exit 0. No AWS SDK calls, no terraform binary, no network. ### Scenario B — Local CI mirror ```bash bash scripts/run_ci.sh # expected: === CI PIPELINE OK === ; exit 0 ``` Steps: (1) lint `py_compile $(find core/ adapters/ scripts/ -name '*.py')`; (2) test `pytest`; (3) check-only `run_platform.sh --check-only`. Print banner → exit 0. ## UX Acceptance Criteria v1.0 is accepted iff ALL hold: 1. **AC-1 (offline validation works):** `run_platform.sh --check-only contracts/static-assets.yml` exits 0 + stdout contains `=== PLATFORM CHECK OK ===`. (tests/test_run_platform_check_only.py) 2. **AC-2 (local CI works):** `run_ci.sh` exits 0 + stdout contains `=== CI PIPELINE OK ===`. (tests/test_run_ci.py) 3. **AC-3 (contract schema is the API):** all 10 sample contracts validate against contract.schema.json. (tests/test_contract_schema.py) 4. **AC-4 (resolver pure + correct):** `resolve()` returns stack validating against stack.schema.json; raises `ModuleNotFoundError`/ `VersionNotFoundError`. (tests/test_contract_resolver.py + test_stack_schema.py) 5. **AC-5 (adapter compiles + boundary holds):** `adapt(stack, repo_root)` (C-1 fix) emits valid HCL; no `.py` outside `adapters/terraform/` contains forbidden strings. (tests/test_terraform_adapter.py + test_engine_boundary.py) 6. **AC-6 (module catalog complete):** registry.json has 13 L1 + 2 L2 = 15 entries; all interface.json + terraform/main.tf exist; L2 entries include `terraform_dir` (D-013). 7. **AC-7 (Terraform roots + bootstrap exist):** terraform/{bootstrap, ci-vpc,platform,microservice,onboarding}/ per REQ-14..19 + D-022..D-025. Lock table `nova-tfstate-locks`; account parameterized. 8. **AC-8 (docs cover consumer journey):** README + docs/{architecture, consumer-guide,modules/index,environments/index,contracts/index}.md exist, no OOS sections. Mechanically checked by grep for OOS section headings ("Security", "Compliance", "OIDC", "Attestation", "ABAC") inside `docs/*.md` + `modules/*/README.md` — zero matches (C-3 fix). 9. **AC-9 (reproducibility):** requirements-test.txt pins 5 deps; pyproject.toml no `[project.scripts]`; rotate_spike_key.sh writes .env.secrets (0600); .gitignore covers .env*/terraform state. 10. **AC-10 (engine-agnostic invariant):** engine-boundary test passes. --- ## Phase 1 — Contract Surface + Schemas + Resolver **Goal:** A contract can be validated, resolved to a stack, environment checked, stack validated — all offline, no apply, no engine terms in contract/core layer. **REQs:** REQ-01, REQ-02, REQ-03, REQ-04, REQ-05, REQ-06, REQ-23, REQ-24, REQ-27, REQ-28. **Personas:** backend-engineer (contract schema, resolver, environment_check, contracts, tests); data-engineer (stack schema, environment schema, dev.json). **Ships as:** `v0.1.1` on `phase/01-contract-surface-schemas-resolver`. ### Wave 1 (parallel — schemas + env data) | Task | Persona | REQs | Files | Must-have | |------|---------|------|-------|-----------| | P1-W1-T1 | backend | REQ-01, D-015 | `schemas/contract.schema.json` | Draft 2020-12; `required:[id,name,environment,infrastructure]`; `id` pattern `^[a-z][a-z0-9-]{2,5}$`; `infrastructure` ARRAY (D-015) items `{module,version?,inputs}`; no engine terms. | | P1-W1-T2 | data | REQ-04, D-012 | `schemas/stack.schema.json` | Flat per D-012; `required:[contract_id,contract_name,environment,resources]`; resources `{module,version,source,inputs}`; no stack wrapper/relationships/nfrs. | | P1-W1-T3 | data | D-017, D-018 | `schemas/environment.schema.json` | `required:[name,account_id,region,state_backend,network]`; no runner_role_arn/autonomy/confidence_threshold; `additionalProperties:false`. | | P1-W1-T4 | data | REQ-06, D-018 | `core/environments/dev.json` | Validates against environment schema; `name:"dev"`, placeholder account, `us-east-1`, state_backend, network. | ### Wave 2 (resolver + env_check — depends on W1) | Task | Persona | REQs | Files | Must-have | |------|---------|------|-------|-----------| | P1-W2-T1 | backend | REQ-03, D-011, D-016, D-037 | `core/contract_resolver.py` | `resolve(contract, registry, modules_dir) -> dict` (D-011). Interpolation kept (D-016). Named exceptions `ModuleNotFoundError`/`VersionNotFoundError`. L2 opaque (D-012). **No `source` in stack (D-037/C-1 fix) — stack is engine-agnostic `{contract_id, contract_name, environment, resources:[{module,version,inputs}]}`.** No engine terms. | | P1-W2-T2 | backend | REQ-05, D-019 | `core/environment_check.py` | `check(env_name, environments_dir) -> dict` (D-019). Raises `EnvironmentNotFoundError`. No tuple/onboarding_message/CLI. No engine terms. | ### Wave 3 (sample contracts — depends on W1+W2) | Task | Persona | REQs | Files | Must-have | |------|---------|------|-------|-----------| | P1-W3-T1 | backend | REQ-02, D-033, D-035 | `contracts/static-assets.{yaml,dev.yml,qa.yml,prod.yml,dr.yml}` | Validate against contract schema; `static-assets` L2; inputs incl `bucket_name`/`index_document` (D-035); per-env differ only in `environment` (D-033); interpolation tokens. | | P1-W3-T2 | backend | REQ-02, D-033 | `contracts/microservice.{yaml,dev.yml,qa.yml,prod.yml,dr.yml}` | Validate; `microservice` L2; per-env differ only in `environment`; interpolation. | ### Wave 4 (parallel — tests, depends on W1-W3) | Task | Persona | REQs | Files | Must-have | |------|---------|------|-------|-----------| | P1-W4-T1 | backend | REQ-23 | `tests/test_contract_resolver.py` | Happy path, `ModuleNotFoundError`, `VersionNotFoundError`, empty infra. Pass. | | P1-W4-T2 | backend | REQ-24 | `tests/test_environment_check.py` | `check("dev",...)` returns dict; missing → `EnvironmentNotFoundError`; malformed. Pass. | | P1-W4-T3 | backend | REQ-27 | `tests/test_contract_schema.py` | All 10 contracts validate; negative cases raise. Pass. | | P1-W4-T4 | backend | REQ-28 | `tests/test_stack_schema.py` | `resolve()` stack validates; negative cases. Pass. | ### Must-haves - 3 schemas parse as valid JSON Schema draft 2020-12. - `resolve()` + `check()` exposed with named exceptions; no engine terms. - `dev.json` + 10 contracts validate. - `pytest -q tests/test_contract_resolver.py tests/test_environment_check.py tests/test_contract_schema.py tests/test_stack_schema.py` exit 0. --- ## Phase 2 — Terraform Adapter + Engine Boundary **Goal:** A resolved stack compiles to valid HCL via the stateless adapter; engine boundary enforced by grep test. **REQs:** REQ-07, REQ-08, REQ-09, REQ-25, REQ-26. **Personas:** backend-engineer (adapter + tests). **Ships as:** `v0.1.2` on `phase/02-terraform-adapter-engine-boundary`. > **Dependency (C-4 fix):** P2 depends on P3-W1 (registry.json). The > adapter loads `registry.json` internally (C-1 fix). Reorder: run > P3-W1 (registry.json only) before P2-W1, then continue P3-W2 (L1 > terraform dirs) in parallel with P2. ### Wave 1 (adapter — depends on P3-W1 registry.json) | Task | Persona | REQs | Files | Must-have | |------|---------|------|-------|-----------| | P2-W1-T1 | backend | REQ-07, REQ-08, D-013, D-037 | `adapters/terraform/adapter.py`, `adapters/terraform/__init__.py` | `adapt(stack, repo_root) -> str` (C-1/D-037 fix). Loads `modules/registry.json` internally to map `module` → `terraform_dir`. Stateless, <250 lines. Emits `module "x" { source; }` per resource. L2 `terraform_dir` from registry (D-013). `__init__.py` re-exports. ONLY place engine terms appear. | ### Wave 2 (parallel — tests) | Task | Persona | REQs | Files | Must-have | |------|---------|------|-------|-----------| | P2-W2-T1 | backend | REQ-25 | `tests/test_terraform_adapter.py` | Single/multi resource, input passthrough, HCL validity. Pass. | | P2-W2-T2 | backend | REQ-09, REQ-26, D-034 | `tests/test_engine_boundary.py` | Grep `.py` in core/schemas/contracts/tests/scripts/root; exclude adapters/terraform/ + modules/ + .tf/.md/.json; zero matches for forbidden strings. Pass. | ### Must-haves - `adapt()` stateless, <250 lines, returns HCL. - `__init__.py` re-exports. - Both tests pass; boundary proven. --- ## Phase 3 — L1 Primitives + Registry **Goal:** Full module catalog — registry.json indexing 13 L1 + 2 L2, each L1 with interface.json + terraform/ (main/variables/outputs/versions/locals). **REQs:** REQ-10, REQ-11, REQ-13. **Personas:** data-engineer (registry, 13 L1 interface.json + terraform, READMEs, docs/modules/index); backend-engineer (registry test, conftest). **Ships as:** `v0.1.3` on `phase/03-l1-primitives-registry`. ### Wave 1 (parallel — registry + 13 L1 interface.json + READMEs + docs index) | Task | Persona | REQs | Files | Must-have | |------|---------|------|-------|-----------| | P3-W1-T1 | data | REQ-10, D-013 | `modules/registry.json` | 15 entries; L1 `{interface,terraform_dir,published_at,deprecated,kind:"l1"}`; L2 includes `terraform_dir` (D-013). | | P3-W1-T2..T14 | data | REQ-11, D-014 | `modules/l1/{s3,vpc,ecs-cluster,ecs-service,iam-role,alb,ecr,cloudfront,waf,rds,kms-key,dynamodb,uptime}/interface.json` | `{name,version,kind:"l1",type,description,inputs,outputs}` + `resources[]` for multi-resource. No `nfrs`/`intra_refs` (D-014). s3 stays ref interface (D-035). | | P3-W1-T15 | data | REQ-13, D-029 | `modules/README.md`, `modules/README-TEMPLATE.md` | L1/L2 distinction, registry format, add-a-module. 13+2 tables. Trimmed of security/compliance. DROP NFRs + Compliance sections. No STANDARDS.md. | | P3-W1-T16 | data | REQ-34, D-028 | `docs/modules/index.md` | Catalog table → links to `modules/l1//README.md` + `modules/l2//README.md`. 15 rows. | ### Wave 2 (parallel — 13 L1 terraform/ dirs) | Task | Persona | REQs | Files | Must-have | |------|---------|------|-------|-----------| | P3-W2-T1..T13 | data | REQ-11 | `modules/l1//terraform/{main,variables,outputs,versions,locals}.tf` | `count = var.enabled ? 1 : 0`; `required_version = ">= 1.9, < 1.10"`; `aws ~> 5.0`; variables match interface.json inputs. | ### Wave 3 (registry test + conftest) | Task | Persona | REQs | Files | Must-have | |------|---------|------|-------|-----------| | P3-W3-T1 | backend | REQ-10 | `tests/test_registry.py` | 15 entries, L2 has `terraform_dir`, interface paths resolve, terraform_dir/main.tf exist. Pass. | | P3-W3-T2 | backend | (support) | `tests/conftest.py` (partial) | `repo_root` + `registry` fixtures. No `stack_instance`/`policy_check_result_schema`. | ### Must-haves - registry.json valid, 15 entries, L2 has `terraform_dir`. - 13 L1 interface.json + terraform dirs exist, conform to D-014. - READMEs trimmed; docs/modules/index links to all 15. - test_registry.py + conftest fixtures pass. --- ## Phase 4 — L2 Patterns + Terraform Bootstrap + Platform **Goal:** Two L2 patterns composing L1 internally; AWS bootstrap scripted; platform/ci-vpc/microservice/onboarding roots exist. **REQs:** REQ-12, REQ-14, REQ-15, REQ-16, REQ-17, REQ-18, REQ-19. **Personas:** data-engineer (L2 interfaces + terraform, bootstrap policy + README, 4 terraform roots); backend-engineer (bootstrap py scripts, rotate_spike_key.sh). **Ships as:** `v0.1.4` on `phase/04-l2-patterns-bootstrap-platform`. ### Wave 1 (parallel — L2 interfaces + bootstrap policy + 4 roots + README) | Task | Persona | REQs | Files | Must-have | |------|---------|------|-------|-----------| | P4-W1-T1 | data | REQ-12, D-012, D-013 | `modules/l2/microservice/interface.json` | `{name,version,kind:"l2",description,inputs,outputs}` — L2-level only, no children/wires. | | P4-W1-T2 | data | REQ-12, D-012, D-035 | `modules/l2/static-assets/interface.json` | Inputs incl `bucket_name`/`index_document` (D-035). | | P4-W1-T3 | data | REQ-15, D-026 | `terraform/bootstrap/spike_runner_policy.json` | Account parameterized (NOT hardcoded). Grants S3/DynamoDB lock/ECS/ECR/ELB/IAM/EC2/CloudFront/WAF/KMS. DROP Lambda/Secrets/SNS/CostExplorer/OIDC. | | P4-W1-T4 | data | REQ-16, D-024 | `terraform/ci-vpc/main.tf` | Short-lived test VPC; VPC+2 subnets+IGW+route table+ECS SG+cluster; 4 outputs; state key `spike/ci-vpc/terraform.tfstate`. | | P4-W1-T5 | data | REQ-17, D-023 | `terraform/platform/main.tf` | ONLY shared VPC per D-023; VPC+2 subnets+IGW+route table+ECS SG; outputs `vpc_id`/`subnet_ids`/`ecs_security_group_id`. DROP Lambda/DynamoDB/KMS/Secrets/SNS. | | P4-W1-T6 | data | REQ-18 | `terraform/microservice/main.tf` | Instantiates L2 module + `data.terraform_remote_state` to platform VPC. State key `spike/microservice//terraform.tfstate`. | | P4-W1-T7 | data | REQ-19, D-025 | `terraform/onboarding/main.tf` | IAM ROLE (not user) per D-025. Cross-account `sts:AssumeRole`. NO OIDC. `consumer_repo`/`owner_id` vars. DROP `lambda:InvokeFunctionUrl`. Outputs role arn/name. | | P4-W1-T8 | data | REQ-14, D-022 | `terraform/bootstrap/README.md` | Documents `nova-tfstate-locks` (D-022 — NOT `nova-outbox`), `nova-spike-runner`, parameterized account, `NOVA_BOOTSTRAP_AWS_*`. | ### Wave 2 (parallel — 2 L2 terraform composing L1, depends on W1 + P3) | Task | Persona | REQs | Files | Must-have | |------|---------|------|-------|-----------| | P4-W2-T1 | data | REQ-12, D-012, D-038 | `modules/l2/microservice/terraform/{main,variables,outputs,versions}.tf` | `module "vpc"{source="../../l1/vpc/terraform"}` + cluster/service/role/ecr/alb (6 L1s per D-038). Variables match L2 interface. | | P4-W2-T2 | data | REQ-12, D-012, D-035, D-038 | `modules/l2/static-assets/terraform/{main,variables,outputs,versions}.tf` | `module "s3"{source="../../l1/s3/terraform"}` + cloudfront + kms (3 L1s per D-038, drops waf). `index_document` → s3 website. | ### Wave 3 (parallel — bootstrap py scripts + rotate, depends on W1 policy) | Task | Persona | REQs | Files | Must-have | |------|---------|------|-------|-----------| | P4-W3-T1 | backend | REQ-14, D-022 | `terraform/bootstrap/create_state_backend.py` | S3 `nova-tfstate--` + DynamoDB `nova-tfstate-locks`. Idempotent. `NOVA_BOOTSTRAP_AWS_*`→`NOVA_AWS_*`→`AWS_*`. `py_compile` clean. No engine-boundary violation (boto3, not HCL). | | P4-W3-T2 | backend | REQ-15, D-026 | `terraform/bootstrap/create_iam_user.py` | `nova-spike-runner` + policy + key. Prints `NOVA_AWS_*`. Idempotent. `py_compile` clean. No HCL strings. | | P4-W3-T3 | backend | REQ-22, D-032 | `scripts/rotate_spike_key.sh` | Rotates key → `.env.secrets` (0600). `NOVA_AWS_*` (D-032). `bash -n` clean. | ### Must-haves - Both L2 interface.json + terraform exist; compose L1 via `module` blocks with `../../l1/...`. - bootstrap/ has 4 files; policy account-parameterized; lock table `nova-tfstate-locks`. - 4 terraform roots exist per D-023/D-024/D-025. - rotate_spike_key.sh syntax-valid. - Bootstrap py `py_compile` clean, no engine-boundary violation. --- ## Phase 5 — Shell Reproducibility + Test Suite + Docs **Goal:** Platform fully reproducible from shell — `run_platform.sh --check-only` + `run_ci.sh` exit 0 with banners; full test suite passes; docs complete. Happy path green. **REQs:** REQ-20, REQ-21, REQ-22 (verify), REQ-29, REQ-30, REQ-31, REQ-32, REQ-33, REQ-34, REQ-35, REQ-36, REQ-37, REQ-38. **Personas:** backend-engineer (shell scripts, shell tests, conftest final, pyproject, requirements); lead-developer (README, architecture, consumer-guide); data-engineer (docs/modules/index finalize, environments/index, contracts/index). **Ships as:** `v0.1.5` on `phase/05-shell-reproducibility-tests-docs`. ### Wave 1 (parallel — scripts + deps + docs) | Task | Persona | REQs | Files | Must-have | |------|---------|------|-------|-----------| | P5-W1-T1 | backend | REQ-20, D-020, D-031, D-032 | `scripts/run_platform.sh` | Flags: `--check-only`/`--plan-only`/`--quiet`/`--help`. check-only: env_check→validate→resolve→adapter→validate output→`=== PLATFORM CHECK OK ===`. plan-only: +creds (`NOVA_AWS_*`→`AWS_*` then unset)→init/validate/plan→`=== PLATFORM PLAN OK ===`. default: +apply→`=== PLATFORM APPLY OK ===`. `bash -n` clean. check-only exits 0 offline. | | P5-W1-T2 | backend | REQ-21, D-021 | `scripts/run_ci.sh` | 3 stages: lint (glob py_compile)→test (pytest)→check-only. `=== CI PIPELINE OK ===`. `--quiet`. `bash -n` clean. | | P5-W1-T3 | backend | REQ-31, D-027 | `pyproject.toml`, `requirements-test.txt` | No `[project.scripts]`, no `nova/` dir. 5 test deps. `addopts="-v --tb=short"`. `markers=[offline,slow]`. packages.find: `core,core.*,adapters.*`. | | P5-W1-T4 | backend | REQ-38 | `.gitignore` | Verify/extend: `.env*`, terraform state, credentials, `__pycache__/`, `.ciagent/logs/`, `nova_platform.egg-info/`. | | P5-W1-T5 | lead | REQ-32 | `README.md` | What platform is, run offline, run tests, run against AWS, repo layout, credentials (static-key only), consumer-guide pointer. No security/identity sections. | | P5-W1-T6 | lead | REQ-33 | `docs/architecture.md` | Mirrors `.ciagent/ARCHITECTURE.md`. 4 layers + boundary + OOS list + catalog. No cross-cutting sections. | | P5-W1-T7 | lead | REQ-37 | `docs/consumer-guide.md` | Infra-only: create repo, write contract, run check-only, run against AWS. Interpolation table. DROP OIDC/reusable-workflow/decommission/compliance. | | P5-W1-T8 | data | REQ-34, D-028 | `docs/modules/index.md` (finalize) | Catalog table → 15 module READMEs. | | P5-W1-T9 | data | REQ-36, D-018 | `docs/environments/index.md` | Env model: account/network/state backend — NO IAM/ABAC. Autonomy table: dev autonomous; qa/prod/dr manual. No HITL gates. | | P5-W1-T10 | data | REQ-35, D-033 | `docs/contracts/index.md` | Array-based infrastructure schema (D-015) + samples + per-env variants (D-033). Interpolation table. | ### Wave 2 (parallel — shell tests + conftest, depends on W1) | Task | Persona | REQs | Files | Must-have | |------|---------|------|-------|-----------| | P5-W2-T1 | backend | REQ-29 | `tests/test_run_platform_check_only.py` | `subprocess` `run_platform.sh --check-only contracts/static-assets.yml`; assert exit 0 + `=== PLATFORM CHECK OK ===`. Pass. | | P5-W2-T2 | backend | REQ-30 | `tests/test_run_ci.py` | `subprocess` `run_ci.sh`; assert exit 0 + `=== CI PIPELINE OK ===`. Pass. | | P5-W2-T3 | backend | (support) | `tests/conftest.py` (finalize) | `repo_root`, `registry`, `stack_schema`, `contract_schema` fixtures. `sys.path.insert` for core/ + adapters/. | ### Wave 3 (full-suite green — depends on W1-W2) | Task | Persona | REQs | Files | Must-have | |------|---------|------|-------|-----------| | P5-W3-T1 | backend | (gate) | none | `pytest -q` all pass. `bash scripts/run_ci.sh` exit 0 + banner. | ### Must-haves (MVP/UX gate) - `run_platform.sh --check-only contracts/static-assets.yml` exit 0 + `=== PLATFORM CHECK OK ===`. - `run_ci.sh` exit 0 + `=== CI PIPELINE OK ===`. - Full `pytest` suite passes. - pyproject + requirements configure pytest/py_compile, no CLI. - .gitignore covers all patterns. - 6 docs exist, no OOS sections. --- ## Phase 6 — Final Review + Ship **Goal:** Review v1.0 against AC-1..AC-10, audit for boundary leaks + OOS-creep, ship: merge `phase/06`→`milestone/v1.0-nova-platform`→`main`, tag `v1.0.0` (major — initial release per D-001), Gitea release, delete branches. **REQs:** none new. **Personas:** lead-developer (review, audit, ship); backend/data consulted for fix-forward. **Ships as:** `v1.0.0` (major tag). ### Wave 1 (review) | Task | Persona | REQs | Files | Must-have | |------|---------|------|-------|-----------| | P6-W1-T1 | lead | AC-1..AC-10 | none | Walk all 10 ACs. Record pass/fail. All must PASS before proceeding. Escalate on failure (supervised). | ### Wave 2 (fix-forward, conditional) | Task | Persona | REQs | Files | Must-have | |------|---------|------|-------|-----------| | P6-W2-T1..Tn | backend/data | (varies) | (varies) | Fix specific AC failures. Re-verify. Max 2 revision iterations. | ### Wave 3 (audit) | Task | Persona | REQs | Files | Must-have | |------|---------|------|-------|-----------| | P6-W3-T1 | lead | REQ-09, OOS list | none | (1) `pytest -q tests/test_engine_boundary.py` pass. (2) Grep repo for OOS file names (policy_engine, confidence_signal, outbox_writer, abac_*, pat_*, jws_*, kms_signing, hitl_*, attestation_*, separation_*, submission_*, env_transition, decommission_*, mode_resolver, onboarding.py, regression_verify*, metrics/, pipelines/, .github/workflows/, adapters/kyverno-json, adapters/wiz, adapters/checkov, schemas/pipeline*, schemas/deploy-pipeline*, schemas/policy_check_result*, schemas/metrics_*). ZERO matches. (3) No STANDARDS.md, no PPTX/marp. | ### Wave 4 (ship) | Task | Persona | REQs | Files | Must-have | |------|---------|------|-------|-----------| | P6-W4-T1 | lead | D-001, D-009 | git refs | Merge `phase/06-final-review-ship`→`milestone/v1.0-nova-platform`. Merge milestone→`main`. Tag `v1.0.0` on main. **`confirm_before_ship=true` per D-009 — escalate before tagging.** Gitea release via `NOVA_FORGE_TOKEN`. Delete phase/0*+1* branches. Verify tag + release URL. | ### Must-haves - All 10 ACs PASS. - Engine boundary passes; zero OOS files. - `v1.0.0` tag on main. - Gitea release `v1.0.0` created. - All phase branches deleted. --- ## Cross-phase invariants (hold after EVERY phase) 1. **Engine boundary:** no `.py` outside `adapters/terraform/` contains `aws_`/`module "`/`terraform`/`provider "`/`resource "` (REQ-09/D-034). 2. **No OOS-creep:** no file from PROJECT.md/REQUIREMENTS.md OOS list created in any phase. 3. **Structural conventions:** directory names, file roles, interface shape, registry shape, banner strings, state key, tag convention preserved without deviation. 4. **Tests stay green:** once a test file exists, subsequent phases must not break it. `pytest -q` passes at end of every phase. 5. **Supervised escalation:** `ship` (P6-W4-T1) + verification failures escalate to human per `escalation_timeout_ms=300000`. ## Wave dependency graph ``` P1: W1(schemas+env) → W2(resolver+env_check) → W3(contracts) → W4(tests) P3-W1(registry.json ONLY) → P2: W1(adapter loads registry) → W2(adapter tests + boundary) [C-4 fix: P3-W1 before P2-W1] P3: W1(registry+13 L1 interface+READMEs+docs) → W2(13 L1 terraform) → W3(registry test+conftest) [W1 split: registry.json first, then rest] P4: W1(L2 interfaces+bootstrap policy+4 roots+README) → W2(2 L2 terraform per D-038) → W3(2 bootstrap py+rotate) [deps P3 L1 terraform] P5: W1(2 scripts+pyproject+reqs+gitignore+README+arch+consumer-guide+3 docs) → W2(2 shell tests+conftest) → W3(full-suite green) [deps P1-P4] P6: W1(review AC-1..10) → [W2 fix-forward] → W3(audit + docs OOS grep C-3) → W4(ship v1.0.0) [deps P5] ``` > **Concurrency note (C-5):** P3-W1 (13 L1 interface.json tasks) + > P3-W2 (13 L1 terraform tasks) + P4-W1 (8 tasks) exceed > `max_concurrent_agents=5`. These waves batch-serialize into 3-5 > rounds. Schedule estimate reflects ~2-3× wall-clock for P3. Total: 6 phases, ~45 tasks across ~13 waves, 3 active personas (max concurrency 5). No wave has >5 parallel tasks.