Compare commits
20 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| fe312c6292 | |||
| c19a5d66f2 | |||
| e8effef415 | |||
| 4ac68b7270 | |||
| 518bbe32a7 | |||
| ed36519223 | |||
| aa3e385606 | |||
| ab3a9a8548 | |||
| 5492308140 | |||
| 76714bebc4 | |||
| f12f6edd23 | |||
| 1db5ca8286 | |||
| 93ae9e4a39 | |||
| d3179fff37 | |||
| c029b102a3 | |||
| 2806c6c3ed | |||
| e048acd4dd | |||
| 9421442afd | |||
| bb43d94563 | |||
| ee5c372e65 |
@@ -1,12 +1,9 @@
|
|||||||
{
|
{
|
||||||
"phase": 21,
|
"phase": 1,
|
||||||
"stage": "complete",
|
"stage": "execute",
|
||||||
"milestone": "v1.16",
|
"milestone": "v1.16",
|
||||||
"phase_role": "final",
|
"phase_role": "execution",
|
||||||
"attempts": 0,
|
"attempts": 0,
|
||||||
"updated_at": "2026-07-30T16:00:00Z",
|
"updated_at": "2026-07-30T15:30:00Z",
|
||||||
"milestone_complete": true,
|
"milestone_complete": false
|
||||||
"tag": "v1.15.26",
|
|
||||||
"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"],
|
|
||||||
"regression": {"Verified": 18, "Decayed": 0, "Broken": 0, "Skipped": 4}
|
|
||||||
}
|
}
|
||||||
+1
-1
@@ -989,7 +989,7 @@ conversation before execution; D-108..D-112 resolved at CLARIFY.
|
|||||||
| D-111 | Lambda env-var defaults (`CONTRACTS_TABLE` default `"acdl-contracts"`, etc.) → `nova-contracts`. | `core/lambda/contract_ingestor.py` has hardcoded `acdl-*` default table names. These become `nova-*` in P4 (resource migration). P2 changes the env-var name (`ACDL_*`→`NOVA_*`); P4 changes the default values to `nova-*`. | P4 updates Lambda defaults. |
|
| D-111 | Lambda env-var defaults (`CONTRACTS_TABLE` default `"acdl-contracts"`, etc.) → `nova-contracts`. | `core/lambda/contract_ingestor.py` has hardcoded `acdl-*` default table names. These become `nova-*` in P4 (resource migration). P2 changes the env-var name (`ACDL_*`→`NOVA_*`); P4 changes the default values to `nova-*`. | P4 updates Lambda defaults. |
|
||||||
| D-112 | `nova` slug: no `project:` prefix on branches (single-project mode). | `config.json` has `projects[]` with one entry (slug `acdl`) but `git.branching_strategy` is `flat` and the established convention since v1.0 is flat branches (no `<slug>/` prefix). Nova rebrand does NOT change the branch prefix convention. Commit `---ci---` blocks use `project: acdl` (the config slug, unchanged). | Branches stay `milestone/v1.15-nova`, `phase/NN-*`; no `acdl/` or `nova/` prefix. |
|
| D-112 | `nova` slug: no `project:` prefix on branches (single-project mode). | `config.json` has `projects[]` with one entry (slug `acdl`) but `git.branching_strategy` is `flat` and the established convention since v1.0 is flat branches (no `<slug>/` prefix). Nova rebrand does NOT change the branch prefix convention. Commit `---ci---` blocks use `project: acdl` (the config slug, unchanged). | Branches stay `milestone/v1.15-nova`, `phase/NN-*`; no `acdl/` or `nova/` prefix. |
|
||||||
|
|
||||||
## Objective for Milestone v1.16 (complete — NFR Simplification, tag `v1.15.26`)
|
## Objective for Milestone v1.16 (active — NFR Simplification)
|
||||||
|
|
||||||
A 20-phase NFR sweep (no new features) themed around five axes the user
|
A 20-phase NFR sweep (no new features) themed around five axes the user
|
||||||
directed during ideation: **Simplify without regressions**, **Security**,
|
directed during ideation: **Simplify without regressions**, **Security**,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"run_id": "regr-1785591207",
|
"run_id": "regr-1785588523",
|
||||||
"run_at_utc": "2026-08-01T13:33:27Z",
|
"run_at_utc": "2026-08-01T12:48:43Z",
|
||||||
"milestone": "v1.10",
|
"milestone": "v1.10",
|
||||||
"phase": 52,
|
"phase": 52,
|
||||||
"summary": {
|
"summary": {
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
"status": "Verified",
|
"status": "Verified",
|
||||||
"detail": "exit 0; 2 sample contracts validate",
|
"detail": "exit 0; 2 sample contracts validate",
|
||||||
"tier": "local",
|
"tier": "local",
|
||||||
"duration_ms": 235
|
"duration_ms": 260
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"capability_id": "CAP-002",
|
"capability_id": "CAP-002",
|
||||||
@@ -25,7 +25,7 @@
|
|||||||
"status": "Verified",
|
"status": "Verified",
|
||||||
"detail": "exit 0; env schema validates",
|
"detail": "exit 0; env schema validates",
|
||||||
"tier": "local",
|
"tier": "local",
|
||||||
"duration_ms": 201
|
"duration_ms": 202
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"capability_id": "CAP-003",
|
"capability_id": "CAP-003",
|
||||||
@@ -33,7 +33,7 @@
|
|||||||
"status": "Verified",
|
"status": "Verified",
|
||||||
"detail": "exit 0; ",
|
"detail": "exit 0; ",
|
||||||
"tier": "local",
|
"tier": "local",
|
||||||
"duration_ms": 261
|
"duration_ms": 266
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"capability_id": "CAP-004",
|
"capability_id": "CAP-004",
|
||||||
@@ -41,7 +41,7 @@
|
|||||||
"status": "Verified",
|
"status": "Verified",
|
||||||
"detail": "exit 0; ",
|
"detail": "exit 0; ",
|
||||||
"tier": "local",
|
"tier": "local",
|
||||||
"duration_ms": 259
|
"duration_ms": 248
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"capability_id": "CAP-005",
|
"capability_id": "CAP-005",
|
||||||
@@ -57,7 +57,7 @@
|
|||||||
"status": "Verified",
|
"status": "Verified",
|
||||||
"detail": "exit 0; interpolation ok",
|
"detail": "exit 0; interpolation ok",
|
||||||
"tier": "local",
|
"tier": "local",
|
||||||
"duration_ms": 242
|
"duration_ms": 209
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"capability_id": "CAP-007",
|
"capability_id": "CAP-007",
|
||||||
@@ -65,7 +65,7 @@
|
|||||||
"status": "Verified",
|
"status": "Verified",
|
||||||
"detail": "exit 0; confidence band=pass",
|
"detail": "exit 0; confidence band=pass",
|
||||||
"tier": "local",
|
"tier": "local",
|
||||||
"duration_ms": 91
|
"duration_ms": 80
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"capability_id": "CAP-008",
|
"capability_id": "CAP-008",
|
||||||
@@ -73,15 +73,15 @@
|
|||||||
"status": "Verified",
|
"status": "Verified",
|
||||||
"detail": "exit 0; outbox hash chain ok",
|
"detail": "exit 0; outbox hash chain ok",
|
||||||
"tier": "local",
|
"tier": "local",
|
||||||
"duration_ms": 456
|
"duration_ms": 319
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"capability_id": "CAP-009",
|
"capability_id": "CAP-009",
|
||||||
"name": "offline pytest suite passes",
|
"name": "offline pytest suite passes",
|
||||||
"status": "Verified",
|
"status": "Verified",
|
||||||
"detail": "exit 0; [ 98%]\ntests/test_wiz_adapter_real_client.py ......... [100%]\n\n================= 586 passed, 2 deselected in 71.63s (0:01:11) =================",
|
"detail": "exit 0; [ 98%]\ntests/test_wiz_adapter_real_client.py ......... [100%]\n\n====================== 577 passed, 2 deselected in 53.53s ======================",
|
||||||
"tier": "local",
|
"tier": "local",
|
||||||
"duration_ms": 72988
|
"duration_ms": 55005
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"capability_id": "CAP-010",
|
"capability_id": "CAP-010",
|
||||||
@@ -89,23 +89,23 @@
|
|||||||
"status": "Verified",
|
"status": "Verified",
|
||||||
"detail": "exit 0; resource(s))\n\n=== PLATFORM CHECK OK ===\ncontract -> resolver -> stack -> adapter -> structure validated (offline, no AWS)\ncheck-only: OK\n\n=== CI PIPELINE OK ===\n3 stages passed: lint, test, check-only",
|
"detail": "exit 0; resource(s))\n\n=== PLATFORM CHECK OK ===\ncontract -> resolver -> stack -> adapter -> structure validated (offline, no AWS)\ncheck-only: OK\n\n=== CI PIPELINE OK ===\n3 stages passed: lint, test, check-only",
|
||||||
"tier": "local",
|
"tier": "local",
|
||||||
"duration_ms": 73275
|
"duration_ms": 59882
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"capability_id": "CAP-011",
|
"capability_id": "CAP-011",
|
||||||
"name": "headline E2E runs against the local emulating tier (microservice)",
|
"name": "headline E2E runs against the local emulating tier (microservice)",
|
||||||
"status": "Verified",
|
"status": "Verified",
|
||||||
"detail": "exit 0; al-emulator\",\n \"desired_count\": 1,\n \"running_count\": 1\n },\n \"outbox_dir\": \"/tmp/nova_local_e2e_6vnrnin1/outbox\",\n \"outbox_events\": 2,\n \"outbox_chain_verified\": true,\n \"lambda_status\": 200\n}",
|
"detail": "exit 0; al-emulator\",\n \"desired_count\": 1,\n \"running_count\": 1\n },\n \"outbox_dir\": \"/tmp/nova_local_e2e_cuwlkzrj/outbox\",\n \"outbox_events\": 2,\n \"outbox_chain_verified\": true,\n \"lambda_status\": 200\n}",
|
||||||
"tier": "local",
|
"tier": "local",
|
||||||
"duration_ms": 634
|
"duration_ms": 561
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"capability_id": "CAP-012",
|
"capability_id": "CAP-012",
|
||||||
"name": "local E2E on the static-assets stack (no ECS)",
|
"name": "local E2E on the static-assets stack (no ECS)",
|
||||||
"status": "Verified",
|
"status": "Verified",
|
||||||
"detail": "exit 0; nova_local_e2e_uq4kkhze/tf\",\n \"backend\": \"local\",\n \"ecs\": null,\n \"outbox_dir\": \"/tmp/nova_local_e2e_uq4kkhze/outbox\",\n \"outbox_events\": 2,\n \"outbox_chain_verified\": true,\n \"lambda_status\": 200\n}",
|
"detail": "exit 0; nova_local_e2e_mfeuiylw/tf\",\n \"backend\": \"local\",\n \"ecs\": null,\n \"outbox_dir\": \"/tmp/nova_local_e2e_mfeuiylw/outbox\",\n \"outbox_events\": 2,\n \"outbox_chain_verified\": true,\n \"lambda_status\": 200\n}",
|
||||||
"tier": "local",
|
"tier": "local",
|
||||||
"duration_ms": 584
|
"duration_ms": 492
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"capability_id": "CAP-013",
|
"capability_id": "CAP-013",
|
||||||
@@ -113,7 +113,7 @@
|
|||||||
"status": "Skipped",
|
"status": "Skipped",
|
||||||
"detail": "terraform init: state bucket absent (post-v1.11-teardown, D-096) [microservice]",
|
"detail": "terraform init: state bucket absent (post-v1.11-teardown, D-096) [microservice]",
|
||||||
"tier": "live-aws",
|
"tier": "live-aws",
|
||||||
"duration_ms": 737
|
"duration_ms": 851
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"capability_id": "CAP-014",
|
"capability_id": "CAP-014",
|
||||||
@@ -121,7 +121,7 @@
|
|||||||
"status": "Skipped",
|
"status": "Skipped",
|
||||||
"detail": "terraform init: state bucket absent (post-v1.11-teardown, D-096) [static-assets]",
|
"detail": "terraform init: state bucket absent (post-v1.11-teardown, D-096) [static-assets]",
|
||||||
"tier": "live-aws",
|
"tier": "live-aws",
|
||||||
"duration_ms": 676
|
"duration_ms": 724
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"capability_id": "CAP-015",
|
"capability_id": "CAP-015",
|
||||||
@@ -129,7 +129,7 @@
|
|||||||
"status": "Skipped",
|
"status": "Skipped",
|
||||||
"detail": "nova-outbox absent (post-v1.11-teardown steady state, D-096)",
|
"detail": "nova-outbox absent (post-v1.11-teardown steady state, D-096)",
|
||||||
"tier": "live-aws",
|
"tier": "live-aws",
|
||||||
"duration_ms": 664
|
"duration_ms": 487
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"capability_id": "CAP-016",
|
"capability_id": "CAP-016",
|
||||||
@@ -137,7 +137,7 @@
|
|||||||
"status": "Skipped",
|
"status": "Skipped",
|
||||||
"detail": "state bucket nova-tfstate-581513795199-us-east-1 absent (post-v1.11-teardown, D-096)",
|
"detail": "state bucket nova-tfstate-581513795199-us-east-1 absent (post-v1.11-teardown, D-096)",
|
||||||
"tier": "live-aws",
|
"tier": "live-aws",
|
||||||
"duration_ms": 245
|
"duration_ms": 300
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"capability_id": "CAP-017",
|
"capability_id": "CAP-017",
|
||||||
@@ -145,7 +145,7 @@
|
|||||||
"status": "Verified",
|
"status": "Verified",
|
||||||
"detail": "terraform files present + fmt -check passes + simple/complex contracts resolve",
|
"detail": "terraform files present + fmt -check passes + simple/complex contracts resolve",
|
||||||
"tier": "lifecycle-pipeline",
|
"tier": "lifecycle-pipeline",
|
||||||
"duration_ms": 586
|
"duration_ms": 579
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"capability_id": "CAP-018",
|
"capability_id": "CAP-018",
|
||||||
@@ -153,7 +153,7 @@
|
|||||||
"status": "Verified",
|
"status": "Verified",
|
||||||
"detail": "LocalLambdaStub instantiates (local tier evidence)",
|
"detail": "LocalLambdaStub instantiates (local tier evidence)",
|
||||||
"tier": "lifecycle-pipeline",
|
"tier": "lifecycle-pipeline",
|
||||||
"duration_ms": 138
|
"duration_ms": 139
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"capability_id": "CAP-019",
|
"capability_id": "CAP-019",
|
||||||
@@ -161,7 +161,7 @@
|
|||||||
"status": "Verified",
|
"status": "Verified",
|
||||||
"detail": "L2 composition resolves (simple + complex contracts; offline proxy)",
|
"detail": "L2 composition resolves (simple + complex contracts; offline proxy)",
|
||||||
"tier": "lifecycle-pipeline",
|
"tier": "lifecycle-pipeline",
|
||||||
"duration_ms": 519
|
"duration_ms": 553
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"capability_id": "CAP-020",
|
"capability_id": "CAP-020",
|
||||||
@@ -169,7 +169,7 @@
|
|||||||
"status": "Verified",
|
"status": "Verified",
|
||||||
"detail": "L2 composition resolves (simple + complex contracts; offline proxy)",
|
"detail": "L2 composition resolves (simple + complex contracts; offline proxy)",
|
||||||
"tier": "lifecycle-pipeline",
|
"tier": "lifecycle-pipeline",
|
||||||
"duration_ms": 521
|
"duration_ms": 561
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"capability_id": "CAP-021",
|
"capability_id": "CAP-021",
|
||||||
@@ -177,7 +177,7 @@
|
|||||||
"status": "Verified",
|
"status": "Verified",
|
||||||
"detail": "terraform files present + fmt -check passes + simple/complex contracts resolve",
|
"detail": "terraform files present + fmt -check passes + simple/complex contracts resolve",
|
||||||
"tier": "lifecycle-pipeline",
|
"tier": "lifecycle-pipeline",
|
||||||
"duration_ms": 562
|
"duration_ms": 598
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"capability_id": "CAP-022",
|
"capability_id": "CAP-022",
|
||||||
@@ -185,7 +185,7 @@
|
|||||||
"status": "Verified",
|
"status": "Verified",
|
||||||
"detail": "terraform files present + fmt -check passes + simple/complex contracts resolve",
|
"detail": "terraform files present + fmt -check passes + simple/complex contracts resolve",
|
||||||
"tier": "lifecycle-pipeline",
|
"tier": "lifecycle-pipeline",
|
||||||
"duration_ms": 611
|
"duration_ms": 570
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -1,51 +1,51 @@
|
|||||||
# Regression Report — v1.10 Phase 52
|
# Regression Report — v1.10 Phase 52
|
||||||
|
|
||||||
- **Run ID:** `regr-1785591207`
|
- **Run ID:** `regr-1785588523`
|
||||||
- **Run at (UTC):** 2026-08-01T13:33:27Z
|
- **Run at (UTC):** 2026-08-01T12:48:43Z
|
||||||
- **Summary:** {'Verified': 18, 'Decayed': 0, 'Broken': 0, 'Skipped': 4}
|
- **Summary:** {'Verified': 18, 'Decayed': 0, 'Broken': 0, 'Skipped': 4}
|
||||||
- **Passed (milestone gate):** True
|
- **Passed (milestone gate):** True
|
||||||
|
|
||||||
| Capability | Name | Tier | Status | Duration (ms) | Detail |
|
| Capability | Name | Tier | Status | Duration (ms) | Detail |
|
||||||
|-----------|------|------|--------|--------------|--------|
|
|-----------|------|------|--------|--------------|--------|
|
||||||
| CAP-001 | contract.schema.json validates sample contracts | local | **Verified** | 235 | exit 0; 2 sample contracts validate |
|
| CAP-001 | contract.schema.json validates sample contracts | local | **Verified** | 260 | exit 0; 2 sample contracts validate |
|
||||||
| CAP-002 | environment.schema.json validates env files | local | **Verified** | 201 | exit 0; env schema validates |
|
| CAP-002 | environment.schema.json validates env files | local | **Verified** | 202 | exit 0; env schema validates |
|
||||||
| CAP-003 | contract_resolver resolves static-assets | local | **Verified** | 261 | exit 0; |
|
| CAP-003 | contract_resolver resolves static-assets | local | **Verified** | 266 | exit 0; |
|
||||||
| CAP-004 | contract_resolver resolves microservice | local | **Verified** | 259 | exit 0; |
|
| CAP-004 | contract_resolver resolves microservice | local | **Verified** | 248 | exit 0; |
|
||||||
| CAP-005 | terraform adapter emits .tf files | local | **Verified** | 337 | exit 0; |
|
| CAP-005 | terraform adapter emits .tf files | local | **Verified** | 337 | exit 0; |
|
||||||
| CAP-006 | contract interpolation expands env/contract tokens | local | **Verified** | 242 | exit 0; interpolation ok |
|
| CAP-006 | contract interpolation expands env/contract tokens | local | **Verified** | 209 | exit 0; interpolation ok |
|
||||||
| CAP-007 | confidence_signal.compute returns a band | local | **Verified** | 91 | exit 0; confidence band=pass |
|
| CAP-007 | confidence_signal.compute returns a band | local | **Verified** | 80 | exit 0; confidence band=pass |
|
||||||
| CAP-008 | outbox_writer builds a hash-chained item | local | **Verified** | 456 | exit 0; outbox hash chain ok |
|
| CAP-008 | outbox_writer builds a hash-chained item | local | **Verified** | 319 | exit 0; outbox hash chain ok |
|
||||||
| CAP-009 | offline pytest suite passes | local | **Verified** | 72988 | exit 0; [ 98%]
|
| CAP-009 | offline pytest suite passes | local | **Verified** | 55005 | exit 0; [ 98%]
|
||||||
tests/test_wiz_adapter_real_client.py ......... [100%]
|
tests/test_wiz_adapter_real_client.py ......... [100%]
|
||||||
|
|
||||||
================= 586 passed, 2 |
|
====================== 577 passe |
|
||||||
| CAP-010 | run_ci.sh reproduces CI pipeline locally | local | **Verified** | 73275 | exit 0; resource(s))
|
| CAP-010 | run_ci.sh reproduces CI pipeline locally | local | **Verified** | 59882 | exit 0; resource(s))
|
||||||
|
|
||||||
=== PLATFORM CHECK OK ===
|
=== PLATFORM CHECK OK ===
|
||||||
contract -> resolver -> stack -> adapter -> structure validated (offline, no AWS)
|
contract -> resolver -> stack -> adapter -> structure validated (offline, no AWS)
|
||||||
check-only: OK
|
check-only: OK
|
||||||
|
|
||||||
=== CI PIPELIN |
|
=== CI PIPELIN |
|
||||||
| CAP-011 | headline E2E runs against the local emulating tier (microservice) | local | **Verified** | 634 | exit 0; al-emulator",
|
| CAP-011 | headline E2E runs against the local emulating tier (microservice) | local | **Verified** | 561 | exit 0; al-emulator",
|
||||||
"desired_count": 1,
|
"desired_count": 1,
|
||||||
"running_count": 1
|
"running_count": 1
|
||||||
},
|
},
|
||||||
"outbox_dir": "/tmp/nova_local_e2e_6vnrnin1/outbox",
|
"outbox_dir": "/tmp/nova_local_e2e_cuwlkzrj/outbox",
|
||||||
"outbox_events": 2,
|
"outbox_events": 2,
|
||||||
"outbox |
|
"outbox |
|
||||||
| CAP-012 | local E2E on the static-assets stack (no ECS) | local | **Verified** | 584 | exit 0; nova_local_e2e_uq4kkhze/tf",
|
| CAP-012 | local E2E on the static-assets stack (no ECS) | local | **Verified** | 492 | exit 0; nova_local_e2e_mfeuiylw/tf",
|
||||||
"backend": "local",
|
"backend": "local",
|
||||||
"ecs": null,
|
"ecs": null,
|
||||||
"outbox_dir": "/tmp/nova_local_e2e_uq4kkhze/outbox",
|
"outbox_dir": "/tmp/nova_local_e2e_mfeuiylw/outbox",
|
||||||
"outbox_events": 2,
|
"outbox_events": 2,
|
||||||
"outbox |
|
"outbox |
|
||||||
| CAP-013 | terraform init+validate+plan live AWS (microservice) | live-aws | **Skipped** | 737 | terraform init: state bucket absent (post-v1.11-teardown, D-096) [microservice] |
|
| CAP-013 | terraform init+validate+plan live AWS (microservice) | live-aws | **Skipped** | 851 | terraform init: state bucket absent (post-v1.11-teardown, D-096) [microservice] |
|
||||||
| CAP-014 | terraform init+validate+plan live AWS (static-assets) | live-aws | **Skipped** | 676 | terraform init: state bucket absent (post-v1.11-teardown, D-096) [static-assets] |
|
| CAP-014 | terraform init+validate+plan live AWS (static-assets) | live-aws | **Skipped** | 724 | terraform init: state bucket absent (post-v1.11-teardown, D-096) [static-assets] |
|
||||||
| CAP-015 | DynamoDB outbox table exists (live AWS) | live-aws | **Skipped** | 664 | nova-outbox absent (post-v1.11-teardown steady state, D-096) |
|
| CAP-015 | DynamoDB outbox table exists (live AWS) | live-aws | **Skipped** | 487 | nova-outbox absent (post-v1.11-teardown steady state, D-096) |
|
||||||
| CAP-016 | S3 state bucket exists + readable (live AWS) | live-aws | **Skipped** | 245 | state bucket nova-tfstate-581513795199-us-east-1 absent (post-v1.11-teardown, D-096) |
|
| CAP-016 | S3 state bucket exists + readable (live AWS) | live-aws | **Skipped** | 300 | state bucket nova-tfstate-581513795199-us-east-1 absent (post-v1.11-teardown, D-096) |
|
||||||
| CAP-017 | DynamoDB nova-contracts table (lifecycle pipeline evidence) | lifecycle-pipeline | **Verified** | 586 | terraform files present + fmt -check passes + simple/complex contracts resolve |
|
| CAP-017 | DynamoDB nova-contracts table (lifecycle pipeline evidence) | lifecycle-pipeline | **Verified** | 579 | terraform files present + fmt -check passes + simple/complex contracts resolve |
|
||||||
| CAP-018 | Lambda contract-ingestor (local stub + lifecycle evidence) | lifecycle-pipeline | **Verified** | 138 | LocalLambdaStub instantiates (local tier evidence) |
|
| CAP-018 | Lambda contract-ingestor (local stub + lifecycle evidence) | lifecycle-pipeline | **Verified** | 139 | LocalLambdaStub instantiates (local tier evidence) |
|
||||||
| CAP-019 | ECS cluster + service (L2 microservice lifecycle evidence) | lifecycle-pipeline | **Verified** | 519 | L2 composition resolves (simple + complex contracts; offline proxy) |
|
| CAP-019 | ECS cluster + service (L2 microservice lifecycle evidence) | lifecycle-pipeline | **Verified** | 553 | L2 composition resolves (simple + complex contracts; offline proxy) |
|
||||||
| CAP-020 | CloudFront + WAF (L2 static-assets lifecycle evidence) | lifecycle-pipeline | **Verified** | 521 | L2 composition resolves (simple + complex contracts; offline proxy) |
|
| CAP-020 | CloudFront + WAF (L2 static-assets lifecycle evidence) | lifecycle-pipeline | **Verified** | 561 | L2 composition resolves (simple + complex contracts; offline proxy) |
|
||||||
| CAP-021 | uptime-kuma (L1 uptime lifecycle evidence) | lifecycle-pipeline | **Verified** | 562 | terraform files present + fmt -check passes + simple/complex contracts resolve |
|
| CAP-021 | uptime-kuma (L1 uptime lifecycle evidence) | lifecycle-pipeline | **Verified** | 598 | terraform files present + fmt -check passes + simple/complex contracts resolve |
|
||||||
| CAP-022 | OIDC role (L1 iam-role lifecycle evidence) | lifecycle-pipeline | **Verified** | 611 | terraform files present + fmt -check passes + simple/complex contracts resolve |
|
| CAP-022 | OIDC role (L1 iam-role lifecycle evidence) | lifecycle-pipeline | **Verified** | 570 | terraform files present + fmt -check passes + simple/complex contracts resolve |
|
||||||
|
|||||||
+20
-20
@@ -921,26 +921,26 @@ simplification and the first self-service onboarding request path.
|
|||||||
|
|
||||||
| Requirement | Phase | Status |
|
| Requirement | Phase | Status |
|
||||||
|-------------|-------|--------|
|
|-------------|-------|--------|
|
||||||
| REQ-165 | P1 | complete |
|
| REQ-165 | P1 | pending |
|
||||||
| REQ-166 | P2 | complete |
|
| REQ-166 | P2 | pending |
|
||||||
| REQ-167 | P3 | complete |
|
| REQ-167 | P3 | pending |
|
||||||
| REQ-168 | P4 | complete |
|
| REQ-168 | P4 | pending |
|
||||||
| REQ-169 | P5 | complete |
|
| REQ-169 | P5 | pending |
|
||||||
| REQ-170 | P6 | complete |
|
| REQ-170 | P6 | pending |
|
||||||
| REQ-171 | P7 | complete |
|
| REQ-171 | P7 | pending |
|
||||||
| REQ-172 | P8 | complete |
|
| REQ-172 | P8 | pending |
|
||||||
| REQ-173 | P9 | complete |
|
| REQ-173 | P9 | pending |
|
||||||
| REQ-174 | P10 | complete |
|
| REQ-174 | P10 | pending |
|
||||||
| REQ-175 | P11 | complete |
|
| REQ-175 | P11 | pending |
|
||||||
| REQ-176 | P12 | complete |
|
| REQ-176 | P12 | pending |
|
||||||
| REQ-177 | P13 | complete |
|
| REQ-177 | P13 | pending |
|
||||||
| REQ-178 | P14 | complete |
|
| REQ-178 | P14 | pending |
|
||||||
| REQ-179 | P15 | complete |
|
| REQ-179 | P15 | pending |
|
||||||
| REQ-180 | P16 | complete |
|
| REQ-180 | P16 | pending |
|
||||||
| REQ-181 | P17 | complete |
|
| REQ-181 | P17 | pending |
|
||||||
| REQ-182 | P18 | complete |
|
| REQ-182 | P18 | pending |
|
||||||
| REQ-183 | P19 | complete |
|
| REQ-183 | P19 | pending |
|
||||||
| REQ-184 | P20 | complete |
|
| REQ-184 | P20 | pending |
|
||||||
|
|
||||||
### Out of Scope (v1.16)
|
### Out of Scope (v1.16)
|
||||||
- New features (feat phases). v1.16 is NFR-only.
|
- New features (feat phases). v1.16 is NFR-only.
|
||||||
|
|||||||
@@ -1630,56 +1630,3 @@ milestone release). (G-104 binding.)
|
|||||||
- Tag `v1.15.4` created; milestone merged to main.
|
- Tag `v1.15.4` created; milestone merged to main.
|
||||||
|
|
||||||
After Phase P5: milestone COMPLETE — `v1.15.4` IS the v1.15 release.
|
After Phase P5: milestone COMPLETE — `v1.15.4` IS the v1.15 release.
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## v1.16 (complete — Nova Simplification, tag `v1.15.26`)
|
|
||||||
|
|
||||||
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**. The v1.15
|
|
||||||
rebrand left a fresh debt layer (stale brand strings, a state-bucket
|
|
||||||
drift, a Kyverno policy contradicting the Nova tagging standard, dead
|
|
||||||
code) that this milestone cleared, alongside genuine simplification
|
|
||||||
(dedup helpers, a workflow generator, file splits) and the first
|
|
||||||
self-service onboarding request path (request-path only; real AWS
|
|
||||||
provisioning deferred, D-113).
|
|
||||||
|
|
||||||
**Milestone type:** NFR (all phases fix/chore/docs/refactor/test). The
|
|
||||||
final phase's patch IS the deliverable. Tags 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).
|
|
||||||
|
|
||||||
**Regression gate (D-118, G-111):** 18 Verified + 4 Skipped (CAP-013..016
|
|
||||||
live-AWS caps are the post-v1.11-teardown steady state, D-096; re-
|
|
||||||
provisioning is a future feature). 0 Decayed/Broken at P9 + P21.
|
|
||||||
|
|
||||||
**Grill:** PASS-with-binding (G-111..G-113, E-002 deferred to P21).
|
|
||||||
G-111: gate criterion restated 18V+4S + Skipped logic. G-112: P9 source
|
|
||||||
model pinned. G-113: P12/P13 import direction documented.
|
|
||||||
|
|
||||||
**Wave outcomes:**
|
|
||||||
- Wave 1 (P1–P4): state-bucket + Kyverno rebrand fix (correctness
|
|
||||||
regression), user-facing ACDL→Nova sweep, dead-code cleanup, except
|
|
||||||
narrowing.
|
|
||||||
- Wave 2 (P5–P9): regression-verify dedup (~70 lines), run-platform
|
|
||||||
HITL fn + config, contract-resolver envloader + registry kind, workflow
|
|
||||||
generator (sync_workflows.py + workflows-src/), run-platform split
|
|
||||||
(decommission + uptime helpers). Gate PASS at P9.
|
|
||||||
- Wave 3 (P10–P14): ingestor defense-in-depth (fail closed on missing
|
|
||||||
IAM), payload validation (size cap + schema), split contract-resolver
|
|
||||||
(decommission + CLI modules), split regression-verify (CLI module),
|
|
||||||
schema-driven outputs + schema cache. Mid-milestone checkpoint clean.
|
|
||||||
- Wave 4 (P15–P17): run-platform --help + flags doc, workflows README
|
|
||||||
catalog (7 workflows), getting-started consolidation (offline-first).
|
|
||||||
- Wave 5 (P18–P20): onboarding schema + onboard_consumer Lambda action,
|
|
||||||
env-file autogen (core/onboarding.py), cross-account role Terraform
|
|
||||||
(offline-proven, D-114).
|
|
||||||
|
|
||||||
**Outcome:** 20 requirements (REQ-165..184) satisfied; ~630 tests pass;
|
|
||||||
regression gate 18V+4S; the onboarding request path is self-service (no
|
|
||||||
"contact the platform team" handoff); real AWS provisioning explicitly
|
|
||||||
deferred (D-113/D-114).
|
|
||||||
|
|
||||||
Ship tag at milestone COMPLETE: `v1.15.26` (NFR milestone; final patch IS
|
|
||||||
the release). **DONE.**
|
|
||||||
|
|||||||
@@ -1,87 +0,0 @@
|
|||||||
# Nova Onboarding — No-Humans Request Path (v1.16, REQ-182..184)
|
|
||||||
|
|
||||||
The v1.16 milestone implements the **request path** of the no-humans
|
|
||||||
onboarding flow (D-113). A consumer can submit an onboarding request
|
|
||||||
without contacting the platform team; the platform generates an
|
|
||||||
environment binding + (in a future milestone) provisions the AWS resources.
|
|
||||||
|
|
||||||
## The 3-step request path
|
|
||||||
|
|
||||||
### Step 1 — Submit an onboarding request (P18, REQ-182)
|
|
||||||
|
|
||||||
A consumer submits an onboarding request to the Nova platform Lambda:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Via the Lambda Function URL (IAM auth):
|
|
||||||
curl -X POST "$NOVA_LAMBDA_URL" \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-d '{
|
|
||||||
"action": "onboard_consumer",
|
|
||||||
"consumerRepo": "acdl/my-app",
|
|
||||||
"requestedEnvironment": "dev",
|
|
||||||
"ownerId": "team-x",
|
|
||||||
"billingTag": "cost-center-x"
|
|
||||||
}'
|
|
||||||
```
|
|
||||||
|
|
||||||
The Lambda validates the payload against
|
|
||||||
[`schemas/onboarding.schema.json`](../schemas/onboarding.schema.json),
|
|
||||||
then writes a `pending` row to the `nova-contracts` DynamoDB table
|
|
||||||
(D-119). No AWS resources are created by this action (D-113).
|
|
||||||
|
|
||||||
### Step 2 — Generate an environment binding (P19, REQ-183)
|
|
||||||
|
|
||||||
The platform (or the consumer locally) generates an environment binding
|
|
||||||
file from the request:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
python3 core/onboarding.py --request '{
|
|
||||||
"consumerRepo": "acdl/my-app",
|
|
||||||
"requestedEnvironment": "qa",
|
|
||||||
"ownerId": "team-x",
|
|
||||||
"billingTag": "cost-center-x"
|
|
||||||
}' --out core/environments/qa.json
|
|
||||||
```
|
|
||||||
|
|
||||||
This produces a `<env>.json` from the `dev.json` template, filling in
|
|
||||||
the `ownerId` + `billingTag` + a description. The `account_id` is a
|
|
||||||
placeholder (`000000000000`) for the platform team to fill with the real
|
|
||||||
account. The generated file validates against
|
|
||||||
[`schemas/environment.schema.json`](../schemas/environment.schema.json).
|
|
||||||
|
|
||||||
### Step 3 — Cross-account role + ABAC tag grant (P20, REQ-184)
|
|
||||||
|
|
||||||
The platform authors the consumer deploy-role + `nova:owner` ABAC tag
|
|
||||||
grant via Terraform:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd terraform/onboarding
|
|
||||||
terraform init -backend=false
|
|
||||||
terraform validate
|
|
||||||
NOVA_AWS_ACCOUNT_ID=123456789012 terraform plan \
|
|
||||||
-var consumer_repo=acdl/my-app \
|
|
||||||
-var owner_id=team-x
|
|
||||||
```
|
|
||||||
|
|
||||||
**Offline-proven only (D-114):** `terraform validate` + `terraform plan`
|
|
||||||
pass; **no live apply** in v1.16. The live apply (creating the real
|
|
||||||
cross-account role + OIDC trust) is deferred to a future feature
|
|
||||||
milestone (D-113).
|
|
||||||
|
|
||||||
## What is NOT automated (deferred)
|
|
||||||
|
|
||||||
- **Real AWS account/network/state provisioning** — the request path
|
|
||||||
generates a binding file with a placeholder `account_id`; the actual
|
|
||||||
AWS account creation + VPC + state backend is a future feature (D-113).
|
|
||||||
- **Live cross-account role apply** — the Terraform is offline-proven
|
|
||||||
only (D-114); live apply is deferred.
|
|
||||||
- **OIDC trust policy** — the onboarding Terraform uses a placeholder
|
|
||||||
OIDC provider; real OIDC federation is blocked on
|
|
||||||
go-gitea/gitea#36988 (carries forward from v1.1).
|
|
||||||
|
|
||||||
## See also
|
|
||||||
|
|
||||||
- [`schemas/onboarding.schema.json`](../schemas/onboarding.schema.json) — the request schema
|
|
||||||
- [`core/onboarding.py`](../core/onboarding.py) — the env-file generator
|
|
||||||
- [`terraform/onboarding/`](../terraform/onboarding/) — the role-grant Terraform
|
|
||||||
- [`core/environments/README.md`](../core/environments/README.md) — environment binding docs
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
# terraform/onboarding/ — Consumer deploy-role + ABAC tag grant (P20, REQ-184)
|
|
||||||
|
|
||||||
Offline-proven Terraform for the cross-account consumer deploy-role +
|
|
||||||
`nova:owner` ABAC tag grant. This is the "role grant" half of the
|
|
||||||
no-humans onboarding flow (D-113); the "request" half is P18 (Lambda
|
|
||||||
action) + P19 (env-file autogen).
|
|
||||||
|
|
||||||
## Scope (D-114)
|
|
||||||
|
|
||||||
This Terraform is **offline-proven only** in v1.16:
|
|
||||||
- `terraform validate` passes.
|
|
||||||
- `terraform plan` (with `NOVA_AWS_ACCOUNT_ID` set) produces the expected
|
|
||||||
role + policy.
|
|
||||||
- **No live apply** — `NOVA_LIFECYCLE_MODE=plan` default. Live apply is
|
|
||||||
deferred to a future feature milestone (D-113/D-114).
|
|
||||||
|
|
||||||
## Variables
|
|
||||||
|
|
||||||
| Variable | Description | Default |
|
|
||||||
|----------|-------------|---------|
|
|
||||||
| `consumer_repo` | The consumer repository (org/repo) | `acdl/consumer-a` |
|
|
||||||
| `owner_id` | The owning team (for `nova:owner` tag) | `team-a` |
|
|
||||||
| `account_id` | The consumer's AWS account ID | `000000000000` |
|
|
||||||
| `region` | AWS region | `us-east-1` |
|
|
||||||
|
|
||||||
## Resources
|
|
||||||
|
|
||||||
- `aws_iam_role.consumer_deploy` — the consumer's deploy role with a
|
|
||||||
trust policy (assumed by the consumer's CI runner).
|
|
||||||
- `aws_iam_role_policy.consumer_invoke` — inline policy granting
|
|
||||||
`lambda:InvokeFunctionUrl` on the platform Lambda, scoped via
|
|
||||||
`aws:PrincipalTag/nova:owner == var.owner_id` (ABAC).
|
|
||||||
- `aws_iam_tag.owner` — tags the role with `nova:owner` + `nova:contract`.
|
|
||||||
|
|
||||||
## Usage (offline)
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd terraform/onboarding
|
|
||||||
terraform init -backend=false
|
|
||||||
terraform validate
|
|
||||||
NOVA_AWS_ACCOUNT_ID=123456789012 terraform plan -var consumer_repo=acdl/my-app -var owner_id=team-x
|
|
||||||
```
|
|
||||||
@@ -1,120 +0,0 @@
|
|||||||
terraform {
|
|
||||||
required_version = ">= 1.9, < 1.10"
|
|
||||||
required_providers {
|
|
||||||
aws = {
|
|
||||||
source = "hashicorp/aws"
|
|
||||||
version = "~> 5.0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
variable "consumer_repo" {
|
|
||||||
description = "The consumer repository (org/repo) — for the nova:contract tag."
|
|
||||||
type = string
|
|
||||||
default = "acdl/consumer-a"
|
|
||||||
}
|
|
||||||
|
|
||||||
variable "owner_id" {
|
|
||||||
description = "The owning team (for the nova:owner ABAC tag)."
|
|
||||||
type = string
|
|
||||||
default = "team-a"
|
|
||||||
}
|
|
||||||
|
|
||||||
variable "account_id" {
|
|
||||||
description = "The consumer's AWS account ID (where the deploy role is created)."
|
|
||||||
type = string
|
|
||||||
default = "000000000000"
|
|
||||||
}
|
|
||||||
|
|
||||||
variable "region" {
|
|
||||||
description = "AWS region."
|
|
||||||
type = string
|
|
||||||
default = "us-east-1"
|
|
||||||
}
|
|
||||||
|
|
||||||
provider "aws" {
|
|
||||||
region = var.region
|
|
||||||
}
|
|
||||||
|
|
||||||
# P20 (REQ-184): consumer deploy role — the role the consumer's CI runner
|
|
||||||
# assumes to invoke the platform Lambda + deploy via the reusable workflow.
|
|
||||||
# The trust policy allows the consumer's CI runner (GitHub Actions /
|
|
||||||
# Gitea act_runner) to assume this role. In a real deployment, the trust
|
|
||||||
# policy is scoped to the consumer's OIDC provider; for offline-proven
|
|
||||||
# mode, a placeholder trust is used.
|
|
||||||
resource "aws_iam_role" "consumer_deploy" {
|
|
||||||
name = "nova-${replace(var.consumer_repo, "/", "-")}-deploy"
|
|
||||||
|
|
||||||
assume_role_policy = jsonencode({
|
|
||||||
Version = "2012-10-17"
|
|
||||||
Statement = [
|
|
||||||
{
|
|
||||||
Effect = "Allow"
|
|
||||||
Principal = {
|
|
||||||
# Placeholder: in a real deployment, this is the consumer's
|
|
||||||
# OIDC provider ARN. Offline-proven mode uses a wildcard.
|
|
||||||
Federated = "arn:aws:iam::${var.account_id}:oidc-provider/token.actions.githubusercontent.com"
|
|
||||||
}
|
|
||||||
Action = "sts:AssumeRoleWithWebIdentity"
|
|
||||||
Condition = {
|
|
||||||
StringEquals = {
|
|
||||||
"token.actions.githubusercontent.com:aud" = "sts.amazonaws.com"
|
|
||||||
}
|
|
||||||
StringLike = {
|
|
||||||
"token.actions.githubusercontent.com:sub" = "repo:${var.consumer_repo}:*"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
})
|
|
||||||
|
|
||||||
tags = {
|
|
||||||
"nova:owner" = var.owner_id
|
|
||||||
"nova:contract" = var.consumer_repo
|
|
||||||
"nova:environment" = "dev"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# P20 (REQ-184): inline policy granting the consumer's deploy role the
|
|
||||||
# right to invoke the platform Lambda's Function URL, scoped via ABAC
|
|
||||||
# (aws:PrincipalTag/nova:owner == var.owner_id). The platform Lambda's
|
|
||||||
# resource-based policy + the consumer_invoke_policy.json template
|
|
||||||
# enforce the ABAC scope at the Lambda side; this policy grants the
|
|
||||||
# invoke permission on the consumer side.
|
|
||||||
resource "aws_iam_role_policy" "consumer_invoke" {
|
|
||||||
name = "nova-consumer-invoke"
|
|
||||||
role = aws_iam_role.consumer_deploy.id
|
|
||||||
|
|
||||||
policy = jsonencode({
|
|
||||||
Version = "2012-10-17"
|
|
||||||
Statement = [
|
|
||||||
{
|
|
||||||
Effect = "Allow"
|
|
||||||
Action = [
|
|
||||||
"lambda:InvokeFunctionUrl",
|
|
||||||
]
|
|
||||||
Resource = [
|
|
||||||
# The platform Lambda ARN (cross-account). The account_id is
|
|
||||||
# the platform account, not the consumer account. For offline-
|
|
||||||
# proven mode, a placeholder ARN is used.
|
|
||||||
"arn:aws:lambda:${var.region}:000000000000:function:nova-contract-ingestor"
|
|
||||||
]
|
|
||||||
Condition = {
|
|
||||||
StringEquals = {
|
|
||||||
"aws:PrincipalTag/nova:owner" = var.owner_id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
output "consumer_deploy_role_arn" {
|
|
||||||
description = "The ARN of the consumer deploy role."
|
|
||||||
value = aws_iam_role.consumer_deploy.arn
|
|
||||||
}
|
|
||||||
|
|
||||||
output "consumer_deploy_role_name" {
|
|
||||||
description = "The name of the consumer deploy role."
|
|
||||||
value = aws_iam_role.consumer_deploy.name
|
|
||||||
}
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
"""Unit tests for terraform/onboarding (P20, REQ-184)."""
|
|
||||||
|
|
||||||
import subprocess
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
import pytest
|
|
||||||
|
|
||||||
ROOT = Path(__file__).resolve().parent.parent
|
|
||||||
ONBOARDING_DIR = ROOT / "terraform" / "onboarding"
|
|
||||||
|
|
||||||
|
|
||||||
def test_onboarding_terraform_dir_exists():
|
|
||||||
"""P20 (REQ-184): terraform/onboarding/ exists with main.tf + README."""
|
|
||||||
assert ONBOARDING_DIR.is_dir()
|
|
||||||
assert (ONBOARDING_DIR / "main.tf").is_file()
|
|
||||||
assert (ONBOARDING_DIR / "README.md").is_file()
|
|
||||||
|
|
||||||
|
|
||||||
def test_onboarding_terraform_validates():
|
|
||||||
"""P20 (REQ-184): terraform validate passes for the onboarding module
|
|
||||||
(offline-proven, D-114). Skipped if terraform is not installed."""
|
|
||||||
if not subprocess.call(["which", "terraform"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) == 0:
|
|
||||||
pytest.skip("terraform not installed")
|
|
||||||
rc = subprocess.call(
|
|
||||||
["terraform", "validate"],
|
|
||||||
cwd=str(ONBOARDING_DIR),
|
|
||||||
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL,
|
|
||||||
)
|
|
||||||
assert rc == 0, "terraform validate failed for terraform/onboarding/"
|
|
||||||
|
|
||||||
|
|
||||||
def test_onboarding_main_tf_has_nova_tags():
|
|
||||||
"""P20 (REQ-184): the deploy role is tagged with nova:owner + nova:contract."""
|
|
||||||
main_tf = (ONBOARDING_DIR / "main.tf").read_text()
|
|
||||||
assert '"nova:owner"' in main_tf
|
|
||||||
assert '"nova:contract"' in main_tf
|
|
||||||
assert "aws_iam_role" in main_tf
|
|
||||||
assert "lambda:InvokeFunctionUrl" in main_tf
|
|
||||||
Reference in New Issue
Block a user