ee5c372e65
Phase 0: SPECIFY → CLARIFY → RESEARCH → IDEATE → PLAN → GRILL. NFR milestone v1.16 (Nova Simplification), 20 execution phases + final. Tags on v1.15.x line: v1.15.5 (this phase) → v1.15.6..v1.15.25 (P1-P20) → v1.15.26 (P21 final = release). Scope (D-113..D-119): Simplify without regressions, Security, Maintainability, User/Developer Experience, No Humans Onboarding Flow (request-path only; real AWS provisioning deferred). Regression gate (D-118, G-111) gates P9 + P21 at 20/22 Verified + 2 Skipped. Grill: PASS-with-binding (G-111..G-113, E-002 deferred to P21). ---ci--- project: acdl phase: 0 milestone: v1.16 status: complete phase_role: pre_execution requirements: covered: [REQ-165, REQ-166, REQ-167, REQ-168, REQ-169, REQ-170, REQ-171, REQ-172, REQ-173, REQ-174, REQ-175, REQ-176, REQ-177, REQ-178, REQ-179, REQ-180, REQ-181, REQ-182, REQ-183, REQ-184] partial: [] ---/ci---
420 lines
22 KiB
Markdown
420 lines
22 KiB
Markdown
---
|
||
phase: P0
|
||
name: pre-execution
|
||
milestone: v1.16
|
||
requirements: [REQ-165, REQ-166, REQ-167, REQ-168, REQ-169, REQ-170, REQ-171, REQ-172, REQ-173, REQ-174, REQ-175, REQ-176, REQ-177, REQ-178, REQ-179, REQ-180, REQ-181, REQ-182, REQ-183, REQ-184]
|
||
wave: 0
|
||
depends_on: []
|
||
---
|
||
|
||
# v1.16 — Nova Simplification Plan (20 execution phases + 1 final)
|
||
|
||
**Milestone:** v1.16 (Nova Simplification — NFR)
|
||
**Type:** NFR (all phases fix/chore/docs/refactor/test). The final
|
||
phase's patch IS the deliverable — no separate milestone tag. Tags run
|
||
on the v1.15.x line: `v1.15.5` (P0) → `v1.15.6..v1.15.25` (P1–P20) →
|
||
`v1.15.26` (P21 final = milestone release).
|
||
|
||
**Objective:** A 20-phase NFR sweep (no new features) themed around five
|
||
user-directed axes: Simplify without regressions, Security,
|
||
Maintainability, User/Developer Experience, No Humans Onboarding Flow.
|
||
Clears the fresh debt the v1.15 rebrand left, delivers genuine
|
||
simplification, and implements the first self-service onboarding
|
||
request path (request-path only; real AWS provisioning deferred, D-113).
|
||
|
||
## Wave ordering
|
||
|
||
- **Wave 1 (P1–P4): correctness + brand regression fixes.** P1 first —
|
||
the state-bucket drift (`adapter.py:117` emits `acdl-tfstate-*` while
|
||
the live bucket is `nova-tfstate-*`) and the Kyverno policy
|
||
contradiction (enforces `acdl:*` labels that `nova_tagging.py` hard-
|
||
fails) are the highest-severity findings, both correctness regressions
|
||
left by the rebrand. P2–P4 independent brand/dead-code/except work.
|
||
- **Wave 2 (P5–P9): simplify without regressions.** P5 before P6/P9
|
||
(regression-verify dedup is independent; P6/P9 both touch
|
||
`run_platform.sh`). P8 changes the workflow byte-identity test →
|
||
generator (D-115). P9 must run the regression gate (D-118) at the end
|
||
of Wave 2 — 22/22 capabilities must stay Verified.
|
||
- **Wave 3 (P10–P14): security + maintainability.** P10 before P11
|
||
(identity enforcement before payload validation). P12/P13 independent
|
||
file splits. P14 mid-milestone checkpoint (offline) at end of Wave 3.
|
||
- **Wave 4 (P15–P17): developer experience.** Independent; P17 last
|
||
(reflects the consolidated path after P15/P16 land).
|
||
- **Wave 5 (P18–P20): no-humans onboarding (request-path only).** P18
|
||
(schema + Lambda action) before P19 (env-file autogen consumes the
|
||
schema) before P20 (cross-account role, offline-proven per D-114).
|
||
- **Final (P21): review + audit + milestone ship.**
|
||
|
||
## Execution approach
|
||
|
||
- **Per-phase ship:** each execution phase merges `phase/NN-*` →
|
||
`milestone/v1.16-nova-simplification` and tags a patch on the v1.15.x
|
||
line (`v1.15.6` = P1 ... `v1.15.26` = P21).
|
||
- **Verification:** 4-layer verify (structural/behavioral/security/
|
||
quality) per phase; the regression gate (D-091, 22 capabilities) runs
|
||
at P9 (end of Wave 2) and P21 (milestone complete) per D-118.
|
||
- **No live AWS:** `NOVA_LIFECYCLE_MODE=plan` default; terraform changes
|
||
validated via `terraform validate` + `--check-only`. P20 cross-account
|
||
Terraform is offline-proven only (D-114).
|
||
- **Test discipline:** each phase that changes runtime code adds/updates
|
||
tests; `bash scripts/run_ci.sh` exits 0 at every phase boundary.
|
||
|
||
## Wave 1 — Correctness + Brand Regression Fixes (P1–P4)
|
||
|
||
### Phase P1 — state-bucket-and-kyverno-rebrand-fix (REQ-165)
|
||
- **Lead:** backend-engineer; **Contributor:** data-engineer (kyverno)
|
||
- **Must-haves:**
|
||
- `adapters/terraform/adapter.py:117` `state_bucket =
|
||
f"acdl-tfstate-{account_id}-us-east-1"` → `f"nova-tfstate-{account_id}-us-east-1"`.
|
||
- `adapters/kyverno/policies/require-resource-labels.yml`: annotation
|
||
title `Require ACDL Resource Labels` → `Require Nova Resource Labels`;
|
||
rule names `require-acdl-owner-label`/`require-acdl-environment-label`
|
||
→ `require-nova-owner-label`/`require-nova-environment-label`;
|
||
messages + patterns `acdl:owner`/`acdl:environment` → `nova:owner`/
|
||
`nova:environment`.
|
||
- Update any test fixtures referencing the old bucket name / label keys.
|
||
- **Verify:** `terraform validate` (adapter-emitted); pytest passes;
|
||
`run_ci.sh` exits 0; regression gate 22/22 (run at P9, but P1 must not
|
||
break any cap locally).
|
||
|
||
### Phase P2 — user-facing-acdl-to-nova-sweep (REQ-166)
|
||
- **Lead:** lead-developer; **Contributor:** backend-engineer
|
||
- **Must-haves:**
|
||
- `core/environment_check.py:59,61` onboarding message header/body
|
||
"ACDL" → "Nova".
|
||
- `core/lambda/contract_ingestor.py:145` alert title `[ACDL-ALERT]` →
|
||
`[NOVA-ALERT]`; `:191` issue body "ACDL platform Lambda" → "Nova
|
||
platform Lambda".
|
||
- `scripts/post_stage_comment.sh:39` PR comment header "ACDL Stage" →
|
||
"Nova Stage"; `:46` footer "ACDL deploy pipeline" → "Nova deploy
|
||
pipeline".
|
||
- `scripts/run_ci.sh:39` CI banner "ACDL CI Pipeline" → "Nova CI
|
||
Pipeline".
|
||
- Module docstrings: `core/contract_resolver.py:1,474`,
|
||
`core/confidence_signal.py:1`, `adapters/terraform/adapter.py:1`,
|
||
`adapters/kyverno/kyverno_adapter.py:1`, `adapters/wiz/wiz_adapter.py:1`,
|
||
`adapters/README.md:1`, `adapters/kyverno/README.md:4,18` → Nova.
|
||
- Update tests that assert these strings.
|
||
- **Verify:** pytest passes; `run_ci.sh` exits 0.
|
||
|
||
### Phase P3 — dead-code-and-stale-prefix-cleanup (REQ-167)
|
||
- **Lead:** lead-developer
|
||
- **Must-haves:**
|
||
- `scripts/run_platform.sh:153` remove the dead
|
||
`export ACDL_ENVIRONMENT_OVERRIDE=...` line (comment says "removed
|
||
in P5" but the line is present).
|
||
- Stale dual-read comments: drop the "ACDL_* fallback until P5" /
|
||
"dual-read NOVA_* first, ACDL_* fallback per G-106" comments in
|
||
`core/local_emulators.py:15-16,503,505`,
|
||
`core/regression_verify.py:318-319,333`, and the lifecycle scripts
|
||
(the G-106 fallback is retired per `core/env.py:4-5`).
|
||
- `acdl_*` temp-dir prefixes → `nova_*`: `core/local_emulators.py:71,252`
|
||
(`acdl_outbox_`/`acdl_tfstate_`), `core/regression_verify.py:183,234`
|
||
(`acdl_regr_`/`acdl_outbox_`), `scripts/run_pattern_plan.sh:29`,
|
||
`scripts/run_primitive_plan.sh:29`, `scripts/run_lifecycle_test.sh:41`,
|
||
`scripts/run_lifecycle_destroy.sh:36`.
|
||
- `core/regression_verify.py:214` interpolation fixture `acdl-` → `nova-`
|
||
(or make it a clearly-generic token).
|
||
- **Verify:** pytest passes; `run_ci.sh` exits 0.
|
||
|
||
### Phase P4 — migrate-ssm-except-narrowing (REQ-168)
|
||
- **Lead:** backend-engineer
|
||
- **Must-haves:**
|
||
- `scripts/migrate_ssm_paths.py:113` `except Exception: pass` →
|
||
narrow to `ParameterNotFound` + structured log on the non-
|
||
ParameterNotFound path.
|
||
- Narrow `core/output_publisher.py:112,182` `except Exception` →
|
||
specific `(ClientError, OSError)` + structured stderr log.
|
||
- Test that a non-ParameterNotFound error is raised (not swallowed).
|
||
- **Verify:** pytest passes; `run_ci.sh` exits 0.
|
||
|
||
## Wave 2 — Simplify Without Regressions (P5–P9)
|
||
|
||
### Phase P5 — regression-verify-dedup (REQ-169)
|
||
- **Lead:** backend-engineer
|
||
- **Must-haves:**
|
||
- Extract `_check_live_terraform_plan(contract_path, label)` from the
|
||
two ~95% identical methods `_check_live_terraform_plan_microservice`
|
||
+ `_check_live_terraform_plan_static_assets` (~35 lines saved).
|
||
- Extract `_check_resolver(contract_path)` from
|
||
`_check_resolver_static_assets` + `_check_resolver_microservice`.
|
||
- Extract `_assert_contracts_resolve(module_dir)` from the duplicated
|
||
lifecycle-contract-resolve block in
|
||
`_check_lifecycle_module_terraform` + `_check_lifecycle_l2_module`.
|
||
- Behavior preserved (the regression gate output is unchanged).
|
||
- **Verify:** pytest passes; `run_ci.sh` exits 0.
|
||
|
||
### Phase P6 — run-platform-deadcode-and-hitl-fn (REQ-170)
|
||
- **Lead:** lead-developer
|
||
- **Must-haves:**
|
||
- Extract the duplicated HITL attestation block (`:336-350` + `:452-466`)
|
||
into a shell function `run_hitl_gate()` invoked at both sites (~14
|
||
lines saved).
|
||
- `scripts/run_platform.sh:145` hardcoded `CONTRACT_ID` UUID →
|
||
`NOVA_CONTRACT_ID` env with the existing UUID as default.
|
||
- `scripts/run_platform.sh:146` `WORK="/tmp/acdl_platform_run_v18"` →
|
||
`WORK="${NOVA_WORK_DIR:-/tmp/nova_platform_run}"` (drop the stale
|
||
`v18` stamp + `acdl_` prefix).
|
||
- Drop the stale brand comment `run_platform.sh:2` "the ACDL platform
|
||
pipeline" → "the Nova platform pipeline".
|
||
- **Verify:** pytest passes; `run_ci.sh` exits 0; `run_platform.sh
|
||
--check-only` exits 0.
|
||
|
||
### Phase P7 — contract-resolver-envloader-and-kind (REQ-171)
|
||
- **Lead:** backend-engineer
|
||
- **Must-haves:**
|
||
- `core/contract_resolver.py:50-68` `_load_env` → import
|
||
`core/environment_check.py:load()` (dedup; both load + placeholder
|
||
warning).
|
||
- Add a `kind` field (`"l1"` / `"l2"`) to each `modules/registry.json`
|
||
entry; the resolver reads `kind` directly instead of the fragile
|
||
`is_l2 = "l2" in interface_path or "composition" in interface_path`
|
||
heuristic (`contract_resolver.py:540`).
|
||
- Collapse the redundant `kind` computation (`:584-589`) →
|
||
`kind = "l2" if (multi_module or any_l2) else "l1"` (after the
|
||
registry `kind` field is authoritative, simplify further).
|
||
- **Verify:** pytest passes; `run_ci.sh` exits 0; resolver behavior
|
||
unchanged (all contracts still resolve to the same stacks).
|
||
|
||
### Phase P8 — workflow-generator-dedup (REQ-172)
|
||
- **Lead:** lead-developer; **Contributor:** backend-engineer (test)
|
||
- **Must-haves:**
|
||
- Author `scripts/sync_workflows.py` — reads one source workflow per
|
||
pair (e.g. `workflows-src/ci.yml`, `workflows-src/deploy.yml`,
|
||
`workflows-src/modules-lifecycle.yml`) and writes byte-identical
|
||
copies to both `.gitea/workflows/` and `.github/workflows/`.
|
||
Establish the `workflows-src/` dir as the single source.
|
||
- Replace the byte-identity assertions in
|
||
`tests/test_pipeline_contract.py` with a "generated outputs match
|
||
committed files" test (run `sync_workflows.py --check` → exit 0 if
|
||
the committed files match the generated output, non-zero + diff if
|
||
drift).
|
||
- Migrate the 3 existing pairs to the `workflows-src/` source; remove
|
||
the hand-maintained duplicates (the generator owns them).
|
||
- **Verify:** `python3 scripts/sync_workflows.py --check` exits 0;
|
||
pytest passes; `run_ci.sh` exits 0; the 4 GitHub-only workflows are
|
||
untouched (they have no pair).
|
||
|
||
### Phase P9 — run-platform-split (REQ-173)
|
||
- **Lead:** lead-developer
|
||
- **Must-haves:**
|
||
- Extract the decommission block (`scripts/run_platform.sh:180-237`)
|
||
into `scripts/run_decommission.sh` (sourced or invoked).
|
||
- Extract the uptime block (`:520-606`) into `scripts/run_uptime.sh`.
|
||
- `run_platform.sh` invokes the helpers; behavior unchanged.
|
||
- **G-112 binding:** the helpers are **`source`d** (shared shell env),
|
||
not invoked as subshells — the extracted blocks reference
|
||
`run_platform.sh`-local vars (`NOVA_CONTRACT_ID`/`NOVA_WORK_DIR` from
|
||
P6); a subshell would not inherit them.
|
||
- **G-111 binding:** update `core/regression_verify.py` CAP-015/016
|
||
checks — when the live resource is absent
|
||
(`ResourceNotFoundException`/`404`), mark `Skipped (post-teardown,
|
||
D-096)` not `Decayed`, so a clean local run reports 20/20 Verified +
|
||
2 Skipped (not a strict-`all` failure on the known teardown state).
|
||
- **Run the regression gate (D-118, end of Wave 2):** **20/22 Verified**
|
||
is the passing bar (CAP-015/016 Skipped — post-v1.11-teardown steady
|
||
state, D-096; re-provisioning is a future feature, not an NFR). Any
|
||
non-Verified/non-Skipped capability halts Wave 3.
|
||
- **Verify:** pytest passes; `run_ci.sh` exits 0; `run_platform.sh
|
||
--check-only` exits 0; **regression gate 20/22 Verified + 2 Skipped**.
|
||
|
||
## Wave 3 — Security + Maintainability (P10–P14)
|
||
|
||
### Phase P10 — contract-ingestor-defense-in-depth (REQ-174)
|
||
- **Lead:** backend-engineer; **Contributor:** lead-developer (review)
|
||
- **Must-haves:**
|
||
- `core/lambda/contract_ingestor.py:251-252` `if not caller_arn: pass`
|
||
→ fail closed: return a 401/403 with a clear message when IAM identity
|
||
is absent (defense-in-depth; ABAC layer still the primary control).
|
||
- `core/lambda/contract_ingestor.py:269` hardcoded
|
||
`valid_envs = {"dev","qa","prod","dr"}` → derive from the
|
||
`core/environments/` directory (list `*.json` filenames).
|
||
- Document the ABAC reliance explicitly in the function docstring +
|
||
ARCHITECTURE.md.
|
||
- Test: a request without IAM identity is rejected; a request with an
|
||
unknown environment is rejected.
|
||
- **Verify:** pytest passes; `run_ci.sh` exits 0.
|
||
|
||
### Phase P11 — contract-ingestor-payload-validation (REQ-175)
|
||
- **Lead:** backend-engineer
|
||
- **Must-haves:**
|
||
- `submit_contract`: size-cap the `contract` blob (e.g. 256 KB) before
|
||
the DynamoDB write; reject oversized payloads with 413.
|
||
- Schema-validate the contract blob against `schemas/contract.schema.json`
|
||
before the write; reject invalid with 400.
|
||
- Consistent caps: `error` and `stackTrace` use the same cap (align the
|
||
10k vs 2k inconsistency).
|
||
- Tests for size-limit + schema-rejection paths.
|
||
- **Verify:** pytest passes; `run_ci.sh` exits 0.
|
||
|
||
### Phase P12 — split-contract-resolver (REQ-176)
|
||
- **Lead:** backend-engineer
|
||
- **Must-haves:**
|
||
- Split `core/contract_resolver.py` (638 lines) into:
|
||
`core/contract_resolve.py` (the resolve + interpolation core),
|
||
`core/decommission_transform.py` (the decommission zero-counts
|
||
transform), `core/contract_resolver_cli.py` (the `__main__` CLI).
|
||
- `core/contract_resolver.py` becomes a thin re-export shim for
|
||
backwards compat (existing imports keep working).
|
||
- **G-113 binding:** import direction is one-way — split modules
|
||
import only each other + stdlib; the re-export shim imports the
|
||
split modules; nothing imports the shim except external callers
|
||
(prevents the latent cycle shim → split → split → shim).
|
||
- Behavior unchanged; all tests pass without modification.
|
||
- **Verify:** pytest passes; `run_ci.sh` exits 0.
|
||
|
||
### Phase P13 — split-regression-verify (REQ-177)
|
||
- **Lead:** backend-engineer
|
||
- **Must-haves:**
|
||
- Split `core/regression_verify.py` (670 lines) into:
|
||
`core/regression_capabilities.py` (the CAP-001..022 checks),
|
||
`core/regression_live_plan.py` (the shared live-plan helpers from
|
||
P5), `core/regression_verify_cli.py` (the `__main__` CLI +
|
||
`run_regression` orchestration).
|
||
- `core/regression_verify.py` becomes a thin re-export shim.
|
||
- Behavior unchanged; the regression gate output is identical.
|
||
- **Verify:** pytest passes; `run_ci.sh` exits 0.
|
||
|
||
### Phase P14 — schema-driven-outputs-and-cache (REQ-178)
|
||
- **Lead:** backend-engineer; **Contributor:** data-engineer (interface.json)
|
||
- **Must-haves:**
|
||
- `core/output_publisher.py:38-55` `SAFE_OUTPUT_NAMES` hardcoded set →
|
||
derived from `modules/l1/*/interface.json` `outputs[].sensitive`
|
||
annotations (non-sensitive outputs are safe to publish).
|
||
- `core/contract_resolver.py:498,617` (now in the split module) —
|
||
cache loaded JSON schemas in a module-level dict (avoid re-reading
|
||
from disk each resolve call).
|
||
- **Mid-milestone checkpoint (offline):** regression gate spot-check
|
||
(not the full P9/P21 gate); confirm Wave 3 introduced no regressions.
|
||
- **Verify:** pytest passes; `run_ci.sh` exits 0.
|
||
|
||
## Wave 4 — Developer Experience (P15–P17)
|
||
|
||
### Phase P15 — run-platform-help-and-flags-doc (REQ-179)
|
||
- **Lead:** lead-developer
|
||
- **Must-haves:**
|
||
- `scripts/run_platform.sh` add a real `--help` / `-h` flag that
|
||
prints all flags + a one-line description each (`--check-only`,
|
||
`--plan-only`, `--apply`, `--destroy`, `--quiet`, `--deploy-uptime`,
|
||
`--decommission`, `--local`, `--environment`). The current `:82`
|
||
reject-unknown-flags path must allow `--help` to print + exit 0.
|
||
- Document `--deploy-uptime` in the header comment block (currently
|
||
used at `:532` but absent from the header).
|
||
- Surface `--local` (D-092 local emulating tier) in the README "How to
|
||
run" section.
|
||
- **Verify:** `run_platform.sh --help` exits 0 and lists all flags;
|
||
pytest passes; `run_ci.sh` exits 0.
|
||
|
||
### Phase P16 — workflows-readme-catalog (REQ-180)
|
||
- **Lead:** lead-developer
|
||
- **Must-haves:**
|
||
- Author `.github/workflows/README.md` cataloging all 7 workflows:
|
||
`ci.yml`, `deploy.yml`, `platform-test.yml`, `primitives-plan.yml`,
|
||
`patterns-plan.yml`, `release.yml`, `modules-lifecycle.yml`. For
|
||
each: trigger (`on:`), inputs (reusable-workflow `workflow_call`
|
||
inputs), required secrets, and one-line purpose.
|
||
- Note which 3 are byte-identical Gitea mirrors (post-P8, generated by
|
||
`sync_workflows.py`) and which 4 are GitHub-only (Gitea act_runner
|
||
feature gaps).
|
||
- Add a `tests/test_docs_coverage.py` assertion that the README exists
|
||
+ lists all 7 workflow filenames.
|
||
- **Verify:** pytest passes; `run_ci.sh` exits 0.
|
||
|
||
### Phase P17 — getting-started-consolidation (REQ-181)
|
||
- **Lead:** lead-developer
|
||
- **Must-haves:**
|
||
- Consolidate the README "How to run" into a single getting-started
|
||
section: **offline happy path first** (`bash scripts/run_ci.sh` +
|
||
`bash scripts/run_platform.sh --check-only` / `--local` — no AWS
|
||
needed), then the **AWS path** (bootstrap + `--apply`).
|
||
- Remove the fragmented 3-step bootstrap as the lead; demote it to
|
||
the AWS-path subsection.
|
||
- Cross-link `docs/CONSUMER_GUIDE.md` for the consumer contract model.
|
||
- **Verify:** pytest passes; `run_ci.sh` exits 0.
|
||
|
||
## Wave 5 — No Humans Onboarding Flow (P18–P20)
|
||
|
||
### Phase P18 — onboarding-schema-and-lambda-action (REQ-182)
|
||
- **Lead:** backend-engineer; **Contributor:** lead-developer (schema)
|
||
- **Must-haves:**
|
||
- Author `schemas/onboarding.schema.json` (JSON Schema draft 2020-12):
|
||
required fields `consumerRepo` (string, format), `requestedEnvironment`
|
||
(string, enum from environments dir), `ownerId` (string), `billingTag`
|
||
(string); optional `notes`.
|
||
- `core/lambda/contract_ingestor.py` add an `onboard_consumer` action
|
||
(D-119): validates the payload against the onboarding schema, writes
|
||
a `pending` row to `nova-contracts` (PK `consumerRepo`, SK
|
||
`onboarding#<requestedEnvironment>#<timestamp>`, status `pending`).
|
||
No AWS resources created (D-113).
|
||
- Tests: valid onboarding request writes a pending row; invalid request
|
||
rejected with 400; offline-testable via moto/local Lambda stub.
|
||
- **Verify:** pytest passes; `run_ci.sh` exits 0.
|
||
|
||
### Phase P19 — onboarding-envfile-autogen (REQ-183)
|
||
- **Lead:** backend-engineer; **Contributor:** lead-developer (docs)
|
||
- **Must-haves:**
|
||
- Author `core/onboarding.py` with `generate_env_file(request,
|
||
template_env="dev")` — produces a `<env>.json` from a consumer
|
||
onboarding request (fills `account_id` placeholder, `ownerId`,
|
||
`billingTag` into the env template). Emits the file + a git patch /
|
||
PR-branch instruction.
|
||
- Rebrand `core/environment_check.py:57-77` onboarding message to
|
||
Nova; replace the "1. Contact the platform team" handoff with the
|
||
self-service request path: "Run `nova onboard` (or POST to the
|
||
Lambda `onboard_consumer` action) to request an environment; the
|
||
platform generates a binding + opens a PR."
|
||
- Update `core/environments/README.md:34-37` — self-service request
|
||
path is now implemented (real provisioning still a future feature).
|
||
- Tests: `generate_env_file` produces a valid env JSON; the rebranded
|
||
message no longer says "contact the platform team".
|
||
- **Verify:** pytest passes; `run_ci.sh` exits 0.
|
||
|
||
### Phase P20 — cross-account-role-automation-offline (REQ-184)
|
||
- **Lead:** data-engineer; **Contributor:** backend-engineer (ABAC)
|
||
- **Must-haves:**
|
||
- Author `terraform/onboarding/` (new dir): `main.tf` defining the
|
||
consumer deploy-role + `nova:owner` ABAC tag grant (cross-account
|
||
IAM role + trust policy + tag-based permission boundary). Variables
|
||
for `consumer_repo`, `owner_id`, `account_id`.
|
||
- `terraform validate` passes; `terraform plan` (offline / no live
|
||
apply per D-114) produces the expected role + policy.
|
||
- Document the onboarding Terraform in `docs/ONBOARDING.md` — the
|
||
request path (P18) → env-file autogen (P19) → role grant (P20, this
|
||
phase, offline-proven; live apply deferred).
|
||
- Tests: `terraform validate` for the onboarding module; a
|
||
`test_onboarding_terraform.py` asserting the module validates.
|
||
- **Verify:** `terraform validate` (onboarding module) passes; pytest
|
||
passes; `run_ci.sh` exits 0.
|
||
|
||
## Final Phase — P21 — final-review-ship
|
||
|
||
- **Lead:** lead-developer; **Contributors:** all active (review)
|
||
- **Must-haves:**
|
||
- Multi-persona code review across all v1.16 phases (ci-code-reviewer).
|
||
Auto-apply P0 fixes; flag P1+ for post-hoc review. If P1+ found, fix
|
||
in this phase (not loop back to EXECUTE).
|
||
- Audit (ciagent-audit): reconstruction test (git log matches
|
||
`.ciagent/` files), file discipline, branch hygiene, commit
|
||
discipline. Fix critical issues in this phase.
|
||
- **Run the regression gate (D-118, milestone complete):** **20/22
|
||
Verified** (CAP-015/016 Skipped — post-teardown steady state, D-096).
|
||
- Update `.ciagent/REQUIREMENTS.md` — mark REQ-165..184 complete.
|
||
- Update `.ciagent/ROADMAP.md` — mark v1.16 complete.
|
||
- Update `.ciagent/PROJECT.md` — v1.16 complete summary.
|
||
- Ship: merge `phase/21-final-review-ship` →
|
||
`milestone/v1.16-nova-simplification`; merge milestone → `main`;
|
||
tag `v1.15.26` (= milestone release); create Gitea release with full
|
||
milestone summary.
|
||
- Clear CHECKPOINT.json (milestone complete).
|
||
|
||
## Success Criteria (milestone gate)
|
||
|
||
- All 20 requirements (REQ-165..184) satisfied; 0 partial.
|
||
- Regression gate **20/22 Verified + 2 Skipped** at P9 + P21 (D-118,
|
||
G-111; CAP-015/016 are the post-v1.11-teardown steady state, D-096).
|
||
- `bash scripts/run_ci.sh` exits 0 at every phase boundary.
|
||
- Review: 0 new P0; P1+ flagged or auto-fixed.
|
||
- Audit: clean; reconstruction test passes.
|
||
- Tag `v1.15.26` created; milestone merged to main.
|
||
- Onboarding request path implemented (P18–P20); real AWS provisioning
|
||
explicitly deferred (D-113, D-114). |