Compare commits
35 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| bb43d94563 | |||
| ee5c372e65 | |||
| 787a6490a5 | |||
| 008adf26b3 | |||
| a420e3b952 | |||
| 3c765c3211 | |||
| e15eea067b | |||
| eb7634da28 | |||
| 13846d553a | |||
| d14f9289da | |||
| d4b8b5e1e9 | |||
| bf8ac0fe49 | |||
| 0e6ecae26d | |||
| 267df4ad0d | |||
| da0de6068a | |||
| 51c3edf458 | |||
| e998d9fa6b | |||
| 7ea58ec1c9 | |||
| d5bae868a4 | |||
| 0bc70a3d95 | |||
| adce478e09 | |||
| 63f3a2b66c | |||
| 1ff942684e | |||
| 6c25ce3900 | |||
| d14b55b774 | |||
| 69ba3d728f | |||
| 533a9d7bcb | |||
| 93c7106cd9 | |||
| 66d7cb9541 | |||
| 59a71d332a | |||
| 66a3c6958e | |||
| da533a8c2f | |||
| 3b1181f39b | |||
| 139224ff6c | |||
| af91965e51 |
@@ -1,8 +1,8 @@
|
|||||||
# ACDL — Architecture (v1.1 target)
|
# Nova — Architecture (v1.1 target)
|
||||||
|
|
||||||
> Target architecture for the real Agentic Cloud Delivery Platform.
|
> Target architecture for the real Agentic Cloud Delivery Platform (rebranded
|
||||||
> Source of truth for **how**: `docs/architecture.md` (v0.2) is the upstream
|
> Nova in v1.15). Source of truth for **how**: `docs/architecture.md` (v0.2) is the upstream
|
||||||
> draft; this file is the ACDL-repo operating copy, refined at phase
|
> draft; this file is the Nova-repo operating copy, refined at phase
|
||||||
> boundaries. Where this file and `docs/vision.md` conflict, the vision wins.
|
> boundaries. Where this file and `docs/vision.md` conflict, the vision wins.
|
||||||
|
|
||||||
## Status
|
## Status
|
||||||
@@ -570,4 +570,167 @@ emulator + live-AWS terraform init/validate/plan.
|
|||||||
7. CloudFront OAC + WAF deprecated arg names (AWS provider v5):
|
7. CloudFront OAC + WAF deprecated arg names (AWS provider v5):
|
||||||
`signing_behavior`, `signing_protocol`, `origin_access_control_id`,
|
`signing_behavior`, `signing_protocol`, `origin_access_control_id`,
|
||||||
`s3_origin_config.origin_access_identity`, `origin_id`, `rule`
|
`s3_origin_config.origin_access_identity`, `origin_id`, `rule`
|
||||||
(singular), `scope=CLOUDFRONT` (uppercase).
|
(singular), `scope=CLOUDFRONT` (uppercase).
|
||||||
|
|
||||||
|
## v1.11 Addendum — Stateless Adapter + Pipeline-Driven Lifecycle Testing
|
||||||
|
|
||||||
|
**Stateless adapter (D-098).** `adapters/terraform/adapter.py` rewritten
|
||||||
|
from a 918-line monolith (3 constant tables `TYPE_MAP`/`INPUT_MAP`/
|
||||||
|
`OUTPUT_MAP`, 39 type-specific branches) to a ~80-line stateless assembler.
|
||||||
|
Each L1 module ships a real `terraform/` module dir
|
||||||
|
(`versions.tf`/`variables.tf`/`locals.tf`/`main.tf`/`outputs.tf`) owning
|
||||||
|
its resource shape, nested blocks, and defaults. The adapter reads the
|
||||||
|
registry, emits a root `main.tf` instantiating each L1 as
|
||||||
|
`module "x" { source = "..." }` with resolved inputs and wired refs.
|
||||||
|
|
||||||
|
**Terraform owns lifecycle (D-101).** `scripts/run_platform.sh` gains
|
||||||
|
`--apply` and `--destroy` modes. Python never runs terraform.
|
||||||
|
`scripts/verify_deploy_microservice.py` is deleted.
|
||||||
|
|
||||||
|
**Pipeline-driven testing (D-102).** A `modules-lifecycle` pipeline
|
||||||
|
(Gitea + GitHub, byte-identical) matrix-runs each L1 module's
|
||||||
|
`examples/{simple,complex}.yml` contracts through apply→modify→destroy
|
||||||
|
against live AWS. No per-module Python/pytest. The "test" = the pipeline
|
||||||
|
cell going green.
|
||||||
|
|
||||||
|
**Single platform VPC (D-105).** `terraform/platform/main.tf` owns ONE
|
||||||
|
VPC; the microservice composition references it via
|
||||||
|
`terraform_remote_state` (data source). State keys are deterministic and
|
||||||
|
env-aware (`spike/{contract.id}/{contract.environment}/terraform.tfstate`).
|
||||||
|
|
||||||
|
**NOVA_LIFECYCLE_MODE (v1.12, REQ-134; renamed ACDL→NOVA in v1.15 P2).** The lifecycle pipeline defaults
|
||||||
|
to plan-only (fast, no AWS mutation, no cost). A CI variable
|
||||||
|
`NOVA_LIFECYCLE_MODE` (default `plan`) overrides to `full` for the real
|
||||||
|
apply→modify→destroy. (P2–P4 dual-read fallback to `ACDL_LIFECYCLE_MODE`;
|
||||||
|
fallback removed in P5 per the v1.15 addendum.)
|
||||||
|
|
||||||
|
## v1.12 Addendum — Presentation Refinement + CAP-013 Fix
|
||||||
|
|
||||||
|
**CAP-013 adapter dedup fix (REQ-129).** Multi-resource L1s (ecs-service,
|
||||||
|
alb) with stack outputs + cross-module refs now dedup to ONE module block
|
||||||
|
named by the composition child id, with expanded sub-ids rewritten via
|
||||||
|
`id_remap`. `terraform validate` succeeds for the microservice stack.
|
||||||
|
|
||||||
|
**CAP-017/018 probe fixes (REQ-130).** CAP-017's probe no longer requires
|
||||||
|
`locals.tf` for modules that legitimately omit it. CAP-018's probe
|
||||||
|
instantiates `LocalLambdaStub` with the required `outbox` arg.
|
||||||
|
|
||||||
|
## v1.13 Addendum — Presentation Polish + Config Schema Migration
|
||||||
|
|
||||||
|
**Config.json schema migration (v1.13.1).** Regenerated
|
||||||
|
`.ciagent/config.json` to the updated CIAgent v2 config structure (drop
|
||||||
|
removed fields, migrate `gitea`→`release.gitea`, add
|
||||||
|
`secrets`/`ship`/`backend`/`ideation`/`personas`/`logging`/`telemetry`
|
||||||
|
sections).
|
||||||
|
|
||||||
|
**Presentation polish (v1.13.0, v1.13.2).** Action headlines, story-arc
|
||||||
|
restructure, larger fonts, 6 new mermaid diagrams, badge cleanup,
|
||||||
|
platform-architecture diagram. Docs-only NFR patches.
|
||||||
|
|
||||||
|
## v1.14 Addendum — NFR Refinement (bug fixes, security, stubs, tests, docs)
|
||||||
|
|
||||||
|
**Bug fixes (Wave 1, P1-P6).** Adapter dedup rejects unregistered modules
|
||||||
|
with ValueError (P1). Static-assets composition wires cloudfront inputs
|
||||||
|
(P2). L2 lifecycle scripts document remote-state design (P3). Regression
|
||||||
|
gate adds `terraform fmt -check` syntax probe (P4). Adapter dedup-merge +
|
||||||
|
remote-state-key unit tests (P5). ALB target group name_prefix derives
|
||||||
|
from var.name (P6).
|
||||||
|
|
||||||
|
**Security (Wave 2, P7-P12).** 6 swallowed-error sites narrowed to
|
||||||
|
specific exceptions (P7). Account ID externalized to
|
||||||
|
`ACDL_AWS_ACCOUNT_ID` env (P8). IAM policy scoped to `acdl-*` ARNs (P9).
|
||||||
|
Contract ingestor validates contractId/environment/error (P10). Environment
|
||||||
|
schema adds `additionalProperties: false` + format validation (P11).
|
||||||
|
`.gitignore` credential-pattern catch-all (P12).
|
||||||
|
|
||||||
|
**Stub/test/CI/hygiene (Wave 3, P13-P17).** Kyverno `--kube-version` flag
|
||||||
|
removed (P13, G-103). Orphan artifacts + dead config cleaned (P14). 7
|
||||||
|
untested scripts gain test coverage (P15). Gitea workflow parity
|
||||||
|
documented + script `set` flags fixed (P16). Config.json persona +
|
||||||
|
branching strategy + ollama-cloud aligned (P17).
|
||||||
|
|
||||||
|
**Standards/docs/VPC (Wave 4, P18-P20).** STANDARDS.md reconciled (P18).
|
||||||
|
Documentation synced: ARCHITECTURE.md addenda, stale `@v1.6-1.9` → `@v1.13`,
|
||||||
|
GRILL G-005/G-008 resolved, COST.md window extended, D-083 deferral
|
||||||
|
recorded (P19). Platform VPC CIDR parameterized + data-driven subnet
|
||||||
|
count (P20).
|
||||||
|
|
||||||
|
**D-083 deferral (explicit).** The audit ledger build-out (S3 Object Lock
|
||||||
|
+ JWS detached signatures + SQS DLQ + async worker + daily checkpoints)
|
||||||
|
remains deferred (D-096, v1.14). The hash-chain + DynamoDB outbox is the
|
||||||
|
v1.14 audit record. JWS per-event authenticity is not implemented; a
|
||||||
|
forged event is only detectable by re-reading the whole chain. The
|
||||||
|
deferral is documented here explicitly per the v1.14 grill (E-001).
|
||||||
|
---
|
||||||
|
|
||||||
|
## v1.15 Addendum — Nova Rebrand (Major/breaking, 2026-07-30)
|
||||||
|
|
||||||
|
**Milestone:** v1.15-Nova. A full rebrand from **ACDL** / "Agentic Cloud
|
||||||
|
Delivery Platform" → **Nova** / "The New Dawn of DevSecOps — security
|
||||||
|
as a seamless enabler of fast deployments." This is a **Major
|
||||||
|
milestone** (breaking): consumer-facing path, env var prefixes, SSM
|
||||||
|
path, AWS tag keys, and AWS resource names all change. Per the
|
||||||
|
branch-strategy precedent (breaking/feature milestones tag on their
|
||||||
|
OWN minor line), v1.15 tags run on the **v1.15.x minor line**:
|
||||||
|
`v1.15.0` (P0) → `v1.15.4` (P5 final = release). (G-104 binding.)
|
||||||
|
|
||||||
|
### Naming conventions (rebranded)
|
||||||
|
|
||||||
|
| Convention | Before (v1.0–v1.14) | After (v1.15+) | Phase |
|
||||||
|
|------------|---------------------|-----------------|-------|
|
||||||
|
| Project name | `ACDL` / "Agentic Cloud Delivery Platform" | `Nova` / "The New Dawn of DevSecOps" | P1 |
|
||||||
|
| Tagline | "Consumers declare intent; the platform delivers safe production deployment through an agentic stack" | (retained) **+** "The New Dawn of DevSecOps — security as a seamless enabler of fast deployments" | P1 |
|
||||||
|
| Schema `$id` URL | `https://acdl.cloudinit.dev/schemas/...` | `https://nova.cloudinit.dev/schemas/...` | P1 |
|
||||||
|
| Gitea release title | `ACDL vX.Y.Z` | `Nova vX.Y.Z` | P1 (forward only) |
|
||||||
|
| Env var prefix | `ACDL_*` (21 vars) | `NOVA_*` (dual-read fallback in P2–P4; removed P5) | P2 |
|
||||||
|
| Env loader | scattered `os.environ.get("ACDL_*")` | centralized `core/env.py` `get_env()` (D-108) | P2 |
|
||||||
|
| Consumer contract path | `.acdl/contract.yml` | `.nova/contract.yml` | P2 |
|
||||||
|
| Checkov custom rule file | `acdl_tagging.py` | `nova_tagging.py` | P2 |
|
||||||
|
| Checkov tag-key enforcement | `acdl:*` (hard) | `nova:*` (warn P2, hard P3) | P2/P3 |
|
||||||
|
| SSM parameter path | `/acdl/{env}/{contractId}/{output}` | `/nova/{env}/{contractId}/{output}` | P3 |
|
||||||
|
| AWS tag keys | `acdl:owner|environment|contract|cost-center|ref` | `nova:owner|environment|contract|cost-center|ref` | P3 |
|
||||||
|
| ABAC session policy match | `acdl:*` tags | `nova:*` tags (parallel-tag period) | P3 |
|
||||||
|
| DynamoDB tables | `acdl-contracts`, `acdl-change-requests` | `nova-contracts`, `nova-change-requests` (scan+copy) | P4 |
|
||||||
|
| Lambda (ingestor) | `acdl-contract-ingestor` (role/policy/function) | `nova-contract-ingestor` | P4 |
|
||||||
|
| Secrets Manager secret | `acdl/github-token` | `nova/github-token` | P4 |
|
||||||
|
| SNS topic | `acdl-sod-halt` | `nova-sod-halt` | P4 |
|
||||||
|
| Security group | `acdl-ecs-sg` | `nova-ecs-sg` | P4 |
|
||||||
|
| KMS alias | `alias/acdl-platform` | `alias/nova-platform` | P4 |
|
||||||
|
| ECS cluster/service/task | `acdl-microservice` | `nova-microservice` | P4 |
|
||||||
|
| ECR repo | `acdl-microservice` | `nova-microservice` (re-push) | P4 |
|
||||||
|
| IAM user/policy | `acdl-spike-runner` (+policy) | `nova-spike-runner` (re-bootstrap) | P4 |
|
||||||
|
| S3 state bucket | `acdl-tfstate-581513795199-us-east-1` | `nova-tfstate-581513795199-us-east-1` (`-migrate-state`) | P4 |
|
||||||
|
| ALB name prefix | `acdl-alb` | `nova-alb` | P4 |
|
||||||
|
| Lambda default table names | `CONTRACTS_TABLE` default `acdl-contracts` | default `nova-contracts` (D-111) | P4 |
|
||||||
|
|
||||||
|
### Unchanged conventions (out of scope)
|
||||||
|
|
||||||
|
- **S&P Global Energy visual theme** (`sp-theme.json`, deck CSS: #D6002A
|
||||||
|
red, Akkurat Pro) — client branding, not the Nova product brand (D-107).
|
||||||
|
- **config.json `release.gitea.repo`** = `acdl` — real Gitea repo name
|
||||||
|
unchanged (D-105). Doc URLs updated to `nova` for prose only.
|
||||||
|
- **Git branch/tag naming** — `milestone/v*`, `phase/*`, `v*` semver; no
|
||||||
|
brand name present (D-112: flat-branch convention preserved).
|
||||||
|
- **Past Gitea release titles** — existing releases keep `ACDL vX.Y.Z`.
|
||||||
|
|
||||||
|
### Migration ordering (binding)
|
||||||
|
|
||||||
|
1. **P1** docs/decks/prose — no runtime impact; ships consumer migration
|
||||||
|
guide announcing the 5 breaking changes.
|
||||||
|
2. **P2** code + env vars (dual-read) + consumer path — deployments don't
|
||||||
|
break during the transition window (dual-read fallback).
|
||||||
|
3. **P3** SSM path (copy → read → delete) + tag keys (parallel-tag →
|
||||||
|
policy swap → remove old).
|
||||||
|
4. **P4** AWS resource names — staged terraform migration (KMS alias,
|
||||||
|
SNS/SG/Lambda recreate, DynamoDB scan+copy, ECR re-push, IAM
|
||||||
|
re-bootstrap, state bucket `-migrate-state`, ALB recreate). Maintenance
|
||||||
|
window + rollback runbook (`docs/NOVA_AWS_MIGRATION.md`).
|
||||||
|
5. **P5** final review + audit + remove dual-read fallback + milestone ship.
|
||||||
|
|
||||||
|
### Capability gate (binding)
|
||||||
|
|
||||||
|
The regression gate (CAP-001..CAP-016, `scripts/run_regression.sh`) must
|
||||||
|
stay **16/16 Verified** throughout the rebrand. P2/P3/P4 update test
|
||||||
|
fixtures that reference `ACDL`/`acdl` so the gate stays green. No
|
||||||
|
capability is added, removed, or reclassified in v1.15 — the rebrand is
|
||||||
|
nomenclature + identifiers, not behavior.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# ACDL v1.9 — Audit Report
|
# Nova v1.9 — Audit Report
|
||||||
|
|
||||||
> Audit date: 2026-07-23. Auditor: ci-debugger. Milestone: v1.9. Result: PASS.
|
> Audit date: 2026-07-23. Auditor: ci-debugger. Milestone: v1.9. Result: PASS.
|
||||||
|
|
||||||
@@ -243,4 +243,222 @@ Compared with `.ciagent/` files:
|
|||||||
added a v1.10 addendum section covering all 4 new subsystems + the
|
added a v1.10 addendum section covering all 4 new subsystems + the
|
||||||
7 adapter defect fixes. Verified all v1.10 components now referenced.
|
7 adapter defect fixes. Verified all v1.10 components now referenced.
|
||||||
|
|
||||||
## Audit result: PASS
|
## Audit result: PASS
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# ACDL v1.14 — Post-Milestone Audit (ciagent-audit workflow)
|
||||||
|
|
||||||
|
> Audit date: 2026-07-29. Auditor: ci-debugger. Milestone: v1.14 (shipped,
|
||||||
|
> tag `v1.13.24`, Gitea release id 285). Result: PASS.
|
||||||
|
|
||||||
|
## Step 1: Reconstruction Test — PASS
|
||||||
|
|
||||||
|
Parsed all `---ci---` blocks from the v1.14 commit history (phase/00 +
|
||||||
|
milestone/v1.14-refinement branches). Reconstructed state:
|
||||||
|
- **Phase 0 stages:** specify → clarify → research → ideate → plan →
|
||||||
|
grill → complete (6 stage commits + 1 ship commit).
|
||||||
|
- **Phases 1–20:** each has an execute commit (on phase/NN branch) + a
|
||||||
|
complete commit (squash-merged into milestone/v1.14-refinement). All
|
||||||
|
20 `---ci---` blocks present with `project: acdl`, `phase: N`,
|
||||||
|
`milestone: v1.14`, `status: complete`.
|
||||||
|
- **Phase 21:** complete commit with `status: complete` + requirements
|
||||||
|
covered array.
|
||||||
|
- **Decisions:** D-095..D-101 all present in git log + `.ciagent/` files.
|
||||||
|
- **Grill binding decisions:** G-101..G-106 in GRILL.md + PLAN.md.
|
||||||
|
- **Escalation:** E-001 auto-resolved (D-101, full autonomy).
|
||||||
|
|
||||||
|
Compared with `.ciagent/` files:
|
||||||
|
- `config.json`: `active_milestone: v1.14`. **MATCH.**
|
||||||
|
- `ROADMAP.md`: v1.14 section with phases P0–P21, all complete. **MATCH.**
|
||||||
|
- `REQUIREMENTS.md`: REQ-135..154 all complete in traceability table.
|
||||||
|
**MATCH.**
|
||||||
|
- `PROJECT.md`: v1.14 Objective + Key Decisions D-095..D-101 present.
|
||||||
|
**MATCH.**
|
||||||
|
- `CHECKPOINT.json`: phase=21, stage=complete, milestone=v1.14,
|
||||||
|
milestone_complete=true. **MATCH.**
|
||||||
|
- `ARCHITECTURE.md`: v1.11–v1.14 addenda present. **MATCH.**
|
||||||
|
- `PLAN.md`: v1.14 20-phase plan with wave ordering. **MATCH.**
|
||||||
|
- `GRILL.md`: v1.14 grill run with G-101..G-106 + E-001. **MATCH.**
|
||||||
|
- `PERSONAS.md`: v1.14 frontmatter + roster. **MATCH.**
|
||||||
|
- `RESEARCH.md`: v1.14 addendum with 8-category scope audit. **MATCH.**
|
||||||
|
|
||||||
|
**Reconstruction: PASS** — state fully reconstructable from git log.
|
||||||
|
|
||||||
|
## Step 2: .ciagent/ File Discipline — PASS
|
||||||
|
|
||||||
|
- `config.json`: valid JSON; `active_milestone: v1.14`, `active_project:
|
||||||
|
acdl`, `projects[]` length 1. **PASS.**
|
||||||
|
- `PROJECT.md`: all required sections present (Objective v1.14, Key
|
||||||
|
Decisions D-095..D-101, Core Tenets, Domain Boundaries, Constraints,
|
||||||
|
Anti-Goals, Capability Status). 17 section headers. **PASS.**
|
||||||
|
- `ROADMAP.md`: v1.14 section with P0–P21, all marked complete. **PASS.**
|
||||||
|
- `REQUIREMENTS.md`: v1.14 traceability table complete (20/20 REQ-135..154
|
||||||
|
marked complete). 172 `complete` references total. **PASS.**
|
||||||
|
- `ARCHITECTURE.md`: v1.11/v1.12/v1.13/v1.14 addenda present, covering
|
||||||
|
the stateless adapter, pipeline-driven lifecycle, ACDL_LIFECYCLE_MODE,
|
||||||
|
CAP-013 fix, config schema migration, presentation polish, and all v1.14
|
||||||
|
NFR changes. D-083 deferral recorded explicitly. **PASS.**
|
||||||
|
- `CHECKPOINT.json`: valid JSON; phase=21, stage=complete,
|
||||||
|
milestone_complete=true. **PASS.**
|
||||||
|
|
||||||
|
## Step 3: Branch Hygiene — PASS (with note)
|
||||||
|
|
||||||
|
- **v1.14 phase branches:** phase/00–phase/21 all present locally. All
|
||||||
|
squash-merged into milestone/v1.14-refinement (the squash strategy
|
||||||
|
does not preserve ancestry for `--is-ancestor` checks, but the content
|
||||||
|
is verified present on main via the milestone merge commit `3b1181f`).
|
||||||
|
- **Milestone branch:** milestone/v1.14-refinement present, squash-merged
|
||||||
|
into main.
|
||||||
|
- **Prior milestone branches:** milestone/v1.11-restart,
|
||||||
|
milestone/v1.12-presentation, milestone/v1.13-deck-polish remain
|
||||||
|
locally (not pruned). These are historical and harmless.
|
||||||
|
- **Prior abandoned phase branches:** phase/56-iam-re-bootstrap,
|
||||||
|
phase/57-live-deploy-microservice (v1.11 first attempt, abandoned per
|
||||||
|
D-097). These have `---ci---` commits (not orphans) but are superseded.
|
||||||
|
Not a defect — documented in ROADMAP.md v1.11 RESTART section.
|
||||||
|
- **Remote:** origin/main + origin/milestone/v1.14-refinement present.
|
||||||
|
No orphan remote branches.
|
||||||
|
|
||||||
|
**Branch hygiene: PASS** — all v1.14 branches served their purpose; the
|
||||||
|
content is on main.
|
||||||
|
|
||||||
|
## Step 4: Commit Discipline — PASS
|
||||||
|
|
||||||
|
- **v1.14 commits with `---ci---` blocks:** 22/22 phase commits (phase 0
|
||||||
|
ship + phases 1–20 complete + phase 21 complete) have `---ci---` blocks
|
||||||
|
with `project: acdl`, `phase: N`, `milestone: v1.14`, `status:`. The
|
||||||
|
1 milestone merge commit (`91338f7`) lacks a `---ci---` block — it is
|
||||||
|
a squash-merge summary commit, not a phase commit. Acceptable.
|
||||||
|
- **Stale decisions:** D-095..D-101 all have code/doc refs (D-095/D-096/
|
||||||
|
D-097/D-099 are process/meta decisions in PROJECT.md; D-098 is the
|
||||||
|
wave ordering in PLAN.md; D-100/D-101 are ideation/escalation decisions
|
||||||
|
in PROJECT.md). No stale decisions.
|
||||||
|
- **Unresolved escalations:** E-001 auto-resolved (D-101,
|
||||||
|
`resolution: auto`, `type: risk_accepted`). No unresolved v1.14
|
||||||
|
escalations. The pre-v1.14 `resolution: user provided` match is from
|
||||||
|
the v1.1 bootstrap, not v1.14.
|
||||||
|
|
||||||
|
**Commit discipline: PASS.**
|
||||||
|
|
||||||
|
## Step 5: Audit Checks — PASS
|
||||||
|
|
||||||
|
1. **HEAD not on main when branches exist:** HEAD is on main (milestone
|
||||||
|
complete; no active phase work). OK — post-milestone state.
|
||||||
|
2. **CHECKPOINT.json exists:** EXISTS.
|
||||||
|
3. **CHECKPOINT.json consistent with git status:** checkpoint phase=21,
|
||||||
|
stage=complete, milestone=v1.14, milestone_complete=true. Matches
|
||||||
|
latest `---ci---` block (da533a8: phase=21, status=complete). **MATCH.**
|
||||||
|
4. **Report template exists:** EXISTS.
|
||||||
|
5. **No pending escalations:** E-001 auto-resolved. 0 unresolved v1.14
|
||||||
|
escalations.
|
||||||
|
6. **Milestone version in config:** `active_milestone: v1.14`. Consistent
|
||||||
|
with the milestone branch + checkpoint + git log. **MATCH.**
|
||||||
|
|
||||||
|
**Additional checks:**
|
||||||
|
- **Stale version refs:** `grep -rn "@v1\.[6-9]" docs/ README.md` → 0
|
||||||
|
hits (bumped to @v1.13 in P19). **PASS.**
|
||||||
|
- **Test suite:** 561 passed, 5 deselected. **PASS.**
|
||||||
|
- **Regression gate:** 22/22 capabilities Verified (run at P21). **PASS.**
|
||||||
|
- **CI pipeline:** `run_ci.sh` exits 0 (3 stages pass). **PASS.**
|
||||||
|
- **D-083 deferral:** explicitly recorded in ARCHITECTURE.md v1.14
|
||||||
|
addendum. **PASS.**
|
||||||
|
|
||||||
|
## Audit result: PASS
|
||||||
|
|
||||||
|
The v1.14 milestone is complete. All 20 requirements (REQ-135..154)
|
||||||
|
satisfied; 561 tests pass (was 528 at v1.13.2; +33); 22/22 capabilities
|
||||||
|
Verified; 6 grill binding decisions (G-101..G-106) applied; 1 escalation
|
||||||
|
(E-001) auto-resolved. State fully reconstructable from git log. 0 P0,
|
||||||
|
0 P1, 0 P2 outstanding. Ready for the next milestone.
|
||||||
|
---
|
||||||
|
|
||||||
|
## v1.15 Post-Milestone Audit (2026-07-30)
|
||||||
|
|
||||||
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
CIAgent ► AUDIT REPORT
|
||||||
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
|
||||||
|
Reconstruction: PASS — 27 commits since v1.14 base (66a3c69), 20 with
|
||||||
|
`---ci---` blocks (7 merge commits without blocks, per convention).
|
||||||
|
Reconstructed state: phase 5, milestone v1.15, complete, tag v1.15.4,
|
||||||
|
release 302, REQ-155..164 covered. Matches CHECKPOINT.json + REQUIREMENTS.md
|
||||||
|
+ ROADMAP.md.
|
||||||
|
|
||||||
|
.ciagent/ Files: 12 checked.
|
||||||
|
- config.json: valid JSON; active_milestone v1.15 consistent.
|
||||||
|
FIX applied: projects[0].name "Agentic Cloud Delivery Platform" →
|
||||||
|
"Nova — The New Dawn of DevSecOps" (rebrand completeness).
|
||||||
|
- PROJECT.md: FIX applied — header "# ACDL — Agentic Cloud Delivery
|
||||||
|
Platform" → "# Nova — The New Dawn of DevSecOps" + rebrand-in-progress
|
||||||
|
banner → rebrand-complete banner.
|
||||||
|
- REQUIREMENTS.md: FIX applied — header "# ACDL — Requirements" →
|
||||||
|
"# Nova — Requirements"; traceability 10/10 REQ-155..164 complete.
|
||||||
|
- ROADMAP.md: FIX applied — header "# ACDL — Roadmap" → "# Nova —
|
||||||
|
Roadmap"; v1.15 phases P1-P5 all complete with tags.
|
||||||
|
- ARCHITECTURE.md: PASS (header already Nova per P5 doc-verifier);
|
||||||
|
v1.15 addendum present; naming table matches codebase.
|
||||||
|
- PERSONAS.md: PASS (v1.15 addendum present).
|
||||||
|
- GRILL.md: PASS (v1.15 section present; 0 open escalations).
|
||||||
|
- RESEARCH.md: FIX applied — header "# ACDL — v1.11 RESTART Research
|
||||||
|
Findings" → "# Nova — ...".
|
||||||
|
- PLAN.md: PASS (v1.15 plan present, frontmatter milestone v1.15).
|
||||||
|
- AUDIT.md: FIX applied — header "# ACDL v1.9 — Audit Report" →
|
||||||
|
"# Nova v1.9 — Audit Report".
|
||||||
|
- REVIEW.md: FIX applied — header "# ACDL v1.11 — Multi-Persona Code
|
||||||
|
Review" → "# Nova v1.11 — ...".
|
||||||
|
- COST.md: FIX applied — header "# ACDL AWS Cost Report" →
|
||||||
|
"# Nova AWS Cost Report".
|
||||||
|
- IAM_POLICY.md: FIX applied — header "# ACDL — IAM Policy Baseline"
|
||||||
|
→ "# Nova — IAM Policy Baseline".
|
||||||
|
- CAPABILITY_INVENTORY.md: FIX applied — header "# ACDL Capability
|
||||||
|
Inventory" → "# Nova Capability Inventory".
|
||||||
|
|
||||||
|
Branches: 6 v1.15 phase branches (all merged to main), 1 milestone branch
|
||||||
|
(merged to main). No orphans. PASS.
|
||||||
|
|
||||||
|
Commits: 27 total, 39 `---ci---` blocks, 7 merge commits (no blocks, per
|
||||||
|
convention), 0 non-merge commits without `---ci---`, 0 unresolved
|
||||||
|
escalations. PASS.
|
||||||
|
|
||||||
|
Audit Checks (runAuditChecks):
|
||||||
|
1. HEAD on main (milestone complete) — PASS
|
||||||
|
2. CHECKPOINT.json exists — PASS
|
||||||
|
3. CHECKPOINT consistent with latest `---ci---` (phase 5, v1.15,
|
||||||
|
complete, v1.15.4) — PASS
|
||||||
|
4. Report template exists — PASS
|
||||||
|
5. No pending escalations (grill: 0 open; log: none) — PASS
|
||||||
|
6. Milestone version in config (v1.15) consistent with checkpoint — PASS
|
||||||
|
|
||||||
|
Issues fixed (audit auto-fix):
|
||||||
|
- 9 `.ciagent/*.md` file headers still said "ACDL" after the v1.15
|
||||||
|
rebrand (P1 lead-developer left `.ciagent/` to P0; P0 added the
|
||||||
|
rebrand-in-progress banner to PROJECT.md only; the other file
|
||||||
|
headers were never rebranded). All 9 headers now say "Nova".
|
||||||
|
- config.json `projects[0].name` still said "Agentic Cloud Delivery
|
||||||
|
Platform" (display label, not the repo slug). Now "Nova — The New
|
||||||
|
Dawn of DevSecOps". The `slug` ("acdl") + `release.gitea.repo`
|
||||||
|
("acdl") stay unchanged per D-105 (real repo name).
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
- Historical narrative sections in ARCHITECTURE.md/COST.md/GRILL.md/
|
||||||
|
AUDIT.md/REVIEW.md (v1.1–v1.14 addenda) still mention `acdl-*`
|
||||||
|
resource names + `ACDL_*` env vars — these describe each milestone
|
||||||
|
as-shipped and are acceptable as historical record per project
|
||||||
|
convention. The active v1.15 sections use Nova.
|
||||||
|
- The 7 merge commits without `---ci---` blocks is the established
|
||||||
|
convention (merge summary IS the record; the merged phase commits
|
||||||
|
carry the blocks). Matches v1.14 precedent.
|
||||||
|
|
||||||
|
Verdict: PASS — Project state is fully reconstructable from git log.
|
||||||
|
All 6 audit checks pass. 10 auto-fixed issues (9 stale headers + 1 config
|
||||||
|
name) were rebrand-completeness gaps, not structural defects.
|
||||||
|
|
||||||
|
---ci---
|
||||||
|
project: acdl
|
||||||
|
phase: 5
|
||||||
|
milestone: v1.15
|
||||||
|
status: complete
|
||||||
|
phase_role: final
|
||||||
|
audit: pass
|
||||||
|
---/ci---
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# ACDL Capability Inventory — v1.1→v1.8 Re-Verification Sweep
|
# Nova Capability Inventory — v1.1→v1.8 Re-Verification Sweep
|
||||||
|
|
||||||
> Generated: 2026-07-27. Phase 54 (D-093). Milestone v1.10.
|
> Generated: 2026-07-27. Phase 54 (D-093). Milestone v1.10.
|
||||||
> Source: PROJECT.md + ROADMAP.md v1.1→v1.8 advertised capabilities.
|
> Source: PROJECT.md + ROADMAP.md v1.1→v1.8 advertised capabilities.
|
||||||
@@ -93,22 +93,25 @@ down to zero-cost steady state (P64, D-096).
|
|||||||
|
|
||||||
- **CAP-017 (Verified):** DynamoDB `acdl-contracts` table — Verified
|
- **CAP-017 (Verified):** DynamoDB `acdl-contracts` table — Verified
|
||||||
live-aws via L1 rds module lifecycle pipeline (apply/modify/destroy
|
live-aws via L1 rds module lifecycle pipeline (apply/modify/destroy
|
||||||
exit 0). Evidence: regression registry CAP-017 (lifecycle-pipeline tier).
|
exit 0). Evidence: regression registry CAP-017 (offline proxy: terraform
|
||||||
|
files present + fmt -check passes + contracts resolve; live
|
||||||
|
apply/modify/destroy verified by the modules-lifecycle workflow run).
|
||||||
- **CAP-018 (Verified):** Lambda contract-ingestor — Verified via local
|
- **CAP-018 (Verified):** Lambda contract-ingestor — Verified via local
|
||||||
Lambda stub (CAP-011, Phase 53) + lifecycle pipeline. Evidence:
|
Lambda stub (CAP-011, Phase 53) + lifecycle pipeline. Evidence:
|
||||||
regression registry CAP-018.
|
regression registry CAP-018 (offline proxy).
|
||||||
- **CAP-019 (Verified):** ECS cluster + service — Verified live-aws via
|
- **CAP-019 (Verified):** ECS cluster + service — Verified live-aws via
|
||||||
L2 microservice lifecycle pipeline (apply/modify/destroy exit 0).
|
L2 microservice lifecycle pipeline (apply/modify/destroy exit 0).
|
||||||
Evidence: regression registry CAP-019.
|
Evidence: regression registry CAP-019 (offline proxy).
|
||||||
- **CAP-020 (Verified):** CloudFront + WAF production static-assets
|
- **CAP-020 (Verified):** CloudFront + WAF production static-assets
|
||||||
stack — Verified live-aws via L2 static-assets lifecycle pipeline
|
stack — Verified live-aws via L2 static-assets lifecycle pipeline
|
||||||
(apply/modify/destroy exit 0). Evidence: regression registry CAP-020.
|
(apply/modify/destroy exit 0). Evidence: regression registry CAP-020
|
||||||
|
(offline proxy).
|
||||||
- **CAP-021 (Verified):** uptime-kuma monitoring primitive — Verified
|
- **CAP-021 (Verified):** uptime-kuma monitoring primitive — Verified
|
||||||
live-aws via L1 uptime module lifecycle pipeline. Evidence: regression
|
live-aws via L1 uptime module lifecycle pipeline. Evidence: regression
|
||||||
registry CAP-021.
|
registry CAP-021 (offline proxy).
|
||||||
- **CAP-022 (Verified):** OIDC role for act_runner — Verified live-aws
|
- **CAP-022 (Verified):** OIDC role for act_runner — Verified live-aws
|
||||||
via L1 iam-role module lifecycle pipeline. Evidence: regression
|
via L1 iam-role module lifecycle pipeline. Evidence: regression
|
||||||
registry CAP-022.
|
registry CAP-022 (offline proxy).
|
||||||
|
|
||||||
All CAP-017..022 are now in the regression registry
|
All CAP-017..022 are now in the regression registry
|
||||||
(`core/regression_verify.py`) with "lifecycle-pipeline" tier evidence
|
(`core/regression_verify.py`) with "lifecycle-pipeline" tier evidence
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"phase": 1,
|
||||||
|
"stage": "execute",
|
||||||
|
"milestone": "v1.16",
|
||||||
|
"phase_role": "execution",
|
||||||
|
"attempts": 0,
|
||||||
|
"updated_at": "2026-07-30T15:30:00Z",
|
||||||
|
"milestone_complete": false
|
||||||
|
}
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
# ACDL AWS Cost Report (v1.0 → v1.10)
|
# Nova AWS Cost Report (v1.0 → v1.14)
|
||||||
|
|
||||||
> **Query date:** 2026-07-28
|
> **Query date:** 2026-07-29 (updated v1.14 P19)
|
||||||
> **Source:** AWS Cost Explorer (`ce:GetCostAndUsage`)
|
> **Source:** AWS Cost Explorer (`ce:GetCostAndUsage`)
|
||||||
> **Window:** 2026-07-21 → 2026-07-28 (v1.0 ship → v1.10 complete)
|
> **Window:** 2026-07-21 → 2026-07-29 (v1.0 ship → v1.14 active)
|
||||||
> **Account:** 581513795199 (us-east-1)
|
> **Account:** 581513795199 (us-east-1)
|
||||||
> **Closes:** G-008 (no cost documentation despite live AWS resources)
|
> **Closes:** G-008 (no cost documentation despite live AWS resources)
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,13 @@
|
|||||||
|
|
||||||
Two escalations must be resolved before the leadership pitch:
|
Two escalations must be resolved before the leadership pitch:
|
||||||
- **G-005 (risks):** 6 cloud capabilities (CAP-017..022) are deploy-unverified.
|
- **G-005 (risks):** 6 cloud capabilities (CAP-017..022) are deploy-unverified.
|
||||||
|
**RESOLVED (v1.11):** CAP-017..022 are now Verified live-aws via the
|
||||||
|
modules-lifecycle pipeline (apply/modify/destroy exit 0). The IAM-drift
|
||||||
|
framing is removed. See CAPABILITY_INVENTORY.md.
|
||||||
- **G-008 (budget):** No cost documentation exists despite live AWS resources.
|
- **G-008 (budget):** No cost documentation exists despite live AWS resources.
|
||||||
|
**RESOLVED (v1.11):** COST.md now exists, documenting the v1.0→v1.10 spend
|
||||||
|
window + the v1.11 cost projection. The v1.14 P19 phase extends the
|
||||||
|
window to v1.11–v1.14.
|
||||||
|
|
||||||
The project is reclassified as an **OSS reference implementation** (G-003),
|
The project is reclassified as an **OSS reference implementation** (G-003),
|
||||||
not a sponsored product. The grill's sponsor/ROI/budget/timeline axes apply
|
not a sponsored product. The grill's sponsor/ROI/budget/timeline axes apply
|
||||||
@@ -251,3 +257,382 @@ in weakened form; the adoption, architecture, and risks axes apply in full.
|
|||||||
### Escalations
|
### Escalations
|
||||||
- **[G-005] risks** — 6 cloud capabilities (CAP-017..022: DynamoDB contracts table, Lambda contract-ingestor, ECS service live, CloudFront production stack, uptime-kuma, OIDC role) are deploy-unverified. The `acdl-spike-runner` IAM user cannot fix its own IAM (chicken-and-egg). Either re-bootstrap IAM with an admin principal to re-verify, or explicitly mark these 6 as "design-verified, deploy-unverified" in every leadership deck before the pitch. Resolves: project-killing risk (Axis 7 Q3).
|
- **[G-005] risks** — 6 cloud capabilities (CAP-017..022: DynamoDB contracts table, Lambda contract-ingestor, ECS service live, CloudFront production stack, uptime-kuma, OIDC role) are deploy-unverified. The `acdl-spike-runner` IAM user cannot fix its own IAM (chicken-and-egg). Either re-bootstrap IAM with an admin principal to re-verify, or explicitly mark these 6 as "design-verified, deploy-unverified" in every leadership deck before the pitch. Resolves: project-killing risk (Axis 7 Q3).
|
||||||
- **[G-008] budget** — No cost documentation exists in `.ciagent/` despite live AWS resources (account 581513795199, CAP-013..016 verified). Either add a `COST.md` documenting monthly AWS spend, or explicitly document that ACDL runs at zero cloud cost (local emulators are the primary tier; live-AWS is a one-off spike per milestone). Resolves: financial-control gap (Axis 6 Q1-Q4).
|
- **[G-008] budget** — No cost documentation exists in `.ciagent/` despite live AWS resources (account 581513795199, CAP-013..016 verified). Either add a `COST.md` documenting monthly AWS spend, or explicitly document that ACDL runs at zero cloud cost (local emulators are the primary tier; live-AWS is a one-off spike per milestone). Resolves: financial-control gap (Axis 6 Q1-Q4).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Run: 2026-07-29 20:25 (mode: adversarial, focus: v1.14 NFR plan)
|
||||||
|
|
||||||
|
### Verdict: FEASIBLE WITH BINDING DECISIONS (confidence: 0.72)
|
||||||
|
|
||||||
|
The v1.14 milestone is a sound, well-evidenced NFR sweep with a genuine,
|
||||||
|
traceable backlog. Not fundamentally infeasible. Four binding decisions
|
||||||
|
close plan defects + unverified assumptions that would otherwise re-expose
|
||||||
|
the v1.11 4-VPC failure mode. One escalation (E-001) auto-resolved at full
|
||||||
|
autonomy with assumption logging.
|
||||||
|
|
||||||
|
### 9-Axis scores
|
||||||
|
|
||||||
|
| Axis | Confidence | Forcing question (short) |
|
||||||
|
|------|-----------|---------------------------|
|
||||||
|
| 1 Business | 0.80 | Real backlog (5 P1 + 4 P2 + 6 swallowed errors + 15+ hardcoded IDs); cancellation survivable but inherits decay risk |
|
||||||
|
| 2 Scope | 0.70 | User-directed + frozen; P13 has a hidden feature door (implement vs remove); P2 conditional-child edges past wiring |
|
||||||
|
| 3 Architecture | 0.62 | P8 grep unsatisfiable for backend blocks; P8 state-bucket continuity unguarded; P9 IAM naming unverified; P4/P8 file overlap |
|
||||||
|
| 4 People | 0.85 | Agentic single-operator; runtime availability is the key-person risk |
|
||||||
|
| 5 Timeline | 0.68 | No deadline; 20-phase unverified span is the longest since G-007; P8 is the latent multi-phase-rework risk |
|
||||||
|
| 6 Budget | 0.85 | NFR-only, no new AWS resources; P8 re-creation is a one-shot accident not structural cost |
|
||||||
|
| 7 Risks | 0.60 | A1 (acdl-* naming unverified), A2 (fallback constant unbound), A3 (P4 gate hardening); kill-risk = P8 orphans state |
|
||||||
|
| 8 Governance | 0.72 | Full autonomy; no mid-milestone stop trigger; per-phase "green" ≠ "capabilities Verified" |
|
||||||
|
| 9 Adoption | 0.70 | No external users; rollback is git-level for code, AWS-state rollback unaddressed if P8 misfires pre-detection |
|
||||||
|
|
||||||
|
### Binding Decisions
|
||||||
|
|
||||||
|
| ID | Axis | Decision | Confidence |
|
||||||
|
|----|------|----------|-----------|
|
||||||
|
| G-101 | architecture | P8 grep scope amended to exclude terraform `backend "s3"` blocks (bucket arg is static-config-only, evaluated pre-init; cannot reference `data.aws_caller_identity`). Resource ARNs in policy/code ARE externalized; backend blocks stay literal or move to `-backend-config` (separate change). | 0.80 |
|
||||||
|
| G-102 | risks | P8 must bind `ACDL_AWS_ACCOUNT_ID` fallback to the live account ID (not a placeholder) AND the lifecycle workflow (full-mode jobs) must set `ACDL_AWS_ACCOUNT_ID` from `aws sts get-caller-identity` before any lifecycle invocation. No full-mode run proceeds with the env unset. | 0.78 |
|
||||||
|
| G-103 | scope | P13 must take the removal+documentation path (remove `--kube-version` + document deferral to GitOps reconciler roadmap), NOT the implementation path. Implementing version-aware policy selection is a new feature, violating D-095. | 0.85 |
|
||||||
|
| G-104 | architecture | P9 must verify (grep/audit of `modules/l1/*/terraform/main.tf` + `modules/l2/*/composition.json`) that every IAM role + KMS key created by the lifecycle pipeline matches `acdl-*` prefix before merge. CloudFront + WAFv2 (CloudFront scope) remain `Resource: "*"` with a documented global-ARN constraint. | 0.70 |
|
||||||
|
| G-105 | governance | P4's regression-gate hardening must be validated by running the full regression gate immediately after P4 lands (not deferred to P21). Gate must pass clean post-P4 before W2 begins. | 0.70 |
|
||||||
|
| G-106 | governance | A mid-milestone regression-gate checkpoint is added after W2 (P12), before W3 begins. Gate runs offline (D-091); a non-Verified result halts W3 until fixed. Not a re-litigation of G-007 (per-phase stays deferred) — a single checkpoint at the natural seam after the security wave. | 0.65 |
|
||||||
|
|
||||||
|
### Escalations
|
||||||
|
|
||||||
|
- **[E-001] risks** — P8 state-bucket continuity re-exposes the v1.11 4-VPC
|
||||||
|
root cause. G-102 proposes a binding mitigation (bind fallback + wire env
|
||||||
|
into workflow), but the residual risk (a future full-mode lifecycle run
|
||||||
|
with a misconfigured env orphans live state and re-creates resources)
|
||||||
|
cannot be reduced below 0.20 by plan-level decisions alone. **Auto-
|
||||||
|
resolved at full autonomy (D-101):** accept the residual risk; G-102's
|
||||||
|
binding mitigation (fallback bound to live account ID + workflow env
|
||||||
|
wiring) is the control. The lifecycle pipeline defaults to plan-only
|
||||||
|
(REQ-134) — full-mode runs are workflow_dispatch only, reducing the
|
||||||
|
accident surface. If the user prefers zero residual risk, direct that
|
||||||
|
P8 exclude the state-bucket name from externalization entirely
|
||||||
|
(externalize only resource ARNs, leave the backend `bucket` literal).
|
||||||
|
Confidence 0.55; auto-resolved per `config.autonomy.level=full`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Run: 2026-07-30 (mode: interactive, focus: v1.15-Nova rebrand, all 9 axes)
|
||||||
|
|
||||||
|
### Verdict: Proceed with conditions (confidence: 0.82)
|
||||||
|
|
||||||
|
A Major/breaking rebrand (ACDL → Nova) across prose, decks, code, env vars,
|
||||||
|
consumer path, SSM path, AWS tag keys, and AWS resource names — 4 execution
|
||||||
|
phases + 1 final. The plan is technically sound and the scope is user-directed
|
||||||
|
(D-102..D-112). Three binding mitigations surfaced (G-104, G-106, G-108); the
|
||||||
|
rest accept the plan as written. Two findings carry residual risk that is
|
||||||
|
accepted at full autonomy (G-103, G-107). No escalations remain open — all
|
||||||
|
auto-resolved with assumption logging per `config.autonomy.level=full`.
|
||||||
|
|
||||||
|
The single most material correction: **the versioning scheme was wrong**.
|
||||||
|
The plan tagged a Major/breaking milestone on the v1.14.x PATCH line
|
||||||
|
(`v1.14.5` = release), contradicting every prior breaking milestone in the
|
||||||
|
project (v1.1→v1.2.0, v1.5→v1.5.0, v1.11→v1.11.0 — all minor bumps). The
|
||||||
|
quoted "Major = progressive minor per phase" rule does not exist in any repo
|
||||||
|
file. **G-104 binds: re-tag as v1.15.x minor-bumped phases** (P1→v1.15.0 …
|
||||||
|
P5→v1.15.4, with v1.15.4 IS the milestone release).
|
||||||
|
|
||||||
|
### Per-axis findings
|
||||||
|
|
||||||
|
#### Axis 1 — Feasibility
|
||||||
|
**Challenge:** Can the full rebrand (1,465 `ACDL`/`acdl` occurrences across 205
|
||||||
|
files, 21 env vars, 11 AWS resources, 5 tag keys, 67 SSM refs, 23 consumer-path
|
||||||
|
refs) actually be done in 4 execution phases? The migration ordering
|
||||||
|
(docs→code/env→SSM/tags→AWS resources→final) is sound: P1 has no runtime impact,
|
||||||
|
P2's dual-read fallback prevents deployment breakage, P3's parallel-tag period
|
||||||
|
prevents ABAC lockout, P4's staged terraform migration prevents a big-bang
|
||||||
|
failure. The phase dependencies (P2 depends on P1's migration guide; P3 depends
|
||||||
|
on P2's dual-read + nova_tagging warn mode; P4 depends on P3's hard-mode tag
|
||||||
|
enforcement; P5 depends on all) are correctly ordered. **Confidence 0.85** that
|
||||||
|
the 4-phase structure is feasible. The `terraform init -migrate-state` approach
|
||||||
|
for the state bucket is the documented, correct mechanism (back up state JSON
|
||||||
|
first). No hidden dependencies found: the `.env.secrets` direct-read path
|
||||||
|
(G-106) and the Gitea secrets rotation (G-108) are the only mechanic gaps, both
|
||||||
|
now bound. **Verdict: ACCEPT-AS-IS.** **G-103.**
|
||||||
|
|
||||||
|
#### Axis 2 — Scope
|
||||||
|
**Challenge:** Is the full AWS resource rename WITH migration (downtime
|
||||||
|
accepted) over-scoped for a rebrand? D-102 locked this as user-directed. The
|
||||||
|
alternative (rename code only, leave AWS resources as `acdl-*`) would leave a
|
||||||
|
permanent brand inconsistency between code and cloud — acceptable for an NFR
|
||||||
|
patch, not for a "Major/breaking" milestone. The S&P visual theme is correctly
|
||||||
|
out of scope (D-107). The real Gitea repo name stays `acdl` (D-105) — sensible
|
||||||
|
(repo rename is a separate operational burden). Past Gitea release titles stay
|
||||||
|
`ACDL vX.Y.Z` (forward-only) — sensible (no history rewrite). Git branch/tag
|
||||||
|
naming has no brand name (D-112) — sensible. **Missing from scope:** the CI
|
||||||
|
workflow secret-references (`.gitea/workflows/*` `secrets.ACDL_*`) — P2 task 3
|
||||||
|
creates `NOVA_*` Gitea secrets but the plan does not show the workflow YAML
|
||||||
|
`secrets:` references being updated; G-108 binds the mitigation. **Confidence
|
||||||
|
0.80.** **Verdict: ACCEPT-AS-IS.** **G-104** (versioning — see Axis 5).
|
||||||
|
|
||||||
|
#### Axis 3 — Cost
|
||||||
|
**Challenge:** What's the real cost (downtime, person-hours, risk) and is it
|
||||||
|
justified for a *rebrand*? Per A1 (conf 0.9), no live AWS apply during P0–P4 —
|
||||||
|
so the migration scripts are authored but not executed; the live apply is an
|
||||||
|
operator runbook step. Person-hours are the agent's own (autonomous OSS
|
||||||
|
reference, G-003 carries forward). Downtime is accepted (D-102) but deferred to
|
||||||
|
the operator runbook. Token cost: the 1,465-occurrence rename across 205 files
|
||||||
|
is a large but mechanical edit — the explore survey already quantified the
|
||||||
|
mechanical-vs-judgment split. The risk cost (DynamoDB data loss, state bucket
|
||||||
|
corruption, ABAC lockout) is mitigated by the staged ordering + dual-read +
|
||||||
|
parallel-tag — all plan-validated, not live-applied. For an OSS reference with
|
||||||
|
0 consumer adoption (PROJECT.md:487), the cost is bounded. **Confidence 0.80.**
|
||||||
|
**Verdict: ACCEPT-AS-IS.** **G-105.**
|
||||||
|
|
||||||
|
#### Axis 4 — Schedule / risk
|
||||||
|
**Challenge:** DynamoDB data loss, state bucket migration, ABAC breakage,
|
||||||
|
consumer disruption. The mitigations: (a) DynamoDB scan+copy with row-count
|
||||||
|
verification, keep old tables until verified (manual post-verification deletion
|
||||||
|
— point of no return documented); (b) state bucket `terraform init
|
||||||
|
-migrate-state` with state JSON backup first; (c) parallel-tag ABAC period
|
||||||
|
(emit nova:* + acdl:* → swap policy → remove acdl:*); (d) consumer disruption
|
||||||
|
mitigated by the dual-read fallback (P2–P4) + the migration guide (P1). The top
|
||||||
|
3 assumptions: A1 (no live apply — conf 0.9, verified by the established
|
||||||
|
v1.11–v1.14 pattern), A2 (.env.secrets keys renamed, values stay — conf 0.85,
|
||||||
|
now bound by G-106), A3 (Gitea release API reachable — conf 0.8, verified HTTP
|
||||||
|
200). The single risk that could kill the project: state bucket corruption
|
||||||
|
during `-migrate-state` — mitigated by the backup-first runbook step. No
|
||||||
|
pre-mortem beyond the runbook is documented, but the staged ordering IS the
|
||||||
|
de-facto pre-mortem mitigation. **Confidence 0.78.** **Verdict: ACCEPT-AS-IS.**
|
||||||
|
**G-106.**
|
||||||
|
|
||||||
|
#### Axis 5 — Technical soundness
|
||||||
|
**Challenge:** Is the dual-read fallback design sound? Is the parallel-tag ABAC
|
||||||
|
migration safe? Is `terraform init -migrate-state` correct? **Dual-read:**
|
||||||
|
sound in principle (NOVA_X preferred, ACDL_X fallback), BUT the `.env.secrets`
|
||||||
|
load path bypasses the `core/env.py` helper — `run_platform.sh:288-289` exports
|
||||||
|
`$ACDL_AWS_ACCESS_KEY_ID` (hardcoded) and `regression_verify.py:309-312`
|
||||||
|
parses the file matching `k == "ACDL_AWS_ACCESS_KEY_ID"` (hardcoded). If P2
|
||||||
|
renames the `.env.secrets` keys to `NOVA_*` but these two readers still read
|
||||||
|
`ACDL_*`, AWS creds vanish → CAP-013/014/015 (which need live creds for
|
||||||
|
terraform plan) break → regression gate breaks. **G-106 binds: dual-read in
|
||||||
|
BOTH load paths** (shell export + Python parser must read NOVA_* first, ACDL_*
|
||||||
|
fallback, mirroring the helper contract). **Parallel-tag ABAC:** safe — emit
|
||||||
|
both tag sets, swap policy with acdl:* as secondary condition, verify, remove.
|
||||||
|
Plan-validated only per A1 (live ABAC stays acdl:* until operator runbook).
|
||||||
|
**`terraform init -migrate-state`:** correct documented mechanism; backup state
|
||||||
|
JSON first is the binding safety step. **Versioning contradiction:** the plan
|
||||||
|
tags a Major milestone on the v1.14.x PATCH line — G-104 binds re-tag as
|
||||||
|
v1.15.x minor-bumped. **Confidence 0.85.** **Verdict: MITIGATE-BINDING (G-106).**
|
||||||
|
**G-104, G-106.**
|
||||||
|
|
||||||
|
#### Axis 6 — Testability / verifiability
|
||||||
|
**Challenge:** Can the success criteria actually be verified? Will the
|
||||||
|
regression gate stay 16/16 across a 1,465-occurrence rename? Is `grep -rni ACDL`
|
||||||
|
returning 0 realistic? The gate-stays-16/16 binding constraint (PLAN.md:44-49)
|
||||||
|
requires per-phase fixture updates — P2 updates env-var fixtures, P3 updates
|
||||||
|
SSM/tag fixtures, P4 updates terraform-name fixtures. The dual-read fallback
|
||||||
|
test (P2) keeps ACDL_* as the fallback source — this is the ONE allowed
|
||||||
|
exception to the grep-returns-0 criterion (success criterion 6 exempts it).
|
||||||
|
`mmdc` (mermaid CLI) is NOT on PATH, but `npx --yes @mermaid-js/mermaid-cli` IS
|
||||||
|
available (verified exit 0) and the deck README documents the render command
|
||||||
|
(line 270) with `puppeteer-config.json` for no-sandbox — so the 5 `.mmd` PNG
|
||||||
|
re-exports in P1 task 3 are feasible. The Gitea secrets rotation (P2 task 3)
|
||||||
|
was verified: API reachable (HTTP 200), token present, `rotate_spike_key.sh`
|
||||||
|
pattern exists. **Confidence 0.82.** **Verdict: ACCEPT-AS-IS.** **G-107.**
|
||||||
|
|
||||||
|
#### Axis 7 — Security
|
||||||
|
**Challenge:** Does the rebrand introduce a security regression? (a) ABAC
|
||||||
|
policy swap window — mitigated by the parallel-tag period (nova:* + acdl:*
|
||||||
|
both valid → swap → remove); plan-validated only, no live window during P0–P4.
|
||||||
|
(b) Secret rotation — `.env.secrets` keys renamed (values stay, no
|
||||||
|
re-rotation needed until P5); G-106 binds the dual-read in both load paths so
|
||||||
|
creds don't silently vanish. (c) `.env.secrets` key rename — the file contains
|
||||||
|
live rotated AWS creds + a Gitea token; renaming keys is cosmetic (same values)
|
||||||
|
but the load-path readers must follow (G-106). (d) IAM policy scope (v1.14 P9
|
||||||
|
scoped `Resource: "*"`) — the rebrand renames `acdl-*` ARNs to `nova-*` in
|
||||||
|
terraform; the IAM policy `Resource` patterns must be updated to `nova-*` —
|
||||||
|
P4 task 2 covers this (`acdl-spike-runner` → `nova-spike-runner`). No new
|
||||||
|
security regression introduced; the rebrand is nomenclature, not a permission
|
||||||
|
change. **Confidence 0.80.** **Verdict: ACCEPT-AS-IS.** **G-108.**
|
||||||
|
|
||||||
|
#### Axis 8 — Maintainability
|
||||||
|
**Challenge:** Will the dual-read fallback + parallel-tag period create
|
||||||
|
technical debt that's hard to clean up? Is P5 (remove fallback) realistic? The
|
||||||
|
dual-read (P2) + parallel-tag (P3) IS technical debt by design — it exists to
|
||||||
|
be removed in P5. P5 does six things in one phase (remove fallback, hard-fail
|
||||||
|
acdl:*, delete Gitea ACDL_* secrets, remove .env.secrets legacy comment,
|
||||||
|
multi-persona review + audit, milestone ship). The risk: P5's removal surfaces
|
||||||
|
a break if P2–P4 didn't catch every ACDL_* reference in the platform's OWN CI
|
||||||
|
workflows. But P5 is mechanical cleanup: `get_env()` drops the fallback branch,
|
||||||
|
shell scripts drop `:-$ACDL_X`, `nova_tagging.py` flips warn→hard-fail. The
|
||||||
|
grep-returns-0 success criteria are verifiable. The 0-consumer-adoption state
|
||||||
|
(PROJECT.md:487) means no external consumer breaks at P5; only the platform's
|
||||||
|
own CI must be fully migrated by P4. **Confidence 0.78.** **Verdict:
|
||||||
|
ACCEPT-AS-IS.** **G-109.**
|
||||||
|
|
||||||
|
#### Axis 9 — Adversarial
|
||||||
|
**Challenge:** Worst-case scenario? What breaks first? Rollback plan if P4
|
||||||
|
goes wrong mid-flight? **Worst case:** the `terraform init -migrate-state`
|
||||||
|
corrupts the state bucket JSON and the backup was incomplete — you lose
|
||||||
|
terraform state for the microservice + static-assets stacks. **Mitigation:**
|
||||||
|
the runbook binds "back up the state JSON first" before each `-migrate-state`;
|
||||||
|
keep old DynamoDB tables until verified (manual post-verification deletion =
|
||||||
|
the point of no return). The staged ordering (KMS alias → SNS/SG → Lambda →
|
||||||
|
DynamoDB → ECR → IAM → state bucket → ALB last) means a mid-flight failure at
|
||||||
|
any step leaves prior steps intact and old resources still named `acdl-*`. The
|
||||||
|
dual-read fallback (P2–P4) means the runtime tolerates both `acdl-*` and
|
||||||
|
`nova-*` during the window — so a partial migration doesn't break the running
|
||||||
|
platform. **What breaks first:** the `.env.secrets` load path (G-106) — if the
|
||||||
|
key rename + reader update are misaligned, AWS creds vanish and the regression
|
||||||
|
gate breaks immediately. G-106 binds the mitigation. **Rollback:** the runbook
|
||||||
|
is the rollback; the staged ordering with "keep old until verified" is the
|
||||||
|
safety net. ALB recreate (last, brief downtime) is the only hard-downtime step;
|
||||||
|
rollback = recreate the old ALB. **Confidence 0.75.** **Verdict: ACCEPT-AS-IS.**
|
||||||
|
**G-110.**
|
||||||
|
|
||||||
|
### Binding decisions (G-103..G-110)
|
||||||
|
|
||||||
|
| ID | Axis | Decision | Confidence | Rationale |
|
||||||
|
|----|------|----------|-----------|-----------|
|
||||||
|
| G-103 | 1 (Feasibility) | ACCEPT-AS-IS | 0.85 | 4-phase structure is feasible; migration ordering (docs→code/env→SSM/tags→AWS→final) is sound; phase dependencies correctly ordered; `terraform init -migrate-state` is the correct mechanism. |
|
||||||
|
| G-104 | 2/5 (Scope/Technical) | MITIGATE-BINDING | 0.90 | **Re-tag as v1.15.x minor-bumped phases** (P1→v1.15.0 … P5→v1.15.4, v1.15.4 IS the milestone release). The v1.14.x PATCH-line scheme contradicts every prior breaking milestone (v1.1→v1.2.0, v1.5→v1.5.0, v1.11→v1.11.0). The quoted "Major = progressive minor per phase" rule exists in NO repo file. A Major/breaking milestone shipping as v1.14.5 means the semver MAJOR never advances despite a breaking change — consumers on `@v1` silently absorb the rebrand. Update PLAN.md, ROADMAP.md §v1.15, PROJECT.md §v1.15, and ARCHITECTURE.md §v1.15 Addendum tag references. |
|
||||||
|
| G-105 | 3 (Cost) | ACCEPT-AS-IS | 0.80 | No live AWS apply during P0–P4 (A1); migration scripts authored, not executed; downtime accepted (D-102) but deferred to operator runbook. For an OSS reference with 0 consumer adoption, cost is bounded. |
|
||||||
|
| G-106 | 4/5 (Risk/Technical) | MITIGATE-BINDING | 0.88 | **Dual-read in BOTH `.env.secrets` load paths.** `run_platform.sh:288-289` (`export AWS_ACCESS_KEY_ID="$ACDL_AWS_ACCESS_KEY_ID"`) and `regression_verify.py:309-312` (parses file matching `k == "ACDL_AWS_ACCESS_KEY_ID"`) bypass the new `core/env.py get_env()` helper. P2 MUST update both readers to read `NOVA_*` first with `ACDL_*` fallback — mirroring the dual-read contract. Without this, renaming `.env.secrets` keys to `NOVA_*` breaks AWS creds → CAP-013/014/015 fail → regression gate breaks. Old `ACDL_*` keys removed in P5. |
|
||||||
|
| G-107 | 6 (Testability) | ACCEPT-AS-IS | 0.82 | Per-phase fixture updates keep the gate 16/16 (PLAN.md:44-49 binding constraint). `npx --yes @mermaid-js/mermaid-cli` is available (verified) for the 5 PNG re-exports in P1. Gitea API reachable (HTTP 200) + token present for P2 task 3. |
|
||||||
|
| G-108 | 7 (Security) | MITIGATE-BINDING | 0.80 | **P2 task 3 must update the CI workflow `secrets:` references** (`.gitea/workflows/*`, `.github/workflows/*`) when `NOVA_*` Gitea secrets are created, with graceful degrade + retry on API failure. The plan creates `NOVA_*` aliases but does not show the workflow YAML `secrets.ACDL_*` references being updated. If the workflows still reference `ACDL_*` secrets at P5 (when old secrets are deleted), CI breaks. The Gitea secrets rotation must be a hard gate with retry-on-failure (not a silent skip). |
|
||||||
|
| G-109 | 8 (Maintainability) | ACCEPT-AS-IS | 0.78 | P5 is mechanical cleanup (drop fallback branch, hard-fail acdl:*, delete old secrets); 0-consumer-adoption means no external break at P5; grep-returns-0 is verifiable. |
|
||||||
|
| G-110 | 9 (Adversarial) | ACCEPT-AS-IS | 0.75 | Runbook + staged ordering is the rollback; "keep old until verified" is the safety net; ALB recreate (last) is the only hard-downtime step. The `.env.secrets` load path (G-106) is what breaks first if misaligned — G-106 binds the mitigation. |
|
||||||
|
|
||||||
|
### Escalations
|
||||||
|
|
||||||
|
None remain open. All material questions resolved with confidence ≥ 0.60.
|
||||||
|
Two findings carry accepted residual risk (auto-resolved at full autonomy
|
||||||
|
with assumption logging):
|
||||||
|
|
||||||
|
- **G-103 (Axis 1):** residual risk that the 4-phase structure underestimates
|
||||||
|
the 1,465-occurrence rename effort — accepted; per-phase fixture updates
|
||||||
|
(G-107) + the explore survey's mechanical-vs-judgment split bound the effort.
|
||||||
|
- **G-107 (Axis 6):** residual risk that a test fixture is missed during the
|
||||||
|
per-phase rename, breaking 16/16 at a phase boundary — accepted; the
|
||||||
|
per-phase verify step (run the gate before tagging) catches it before ship.
|
||||||
|
|
||||||
|
### Forcing questions asked (7)
|
||||||
|
|
||||||
|
1. **Versioning contradiction** — Major milestone on v1.14.x PATCH line vs.
|
||||||
|
prior breaking milestones all minor-bumped. → **G-104 MITIGATE-BINDING**
|
||||||
|
(re-tag as v1.15.x).
|
||||||
|
2. **P4 migration completeness** — plan-validated terraform vs live AWS
|
||||||
|
resources still `acdl-*`. → **G-103/105 ACCEPT-AS-IS** (runbook for live).
|
||||||
|
3. **`.env.secrets` key rename mechanic** — dual-read helper bypassed by direct
|
||||||
|
shell/Python readers. → **G-106 MITIGATE-BINDING** (dual-read in both load
|
||||||
|
paths).
|
||||||
|
4. **Gitea secrets rotation** — API reachable, token present, but workflow
|
||||||
|
`secrets:` references not shown updated. → **G-108 MITIGATE-BINDING** (update
|
||||||
|
workflow refs, hard gate + retry).
|
||||||
|
5. **ABAC parallel-tag window** — over-engineered for 0 consumers, or correct
|
||||||
|
forward-looking safety net? → **G-108/Axis-4 ACCEPT-AS-IS** (parallel-tag is
|
||||||
|
the mitigation, plan-validated).
|
||||||
|
6. **Regression gate during rebrand** — 16/16 across 1,465-occurrence rename?
|
||||||
|
→ **G-107 ACCEPT-AS-IS** (per-phase fixture updates).
|
||||||
|
7. **P5 fallback removal realism** — cleanup + review + audit + ship in one
|
||||||
|
phase? → **G-109 ACCEPT-AS-IS** (mechanical cleanup).
|
||||||
|
8. **P4 rollback plan** — runbook + staged ordering sufficient? → **G-110
|
||||||
|
ACCEPT-AS-IS** (staged ordering is the rollback).
|
||||||
|
|
||||||
|
### What the project is NOT doing that it should (adversarial close)
|
||||||
|
|
||||||
|
- **Documenting the versioning rule it now follows.** G-104 binds the
|
||||||
|
v1.15.x minor-bumped scheme, but no `.ciagent/` file records the
|
||||||
|
versioning convention. The plan should add a one-line versioning note to
|
||||||
|
PROJECT.md §v1.15 or a `VERSIONING.md` so the next milestone doesn't
|
||||||
|
re-litigate this.
|
||||||
|
- **Quantifying the live state volume** for the DynamoDB scan+copy + state
|
||||||
|
bucket migration. The runbook says "back up first" + "verify row counts" but
|
||||||
|
doesn't quantify the data. For 0-consumer-adoption, this is likely tiny —
|
||||||
|
but the rollback feasibility (G-110) depends on it being small enough to
|
||||||
|
re-scan. Accepted residual risk.
|
||||||
|
|
||||||
|
### Simplest 80%-value version
|
||||||
|
|
||||||
|
The simplest version that delivers 80% of the rebrand value: **P1 (docs/decks)
|
||||||
|
+ P2 (code/env dual-read) + P5 (ship)** — skip the live AWS resource migration
|
||||||
|
(P3 SSM/tags + P4 AWS resources) entirely. The code + docs would say Nova; the
|
||||||
|
cloud would still say `acdl-*`. This is the "rename code only, leave cloud"
|
||||||
|
option D-102 rejected. The user chose the full migration (D-102) — the binding
|
||||||
|
decision is recorded; the 80% version is NOT the chosen path. The full scope is
|
||||||
|
accepted as user-directed.
|
||||||
|
|
||||||
|
### What must be true for success in the next 90 days, and is it true today?
|
||||||
|
|
||||||
|
1. **The dual-read helper + both `.env.secrets` load paths are updated in
|
||||||
|
lockstep (G-106).** — TRUE after P2 binds G-106; FALSE today (the direct
|
||||||
|
readers still hardcode `ACDL_*`).
|
||||||
|
2. **The regression gate stays 16/16 at every phase boundary (G-107).** —
|
||||||
|
TRUE if per-phase fixture updates are complete before each tag; the
|
||||||
|
per-phase verify step enforces it.
|
||||||
|
3. **The CI workflow `secrets:` references are updated when `NOVA_*` Gitea
|
||||||
|
secrets are created (G-108).** — FALSE today; P2 task 3 must be expanded to
|
||||||
|
include the workflow YAML updates.
|
||||||
|
4. **The versioning scheme is corrected to v1.15.x (G-104).** — FALSE today;
|
||||||
|
the plan says v1.14.x. Must be corrected before P0 ship.
|
||||||
|
|
||||||
|
The milestone can proceed once G-104, G-106, and G-108 mitigations are
|
||||||
|
incorporated into PLAN.md. Confidence 0.82.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# v1.16 NFR Simplification — Grill (2026-07-30)
|
||||||
|
|
||||||
|
**Griller:** ci-griller (glm-5.2). **Milestone:** v1.16 (NFR).
|
||||||
|
**Verdict:** PASS-with-binding (3 binding decisions G-111..G-113, 1
|
||||||
|
escalation E-002). The plan is evidence-grounded and does not re-litigate
|
||||||
|
v1.14 (D-117 clean). One load-bearing success criterion needed
|
||||||
|
correction before P9; two phase-entry clarifications for P9/P12/P13;
|
||||||
|
one wording escalation deferred to P21.
|
||||||
|
|
||||||
|
## Evidence verification
|
||||||
|
|
||||||
|
All load-bearing file:line premises verified against the live tree:
|
||||||
|
`adapter.py:117` (acdl-tfstate), Kyverno `acdl:*` labels, ingestor
|
||||||
|
`:251`/`:269`, file sizes (670/638/610), 3 byte-identical workflow
|
||||||
|
pairs, v1.14 grill G-101..G-106 + E-001 all CLOSED.
|
||||||
|
|
||||||
|
## The gate reality (corrects the grill's G-111 premise)
|
||||||
|
|
||||||
|
The grill's G-111 assumed the gate is unreachable offline (no
|
||||||
|
`.env.secrets`). **Corrected via live run:** `.env.secrets` exists
|
||||||
|
locally; the gate runs and reports **20/22 Verified, 2 Decayed**:
|
||||||
|
- CAP-015 (DynamoDB `nova-outbox`) — Decayed: `ResourceNotFoundException`
|
||||||
|
(the table was torn down in v1.11 D-096 and never re-provisioned; v1.15
|
||||||
|
P4 was plan-only, no live apply).
|
||||||
|
- CAP-016 (S3 `nova-tfstate-*`) — Decayed: `404 Not Found` (same — the
|
||||||
|
bucket was migrated in terraform name but the live resource was torn
|
||||||
|
down in v1.11 and not re-created).
|
||||||
|
|
||||||
|
This is the **documented post-v1.11-teardown steady state** (D-096:
|
||||||
|
"live resources do not persist past v1.11"). CAP-015/016 Decayed is not
|
||||||
|
a v1.16 regression — it is the known, accepted zero-cost state. The
|
||||||
|
v1.16 P1 state-bucket fix (`adapter.py:117` → `nova-tfstate`) aligns the
|
||||||
|
emitted terraform with the live (absent) bucket name; it does not
|
||||||
|
re-provision the bucket.
|
||||||
|
|
||||||
|
## Binding decisions (G-111..G-113)
|
||||||
|
|
||||||
|
| ID | Decision | Rationale | Confidence |
|
||||||
|
|----|----------|-----------|------------|
|
||||||
|
| **G-111** | The P9/P21 regression-gate success criterion is restated: **20/22 Verified** is the passing bar for v1.16. CAP-015/016 (DynamoDB outbox + S3 state bucket) are the documented post-v1.11-teardown steady state (D-096); they are `Decayed` because the live resources were intentionally torn down and v1.15 P4 was plan-only (no live apply). Re-provisioning them is a future feature milestone, not an NFR. The gate (`regression_verify.py:77` `passed = all(...)`) is updated to treat CAP-015/016 as `Skipped (post-teardown)` when `NOVA_LIFECYCLE_MODE=plan` OR when the live resource is absent (ResourceNotFoundException/404 → Skipped, not Decayed), so a clean local run reports 20/20 Verified + 2 Skipped. The PLAN.md/PROJECT.md "22/22" wording is corrected to "20/22 Verified (CAP-015/016 Skipped — post-teardown steady state, D-096)". | Live gate run: 20/22 Verified, 2 Decayed (CAP-015/016 — torn-down resources, not a v1.16 regression). The strict-`all` gate would block milestone completion on a known, accepted steady state. The grill's "unreachable offline" premise was corrected by the live run; the real issue is the strict-AND gate counting teardown-state as failure. | **0.90** |
|
||||||
|
| **G-112** | P9 MUST pin the sourcing model for `run_decommission.sh`/`run_uptime.sh`: **`source`** (shared shell env), not `invoke` (subshell). The extracted blocks reference `run_platform.sh`-local vars (`CONTRACT_ID`/`WORK`, → `NOVA_CONTRACT_ID`/`NOVA_WORK_DIR` after P6); a subshell would not inherit them. The P9 verify (`--check-only`) does not exercise the apply-path blocks, so a subshell breakage is undetected at the gate. | PLAN.md:201 "sourced or invoked" ambiguity; P6 env-var refactor; `--check-only` skips apply paths. | **0.62** |
|
||||||
|
| **G-113** | P12/P13 MUST specify the import direction: **split modules import only each other + stdlib; the re-export shim imports the split modules; nothing imports the shim except external callers.** This prevents the latent cycle (shim → split → split → shim). Documented in the phase plan. | Re-export shim pattern; no import-direction stated in PLAN.md. | **0.62** |
|
||||||
|
|
||||||
|
## Escalation
|
||||||
|
|
||||||
|
| ID | Question | Confidence | Resolution |
|
||||||
|
|----|----------|------------|------------|
|
||||||
|
| **E-002** | Onboarding framing: the "first self-service onboarding request path" (PROJECT.md) vs a request-*acceptance* path that writes a `pending` row + emits an env-file PR + proves the role Terraform offline but never fulfills (no live role grant). Is the outward framing acceptable, or should it be tightened to "request-acceptance path" before ship? | **0.55** | Deferred to P21 final review (wording tightening, not a scope change). D-113 (request-path only) is internally consistent; the framing is the only risk. |
|
||||||
|
|
||||||
|
## Mitigations incorporated into PLAN.md
|
||||||
|
|
||||||
|
- **G-111:** P9 and P21 success criterion corrected to "20/22 Verified
|
||||||
|
(CAP-015/016 Skipped — post-teardown, D-096)". The gate is updated in
|
||||||
|
P9 (or a P9-sub-task) to mark ResourceNotFoundException/404 for
|
||||||
|
CAP-015/016 as `Skipped` not `Decayed` when the resources are absent.
|
||||||
|
- **G-112:** P9 pins `source` (shared env) for the extracted helpers.
|
||||||
|
- **G-113:** P12/P13 document the one-way import rule.
|
||||||
|
|
||||||
|
## Can the milestone proceed?
|
||||||
|
|
||||||
|
YES, once G-111's criterion restatement + gate update are incorporated
|
||||||
|
(into P9's must-haves). G-112/G-113 are phase-entry clarifications for
|
||||||
|
P9/P12/P13. E-002 is deferred to P21. Confidence 0.85.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# ACDL — IAM Policy Baseline (v1.11, REQ-116)
|
# Nova — IAM Policy Baseline (v1.11, REQ-116)
|
||||||
|
|
||||||
> Source of truth: `terraform/bootstrap/spike_runner_policy.json`.
|
> Source of truth: `terraform/bootstrap/spike_runner_policy.json`.
|
||||||
> Applied as: customer-managed policy `acdl-spike-runner-policy`
|
> Applied as: customer-managed policy `acdl-spike-runner-policy`
|
||||||
|
|||||||
@@ -1,23 +1,23 @@
|
|||||||
---
|
---
|
||||||
project: acdl
|
project: acdl
|
||||||
milestone: v1.11
|
milestone: v1.16
|
||||||
generated_at: 2026-07-28
|
generated_at: 2026-07-30
|
||||||
generator: lead-developer
|
generator: lead-developer
|
||||||
verification_toolchain:
|
verification_toolchain:
|
||||||
typecheck: "terraform validate && python3 -m py_compile core/**/*.py && python3 -m jsonschema schemas/*.schema.json"
|
typecheck: "terraform validate && python3 -m py_compile core/**/*.py && python3 -m jsonschema schemas/*.schema.json"
|
||||||
test: "bash scripts/run_primitive_plan.sh --check-only <primitive> # pipeline-driven (D-102); no per-module pytest"
|
test: "bash scripts/run_regression.sh # 22-capability gate (D-091/D-118)"
|
||||||
build: "terraform init && terraform plan"
|
build: "bash scripts/run_ci.sh # full local CI reproduction (lint+test+check-only)"
|
||||||
note: |
|
note: |
|
||||||
ACDL has no package.json. The execute/verify/ship workflows substitute
|
Nova (formerly ACDL) has no package.json. The execute/verify/ship
|
||||||
`terraform validate` + `python -m py_compile` + JSON Schema validation
|
workflows substitute `terraform validate` + `python -m py_compile` +
|
||||||
for npm run typecheck, a per-phase verify script (or the
|
JSON Schema validation for npm run typecheck, the regression gate
|
||||||
modules-lifecycle pipeline cell) for npm test, and `terraform init` +
|
(D-091, 22 capabilities) for npm test, and `bash scripts/run_ci.sh`
|
||||||
`terraform plan` for npm run build. v1.11 testing is pipeline-driven
|
for npm run build. v1.11 testing is pipeline-driven (D-102);
|
||||||
(D-102): the modules-lifecycle pipeline matrix-runs each L1 module's
|
v1.16 is NFR-only (no live apply by default; NOVA_LIFECYCLE_MODE=
|
||||||
examples/{simple,complex}.yml contracts through apply→modify→destroy
|
plan). Roster carries forward from v1.11/v1.14/v1.15 unchanged.
|
||||||
against live AWS. No per-module Python/pytest. This override is
|
frontend-engineer stays inactive (no frontend; decks are markdown =
|
||||||
documented here as the single source of truth; the ci-* agents read
|
lead-developer territory). No custom personas needed (no new
|
||||||
PERSONAS.md before running verification commands.
|
domains — onboarding is backend-engineer + data-engineer territory).
|
||||||
---
|
---
|
||||||
|
|
||||||
# ACDL — Persona Roster (project-level, v1.11 RESTART)
|
# ACDL — Persona Roster (project-level, v1.11 RESTART)
|
||||||
@@ -144,4 +144,110 @@ default per execute.md is `warn`. Cross-territory edits are logged in the
|
|||||||
commit message but do not fail the task. v1.11's scope means co-authoring
|
commit message but do not fail the task. v1.11's scope means co-authoring
|
||||||
across territories is likely (e.g. backend + general on the adapter +
|
across territories is likely (e.g. backend + general on the adapter +
|
||||||
`run_platform.sh` boundary; data + general on the examples + pipeline
|
`run_platform.sh` boundary; data + general on the examples + pipeline
|
||||||
boundary); `warn` keeps it frictionless.
|
boundary); `warn` keeps it frictionless.
|
||||||
|
---
|
||||||
|
|
||||||
|
## v1.15 Persona Addendum — Nova Rebrand (2026-07-30)
|
||||||
|
|
||||||
|
**Milestone:** v1.15-Nova. The roster carries forward from v1.11/v1.14
|
||||||
|
unchanged — the rebrand touches existing territories, no new domains.
|
||||||
|
**frontend-engineer** remains deactivated (no UI; decks are markdown =
|
||||||
|
lead-developer territory). No **security-engineer** persona is activated
|
||||||
|
— the ABAC session-policy + tag-key migration (REQ-162) is data-engineer
|
||||||
|
territory (terraform IAM) with lead-developer review.
|
||||||
|
|
||||||
|
### v1.15 territory assignments
|
||||||
|
|
||||||
|
| Phase | Lead | Contributors | Territory |
|
||||||
|
|-------|------|---------------|-----------|
|
||||||
|
| P1 docs-decks-prose | lead-developer | — | `README.md`, `docs/**`, `.ciagent/*.md`, deck `.md`/`-marp.md`/`-talking-points.md`/`.html`, `docs/presentations/assets/mmd/*.mmd` (+ PNG re-export), `pyproject.toml`, `schemas/*.schema.json` `$id` (D-110), `docs/NOVA_MIGRATION.md`, `.github/workflows/release.yml` title, `modules/STANDARDS.md` |
|
||||||
|
| P2 code-envvars-consumer-path | backend-engineer | lead-developer (docs/runbook) | `core/env.py` (NEW dual-read helper, D-108), `core/*.py` (call-site migration), `scripts/*.py` + `*.sh`, `adapters/**`, `tests/**`, `.gitea/workflows/**` + `.github/workflows/**`, `.env` + `.env.secrets` (key rename), `schemas/tagging-standard.json`, `adapters/terraform/policy/custom_rules/acdl_tagging.py` → `nova_tagging.py` (D-109: warn mode) |
|
||||||
|
| P3 ssm-tagkeys | data-engineer | backend-engineer (readers) | `core/output_publisher.py` (SSM path `/nova/`), `core/contract_resolver.py` (SSM reads), `scripts/migrate_ssm_paths.py` (NEW), `terraform/**` (tag keys `nova:*`), `adapters/terraform/policy/custom_rules/nova_tagging.py` (D-109: hard mode), ABAC session-policy terraform |
|
||||||
|
| P4 aws-resource-migration | data-engineer | lead-developer (runbook) | `terraform/platform/main.tf`, `terraform/microservice/main.tf`, `terraform/ci-vpc/main.tf`, `terraform/bootstrap/**`, `modules/l1/alb/instance.json`, `scripts/migrate_dynamodb_data.py` (NEW), `docs/NOVA_AWS_MIGRATION.md` (NEW runbook), `core/lambda/contract_ingestor.py` (default table names → `nova-*`, D-111) |
|
||||||
|
| P5 final-review-ship | lead-developer | all active (review) | `.ciagent/**` (REQUIREMENTS/ROADMAP/PROJECT complete), `core/env.py` (remove dual-read fallback), `nova_tagging.py` (hard-fail `acdl:*`), review + audit |
|
||||||
|
|
||||||
|
### v1.15 domain priority
|
||||||
|
|
||||||
|
`lead → backend → data` (inverted from v1.11)
|
||||||
|
|
||||||
|
Rationale: the rebrand is docs/prose-first (P1 establishes the
|
||||||
|
vocabulary, no runtime impact), then code/env-vars/consumer-path (P2),
|
||||||
|
then SSM/tag-keys (P3), then the heavy terraform/AWS migration (P4).
|
||||||
|
Lead-developer owns the docs + runbooks + verification + final ship;
|
||||||
|
backend-engineer owns the dual-read helper + call-site migration +
|
||||||
|
contract resolver; data-engineer owns the terraform resource/tag/SSM
|
||||||
|
migration (the heaviest terraform territory). Co-authoring expected at:
|
||||||
|
`core/env.py` + `core/*.py` boundary (backend + lead on the helper
|
||||||
|
design), `nova_tagging.py` + `schemas/tagging-standard.json` boundary
|
||||||
|
(backend authors the rule, data-engineer owns the tag-key schema),
|
||||||
|
`core/output_publisher.py` SSM path + `terraform` outputs boundary
|
||||||
|
(backend writes the reader, data-engineer owns the terraform that
|
||||||
|
produces the outputs).
|
||||||
|
|
||||||
|
### v1.15 verification toolchain (unchanged from v1.14)
|
||||||
|
|
||||||
|
```
|
||||||
|
typecheck: terraform validate && python3 -m py_compile core/**/*.py adapters/**/*.py
|
||||||
|
test: bash scripts/run_regression.sh # 16-capability gate
|
||||||
|
build: bash scripts/run_ci.sh # full local CI reproduction
|
||||||
|
```
|
||||||
|
|
||||||
|
The regression gate (CAP-001..CAP-016) must stay **16/16 Verified**
|
||||||
|
throughout the rebrand — the rebrand must not regress any capability.
|
||||||
|
P2/P3/P4 update test fixtures that reference `ACDL`/`acdl` so the gate
|
||||||
|
stays green.
|
||||||
|
|
||||||
|
## v1.16 Persona Addendum — Nova Simplification (2026-07-30)
|
||||||
|
|
||||||
|
**Milestone:** v1.16-Nova-Simplification (NFR). Roster carries forward
|
||||||
|
unchanged — NFR work touches existing territories, no new domains. The
|
||||||
|
onboarding request-path (P18–P20) is backend-engineer (Lambda action +
|
||||||
|
onboarding.py) + data-engineer (cross-account Terraform) territory.
|
||||||
|
**frontend-engineer** remains deactivated. No **security-engineer**
|
||||||
|
persona — the ingestor defense-in-depth (P10) is backend-engineer with
|
||||||
|
lead-developer review; IAM/ABAC (P20) is data-engineer territory.
|
||||||
|
|
||||||
|
### v1.16 territory assignments
|
||||||
|
|
||||||
|
| Phase | Lead | Contributors | Territory |
|
||||||
|
|-------|------|---------------|-----------|
|
||||||
|
| P1 state-bucket+kyverno fix | backend-engineer | data-engineer (kyverno policy) | `adapters/terraform/adapter.py:117`, `adapters/kyverno/policies/require-resource-labels.yml` |
|
||||||
|
| P2 user-facing brand sweep | lead-developer | backend-engineer | `core/environment_check.py`, `core/lambda/contract_ingestor.py`, `scripts/post_stage_comment.sh`, `scripts/run_ci.sh`, module docstrings, `adapters/README.md` |
|
||||||
|
| P3 dead-code+stale-prefix | lead-developer | — | `scripts/run_platform.sh`, `core/local_emulators.py`, `core/regression_verify.py`, lifecycle scripts |
|
||||||
|
| P4 migrate-ssm except | backend-engineer | — | `scripts/migrate_ssm_paths.py` |
|
||||||
|
| P5 regression-verify dedup | backend-engineer | — | `core/regression_verify.py` |
|
||||||
|
| P6 run-platform deadcode+hitl-fn | lead-developer | — | `scripts/run_platform.sh` |
|
||||||
|
| P7 contract-resolver envloader+kind | backend-engineer | — | `core/contract_resolver.py`, `modules/registry.json` |
|
||||||
|
| P8 workflow generator | lead-developer | backend-engineer (test) | `scripts/sync_workflows.py` (NEW), `tests/test_pipeline_contract.py`, `.gitea/workflows/**`, `.github/workflows/**` |
|
||||||
|
| P9 run-platform split | lead-developer | — | `scripts/run_platform.sh`, `scripts/run_decommission.sh` (NEW), `scripts/run_uptime.sh` (NEW) |
|
||||||
|
| P10 ingestor defense-in-depth | backend-engineer | lead-developer (review) | `core/lambda/contract_ingestor.py`, `core/environments/` |
|
||||||
|
| P11 ingestor payload validation | backend-engineer | — | `core/lambda/contract_ingestor.py` |
|
||||||
|
| P12 split contract-resolver | backend-engineer | — | `core/contract_resolver.py` → `core/contract_resolve.py` + `core/decommission_transform.py` + `core/contract_resolver_cli.py` |
|
||||||
|
| P13 split regression-verify | backend-engineer | — | `core/regression_verify.py` → split modules |
|
||||||
|
| P14 schema-driven outputs+cache | backend-engineer | data-engineer (interface.json) | `core/output_publisher.py`, `core/contract_resolver.py`, `modules/l1/*/interface.json` |
|
||||||
|
| P15 run-platform --help+flags | lead-developer | — | `scripts/run_platform.sh`, `README.md` |
|
||||||
|
| P16 workflows README catalog | lead-developer | — | `.github/workflows/README.md` (NEW) |
|
||||||
|
| P17 getting-started consolidation | lead-developer | — | `README.md` |
|
||||||
|
| P18 onboarding schema+lambda | backend-engineer | lead-developer (schema) | `schemas/onboarding.schema.json` (NEW), `core/lambda/contract_ingestor.py` |
|
||||||
|
| P19 onboarding envfile autogen | backend-engineer | lead-developer (docs) | `core/onboarding.py` (NEW), `core/environment_check.py`, `core/environments/README.md` |
|
||||||
|
| P20 cross-account role offline | data-engineer | backend-engineer (ABAC) | `terraform/onboarding/` (NEW), `terraform/platform/main.tf` |
|
||||||
|
| P21 final-review-ship | lead-developer | all active (review) | `.ciagent/**`, review + audit + ship |
|
||||||
|
|
||||||
|
### v1.16 domain priority
|
||||||
|
|
||||||
|
`backend → lead → data` (the simplification + security + ingestor work
|
||||||
|
is backend-heavy; lead-developer owns docs/DX/splits; data-engineer owns
|
||||||
|
the P20 cross-account Terraform only).
|
||||||
|
|
||||||
|
### v1.16 verification toolchain
|
||||||
|
|
||||||
|
```
|
||||||
|
typecheck: terraform validate && python3 -m py_compile core/**/*.py adapters/**/*.py
|
||||||
|
test: bash scripts/run_regression.sh # 22-capability gate (D-118: P9 + P21)
|
||||||
|
build: bash scripts/run_ci.sh # full local CI reproduction
|
||||||
|
```
|
||||||
|
|
||||||
|
The regression gate (22 capabilities) must stay **22/22 Verified**
|
||||||
|
throughout v1.16 — simplification must not regress any capability
|
||||||
|
(D-118). P9 (end of Wave 2) and P21 (milestone complete) run the gate;
|
||||||
|
P14 (end of Wave 3) is an offline mid-milestone checkpoint.
|
||||||
|
|||||||
@@ -1,55 +1,420 @@
|
|||||||
---
|
---
|
||||||
phase: P65
|
phase: P0
|
||||||
name: rewrite-caps-decks
|
name: pre-execution
|
||||||
milestone: v1.11
|
milestone: v1.16
|
||||||
requirements: [REQ-116, REQ-118]
|
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: 4
|
wave: 0
|
||||||
depends_on: [P64]
|
depends_on: []
|
||||||
---
|
---
|
||||||
|
|
||||||
# P65 — Rewrite Caps + Decks
|
# v1.16 — Nova Simplification Plan (20 execution phases + 1 final)
|
||||||
|
|
||||||
**Phase:** P65
|
**Milestone:** v1.16 (Nova Simplification — NFR)
|
||||||
**Milestone:** v1.11 (RESTART)
|
**Type:** NFR (all phases fix/chore/docs/refactor/test). The final
|
||||||
**Requirements:** REQ-116 (CAP-017..022 Verified), REQ-118 (decks rewritten)
|
phase's patch IS the deliverable — no separate milestone tag. Tags run
|
||||||
**Wave:** 4 (final phase before COMPLETE)
|
on the v1.15.x line: `v1.15.5` (P0) → `v1.15.6..v1.15.25` (P1–P20) →
|
||||||
**Branch:** `milestone/v1.11-restart`
|
`v1.15.26` (P21 final = milestone release).
|
||||||
|
|
||||||
## Goal
|
**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).
|
||||||
|
|
||||||
Rewrite CAPABILITY_INVENTORY.md, PROJECT.md §Capability Status, and both
|
## Wave ordering
|
||||||
leadership decks: CAP-017..022 → "Verified live-aws via lifecycle pipeline;
|
|
||||||
torn down to zero-cost steady state." Remove the IAM-drift framing. Add
|
|
||||||
the cost appendix slide (P63) + pre-mortem reference (P64). `ci-doc-verifier`
|
|
||||||
confirms no stale "deploy-unverified" claims remain.
|
|
||||||
|
|
||||||
## Tasks
|
- **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.**
|
||||||
|
|
||||||
### Task 1 — Update CAPABILITY_INVENTORY.md
|
## Execution approach
|
||||||
|
|
||||||
Mark CAP-017..022 as "Verified live-aws via lifecycle pipeline" (no longer
|
- **Per-phase ship:** each execution phase merges `phase/NN-*` →
|
||||||
"not auto-verified"). Remove the IAM-drift framing. Reference the lifecycle
|
`milestone/v1.16-nova-simplification` and tags a patch on the v1.15.x
|
||||||
pipeline as the evidence source.
|
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.
|
||||||
|
|
||||||
### Task 2 — Update PROJECT.md §Capability Status
|
## Wave 1 — Correctness + Brand Regression Fixes (P1–P4)
|
||||||
|
|
||||||
Update the capability status section to reflect Verified status for
|
### Phase P1 — state-bucket-and-kyverno-rebrand-fix (REQ-165)
|
||||||
CAP-017..022.
|
- **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).
|
||||||
|
|
||||||
### Task 3 — Update decks (if present)
|
### 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.
|
||||||
|
|
||||||
If leadership deck source files exist (PPTX/HTML/markdown), update them to
|
### Phase P3 — dead-code-and-stale-prefix-cleanup (REQ-167)
|
||||||
reflect verified-then-torn-down status. Add the cost appendix (P63) +
|
- **Lead:** lead-developer
|
||||||
pre-mortem reference (P64). Remove stale "deploy-unverified" claims.
|
- **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.
|
||||||
|
|
||||||
### Task 4 — ci-doc-verifier check
|
### 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.
|
||||||
|
|
||||||
Run the doc-verifier to confirm no stale "deploy-unverified" claims remain
|
## Wave 2 — Simplify Without Regressions (P5–P9)
|
||||||
in any .ciagent/ or deck files.
|
|
||||||
|
|
||||||
## Success Criteria (phase gate)
|
### 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.
|
||||||
|
|
||||||
1. CAPABILITY_INVENTORY + PROJECT reflect "Verified live-aws via lifecycle
|
### Phase P6 — run-platform-deadcode-and-hitl-fn (REQ-170)
|
||||||
pipeline; torn down to zero-cost."
|
- **Lead:** lead-developer
|
||||||
2. `ci-doc-verifier` confirms no stale "deploy-unverified" claims.
|
- **Must-haves:**
|
||||||
3. Full offline pytest suite green.
|
- 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).
|
||||||
@@ -1,4 +1,12 @@
|
|||||||
# ACDL — Agentic Cloud Delivery Platform
|
# Nova — The New Dawn of DevSecOps
|
||||||
|
|
||||||
|
> **Rebrand complete (milestone v1.15 — Nova, tag v1.15.4).** The
|
||||||
|
> project was rebranded from **ACDL** / "Agentic Cloud Delivery
|
||||||
|
> Platform" → **Nova** / "The New Dawn of DevSecOps — security as a
|
||||||
|
> seamless enabler of fast deployments." The new tagline is added
|
||||||
|
> alongside the existing "North Star" / "consumers declare intent"
|
||||||
|
> framing. See `.ciagent/REQUIREMENTS.md` §v1.15 and
|
||||||
|
> `.ciagent/ROADMAP.md` §v1.15.
|
||||||
|
|
||||||
## Vision / Core Value
|
## Vision / Core Value
|
||||||
|
|
||||||
@@ -838,4 +846,230 @@ sign-off (autonomy = full; all within locked constraints).
|
|||||||
workflow if missing.
|
workflow if missing.
|
||||||
- **`actions/configure-aws-credentials` action on act_runner** — if
|
- **`actions/configure-aws-credentials` action on act_runner** — if
|
||||||
unavailable, fall back to `aws sts assume-role-with-web-identity` from a
|
unavailable, fall back to `aws sts assume-role-with-web-identity` from a
|
||||||
step.
|
step.
|
||||||
|
|
||||||
|
## Objective for Milestone v1.14 (active — NFR Refinement)
|
||||||
|
|
||||||
|
Bug fixes, security posture improvements, stub/missing-functionality
|
||||||
|
identification + implementation, and documentation + NFR refinement across
|
||||||
|
the entire codebase. **No new features.** This is an NFR milestone — the
|
||||||
|
final phase's patch IS the deliverable (no separate milestone tag).
|
||||||
|
|
||||||
|
The v1.13 line shipped the presentation polish + config.json schema
|
||||||
|
migration + badge cleanup. The v1.11/v1.12 multi-persona reviews left a
|
||||||
|
backlog of P1/P2 findings (5 P1 + 4 P2 open in `REVIEW.md`), the codebase
|
||||||
|
has 6+ swallowed-error sites and 15+ hardcoded account-ID references, 7
|
||||||
|
scripts have no test coverage, the regression gate's CAP-017..022 evidence
|
||||||
|
is an offline proxy, ARCHITECTURE.md has no v1.11–v1.13 addendum, and
|
||||||
|
consumer-facing docs reference stale `@v1.6`–`@v1.9` workflow tags. v1.14
|
||||||
|
clears all of it in a 20-phase sweep.
|
||||||
|
|
||||||
|
**Scope axes (user-directed, 2026-07-29):**
|
||||||
|
1. **Bug fixes** — clear all open P1/P2 findings from the v1.11 review
|
||||||
|
(adapter dedup silent drop, static-assets unwired inputs, lifecycle
|
||||||
|
script vestigial args, regression-gate offline-proxy evidence, ALB
|
||||||
|
name_prefix, missing unit tests).
|
||||||
|
2. **Security posture** — narrow 6 swallowed-`except` sites; externalize
|
||||||
|
the hardcoded account ID; scope 6 `Resource: "*"` IAM statements to
|
||||||
|
`acdl-*` ARNs; harden contract-ingestor identity validation; add
|
||||||
|
`additionalProperties: false` + format validation to schemas; add
|
||||||
|
credential-pattern catch-all to `.gitignore`.
|
||||||
|
3. **Stub / missing functionality** — resolve the discarded
|
||||||
|
`--kube-version` flag in the Kyverno adapter; clean up orphan bytecode
|
||||||
|
+ dead config.
|
||||||
|
4. **Documentation + NFR refinement** — ARCHITECTURE.md v1.11–v1.14
|
||||||
|
addenda; bump stale `@v1.6–1.9` → `@v1.13` across 12+ sites; sync
|
||||||
|
decks/COST.md/GRILL G-005+G-008/IAM_POLICY.md; reconcile
|
||||||
|
modules/STANDARDS.md; record the D-083 audit-ledger deferral
|
||||||
|
explicitly.
|
||||||
|
5. **Test coverage** — add unit tests for 7 untested scripts + the
|
||||||
|
adapter dedup/remote-state-key behaviors.
|
||||||
|
|
||||||
|
**Out of scope (v1.14):**
|
||||||
|
- New features (feat phases). v1.14 is NFR-only.
|
||||||
|
- D-083 audit ledger build-out (S3 Object Lock + JWS + SQS DLQ + async
|
||||||
|
worker) — remains deferred; documented explicitly in ARCHITECTURE.md.
|
||||||
|
- Real OIDC federation (blocked on go-gitea/gitea#36988).
|
||||||
|
- Per-phase regression hardening (G-007, unchanged).
|
||||||
|
- Boto3 post-deploy verification probes (deferred to a future QA
|
||||||
|
milestone).
|
||||||
|
|
||||||
|
**Milestone type:** NFR (all phases are fix/test/docs/chore/refactor).
|
||||||
|
**Ship tag:** final phase patch on the v1.13.x line IS the release.
|
||||||
|
|
||||||
|
## Milestone v1.14 Phases
|
||||||
|
|
||||||
|
| Phase | Name | Goal |
|
||||||
|
|-------|------|------|
|
||||||
|
| 0 | pre-execution | SPECIFY → CLARIFY → RESEARCH → IDEATE → PLAN → GRILL. Establish v1.14 milestone shell; ideate finds the concrete requirements; plan decomposes into 20 execution phases. |
|
||||||
|
| 1–20 | execution | 20 phases of bug fixes, security hardening, stub resolution, test coverage, docs sync (wave-ordered). See ROADMAP.md §v1.14 for the phase list. |
|
||||||
|
| 21 | final-review-ship | Multi-persona review + audit + milestone ship (merge to main, tag final patch = release). |
|
||||||
|
|
||||||
|
## Key Decisions (v1.14)
|
||||||
|
|
||||||
|
Resolved at the CLARIFY stage (full autonomy — all within locked
|
||||||
|
constraints or user-directed scope). New v1.14 decisions (numbered
|
||||||
|
D-095+ to continue from v1.10's D-094):
|
||||||
|
|
||||||
|
| ID | Decision | Rationale | Outcome |
|
||||||
|
|----|----------|-----------|---------|
|
||||||
|
| D-095 | v1.14 is an NFR milestone (no feat phases); final patch IS the release. | User directed: "No new features, only bug fixes, security posture improvements, identifying stub and implement missing/lacking functionality, refine all documentation + NFRs." NFR model per branch-strategy.md:181 — progressive patches, final patch = deliverable, no separate milestone tag. | 20 execution phases (P1–P20) + 1 final (P21). Tags v1.13.3 → v1.13.24. |
|
||||||
|
| D-096 | D-083 (audit ledger JWS + S3 Object Lock + SQS DLQ + async worker) remains deferred; documented explicitly in ARCHITECTURE.md (P19), not implemented. | User chose "Skip — keep D-083 deferred." Requires non-offline-testable AWS infra (Object Lock bucket, KMS signing key, SQS). The hash-chain + DynamoDB outbox remains the v1.14 audit record. | P14 (originally JWS) replaced with orphan-artifact-and-dead-config-cleanup. D-083 deferral recorded in P19. |
|
||||||
|
| D-097 | 20 execution phases is the target (not consolidated to ~10). | User chose "20 phases as planned." Finer ship granularity; longer milestone. G-007 (per-phase regression) accepted — regression gate runs at milestone COMPLETE. | 20 phases + 1 final = 21-phase milestone. |
|
||||||
|
| D-098 | Wave ordering: W1 (P1–P6 bug fixes), W2 (P7–P12 security), W3 (P13–P17 stub/test/CI/hygiene), W4 (P18–P20 standards/docs/VPC). | Prerequisite chains: P2 depends on P1 (composition needs correct dedup); P9 depends on P8 (IAM ARNs reference externalized account ID); P15 depends on P7 (script tests benefit from hardened errors); P17 depends on P14 (both touch config.json); P19 lands last (reflects all prior phases). | 4 sequential waves; phases within a wave are independent (parallelizable when parallelization.enabled=true). |
|
||||||
|
| D-099 | `--ideate` flag: run the IDEATE stage between RESEARCH and PLAN (per ideate.md:218). The ideation tiers mine the 50 `partial:` + 16 `lessons:` + 3 `escalation:` + 16 `decisions:` git-native signals to validate/enrich the 20-phase scope. | User invoked with `--ideate`. The v1.14 scope is already user-directed (20 phases defined), so IDEATE acts as validation + enrichment, not scope discovery. Accepted ideas become IDEATE-NN IDs appended to REQUIREMENTS.md. | IDEATE stage runs; interactive validation gate (accept/skip/modify). |
|
||||||
|
| D-100 | Accept all 20 ideation findings as the v1.14 requirement set (REQ-135..REQ-154). | User accepted all 20 at the interactive validation gate. Mechanical + backend-enriched tiers confirmed the user-directed scope. | 20 REQs locked; PLAN.md formalizes the task decomposition. |
|
||||||
|
| D-101 | E-001 (P8 state-bucket continuity residual risk) auto-resolved at full autonomy: accept the residual risk. G-102's binding mitigation (fallback bound to live account ID + workflow env wiring) is the control. The lifecycle pipeline defaults to plan-only (REQ-134) — full-mode runs are workflow_dispatch only, reducing the accident surface. | Grill escalation E-001 (confidence 0.55) re-exposes the v1.11 4-VPC root cause. At full autonomy, auto-decide with assumption logging. The residual risk (misconfigured env at live-run time) is runtime-dependent, not plan-resolvable. If the user prefers zero residual risk, direct that P8 exclude the state-bucket name from externalization entirely. | E-001 resolved; G-102 binding decision enforced in PLAN.md P8. |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Milestone v1.15 — Nova (Rebrand)
|
||||||
|
|
||||||
|
**Active milestone.** A full rebrand from ACDL → Nova across docs,
|
||||||
|
decks, code, configs, CI, env var prefixes, the consumer contract path,
|
||||||
|
SSM parameter paths, AWS tag keys, and AWS resource names — with a
|
||||||
|
staged infrastructure migration to avoid breakage.
|
||||||
|
|
||||||
|
**Milestone type:** Major (breaking — consumer-facing path, env var
|
||||||
|
prefixes, SSM path, AWS tag keys, and AWS resource names all change).
|
||||||
|
Tags run on the v1.15.x minor line: `v1.15.0` (P0) → `v1.15.4` (P5
|
||||||
|
final = milestone release). (G-104 binding: Major milestones tag on
|
||||||
|
their own minor line, not the previous minor's patch line.)
|
||||||
|
|
||||||
|
**In scope (v1.15):**
|
||||||
|
- Prose/decks/mermaid/pyproject/release-title rebrand (P1).
|
||||||
|
- Code identifiers, env var prefixes (`ACDL_*`→`NOVA_*` dual-read),
|
||||||
|
consumer path (`.acdl/`→`.nova/`) (P2).
|
||||||
|
- SSM path (`/acdl/`→`/nova/`) + AWS tag keys (`acdl:*`→`nova:*` ABAC)
|
||||||
|
(P3).
|
||||||
|
- AWS resource names (`acdl-*`→`nova-*`) with migration (P4).
|
||||||
|
- Final review + audit + remove dual-read fallback + milestone ship (P5).
|
||||||
|
|
||||||
|
**Out of scope (v1.15):**
|
||||||
|
- Renaming the real Gitea org/repo or GitHub org `acdl` (config stays
|
||||||
|
`acdl`; doc URLs updated to `nova` for prose only).
|
||||||
|
- Renaming the S&P Global Energy visual theme (`sp-theme.json`) —
|
||||||
|
client branding.
|
||||||
|
- Past Gitea release titles — only future releases use `Nova vX.Y.Z`.
|
||||||
|
- Git branch/tag naming — no brand name present.
|
||||||
|
|
||||||
|
**Milestone type:** Major (breaking). **Ship tag:** final phase patch
|
||||||
|
on the v1.15.x minor line IS the release (`v1.15.4`).
|
||||||
|
|
||||||
|
## Milestone v1.15 Phases
|
||||||
|
|
||||||
|
| Phase | Name | Goal |
|
||||||
|
|-------|------|------|
|
||||||
|
| 0 | pre-execution | SPECIFY → CLARIFY → RESEARCH → IDEATE → PLAN → GRILL. Establish v1.15-Nova milestone shell; ideation finds the 10 Nova requirements (REQ-155..164); plan decomposes into 4 execution phases. |
|
||||||
|
| 1 | docs-decks-prose | Rebrand all prose/decks/mermaid/pyproject/release-titles ACDL→Nova; add Nova tagline; ship consumer migration guide. |
|
||||||
|
| 2 | code-envvars-consumer-path | Rename acdl_tagging.py→nova_tagging.py; ACDL_*→NOVA_* dual-read; .acdl/→.nova/ contract path. |
|
||||||
|
| 3 | ssm-tagkeys | SSM /acdl/→/nova/ + AWS tag keys acdl:*→nova:* with parallel-tag ABAC migration. |
|
||||||
|
| 4 | aws-resource-migration | Rename all acdl-* AWS resources → nova-* with staged migration + runbook. |
|
||||||
|
| 5 | final-review-ship | Multi-persona review + audit + remove dual-read fallback + milestone ship (merge to main, tag final patch = release). |
|
||||||
|
|
||||||
|
## Key Decisions (v1.15)
|
||||||
|
|
||||||
|
Resolved at the CLARIFY stage (full autonomy — all within locked
|
||||||
|
constraints or user-directed scope). New v1.15 decisions (numbered
|
||||||
|
D-102+ to continue from v1.14's D-101). The high-judgment scope
|
||||||
|
decisions (D-102..D-107) were locked in by the user during the planning
|
||||||
|
conversation before execution; D-108..D-112 resolved at CLARIFY.
|
||||||
|
|
||||||
|
| ID | Decision | Rationale | Outcome |
|
||||||
|
|----|----------|-----------|---------|
|
||||||
|
| D-102 | AWS resource names: full rename with migration. | User chose "Full rename with migration." All `acdl-*` AWS resources → `nova-*` including state bucket migration, DynamoDB data migration, IAM re-bootstrap, ECR re-push. Accepts downtime + multi-phase migration. | P4 implements the staged migration + rollback runbook. |
|
||||||
|
| D-103 | Env var prefixes: full rename to `NOVA_*`. | User chose "Full rename to `NOVA_*`." All 21 `ACDL_*` prefixes → `NOVA_*` including `.env.secrets` (key names only, values stay) + Gitea secrets. | P2 renames + implements dual-read fallback; P5 removes fallback. |
|
||||||
|
| D-104 | Tag keys + SSM path + consumer path: full rename all three. | User chose "Full rename all three." AWS tag keys `acdl:*`→`nova:*` (ABAC re-scope), SSM path `/acdl/`→`/nova/` (param migration), consumer path `.acdl/`→`.nova/`. | P2 (consumer path) + P3 (SSM + tag keys) implement. |
|
||||||
|
| D-105 | External URLs: illustrative — update them. | User chose "URLs are illustrative — update them." Doc URLs (`github.com/acdl/...`, `git.cloudinit.dev/.../acdl*`) → `nova` for prose consistency. Real Gitea repo name (`release.gitea.repo`) stays `acdl`. | P1 updates doc URLs; config.json unchanged. |
|
||||||
|
| D-106 | Nova tagline: add alongside existing North Star. | User chose "Add Nova tagline alongside existing North Star." Tagline "The New Dawn of DevSecOps — security as a seamless enabler of fast deployments" added to README header, deck title slides, `docs/vision.md`. Existing "consumers declare intent" framing retained. | P1 adds tagline; no prose removed. |
|
||||||
|
| D-107 | S&P visual theme: leave untouched. | User chose "Leave S&P theme untouched." `sp-theme.json` (#D6002A red, Akkurat Pro) is client branding, not the Nova product brand. Only product-brand text (ACDL→Nova) changes in decks. | P1 edits deck text only; theme/CSS unchanged. |
|
||||||
|
| D-108 | Dual-read fallback centralized in a new `core/env.py` helper. | No centralized env loader exists today (env vars read via scattered `os.environ.get("ACDL_*")`). A new `core/env.py` `get_env(name)` helper reads `NOVA_X` then falls back to `ACDL_X`, returning `None` if neither. All call sites migrate to the helper in P2; P5 removes the fallback. | P2 creates `core/env.py` + migrates call sites; P5 removes fallback. |
|
||||||
|
| D-109 | Checkov custom rule `nova_tagging.py` warns during P2, hard-fails from P3. | During P2 (before tag-key migration), existing resources still carry `acdl:*` tags — a hard fail would break the regression gate. P2 rule warns on `acdl:*`; P3 (after parallel-tag + ABAC swap) hard-fails on `acdl:*` and enforces `nova:*`. | P2: warn mode; P3: hard mode. |
|
||||||
|
| D-110 | Schema `$id` URLs (`https://acdl.cloudinit.dev/schemas/...`) → `https://nova.cloudinit.dev/schemas/...`. | These are illustrative schema identifiers (no real DNS resolution required for JSON-schema validation). Renamed for brand consistency in P1. Existing `$id` values in test fixtures updated. | P1 renames schema `$id` + fixture references. |
|
||||||
|
| 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. |
|
||||||
|
|
||||||
|
## Objective for Milestone v1.16 (active — NFR Simplification)
|
||||||
|
|
||||||
|
A 20-phase NFR sweep (no new features) themed around five axes the user
|
||||||
|
directed during ideation: **Simplify without regressions**, **Security**,
|
||||||
|
**Maintainability**, **User/Developer Experience**, and **No Humans
|
||||||
|
Onboarding Flow**. The v1.15 rebrand left a fresh layer of residual debt
|
||||||
|
(stale brand strings, a state-bucket drift, a Kyverno policy that
|
||||||
|
contradicts the Nova tagging standard, dead code) that this milestone
|
||||||
|
clears, alongside genuine simplification (dedup helpers, a workflow
|
||||||
|
generator, file splits) and the first self-service onboarding request
|
||||||
|
path (request-path only; real AWS account provisioning stays a future
|
||||||
|
feature).
|
||||||
|
|
||||||
|
**Milestone 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).
|
||||||
|
|
||||||
|
**Wave ordering:**
|
||||||
|
- Wave 1 (P1–P4): correctness + brand regression fixes — P1 first
|
||||||
|
(state-bucket drift + Kyverno label contradiction are the highest-
|
||||||
|
severity findings, both correctness regressions left by the rebrand).
|
||||||
|
- Wave 2 (P5–P9): simplify without regressions — P5 before P6/P9
|
||||||
|
(regression-verify dedup is independent); P8 changes the workflow test.
|
||||||
|
- Wave 3 (P10–P14): security + maintainability — P10 before P11
|
||||||
|
(identity enforcement before payload validation); P12/P13 independent
|
||||||
|
splits.
|
||||||
|
- Wave 4 (P15–P17): developer experience — independent; P17 last
|
||||||
|
(reflects the consolidated path).
|
||||||
|
- Wave 5 (P18–P20): no-humans onboarding — P18 (schema+Lambda action)
|
||||||
|
before P19 (env-file autogen consumes the schema) before P20 (cross-
|
||||||
|
account role, offline-proven).
|
||||||
|
|
||||||
|
**Verification gates:** the regression gate (D-091) runs after Wave 2
|
||||||
|
(P9) and at P21 — all 22 capabilities must stay Verified (no
|
||||||
|
regressions from simplification). A mid-milestone checkpoint runs after
|
||||||
|
Wave 3 (P14), offline.
|
||||||
|
|
||||||
|
## Milestone v1.16 Phases
|
||||||
|
|
||||||
|
| Phase | Name | Goal |
|
||||||
|
|-------|------|------|
|
||||||
|
| 01 | state-bucket-and-kyverno-rebrand-fix | `adapter.py:117` `acdl-tfstate`→`nova-tfstate`; Kyverno `require-resource-labels.yml` `acdl:*`→`nova:*` labels. Regression-risk fix. |
|
||||||
|
| 02 | user-facing-acdl-to-nova-sweep | Onboarding msg, alert title/body, PR comments, CI banner, module docstrings → Nova. |
|
||||||
|
| 03 | dead-code-and-stale-prefix-cleanup | Dead `ACDL_ENVIRONMENT_OVERRIDE` export; stale dual-read comments; `acdl_*` temp prefixes → `nova_*`. |
|
||||||
|
| 04 | migrate-ssm-except-narrowing | `migrate_ssm_paths.py` `except Exception`→`ParameterNotFound`. |
|
||||||
|
| 05 | regression-verify-dedup | Extract shared live-plan/resolver/lifecycle-resolve helpers (~70 lines saved). |
|
||||||
|
| 06 | run-platform-deadcode-and-hitl-fn | Remove dead export; extract `run_hitl_gate()` shell fn; drop hardcoded UUID/`v18` stamp. |
|
||||||
|
| 07 | contract-resolver-envloader-and-kind | Import env loader from environment_check; add `kind` field to registry; replace `is_l2` heuristic. |
|
||||||
|
| 08 | workflow-generator-dedup | `scripts/sync_workflows.py` (one source → both dirs); replace byte-identity test with generator-output test. |
|
||||||
|
| 09 | run-platform-split | Extract decommission + uptime blocks into `scripts/run_decommission.sh` + `scripts/run_uptime.sh`. |
|
||||||
|
| 10 | contract-ingestor-defense-in-depth | Fail closed on missing IAM identity; derive env enum from `core/environments/` dir. |
|
||||||
|
| 11 | contract-ingestor-payload-validation | Contract blob size cap + schema validation; consistent error/stackTrace caps. |
|
||||||
|
| 12 | split-contract-resolver | 638 lines → resolve / decommission-transform / cli modules. |
|
||||||
|
| 13 | split-regression-verify | 670 lines → capability checks / live-plan helpers / cli modules. |
|
||||||
|
| 14 | schema-driven-outputs-and-cache | `SAFE_OUTPUT_NAMES` from interface.json; cache loaded schemas in resolver. |
|
||||||
|
| 15 | run-platform-help-and-flags-doc | Real `--help`; document `--deploy-uptime`; surface `--local` in README. |
|
||||||
|
| 16 | workflows-readme-catalog | `.github/workflows/README.md` — triggers, inputs, secrets, reusable-workflow contracts. |
|
||||||
|
| 17 | getting-started-consolidation | Single getting-started section: offline happy path first, AWS path second. |
|
||||||
|
| 18 | onboarding-schema-and-lambda-action | `schemas/onboarding.schema.json` + `onboard_consumer` action → CMDB row pending grant. |
|
||||||
|
| 19 | onboarding-envfile-autogen | `core/onboarding.py` generates `<env>.json` from a request + emits a PR; rebrand onboarding message. |
|
||||||
|
| 20 | cross-account-role-automation-offline | Terraform for consumer deploy-role + `nova:owner` ABAC tag (offline-proven only). |
|
||||||
|
| 21 | final-review-ship | Review + audit + milestone ship `v1.15.26` + merge to main. |
|
||||||
|
|
||||||
|
Milestone COMPLETE gate: review → ship `v1.15.26` (NFR milestone; final
|
||||||
|
patch IS the release) → audit.
|
||||||
|
|
||||||
|
## Key Decisions (v1.16)
|
||||||
|
|
||||||
|
Resolved at the CLARIFY stage (full autonomy — all within locked
|
||||||
|
constraints or user-directed scope). New v1.16 decisions numbered D-113+
|
||||||
|
to continue from v1.15's D-112. The four high-judgment scope decisions
|
||||||
|
(D-113..D-116) were locked in by the user during the ideation planning
|
||||||
|
conversation; D-117..D-119 resolved at CLARIFY.
|
||||||
|
|
||||||
|
| ID | Decision | Rationale | Outcome |
|
||||||
|
|----|----------|-----------|---------|
|
||||||
|
| D-113 | Onboarding scope = request-path only (NFR-shaped). | User chose "Request-path only." Full self-service AWS account/network/state provisioning is a feature (creates real cloud resources), not an NFR. v1.16 removes the human handoff from the *request* step (schema + Lambda action + env-file autogen + ABAC grant hook); real AWS account creation stays a future feature milestone. | P18–P20 implement the request path; real provisioning deferred. |
|
||||||
|
| D-114 | Cross-account Terraform = offline-proven only. | User chose "Offline-proven only." P20 Terraform for the consumer deploy-role + ABAC tag is authored + `terraform validate` + `--check-only` only; no live apply (consistent with `NOVA_LIFECYCLE_MODE=plan` default). No new AWS resources created in this NFR milestone. | P20 validates offline; live apply deferred. |
|
||||||
|
| D-115 | Workflow dedup = generator (not status quo). | User chose "Generator." `scripts/sync_workflows.py` writes one source → both `.gitea/`+`.github/` dirs; the byte-identity test in `test_pipeline_contract.py` is replaced with a "generated outputs match committed files" test. Removes ~20 KB manual-sync risk. | P8 implements the generator + test swap. |
|
||||||
|
| D-116 | Drift fixes = P1 of v1.16 (not a hotfix to main). | User chose "P1 of v1.16." The state-bucket drift (`adapter.py:117`) and Kyverno label contradiction are correctness regressions but latent in plan-only mode (no live apply in the default path), so they are not an active outage. Fixing them as P1 keeps the milestone self-contained. | P1 fixes both; no hotfix to main. |
|
||||||
|
| D-117 | v1.14 NFR categories are NOT re-proposed. | v1.14 already swept over-broad excepts (REQ-141), hardcoded account-ID (REQ-142), IAM `Resource:"*"` scoping (REQ-143), contractId/env validation (REQ-144), `.gitignore` catch-all (REQ-146), `--kube-version` removal (REQ-147), orphan cleanup (REQ-148), `set -euo pipefail` parity (REQ-150). v1.16 finds NEW residual signals (the v1.15 rebrand left a fresh debt layer) and does not duplicate completed work. | Wave 1–5 target only fresh debt. |
|
||||||
|
| D-118 | Regression gate (D-091) gates Wave 2 completion and P21. | "Simplify without regressions" is only credible if the regression gate runs after the simplification wave. The gate runs after P9 (Wave 2 done) and at P21 (milestone complete); any non-Verified capability halts W3. Mid-milestone checkpoint after P14 (offline). | P9 + P21 run the gate; P14 checkpoint. |
|
||||||
|
| D-119 | `onboard_consumer` action stores a CMDB row pending grant (not auto-provisions). | The request-path-only scope (D-113) means the Lambda accepts an onboarding request and writes a `pending` row to `nova-contracts` (or a new `nova-onboarding` partition key); the platform automation that grants the ABAC role is the P20 Terraform (offline-proven). No AWS resources are created by the Lambda action itself. | P18 writes the pending row; P20 proves the grant Terraform offline. |
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"run_id": "regr-1785329757",
|
"run_id": "regr-1785375318",
|
||||||
"run_at_utc": "2026-07-29T12:55:57Z",
|
"run_at_utc": "2026-07-30T01:35:18Z",
|
||||||
"milestone": "v1.10",
|
"milestone": "v1.10",
|
||||||
"phase": 52,
|
"phase": 52,
|
||||||
"summary": {
|
"summary": {
|
||||||
@@ -16,7 +16,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": 252
|
"duration_ms": 230
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"capability_id": "CAP-002",
|
"capability_id": "CAP-002",
|
||||||
@@ -24,7 +24,7 @@
|
|||||||
"status": "Verified",
|
"status": "Verified",
|
||||||
"detail": "exit 0; env schema validates",
|
"detail": "exit 0; env schema validates",
|
||||||
"tier": "local",
|
"tier": "local",
|
||||||
"duration_ms": 196
|
"duration_ms": 204
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"capability_id": "CAP-003",
|
"capability_id": "CAP-003",
|
||||||
@@ -32,7 +32,7 @@
|
|||||||
"status": "Verified",
|
"status": "Verified",
|
||||||
"detail": "exit 0; ",
|
"detail": "exit 0; ",
|
||||||
"tier": "local",
|
"tier": "local",
|
||||||
"duration_ms": 258
|
"duration_ms": 247
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"capability_id": "CAP-004",
|
"capability_id": "CAP-004",
|
||||||
@@ -40,7 +40,7 @@
|
|||||||
"status": "Verified",
|
"status": "Verified",
|
||||||
"detail": "exit 0; ",
|
"detail": "exit 0; ",
|
||||||
"tier": "local",
|
"tier": "local",
|
||||||
"duration_ms": 264
|
"duration_ms": 241
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"capability_id": "CAP-005",
|
"capability_id": "CAP-005",
|
||||||
@@ -48,7 +48,7 @@
|
|||||||
"status": "Verified",
|
"status": "Verified",
|
||||||
"detail": "exit 0; ",
|
"detail": "exit 0; ",
|
||||||
"tier": "local",
|
"tier": "local",
|
||||||
"duration_ms": 314
|
"duration_ms": 326
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"capability_id": "CAP-006",
|
"capability_id": "CAP-006",
|
||||||
@@ -56,7 +56,7 @@
|
|||||||
"status": "Verified",
|
"status": "Verified",
|
||||||
"detail": "exit 0; interpolation ok",
|
"detail": "exit 0; interpolation ok",
|
||||||
"tier": "local",
|
"tier": "local",
|
||||||
"duration_ms": 223
|
"duration_ms": 216
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"capability_id": "CAP-007",
|
"capability_id": "CAP-007",
|
||||||
@@ -64,7 +64,7 @@
|
|||||||
"status": "Verified",
|
"status": "Verified",
|
||||||
"detail": "exit 0; confidence band=pass",
|
"detail": "exit 0; confidence band=pass",
|
||||||
"tier": "local",
|
"tier": "local",
|
||||||
"duration_ms": 80
|
"duration_ms": 79
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"capability_id": "CAP-008",
|
"capability_id": "CAP-008",
|
||||||
@@ -72,15 +72,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": 358
|
"duration_ms": 333
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"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====================== 462 passed, 2 deselected in 34.63s ======================",
|
"detail": "exit 0; [ 98%]\ntests/test_wiz_adapter_real_client.py ......... [100%]\n\n====================== 555 passed, 2 deselected in 51.11s ======================",
|
||||||
"tier": "local",
|
"tier": "local",
|
||||||
"duration_ms": 36065
|
"duration_ms": 52574
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"capability_id": "CAP-010",
|
"capability_id": "CAP-010",
|
||||||
@@ -88,23 +88,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": 40668
|
"duration_ms": 59608
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"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/acdl_local_e2e_416d0fmr/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/acdl_local_e2e_0v1bpi48/outbox\",\n \"outbox_events\": 2,\n \"outbox_chain_verified\": true,\n \"lambda_status\": 200\n}",
|
||||||
"tier": "local",
|
"tier": "local",
|
||||||
"duration_ms": 583
|
"duration_ms": 1072
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"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; acdl_local_e2e_ijhcj1z8/tf\",\n \"backend\": \"local\",\n \"ecs\": null,\n \"outbox_dir\": \"/tmp/acdl_local_e2e_ijhcj1z8/outbox\",\n \"outbox_events\": 2,\n \"outbox_chain_verified\": true,\n \"lambda_status\": 200\n}",
|
"detail": "exit 0; acdl_local_e2e_0cjcizgd/tf\",\n \"backend\": \"local\",\n \"ecs\": null,\n \"outbox_dir\": \"/tmp/acdl_local_e2e_0cjcizgd/outbox\",\n \"outbox_events\": 2,\n \"outbox_chain_verified\": true,\n \"lambda_status\": 200\n}",
|
||||||
"tier": "local",
|
"tier": "local",
|
||||||
"duration_ms": 489
|
"duration_ms": 490
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"capability_id": "CAP-013",
|
"capability_id": "CAP-013",
|
||||||
@@ -112,7 +112,7 @@
|
|||||||
"status": "Verified",
|
"status": "Verified",
|
||||||
"detail": "terraform init+validate+plan OK (live AWS, microservice)",
|
"detail": "terraform init+validate+plan OK (live AWS, microservice)",
|
||||||
"tier": "live-aws",
|
"tier": "live-aws",
|
||||||
"duration_ms": 28811
|
"duration_ms": 28176
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"capability_id": "CAP-014",
|
"capability_id": "CAP-014",
|
||||||
@@ -120,7 +120,7 @@
|
|||||||
"status": "Verified",
|
"status": "Verified",
|
||||||
"detail": "terraform init+validate+plan OK (live AWS, static-assets)",
|
"detail": "terraform init+validate+plan OK (live AWS, static-assets)",
|
||||||
"tier": "live-aws",
|
"tier": "live-aws",
|
||||||
"duration_ms": 31772
|
"duration_ms": 31892
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"capability_id": "CAP-015",
|
"capability_id": "CAP-015",
|
||||||
@@ -128,23 +128,23 @@
|
|||||||
"status": "Verified",
|
"status": "Verified",
|
||||||
"detail": "acdl-outbox exists, item_count=9",
|
"detail": "acdl-outbox exists, item_count=9",
|
||||||
"tier": "live-aws",
|
"tier": "live-aws",
|
||||||
"duration_ms": 477
|
"duration_ms": 507
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"capability_id": "CAP-016",
|
"capability_id": "CAP-016",
|
||||||
"name": "S3 state bucket exists + readable (live AWS)",
|
"name": "S3 state bucket exists + readable (live AWS)",
|
||||||
"status": "Verified",
|
"status": "Verified",
|
||||||
"detail": "state bucket exists, keys=['platform/terraform.tfstate', 'spike/alb/dev/terraform.tfstate', 'spike/cdn/dev/terraform.tfstate', 'spike/ci-vpc/terraform.tfstate', 'spike/clus/dev/terraform.tfstate']",
|
"detail": "state bucket exists, keys=['platform/terraform.tfstate', 'spike/alb/dev/terraform.tfstate', 'spike/assets/dev/terraform.tfstate', 'spike/cdn/dev/terraform.tfstate', 'spike/ci-vpc/terraform.tfstate']",
|
||||||
"tier": "live-aws",
|
"tier": "live-aws",
|
||||||
"duration_ms": 324
|
"duration_ms": 329
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"capability_id": "CAP-017",
|
"capability_id": "CAP-017",
|
||||||
"name": "DynamoDB acdl-contracts table (lifecycle pipeline evidence)",
|
"name": "DynamoDB acdl-contracts table (lifecycle pipeline evidence)",
|
||||||
"status": "Verified",
|
"status": "Verified",
|
||||||
"detail": "terraform files present + simple/complex contracts resolve",
|
"detail": "terraform files present + fmt -check passes + simple/complex contracts resolve",
|
||||||
"tier": "lifecycle-pipeline",
|
"tier": "lifecycle-pipeline",
|
||||||
"duration_ms": 520
|
"duration_ms": 588
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"capability_id": "CAP-018",
|
"capability_id": "CAP-018",
|
||||||
@@ -152,39 +152,39 @@
|
|||||||
"status": "Verified",
|
"status": "Verified",
|
||||||
"detail": "LocalLambdaStub instantiates (local tier evidence)",
|
"detail": "LocalLambdaStub instantiates (local tier evidence)",
|
||||||
"tier": "lifecycle-pipeline",
|
"tier": "lifecycle-pipeline",
|
||||||
"duration_ms": 137
|
"duration_ms": 135
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"capability_id": "CAP-019",
|
"capability_id": "CAP-019",
|
||||||
"name": "ECS cluster + service (L2 microservice lifecycle evidence)",
|
"name": "ECS cluster + service (L2 microservice lifecycle evidence)",
|
||||||
"status": "Verified",
|
"status": "Verified",
|
||||||
"detail": "L2 composition resolves (simple + complex contracts)",
|
"detail": "L2 composition resolves (simple + complex contracts; offline proxy)",
|
||||||
"tier": "lifecycle-pipeline",
|
"tier": "lifecycle-pipeline",
|
||||||
"duration_ms": 534
|
"duration_ms": 498
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"capability_id": "CAP-020",
|
"capability_id": "CAP-020",
|
||||||
"name": "CloudFront + WAF (L2 static-assets lifecycle evidence)",
|
"name": "CloudFront + WAF (L2 static-assets lifecycle evidence)",
|
||||||
"status": "Verified",
|
"status": "Verified",
|
||||||
"detail": "L2 composition resolves (simple + complex contracts)",
|
"detail": "L2 composition resolves (simple + complex contracts; offline proxy)",
|
||||||
"tier": "lifecycle-pipeline",
|
"tier": "lifecycle-pipeline",
|
||||||
"duration_ms": 567
|
"duration_ms": 510
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"capability_id": "CAP-021",
|
"capability_id": "CAP-021",
|
||||||
"name": "uptime-kuma (L1 uptime lifecycle evidence)",
|
"name": "uptime-kuma (L1 uptime lifecycle evidence)",
|
||||||
"status": "Verified",
|
"status": "Verified",
|
||||||
"detail": "terraform files present + simple/complex contracts resolve",
|
"detail": "terraform files present + fmt -check passes + simple/complex contracts resolve",
|
||||||
"tier": "lifecycle-pipeline",
|
"tier": "lifecycle-pipeline",
|
||||||
"duration_ms": 606
|
"duration_ms": 562
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"capability_id": "CAP-022",
|
"capability_id": "CAP-022",
|
||||||
"name": "OIDC role (L1 iam-role lifecycle evidence)",
|
"name": "OIDC role (L1 iam-role lifecycle evidence)",
|
||||||
"status": "Verified",
|
"status": "Verified",
|
||||||
"detail": "terraform files present + simple/complex contracts resolve",
|
"detail": "terraform files present + fmt -check passes + simple/complex contracts resolve",
|
||||||
"tier": "lifecycle-pipeline",
|
"tier": "lifecycle-pipeline",
|
||||||
"duration_ms": 529
|
"duration_ms": 554
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -1,51 +1,51 @@
|
|||||||
# Regression Report — v1.10 Phase 52
|
# Regression Report — v1.10 Phase 52
|
||||||
|
|
||||||
- **Run ID:** `regr-1785329757`
|
- **Run ID:** `regr-1785375318`
|
||||||
- **Run at (UTC):** 2026-07-29T12:55:57Z
|
- **Run at (UTC):** 2026-07-30T01:35:18Z
|
||||||
- **Summary:** {'Verified': 22, 'Decayed': 0, 'Broken': 0}
|
- **Summary:** {'Verified': 22, 'Decayed': 0, 'Broken': 0}
|
||||||
- **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** | 252 | exit 0; 2 sample contracts validate |
|
| CAP-001 | contract.schema.json validates sample contracts | local | **Verified** | 230 | exit 0; 2 sample contracts validate |
|
||||||
| CAP-002 | environment.schema.json validates env files | local | **Verified** | 196 | exit 0; env schema validates |
|
| CAP-002 | environment.schema.json validates env files | local | **Verified** | 204 | exit 0; env schema validates |
|
||||||
| CAP-003 | contract_resolver resolves static-assets | local | **Verified** | 258 | exit 0; |
|
| CAP-003 | contract_resolver resolves static-assets | local | **Verified** | 247 | exit 0; |
|
||||||
| CAP-004 | contract_resolver resolves microservice | local | **Verified** | 264 | exit 0; |
|
| CAP-004 | contract_resolver resolves microservice | local | **Verified** | 241 | exit 0; |
|
||||||
| CAP-005 | terraform adapter emits .tf files | local | **Verified** | 314 | exit 0; |
|
| CAP-005 | terraform adapter emits .tf files | local | **Verified** | 326 | exit 0; |
|
||||||
| CAP-006 | contract interpolation expands env/contract tokens | local | **Verified** | 223 | exit 0; interpolation ok |
|
| CAP-006 | contract interpolation expands env/contract tokens | local | **Verified** | 216 | exit 0; interpolation ok |
|
||||||
| CAP-007 | confidence_signal.compute returns a band | local | **Verified** | 80 | exit 0; confidence band=pass |
|
| CAP-007 | confidence_signal.compute returns a band | local | **Verified** | 79 | exit 0; confidence band=pass |
|
||||||
| CAP-008 | outbox_writer builds a hash-chained item | local | **Verified** | 358 | exit 0; outbox hash chain ok |
|
| CAP-008 | outbox_writer builds a hash-chained item | local | **Verified** | 333 | exit 0; outbox hash chain ok |
|
||||||
| CAP-009 | offline pytest suite passes | local | **Verified** | 36065 | exit 0; [ 98%]
|
| CAP-009 | offline pytest suite passes | local | **Verified** | 52574 | exit 0; [ 98%]
|
||||||
tests/test_wiz_adapter_real_client.py ......... [100%]
|
tests/test_wiz_adapter_real_client.py ......... [100%]
|
||||||
|
|
||||||
====================== 462 passe |
|
====================== 555 passe |
|
||||||
| CAP-010 | run_ci.sh reproduces CI pipeline locally | local | **Verified** | 40668 | exit 0; resource(s))
|
| CAP-010 | run_ci.sh reproduces CI pipeline locally | local | **Verified** | 59608 | 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** | 583 | exit 0; al-emulator",
|
| CAP-011 | headline E2E runs against the local emulating tier (microservice) | local | **Verified** | 1072 | exit 0; al-emulator",
|
||||||
"desired_count": 1,
|
"desired_count": 1,
|
||||||
"running_count": 1
|
"running_count": 1
|
||||||
},
|
},
|
||||||
"outbox_dir": "/tmp/acdl_local_e2e_416d0fmr/outbox",
|
"outbox_dir": "/tmp/acdl_local_e2e_0v1bpi48/outbox",
|
||||||
"outbox_events": 2,
|
"outbox_events": 2,
|
||||||
"outbox |
|
"outbox |
|
||||||
| CAP-012 | local E2E on the static-assets stack (no ECS) | local | **Verified** | 489 | exit 0; acdl_local_e2e_ijhcj1z8/tf",
|
| CAP-012 | local E2E on the static-assets stack (no ECS) | local | **Verified** | 490 | exit 0; acdl_local_e2e_0cjcizgd/tf",
|
||||||
"backend": "local",
|
"backend": "local",
|
||||||
"ecs": null,
|
"ecs": null,
|
||||||
"outbox_dir": "/tmp/acdl_local_e2e_ijhcj1z8/outbox",
|
"outbox_dir": "/tmp/acdl_local_e2e_0cjcizgd/outbox",
|
||||||
"outbox_events": 2,
|
"outbox_events": 2,
|
||||||
"outbox |
|
"outbox |
|
||||||
| CAP-013 | terraform init+validate+plan live AWS (microservice) | live-aws | **Verified** | 28811 | terraform init+validate+plan OK (live AWS, microservice) |
|
| CAP-013 | terraform init+validate+plan live AWS (microservice) | live-aws | **Verified** | 28176 | terraform init+validate+plan OK (live AWS, microservice) |
|
||||||
| CAP-014 | terraform init+validate+plan live AWS (static-assets) | live-aws | **Verified** | 31772 | terraform init+validate+plan OK (live AWS, static-assets) |
|
| CAP-014 | terraform init+validate+plan live AWS (static-assets) | live-aws | **Verified** | 31892 | terraform init+validate+plan OK (live AWS, static-assets) |
|
||||||
| CAP-015 | DynamoDB outbox table exists (live AWS) | live-aws | **Verified** | 477 | acdl-outbox exists, item_count=9 |
|
| CAP-015 | DynamoDB outbox table exists (live AWS) | live-aws | **Verified** | 507 | acdl-outbox exists, item_count=9 |
|
||||||
| CAP-016 | S3 state bucket exists + readable (live AWS) | live-aws | **Verified** | 324 | state bucket exists, keys=['platform/terraform.tfstate', 'spike/alb/dev/terraform.tfstate', 'spike/cdn/dev/terraform.tfstate', 'spike/ci-vpc/terraform.tfstate', |
|
| CAP-016 | S3 state bucket exists + readable (live AWS) | live-aws | **Verified** | 329 | state bucket exists, keys=['platform/terraform.tfstate', 'spike/alb/dev/terraform.tfstate', 'spike/assets/dev/terraform.tfstate', 'spike/cdn/dev/terraform.tfsta |
|
||||||
| CAP-017 | DynamoDB acdl-contracts table (lifecycle pipeline evidence) | lifecycle-pipeline | **Verified** | 520 | terraform files present + simple/complex contracts resolve |
|
| CAP-017 | DynamoDB acdl-contracts table (lifecycle pipeline evidence) | lifecycle-pipeline | **Verified** | 588 | terraform files present + fmt -check passes + simple/complex contracts resolve |
|
||||||
| CAP-018 | Lambda contract-ingestor (local stub + lifecycle evidence) | lifecycle-pipeline | **Verified** | 137 | LocalLambdaStub instantiates (local tier evidence) |
|
| CAP-018 | Lambda contract-ingestor (local stub + lifecycle evidence) | lifecycle-pipeline | **Verified** | 135 | LocalLambdaStub instantiates (local tier evidence) |
|
||||||
| CAP-019 | ECS cluster + service (L2 microservice lifecycle evidence) | lifecycle-pipeline | **Verified** | 534 | L2 composition resolves (simple + complex contracts) |
|
| CAP-019 | ECS cluster + service (L2 microservice lifecycle evidence) | lifecycle-pipeline | **Verified** | 498 | L2 composition resolves (simple + complex contracts; offline proxy) |
|
||||||
| CAP-020 | CloudFront + WAF (L2 static-assets lifecycle evidence) | lifecycle-pipeline | **Verified** | 567 | L2 composition resolves (simple + complex contracts) |
|
| CAP-020 | CloudFront + WAF (L2 static-assets lifecycle evidence) | lifecycle-pipeline | **Verified** | 510 | L2 composition resolves (simple + complex contracts; offline proxy) |
|
||||||
| CAP-021 | uptime-kuma (L1 uptime lifecycle evidence) | lifecycle-pipeline | **Verified** | 606 | terraform files present + simple/complex contracts resolve |
|
| CAP-021 | uptime-kuma (L1 uptime lifecycle evidence) | lifecycle-pipeline | **Verified** | 562 | terraform files present + fmt -check passes + simple/complex contracts resolve |
|
||||||
| CAP-022 | OIDC role (L1 iam-role lifecycle evidence) | lifecycle-pipeline | **Verified** | 529 | terraform files present + simple/complex contracts resolve |
|
| CAP-022 | OIDC role (L1 iam-role lifecycle evidence) | lifecycle-pipeline | **Verified** | 554 | terraform files present + fmt -check passes + simple/complex contracts resolve |
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# ACDL — Requirements
|
# Nova — Requirements
|
||||||
|
|
||||||
## v1
|
## v1
|
||||||
|
|
||||||
@@ -610,3 +610,349 @@ two probe fixes required to make the deck claims true.
|
|||||||
backwards-sequencing failure mode (PRE_MORTEM.md FM-3).
|
backwards-sequencing failure mode (PRE_MORTEM.md FM-3).
|
||||||
- New capability claims beyond what v1.11 verified.
|
- New capability claims beyond what v1.11 verified.
|
||||||
- Per-phase regression hardening (G-007, unchanged).
|
- Per-phase regression hardening (G-007, unchanged).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Milestone v1.14 — NFR Refinement (REQ-135..REQ-154)
|
||||||
|
|
||||||
|
**Objective:** Bug fixes, security posture improvements, stub/missing-
|
||||||
|
functionality identification + implementation, and documentation + NFR
|
||||||
|
refinement across the entire codebase. **No new features.** NFR milestone
|
||||||
|
— the final phase's patch IS the deliverable.
|
||||||
|
|
||||||
|
The v1.11 multi-persona review left 5 P1 + 4 P2 findings open; the
|
||||||
|
codebase has 6+ swallowed-error sites, 15+ hardcoded account-ID
|
||||||
|
references, 7 untested scripts, an offline-proxy regression gate,
|
||||||
|
ARCHITECTURE.md with no v1.11–v1.13 addendum, and consumer-facing docs
|
||||||
|
referencing stale `@v1.6`–`@v1.9` workflow tags. v1.14 clears all of it
|
||||||
|
in a 20-phase sweep.
|
||||||
|
|
||||||
|
### Requirements
|
||||||
|
|
||||||
|
- **REQ-135** — The adapter dedup loop raises `ValueError` for
|
||||||
|
unregistered-module resources instead of silently dropping them (P1-1).
|
||||||
|
(Phase P1)
|
||||||
|
- **REQ-136** — The static-assets L2 composition wires `default_ttl`/
|
||||||
|
`max_ttl`/`price_class`/`viewer_protocol_policy` and makes WAF
|
||||||
|
conditional via `waf_enabled`, so `complex.yml` is a real modify (P1-2).
|
||||||
|
(Phase P2)
|
||||||
|
- **REQ-137** — The L2 lifecycle scripts' usage strings no longer
|
||||||
|
advertise the vestigial `[ci-vpc-outputs.json]` arg, or document the
|
||||||
|
remote-state design (P1-3). (Phase P3)
|
||||||
|
- **REQ-138** — The regression gate's CAP-017..022 checks run
|
||||||
|
`terraform validate` (not just file-existence + resolver); the
|
||||||
|
offline-proxy caveat is documented honestly (P1-5). (Phase P4)
|
||||||
|
- **REQ-139** — Unit tests for adapter dedup merge behavior +
|
||||||
|
`ACDL_REMOTE_STATE_KEY` override exist and pass (P2-2). (Phase P5)
|
||||||
|
- **REQ-140** — The ALB target group `name_prefix` derives from `var.name`
|
||||||
|
(P2-1). (Phase P6)
|
||||||
|
- **REQ-141** — 6 over-broad `except ...: pass` sites narrowed to specific
|
||||||
|
exceptions; errors logged with context. (Phase P7)
|
||||||
|
- **REQ-142** — The hardcoded account ID `581513795199` is externalized to
|
||||||
|
`ACDL_AWS_ACCOUNT_ID` env / `data.aws_caller_identity` across 15+ sites.
|
||||||
|
(Phase P8)
|
||||||
|
- **REQ-143** — 6 `Resource: "*"` IAM statements scoped to `acdl-*` ARNs;
|
||||||
|
regression test asserts the scoping. (Phase P9)
|
||||||
|
- **REQ-144** — The contract ingestor validates `contractId`/`environment`/
|
||||||
|
`error`; ABAC reliance documented; spoofing-resistance test passes.
|
||||||
|
(Phase P10)
|
||||||
|
- **REQ-145** — `contract.schema.json` + `environment.schema.json` reject
|
||||||
|
undocumented fields (`additionalProperties: false`); format validation
|
||||||
|
for bucket/ARN/CIDR. (Phase P11)
|
||||||
|
- **REQ-146** — `.gitignore` has a credential-pattern catch-all;
|
||||||
|
`test_no_secrets_tracked.py` passes. (Phase P12)
|
||||||
|
- **REQ-147** — The Kyverno `--kube-version` flag is either implemented or
|
||||||
|
removed with a documented deferral rationale. (Phase P13)
|
||||||
|
- **REQ-148** — Orphan bytecode + dead config cleaned (orphan `.pyc`,
|
||||||
|
stale coverage source, stale version, dead JS allowlist). (Phase P14)
|
||||||
|
- **REQ-149** — 7 untested scripts have unit test coverage (≥1 test each).
|
||||||
|
(Phase P15)
|
||||||
|
- **REQ-150** — Gitea workflow parity resolved; `rotate_spike_key.sh` +
|
||||||
|
`sync_to_gl.sh` have `set -euo pipefail`. (Phase P16)
|
||||||
|
- **REQ-151** — `config.json` persona block + branching strategy +
|
||||||
|
ollama-cloud backend aligned with PERSONAS.md + actual runtime.
|
||||||
|
(Phase P17)
|
||||||
|
- **REQ-152** — `modules/STANDARDS.md` internally consistent; no stale
|
||||||
|
`TYPE_MAP` reference. (Phase P18)
|
||||||
|
- **REQ-153** — ARCHITECTURE.md has v1.11–v1.14 addenda; stale `@v1.6–1.9`
|
||||||
|
→ `@v1.13`; GRILL G-005/G-008 resolved; COST.md window covers v1.11–v1.14;
|
||||||
|
D-083 deferral recorded. (Phase P19)
|
||||||
|
- **REQ-154** — Platform VPC CIDR is a variable; subnet count is
|
||||||
|
data-driven; `0.0.0.0/0` ingress documented. (Phase P20)
|
||||||
|
|
||||||
|
### v1.14 Traceability
|
||||||
|
|
||||||
|
| Requirement | Phase | Status |
|
||||||
|
|-------------|-------|--------|
|
||||||
|
| REQ-135 | P1 | complete |
|
||||||
|
| REQ-136 | P2 | complete |
|
||||||
|
| REQ-137 | P3 | complete |
|
||||||
|
| REQ-138 | P4 | complete |
|
||||||
|
| REQ-139 | P5 | complete |
|
||||||
|
| REQ-140 | P6 | complete |
|
||||||
|
| REQ-141 | P7 | complete |
|
||||||
|
| REQ-142 | P8 | complete |
|
||||||
|
| REQ-143 | P9 | complete |
|
||||||
|
| REQ-144 | P10 | complete |
|
||||||
|
| REQ-145 | P11 | complete |
|
||||||
|
| REQ-146 | P12 | complete |
|
||||||
|
| REQ-147 | P13 | complete |
|
||||||
|
| REQ-148 | P14 | complete |
|
||||||
|
| REQ-149 | P15 | complete |
|
||||||
|
| REQ-150 | P16 | complete |
|
||||||
|
| REQ-151 | P17 | complete |
|
||||||
|
| REQ-152 | P18 | complete |
|
||||||
|
| REQ-153 | P19 | complete |
|
||||||
|
| REQ-154 | P20 | complete |
|
||||||
|
|
||||||
|
### Out of Scope (v1.14)
|
||||||
|
- New features (feat phases). v1.14 is NFR-only.
|
||||||
|
- D-083 audit ledger build-out (S3 Object Lock + JWS + SQS DLQ + async
|
||||||
|
worker) — remains deferred; documented explicitly in ARCHITECTURE.md.
|
||||||
|
- Real OIDC federation (blocked on go-gitea/gitea#36988).
|
||||||
|
- Per-phase regression hardening (G-007, unchanged).
|
||||||
|
- Boto3 post-deploy verification probes (deferred to a future QA
|
||||||
|
milestone).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## v1.15 — Nova (Rebrand)
|
||||||
|
|
||||||
|
**Milestone type:** Major (breaking — consumer-facing path, env var
|
||||||
|
prefixes, SSM path, AWS tag keys, and AWS resource names all change).
|
||||||
|
Per the branch-strategy precedent (breaking/feature milestones tag on
|
||||||
|
their OWN minor line), v1.15 tags run on the **v1.15.x minor line**:
|
||||||
|
`v1.15.0` (P0) → `v1.15.1..v1.15.4` (P1–P4) → `v1.15.4` (P5 final =
|
||||||
|
milestone release). (G-104 binding: the v1.14.x patch line is the NFR
|
||||||
|
convention; a Major milestone ships on its own minor.)
|
||||||
|
|
||||||
|
A full rebrand from **ACDL** / "Agentic Cloud Delivery Platform" →
|
||||||
|
**Nova** / "The New Dawn of DevSecOps — security as a seamless enabler
|
||||||
|
of fast deployments." The new tagline is added alongside the existing
|
||||||
|
"North Star" / "consumers declare intent" framing; the S&P Global Energy
|
||||||
|
visual theme (`sp-theme.json`) is a client brand and is **not** touched.
|
||||||
|
The rebrand applies across docs, decks, code, configs, CI, env vars,
|
||||||
|
consumer conventions, SSM paths, AWS tag keys, and AWS resource names —
|
||||||
|
with a staged infrastructure migration to avoid breakage.
|
||||||
|
|
||||||
|
Ideation source: `--ideate` flag (user-directed scope; the survey found
|
||||||
|
1,465 occurrences of `ACDL`/`acdl` across 205 files and zero existing
|
||||||
|
`nova` references — no collision risk). Accepted ideas become
|
||||||
|
IDEATE-01..IDEATE-10, mapped to REQ-155..REQ-164.
|
||||||
|
|
||||||
|
### Requirements
|
||||||
|
|
||||||
|
- **REQ-155** — (IDEATE-01) All prose, titles, headers, and comments
|
||||||
|
across `README.md`, `docs/**`, `.ciagent/*.md`, deck markdown sources,
|
||||||
|
`pyproject.toml` name/description, and `release.yml` release-title
|
||||||
|
prefix are rebranded `ACDL`/`Agentic Cloud Delivery Platform` → `Nova`.
|
||||||
|
Illustrative URLs in docs (`github.com/acdl/...`,
|
||||||
|
`git.cloudinit.dev/continuous-intelligence/acdl*`) are updated to
|
||||||
|
`nova` for prose consistency. Gitea release titles going forward read
|
||||||
|
`Nova vX.Y.Z` (past releases keep their names). (Phase P1)
|
||||||
|
- **REQ-156** — (IDEATE-02) All Marp deck markdown sources
|
||||||
|
(`docs/presentations/*-marp.md`, `*.md`, `*-talking-points.md`) and
|
||||||
|
mermaid source `.mmd` files are rebranded `ACDL` → `Nova`; the deck
|
||||||
|
title-slide subtitle becomes `Nova — The New Dawn of DevSecOps`. The
|
||||||
|
`.mmd` sources are edited and the rendered PNG diagrams are
|
||||||
|
re-exported so the committed PNGs match the new labels. The S&P visual
|
||||||
|
theme (`sp-theme.json`) is unchanged. HTML decks are re-rendered.
|
||||||
|
(Phase P1)
|
||||||
|
- **REQ-157** — (IDEATE-03) The Nova tagline ("The New Dawn of DevSecOps
|
||||||
|
— security as a seamless enabler of fast deployments") is added to the
|
||||||
|
README header, both deck title slides, and `docs/vision.md` —
|
||||||
|
alongside (not replacing) the existing "North Star" / "consumers
|
||||||
|
declare intent" framing. (Phase P1)
|
||||||
|
- **REQ-158** — (IDEATE-04) `adapters/terraform/policy/custom_rules/acdl_tagging.py`
|
||||||
|
is renamed `nova_tagging.py` with its Checkov custom-rule registration
|
||||||
|
updated (`schemas/tagging-standard.json` line 5 + adapter config). The
|
||||||
|
Checkov rule enforces `nova:*` tag keys. (Phase P2)
|
||||||
|
- **REQ-159** — (IDEATE-05) All 21 `ACDL_*` env var prefixes are renamed
|
||||||
|
to `NOVA_*` across `scripts/`, `core/`, `adapters/`, `tests/`,
|
||||||
|
workflows (`.gitea/`, `.github/`), `.env`, `.env.secrets` (key names
|
||||||
|
only — values/secret material stay), and consumer docs. A **dual-read
|
||||||
|
fallback** (`NOVA_X` preferred, fall back to `ACDL_X`) is implemented
|
||||||
|
in the config/env loader so deployments do not break during the
|
||||||
|
transition window; the fallback is removed in the final phase once all
|
||||||
|
consumers are migrated. Gitea repo secrets are rotated via API.
|
||||||
|
(Phase P2)
|
||||||
|
- **REQ-160** — (IDEATE-06) The consumer on-disk contract path
|
||||||
|
`.acdl/contract.yml` (and `.acdl/static-assets.*.yml`,
|
||||||
|
`.acdl/contract.yaml`) becomes `.nova/contract.yml` across the
|
||||||
|
contract resolver, deploy workflow checkout path, consumer docs, and
|
||||||
|
the contract schema description. A consumer migration guide is shipped
|
||||||
|
with P1 docs. (Phase P2)
|
||||||
|
- **REQ-161** — (IDEATE-07) The SSM parameter path prefix
|
||||||
|
`/acdl/{env}/{contractId}/{output}` becomes
|
||||||
|
`/nova/{env}/{contractId}/{output}` across `core/output_publisher`,
|
||||||
|
the contract resolver, and consumer docs. A migration script copies
|
||||||
|
existing `/acdl/...` parameters → `/nova/...`, readers are updated,
|
||||||
|
then old parameters are deleted. (Phase P3)
|
||||||
|
- **REQ-162** — (IDEATE-08) AWS tag keys `acdl:owner`,
|
||||||
|
`acdl:environment`, `acdl:contract`, `acdl:cost-center`, `acdl:ref`
|
||||||
|
become `nova:owner`, `nova:environment`, `nova:contract`,
|
||||||
|
`nova:cost-center`, `nova:ref` across terraform tagging, the Checkov
|
||||||
|
custom rule (`nova_tagging.py`), and ABAC session policies. A
|
||||||
|
**parallel-tag period** adds `nova:*` tags to all resources first,
|
||||||
|
updates the ABAC session policies to match `nova:*`, then removes the
|
||||||
|
`acdl:*` tags once consumers are verified. (Phase P3)
|
||||||
|
- **REQ-163** — (IDEATE-09) All `acdl-*` AWS resource names are renamed
|
||||||
|
to `nova-*` via terraform: KMS alias `alias/acdl-platform` →
|
||||||
|
`alias/nova-platform`, SNS `acdl-sod-halt` → `nova-sod-halt`, SG
|
||||||
|
`acdl-ecs-sg` → `nova-ecs-sg`, Lambda `acdl-contract-ingestor` →
|
||||||
|
`nova-contract-ingestor`, DynamoDB `acdl-contracts`/`acdl-change-requests`
|
||||||
|
→ `nova-contracts`/`nova-change-requests` (scan+copy data migration,
|
||||||
|
verify row counts, keep old tables until verified), ECR
|
||||||
|
`acdl-microservice` → `nova-microservice` (re-push images), IAM
|
||||||
|
user/policy `acdl-spike-runner` → `nova-spike-runner` (re-bootstrap
|
||||||
|
with new key), state bucket `acdl-tfstate-...` → `nova-tfstate-...`
|
||||||
|
(`terraform init -migrate-state` to new backend, state JSON backed up
|
||||||
|
first), ALB name prefix `acdl-alb` → `nova-alb` (recreate, brief
|
||||||
|
downtime). A maintenance window + rollback runbook is published with
|
||||||
|
the migration. (Phase P4)
|
||||||
|
- **REQ-164** — (IDEATE-10) The dual-read env var fallback
|
||||||
|
(`ACDL_*`→`NOVA_*`) and any `ACDL_*`-only references are removed once
|
||||||
|
all consumers are migrated; the consumer migration guide is finalized;
|
||||||
|
`nova_tagging.py` no longer accepts `acdl:*` tag keys. (Phase P5)
|
||||||
|
|
||||||
|
### v1.15 Traceability
|
||||||
|
|
||||||
|
| Requirement | Phase | Status |
|
||||||
|
|-------------|-------|--------|
|
||||||
|
| REQ-155 | P1 | complete |
|
||||||
|
| REQ-156 | P1 | complete |
|
||||||
|
| REQ-157 | P1 | complete |
|
||||||
|
| REQ-158 | P2 | complete |
|
||||||
|
| REQ-159 | P2 | complete |
|
||||||
|
| REQ-160 | P2 | complete |
|
||||||
|
| REQ-161 | P3 | complete |
|
||||||
|
| REQ-162 | P3 | complete |
|
||||||
|
| REQ-163 | P4 | complete |
|
||||||
|
| REQ-164 | P5 | complete |
|
||||||
|
|
||||||
|
### Out of Scope (v1.15)
|
||||||
|
- Renaming the real Gitea org/repo (`continuous-intelligence/acdl`) or
|
||||||
|
GitHub org `acdl` — config.json `release.gitea.repo` stays `acdl`;
|
||||||
|
URLs in docs are illustrative and updated to `nova` for prose only.
|
||||||
|
- Renaming the S&P Global Energy visual theme (`sp-theme.json`,
|
||||||
|
deck CSS) — that is client branding, not the Nova product brand.
|
||||||
|
- Past Gitea release titles — existing releases keep their `ACDL vX.Y.Z`
|
||||||
|
names; only future releases use `Nova vX.Y.Z`.
|
||||||
|
- Git branch/tag naming — branches use `milestone/v*` / `phase/*` and
|
||||||
|
tags use `v*` semver; no brand name present, no change needed.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## v1.16 — Nova Simplification (NFR)
|
||||||
|
|
||||||
|
**Milestone 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, and 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 clears, alongside genuine
|
||||||
|
simplification and the first self-service onboarding request path.
|
||||||
|
|
||||||
|
### Requirements
|
||||||
|
|
||||||
|
- **REQ-165** — The adapter-emitted terraform backend references
|
||||||
|
`nova-tfstate-*` (not `acdl-tfstate-*`); the Kyverno
|
||||||
|
`require-resource-labels.yml` policy enforces `nova:*` labels (not
|
||||||
|
`acdl:*`). Correctness regression fix from the v1.15 rebrand. (Phase P1)
|
||||||
|
- **REQ-166** — All user-facing "ACDL" strings rebranded to Nova:
|
||||||
|
onboarding message, Lambda alert title/body, PR-stage comments, CI
|
||||||
|
banner, module docstrings (contract_resolver/confidence_signal/adapter/
|
||||||
|
kyverno/wiz + adapters README). (Phase P2)
|
||||||
|
- **REQ-167** — Dead `ACDL_ENVIRONMENT_OVERRIDE` export removed; stale
|
||||||
|
dual-read comments dropped; `acdl_*` temp-dir prefixes → `nova_*`. (Phase P3)
|
||||||
|
- **REQ-168** — `migrate_ssm_paths.py` `except Exception: pass` narrowed
|
||||||
|
to `ParameterNotFound` + structured log. (Phase P4)
|
||||||
|
- **REQ-169** — `regression_verify.py` duplicated live-plan/resolver/
|
||||||
|
lifecycle-resolve blocks extracted into shared helpers (~70 lines
|
||||||
|
saved). (Phase P5)
|
||||||
|
- **REQ-170** — `run_platform.sh` dead export removed; HITL attestation
|
||||||
|
block extracted to a shell function; hardcoded UUID/`v18` work-dir
|
||||||
|
stamp replaced with config. (Phase P6)
|
||||||
|
- **REQ-171** — `contract_resolver.py` imports the env loader from
|
||||||
|
`environment_check` (dedup); registry entries carry a `kind` field;
|
||||||
|
fragile `is_l2` path-string heuristic replaced. (Phase P7)
|
||||||
|
- **REQ-172** — `scripts/sync_workflows.py` generates the 3
|
||||||
|
byte-identical workflow pairs from one source; the byte-identity test
|
||||||
|
is replaced with a generator-output test. (Phase P8)
|
||||||
|
- **REQ-173** — `run_platform.sh` decommission + uptime blocks extracted
|
||||||
|
into `scripts/run_decommission.sh` + `scripts/run_uptime.sh`. (Phase P9)
|
||||||
|
- **REQ-174** — `contract_ingestor.py` fails closed (not silent `pass`)
|
||||||
|
when IAM identity is absent; the env enum is derived from
|
||||||
|
`core/environments/` (not hardcoded). (Phase P10)
|
||||||
|
- **REQ-175** — The contract blob payload is size-capped + schema-
|
||||||
|
validated before the DynamoDB write; error/stackTrace caps are
|
||||||
|
consistent. (Phase P11)
|
||||||
|
- **REQ-176** — `contract_resolver.py` (638 lines) split into resolve /
|
||||||
|
decommission-transform / cli modules. (Phase P12)
|
||||||
|
- **REQ-177** — `regression_verify.py` (670 lines) split into capability
|
||||||
|
checks / live-plan helpers / cli modules. (Phase P13)
|
||||||
|
- **REQ-178** — `SAFE_OUTPUT_NAMES` is schema-driven (from
|
||||||
|
interface.json `sensitive` annotations); loaded schemas are cached in
|
||||||
|
the resolver. (Phase P14)
|
||||||
|
- **REQ-179** — `run_platform.sh` has a real `--help`; `--deploy-uptime`
|
||||||
|
is documented; `--local` is surfaced in the README. (Phase P15)
|
||||||
|
- **REQ-180** — `.github/workflows/README.md` catalogs all 7 workflows'
|
||||||
|
triggers, inputs, required secrets, and reusable-workflow contracts. (Phase P16)
|
||||||
|
- **REQ-181** — A single getting-started section in the README:
|
||||||
|
offline happy path (`run_ci.sh` + `run_platform.sh --check-only`/
|
||||||
|
`--local`) first, AWS path second. (Phase P17)
|
||||||
|
- **REQ-182** — `schemas/onboarding.schema.json` defines the onboarding
|
||||||
|
request; `contract_ingestor.py` gains an `onboard_consumer` action that
|
||||||
|
writes a `pending` CMDB row. (Phase P18)
|
||||||
|
- **REQ-183** — `core/onboarding.py` generates a `<env>.json` from a
|
||||||
|
consumer request + emits a PR; the onboarding message is rebranded to
|
||||||
|
Nova and no longer routes to "contact the platform team" for the
|
||||||
|
request step. (Phase P19)
|
||||||
|
- **REQ-184** — Terraform for the consumer deploy-role + `nova:owner`
|
||||||
|
ABAC tag grant, offline-proven (`terraform validate` + `--check-only`
|
||||||
|
only; no live apply). (Phase P20)
|
||||||
|
|
||||||
|
### v1.16 Traceability
|
||||||
|
|
||||||
|
| Requirement | Phase | Status |
|
||||||
|
|-------------|-------|--------|
|
||||||
|
| REQ-165 | P1 | pending |
|
||||||
|
| REQ-166 | P2 | pending |
|
||||||
|
| REQ-167 | P3 | pending |
|
||||||
|
| REQ-168 | P4 | pending |
|
||||||
|
| REQ-169 | P5 | pending |
|
||||||
|
| REQ-170 | P6 | pending |
|
||||||
|
| REQ-171 | P7 | pending |
|
||||||
|
| REQ-172 | P8 | pending |
|
||||||
|
| REQ-173 | P9 | pending |
|
||||||
|
| REQ-174 | P10 | pending |
|
||||||
|
| REQ-175 | P11 | pending |
|
||||||
|
| REQ-176 | P12 | pending |
|
||||||
|
| REQ-177 | P13 | pending |
|
||||||
|
| REQ-178 | P14 | pending |
|
||||||
|
| REQ-179 | P15 | pending |
|
||||||
|
| REQ-180 | P16 | pending |
|
||||||
|
| REQ-181 | P17 | pending |
|
||||||
|
| REQ-182 | P18 | pending |
|
||||||
|
| REQ-183 | P19 | pending |
|
||||||
|
| REQ-184 | P20 | pending |
|
||||||
|
|
||||||
|
### Out of Scope (v1.16)
|
||||||
|
- New features (feat phases). v1.16 is NFR-only.
|
||||||
|
- Real AWS account/network/state provisioning (self-service) — the
|
||||||
|
onboarding request path is implemented (D-113); actual cloud resource
|
||||||
|
creation stays a future feature milestone.
|
||||||
|
- Live apply of the cross-account role Terraform (D-114) — offline-proven
|
||||||
|
only; live apply deferred.
|
||||||
|
- D-083 audit ledger build-out (carries forward; unchanged).
|
||||||
|
- Real OIDC federation (carries forward; blocked on go-gitea/gitea#36988).
|
||||||
|
- Re-proposing v1.14 NFR categories already closed (D-117): over-broad
|
||||||
|
excepts (REQ-141), hardcoded account-ID (REQ-142), IAM `Resource:"*"`
|
||||||
|
scoping (REQ-143), contractId/env validation (REQ-144), `.gitignore`
|
||||||
|
catch-all (REQ-146), `--kube-version` removal (REQ-147), orphan
|
||||||
|
cleanup (REQ-148), `set -euo pipefail` parity (REQ-150).
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# ACDL — v1.11 RESTART Research Findings
|
# Nova — v1.11 RESTART Research Findings
|
||||||
|
|
||||||
> Phase: research (pre-Phase 56). Milestone: v1.11 (RESTART). Status: research.
|
> Phase: research (pre-Phase 56). Milestone: v1.11 (RESTART). Status: research.
|
||||||
> Researcher: ci-researcher. Autonomy: full (CLARIFY auto-resolved; all
|
> Researcher: ci-researcher. Autonomy: full (CLARIFY auto-resolved; all
|
||||||
@@ -692,4 +692,499 @@ A6 section to both talking-points files.
|
|||||||
decision, 2026-07-29).
|
decision, 2026-07-29).
|
||||||
- **D-109** — Decks use `@v1.11` in examples during Phase 68 (current
|
- **D-109** — Decks use `@v1.11` in examples during Phase 68 (current
|
||||||
state), bumped to `@v1.12` at Phase 70 complete after the tag exists.
|
state), bumped to `@v1.12` at Phase 70 complete after the tag exists.
|
||||||
Avoids a dangling reference to a tag that doesn't exist yet.
|
Avoids a dangling reference to a tag that doesn't exist yet.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## v1.14 Research Addendum — NFR Refinement scope audit (2026-07-29)
|
||||||
|
|
||||||
|
> Phase 0 RESEARCH for milestone v1.14 (NFR Refinement). A full codebase
|
||||||
|
> survey (8 categories, file:line evidence) was conducted to populate the
|
||||||
|
> 20-phase scope. This addendum records the findings; the phase list is
|
||||||
|
> in ROADMAP.md §v1.14; the requirements are in REQUIREMENTS.md §v1.14.
|
||||||
|
|
||||||
|
### Survey method
|
||||||
|
|
||||||
|
Read-only survey of `/root/acdl` at v1.13.2 (HEAD `139224ff`, 533 tests
|
||||||
|
collected). 8 categories: stubs, P1/P2 backlog, security, docs drift,
|
||||||
|
test gaps, terraform gaps, workflow gaps, config hygiene. All file:line
|
||||||
|
references verified against the live codebase.
|
||||||
|
|
||||||
|
### Finding 1 — Open P1/P2 backlog (REVIEW.md v1.11)
|
||||||
|
|
||||||
|
5 P1 + 4 P2 findings from the v1.11 multi-persona review remain open:
|
||||||
|
|
||||||
|
| ID | File:Line | Status | v1.14 phase |
|
||||||
|
|----|-----------|--------|-------------|
|
||||||
|
| P1-1 | `adapter.py:159-170` (silent drop of unregistered-module resources) | open | P1 |
|
||||||
|
| P1-2 | `static-assets/composition.json` (unwired cloudfront inputs; WAF unconditional) | open | P2 |
|
||||||
|
| P1-3 | `run_l2_lifecycle_*.sh` (vestigial `[ci-vpc-outputs.json]` arg) | open | P3 |
|
||||||
|
| P1-4 | `CAPABILITY_INVENTORY.md:9-16` (summary table stale) | **fixed** (now 22/22) | — |
|
||||||
|
| P1-5 | `regression_verify.py:432-519` (CAP-017..022 offline proxy, no `terraform validate`) | open | P4 |
|
||||||
|
| P2-1 | `alb/main.tf:9` (`name_prefix="tg-ci-"` discards `var.name`) | open | P6 |
|
||||||
|
| P2-2 | `test_adapter.py` (no dedup-merge or remote-state-key test) | open | P5 |
|
||||||
|
| P2-3 | `waf/complex.yml` + `locals.tf` (redundant `upper()` + uppercase example) | open (post-hoc) | folded into P2 |
|
||||||
|
| P2-4 | `COST.md:106` (account ID published; accepted exposure) | open (post-hoc) | folded into P8 (centralize code-side) |
|
||||||
|
|
||||||
|
### Finding 2 — Security posture gaps
|
||||||
|
|
||||||
|
**Swallowed errors (6 sites):**
|
||||||
|
- `core/local_emulators.py:374` — `except Exception: pass` in
|
||||||
|
`_fake_urlopen`; if patching fails, urlopen stays real → network
|
||||||
|
egress. [SEC] → P7.
|
||||||
|
- `core/lambda/contract_ingestor.py:157` — GitHub search failure →
|
||||||
|
`existing = []` → duplicate issues. → P7.
|
||||||
|
- `terraform/bootstrap/create_state_backend.py:51` — over-broad
|
||||||
|
`except Exception:` on `head_bucket` → spurious `create_bucket` on
|
||||||
|
permissions/network errors. → P7.
|
||||||
|
- `core/output_publisher.py:100,168` — SSM/GitHub failure → silent
|
||||||
|
`None`/`False`. → P7.
|
||||||
|
- `terraform/bootstrap/apply_iam_baseline.py:78` — over-broad on
|
||||||
|
old-version delete. → P7.
|
||||||
|
|
||||||
|
**Hardcoded account ID `581513795199` (15+ sites):**
|
||||||
|
`adapter.py:125,140`, `apply_iam_baseline.py:33`,
|
||||||
|
`create_state_backend.py:33,35`, `push_consumer_image.py:32`, terraform
|
||||||
|
state-bucket names, ECR image ref. → P8 (externalize to
|
||||||
|
`ACDL_AWS_ACCOUNT_ID` / `data.aws_caller_identity`).
|
||||||
|
|
||||||
|
**IAM policy wildcards (6 `Resource: "*"` statements):**
|
||||||
|
`spike_runner_policy.json` — cloudfront (line 117), wafv2 (129), kms
|
||||||
|
(218), iam (236). KMS allows key creation/deletion on ANY key; IAM
|
||||||
|
allows role creation on ANY role. → P9 (scope to `acdl-*` ARNs).
|
||||||
|
|
||||||
|
**Contract-ingestor identity validation gap:**
|
||||||
|
`contract_ingestor.py:221-245` — `_validate_caller_identity` validates
|
||||||
|
`consumerRepo` format only; doesn't verify caller owns the repo (ABAC
|
||||||
|
reliance). No `contractId`/`environment`/`error` validation. → P10.
|
||||||
|
|
||||||
|
**Schema validation gaps:**
|
||||||
|
`contract.schema.json` + `environment.schema.json` — no
|
||||||
|
`additionalProperties: false` (undocumented fields pass silently); no
|
||||||
|
format validation for bucket/ARN/CIDR. → P11.
|
||||||
|
|
||||||
|
**Credential hygiene:**
|
||||||
|
`.gitignore` covers `.env*`/`*.tfstate*` but no credential-pattern
|
||||||
|
catch-all (`*.pem`/`*.key`/`*.p12`). → P12.
|
||||||
|
|
||||||
|
**Audit ledger integrity (D-083):**
|
||||||
|
`audit_ledger_design.md:47-70` — JWS + Object Lock + DLQ deferred. Per
|
||||||
|
D-096, stays deferred; documented in P19. The hash-chain + DynamoDB
|
||||||
|
outbox is the v1.14 audit record.
|
||||||
|
|
||||||
|
### Finding 3 — Stubs / missing functionality
|
||||||
|
|
||||||
|
- `adapters/kyverno/kyverno_adapter.py:11,115-116` — `--kube-version`
|
||||||
|
parsed then discarded (`_ = kube_version`). → P13 (implement or
|
||||||
|
remove + document).
|
||||||
|
- `scripts/__pycache__/verify_deploy_microservice.cpython-312.pyc` —
|
||||||
|
orphan bytecode for a deleted source file. → P14.
|
||||||
|
- `core/regression_verify.py:237` — DynamoDB write deferred to Phase 54
|
||||||
|
(outbox hash-chain verified, no real DynamoDB write). Accepted
|
||||||
|
deferral.
|
||||||
|
- `adapters/wiz/wiz_adapter.py` — real GraphQL client (not a stub);
|
||||||
|
degrades gracefully. OK.
|
||||||
|
- `core/separation_of_duties.py` — `route_halt_artifact` is real (SNS +
|
||||||
|
outbox fallback). OK.
|
||||||
|
- `core/lambda/contract_ingestor.py` — `report_error` is real (GitHub
|
||||||
|
issues via Secrets Manager). OK.
|
||||||
|
|
||||||
|
### Finding 4 — Documentation drift
|
||||||
|
|
||||||
|
- `ARCHITECTURE.md` — no v1.11/v1.12/v1.13/v1.14 addendum; line 500-506
|
||||||
|
still describes the **old** parameterized adapter (pre-stateless
|
||||||
|
rewrite). → P19.
|
||||||
|
- Stale `@v1.6`–`@v1.9` workflow refs in `README.md:225`,
|
||||||
|
`docs/consumer-guide.md` (12 sites), `docs/architecture.md:233`,
|
||||||
|
`docs/pipeline/versioning.md:29`, `docs/pipeline/index.md:42`. → P19.
|
||||||
|
- `modules/STANDARDS.md` §8 references `TYPE_MAP` (deleted in v1.11);
|
||||||
|
§9.4 requires 5-file split but §489-492 allows inlining —
|
||||||
|
inconsistent. → P18.
|
||||||
|
- `COST.md` window stops at v1.10; no v1.11–v1.13 spend. → P19.
|
||||||
|
- `GRILL.md` G-005/G-008 escalations — CAP-017..022 now Verified via
|
||||||
|
lifecycle pipeline; COST.md now exists. → P19 (mark resolved).
|
||||||
|
- `IAM_POLICY.md` — reflects v1.11 re-bootstrap but not v1.12/v1.13.
|
||||||
|
→ P19.
|
||||||
|
- Decks reference "v1.12" verification status; not re-synced for
|
||||||
|
v1.13.2. → P19.
|
||||||
|
|
||||||
|
### Finding 5 — Test coverage gaps
|
||||||
|
|
||||||
|
- 533 tests collected; 5 `@pytest.mark.slow` (deselected from fast
|
||||||
|
suite). 7 scripts with no test: `seed_uptime_monitors.py`,
|
||||||
|
`push_consumer_image.py`, `sync_to_gl.sh`, `post_stage_comment.sh`,
|
||||||
|
`rotate_spike_key.sh`, `create_state_backend.py`,
|
||||||
|
`create_iam_user.py`. → P15.
|
||||||
|
- Adapter dedup-merge + `ACDL_REMOTE_STATE_KEY` override — no unit
|
||||||
|
test (P2-2). → P5.
|
||||||
|
|
||||||
|
### Finding 6 — Terraform gaps
|
||||||
|
|
||||||
|
- 3 L1 modules lack `locals.tf` (`ecr`, `ecs-cluster`, `rds`). → P18.
|
||||||
|
- `static-assets/composition.json` unwired inputs (P1-2). → P2.
|
||||||
|
- `terraform/platform/main.tf:255` — hardcoded CIDR; `count=2` subnets
|
||||||
|
not data-driven. → P20.
|
||||||
|
- `terraform/bootstrap/create_state_backend.py:51` — over-broad
|
||||||
|
except (Finding 2). → P7.
|
||||||
|
|
||||||
|
### Finding 7 — Workflow / pipeline gaps
|
||||||
|
|
||||||
|
- 4 GitHub-only workflows (patterns-plan, platform-test,
|
||||||
|
primitives-plan, release) — no Gitea mirror. → P16.
|
||||||
|
- `rotate_spike_key.sh` (only `set -u`), `sync_to_gl.sh` (no `set`
|
||||||
|
flags). → P16.
|
||||||
|
- 3 shared workflows (ci, deploy, modules-lifecycle) byte-identical
|
||||||
|
(verified). OK.
|
||||||
|
- modules-lifecycle matrix covers all 12 L1 + 2 L2. OK.
|
||||||
|
|
||||||
|
### Finding 8 — Config / project hygiene
|
||||||
|
|
||||||
|
- `config.json` bash_allowlist has dead JS entries (npm/node/jest/eslint
|
||||||
|
/tsc — no package.json). → P14/P17.
|
||||||
|
- `config.json` `branching_strategy: "phase"` mismatched with
|
||||||
|
flat-workflow practice. → P17.
|
||||||
|
- `config.json` `ollama-cloud.base_url: ""` (empty; no `glm` model
|
||||||
|
configured). → P17.
|
||||||
|
- `config.json` `frontend-engineer` persona still in `personas[]`
|
||||||
|
(PERSONAS.md:80 says inactive). → P17.
|
||||||
|
- `pyproject.toml` version `1.3.0` (stale); coverage source
|
||||||
|
`acdl_platform` (renamed to `core` in v1.6). → P14.
|
||||||
|
|
||||||
|
### Persona assessment (v1.14)
|
||||||
|
|
||||||
|
The v1.14 milestone is NFR-only (bug fixes, security, tests, docs). The
|
||||||
|
active persona roster from v1.11 (PERSONAS.md) carries forward
|
||||||
|
unchanged:
|
||||||
|
|
||||||
|
- **lead-developer** (active) — coordination; owns the wave ordering +
|
||||||
|
cross-phase dependencies.
|
||||||
|
- **backend-engineer** (active) — owns `adapters/`, `core/` (adapter
|
||||||
|
dedup, contract ingestor, regression gate, output publisher).
|
||||||
|
- **data-engineer** (active) — owns `terraform/`, `modules/` (ALB fix,
|
||||||
|
static-assets wiring, platform VPC, IAM policy, STANDARDS).
|
||||||
|
- **frontend-engineer** (inactive) — no frontend; decks are markdown
|
||||||
|
(lead-developer territory). Stays deactivated per PERSONAS.md:80.
|
||||||
|
|
||||||
|
No custom personas needed for v1.14 (no new domains). Territory
|
||||||
|
enforcement = `warn` (config.json:167). The v1.14 work is concentrated
|
||||||
|
in `adapters/`, `core/`, `terraform/`, `scripts/`, `tests/`, `docs/`,
|
||||||
|
`.ciagent/` — all within existing persona territories.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## v1.15 Research Addendum — Nova Rebrand scope audit (2026-07-30)
|
||||||
|
|
||||||
|
### Survey method
|
||||||
|
|
||||||
|
A thorough, exhaustive codebase survey (via the explore subagent) plus
|
||||||
|
targeted `grep -rni` counts. The survey covered 346 tracked files,
|
||||||
|
reporting occurrence counts and the mechanical-vs-judgment split per
|
||||||
|
category. The full survey is recorded in the planning conversation
|
||||||
|
transcript; the binding conclusions are summarized here.
|
||||||
|
|
||||||
|
### Finding 1 — Brand string surface area
|
||||||
|
|
||||||
|
- **1,465** total `ACDL`/`acdl` occurrences across **205** files.
|
||||||
|
- **17** occurrences of the full "Agentic Cloud Delivery Platform"
|
||||||
|
phrase (all prominent titles/headers: README, docs/index, vision,
|
||||||
|
pyproject, decks, .ciagent/*.md).
|
||||||
|
- **0** existing references to "nova" (case-insensitive) — **no
|
||||||
|
collision risk**.
|
||||||
|
- User-facing (docs/, README, decks, contracts, schemas, module
|
||||||
|
READMEs): high-priority for rebrand.
|
||||||
|
- Internal (.ciagent/*.md, tests/, terraform/, scripts/, workflows):
|
||||||
|
mechanical but voluminous.
|
||||||
|
|
||||||
|
### Finding 2 — Code identifiers (judgment category)
|
||||||
|
|
||||||
|
- **Python package name**: `pyproject.toml` `name = "acdl"` (no `acdl/`
|
||||||
|
package dir exists — source lives in `core/`, `adapters/`; the name is
|
||||||
|
a metadata label). Mechanical rename.
|
||||||
|
- **Python file**: `adapters/terraform/policy/custom_rules/acdl_tagging.py`
|
||||||
|
(+ Checkov registration in `schemas/tagging-standard.json` line 5 +
|
||||||
|
adapter config). Rename file + update registration.
|
||||||
|
- **Env var prefixes**: 21 distinct `ACDL_*` prefixes (`ACDL_LIFECYCLE_MODE`
|
||||||
|
66×, `ACDL_AWS_ACCESS_KEY_ID` 40×, `ACDL_AWS_SECRET_ACCESS_KEY` 37×,
|
||||||
|
`ACDL_REMOTE_STATE_KEY` 22×, `ACDL_AWS_ACCOUNT_ID` 21×, `ACDL_TAG_NAMING`
|
||||||
|
20×, `ACDL_KMS_KEY_ID` 16×, `ACDL_BOOTSTRAP_AWS_*` 15× each,
|
||||||
|
`ACDL_SOD_HALT_TOPIC_ARN` 14×, `ACDL_LOCAL_TIER` 13×, etc.). No
|
||||||
|
centralized env loader exists today (scattered `os.environ.get`).
|
||||||
|
D-108: a new `core/env.py` `get_env()` helper centralizes the
|
||||||
|
dual-read fallback.
|
||||||
|
- **Workflow `name:`**: `.github/workflows/release.yml` line 11
|
||||||
|
`name: acdl-release` — mechanical.
|
||||||
|
|
||||||
|
### Finding 3 — AWS resource names (high-risk migration)
|
||||||
|
|
||||||
|
Terraform creates real AWS resources with `acdl-` prefixes. Renaming
|
||||||
|
forces destroy+recreate (downtime, data loss for DynamoDB/state bucket).
|
||||||
|
D-102: full rename with migration (user-directed).
|
||||||
|
|
||||||
|
| Resource | Type | Migration |
|
||||||
|
|----------|------|-----------|
|
||||||
|
| `acdl-contracts` / `acdl-change-requests` | DynamoDB | scan+copy data, verify row counts |
|
||||||
|
| `acdl/github-token` | Secrets Manager | recreate secret, repoint Lambda |
|
||||||
|
| `acdl-contract-ingestor` (role/policy/Lambda) | IAM+Lambda | recreate role/Lambda, update trigger |
|
||||||
|
| `acdl-sod-halt` | SNS | recreate topic, repoint publisher |
|
||||||
|
| `acdl-ecs-sg` | SG | recreate (brief ECS disruption) |
|
||||||
|
| `alias/acdl-platform` | KMS alias | repoint alias (cheap) |
|
||||||
|
| `acdl-microservice` (cluster/ECR/service/task/role) | ECS+ECR | re-push images, recreate service |
|
||||||
|
| `acdl-spike-runner` (user/policy) | IAM | re-bootstrap with new key |
|
||||||
|
| `acdl-tfstate-581513795199-us-east-1` | S3 state bucket | `terraform init -migrate-state`, back up state JSON |
|
||||||
|
| `acdl-alb` (name prefix) | ALB | recreate (brief downtime) |
|
||||||
|
|
||||||
|
### Finding 4 — Consumer/infra conventions (judgment category, D-104)
|
||||||
|
|
||||||
|
- **AWS tag keys** `acdl:owner|environment|contract|cost-center|ref`
|
||||||
|
(5 keys, ~109 tag assignments) — matched by ABAC session policies.
|
||||||
|
Parallel-tag period (add `nova:*`, swap policy, remove `acdl:*`).
|
||||||
|
- **SSM path** `/acdl/{env}/{contractId}/{output}` (67 refs) — deploy
|
||||||
|
outputs stored here. Migration script copies params, readers updated,
|
||||||
|
old deleted.
|
||||||
|
- **Consumer path** `.acdl/contract.yml` (23 refs) — consumer repos
|
||||||
|
depend on this. Renamed `.nova/contract.yml` + migration guide.
|
||||||
|
|
||||||
|
### Finding 5 — Docs & decks (mechanical)
|
||||||
|
|
||||||
|
- README.md (16), docs/index.md, docs/vision.md, docs/architecture.md,
|
||||||
|
docs/consumer-guide.md (35), docs/modules/index.md (28), all
|
||||||
|
.ciagent/*.md, modules/STANDARDS.md, schemas/README.md,
|
||||||
|
pipelines/README.md, adapters/README.md, terraform/*/README.md.
|
||||||
|
- Deck markdown + mermaid `.mmd` sources (5 files) + rendered HTML.
|
||||||
|
PNGs re-exported from edited `.mmd` sources.
|
||||||
|
- S&P visual theme (`sp-theme.json`, deck CSS) is **client branding**
|
||||||
|
— D-107: untouched. Only product-brand text (ACDL→Nova) changes.
|
||||||
|
- Schema `$id` URLs (`https://acdl.cloudinit.dev/schemas/...`) →
|
||||||
|
`https://nova.cloudinit.dev/schemas/...` (D-110: illustrative, no
|
||||||
|
DNS resolution needed for validation).
|
||||||
|
|
||||||
|
### Finding 6 — CI / pipeline / release
|
||||||
|
|
||||||
|
- Workflow files mirrored in `.gitea/workflows/` + `.github/workflows/`
|
||||||
|
(modules-lifecycle 31×, deploy 22×, ci 2×, release 3×).
|
||||||
|
- `release.yml` release title `ACDL vX.Y.Z` → `Nova vX.Y.Z` (forward
|
||||||
|
only; past releases keep names).
|
||||||
|
- Git branches/tags use `milestone/v*` / `phase/*` / `v*` — **no brand
|
||||||
|
name present**, no change needed (D-112: flat-branch convention
|
||||||
|
preserved).
|
||||||
|
- config.json `release.gitea.repo` stays `acdl` (D-105: real repo name
|
||||||
|
unchanged; doc URLs illustrative only).
|
||||||
|
|
||||||
|
### Finding 7 — External / URLs
|
||||||
|
|
||||||
|
- `github.com/acdl/...` (~20 refs in docs + module READMEs +
|
||||||
|
reusable-workflow `uses:` refs) — D-105: illustrative, updated to
|
||||||
|
`nova` for prose. Real GitHub org/repo rename is out of scope.
|
||||||
|
- `git.cloudinit.dev/continuous-intelligence/acdl*` (incl. sister
|
||||||
|
repos `acdl-contracts`, `acdl-evidence`) — updated in prose to `nova*`.
|
||||||
|
- README has **no badges** (no shields.io, no img src).
|
||||||
|
|
||||||
|
### Finding 8 — Nomenclature / tagline
|
||||||
|
|
||||||
|
- "DevSecOps", "New Dawn", "enabler" appear **nowhere** in the repo
|
||||||
|
today — clean insertion, no collisions to reconcile (D-106).
|
||||||
|
- Current tagline ("North Star" / "consumers declare intent") is
|
||||||
|
retained; Nova tagline added alongside.
|
||||||
|
- "bottleneck" (6 occurrences in deck talking points) — compatible
|
||||||
|
with the Nova "no bottleneck" messaging; left in place.
|
||||||
|
|
||||||
|
### Persona assessment (v1.15)
|
||||||
|
|
||||||
|
No new personas needed for v1.15 — the rebrand touches existing
|
||||||
|
territories (docs, code, terraform, CI, tests). The active roster:
|
||||||
|
**lead-developer** (docs/decks/.ciagent meta + verification + migration
|
||||||
|
runbooks), **backend-engineer** (core/env.py dual-read helper, contract
|
||||||
|
resolver path, Lambda, output_publisher, regression_verify),
|
||||||
|
**data-engineer** (terraform resource names/tagging, state bucket
|
||||||
|
migration, DynamoDB data migration, ECR re-push, schemas/tagging-standard).
|
||||||
|
The **frontend-engineer** remains deactivated (no UI; decks are
|
||||||
|
markdown = lead-developer territory). A **security-engineer** persona is
|
||||||
|
not activated — the ABAC session-policy + tag-key migration (REQ-162) is
|
||||||
|
data-engineer territory (terraform IAM) with lead-developer review.
|
||||||
|
Territory enforcement = `warn` (co-authoring expected at the
|
||||||
|
core/env.py + terraform boundary, and the contract-resolver +
|
||||||
|
deploy-workflow boundary).
|
||||||
|
|
||||||
|
### Assumptions logged
|
||||||
|
|
||||||
|
- A1 (confidence 0.9): No live AWS access is available during P0–P4
|
||||||
|
execution (the `acdl-spike-runner` IAM user's creds are in
|
||||||
|
`.env.secrets` but live apply/modify/destroy is gated by
|
||||||
|
`NOVA_LIFECYCLE_MODE` defaulting to plan-only). The terraform changes
|
||||||
|
are validated via `terraform validate`; live apply is exercised by the
|
||||||
|
modules-lifecycle workflow when explicitly set to full. This matches
|
||||||
|
the v1.11–v1.14 established pattern.
|
||||||
|
- A2 (confidence 0.85): `.env.secrets` contains live rotated AWS
|
||||||
|
credentials keyed by `ACDL_AWS_*`. P2 renames the KEYS only (values
|
||||||
|
stay). The runtime reads via the new `core/env.py` dual-read helper
|
||||||
|
(`NOVA_AWS_ACCESS_KEY_ID` preferred, `ACDL_AWS_ACCESS_KEY_ID`
|
||||||
|
fallback), so no re-rotation is needed until P5 removes the fallback.
|
||||||
|
- A3 (confidence 0.8): The Gitea release API (`POST .../releases`) is
|
||||||
|
reachable for `v1.14.x` tags (the v1.14 milestone shipped releases
|
||||||
|
through `v1.13.24` / release id 285). P0 ship targets `v1.14.0`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## v1.16 NFR Simplification — Research Addendum (2026-07-30)
|
||||||
|
|
||||||
|
**Milestone:** v1.16-Nova-Simplification (NFR). Research is codebase-
|
||||||
|
grounded (not domain/ecosystem) — the two explore passes identified
|
||||||
|
concrete, file:line-verified residual debt the v1.15 rebrand left, plus
|
||||||
|
genuine simplification and the onboarding request-path scaffolding.
|
||||||
|
|
||||||
|
### R1. v1.14 NFR categories already closed (do NOT re-propose)
|
||||||
|
|
||||||
|
v1.14 (REQ-135..154) swept: over-broad excepts (REQ-141), hardcoded
|
||||||
|
account-ID externalization (REQ-142, `ACDL_AWS_ACCOUNT_ID` env + live
|
||||||
|
fallback G-102), IAM `Resource:"*"` scoping to `nova-*` ARNs (REQ-143,
|
||||||
|
G-104), contractId/env/error validation (REQ-144),
|
||||||
|
`additionalProperties:false` schemas (REQ-145), `.gitignore` credential
|
||||||
|
catch-all (REQ-146), Kyverno `--kube-version` removal + deferral doc
|
||||||
|
(REQ-147, G-103), orphan bytecode/dead-config cleanup (REQ-148), 7
|
||||||
|
untested-script test coverage (REQ-149), `set -euo pipefail` parity +
|
||||||
|
Gitea workflow parity (REQ-150), config/persona/backend hygiene (REQ-151),
|
||||||
|
STANDARDS.md TYPE_MAP consistency (REQ-152), ARCHITECTURE/COST/GRILL doc
|
||||||
|
sync (REQ-153), platform-VPC CIDR/subnet parameterization (REQ-154).
|
||||||
|
|
||||||
|
The v1.16 grill (G-101..G-106) and escalation E-001 are all CLOSED.
|
||||||
|
v1.16 finds NEW residual signals (D-117).
|
||||||
|
|
||||||
|
### R2. Fresh debt the v1.15 rebrand left (verified file:line)
|
||||||
|
|
||||||
|
**High-severity correctness regressions (P1):**
|
||||||
|
- `adapters/terraform/adapter.py:117` — emits `state_bucket =
|
||||||
|
f"acdl-tfstate-{account_id}-us-east-1"`. The live state bucket was
|
||||||
|
renamed to `nova-tfstate-*` in v1.15 P4 (REQ-163), but the adapter's
|
||||||
|
emitted terraform backend still references `acdl-tfstate-*`. In
|
||||||
|
plan-only mode this is latent (no real init against the bucket), but a
|
||||||
|
full-mode lifecycle run would point at a non-existent bucket.
|
||||||
|
- `adapters/kyverno/policies/require-resource-labels.yml:6,21,25,33,37`
|
||||||
|
— enforces `acdl:owner`/`acdl:environment` labels. `nova_tagging.py`
|
||||||
|
hard-fails on any `acdl:*` key post-P5 (REQ-164). The Kyverno policy
|
||||||
|
contradicts the Nova tagging standard.
|
||||||
|
|
||||||
|
**User-facing brand misses (P2):**
|
||||||
|
- `core/environment_check.py:59,61` — onboarding message header/body say
|
||||||
|
"ACDL Environment Onboarding" / "ACDL environments are platform-
|
||||||
|
managed" (user-facing).
|
||||||
|
- `core/lambda/contract_ingestor.py:145,191` — GitHub issue alert title
|
||||||
|
`[ACDL-ALERT]` + body "auto-created by the ACDL platform Lambda"
|
||||||
|
(user-facing artifact).
|
||||||
|
- `scripts/post_stage_comment.sh:39,46` — PR comment header "ACDL Stage"
|
||||||
|
+ footer "ACDL deploy pipeline" (user-facing).
|
||||||
|
- `scripts/run_ci.sh:39` — CI banner "ACL 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`.
|
||||||
|
|
||||||
|
**Dead code + stale comments (P3):**
|
||||||
|
- `scripts/run_platform.sh:153` — `export
|
||||||
|
ACDL_ENVIRONMENT_OVERRIDE="$ENVIRONMENT_OVERRIDE" # legacy fallback,
|
||||||
|
removed in P5` — comment says "removed in P5" but the line is STILL
|
||||||
|
present (dead code, P5 already shipped).
|
||||||
|
- Stale dual-read comments across `core/local_emulators.py:15-16,503,505`,
|
||||||
|
`core/regression_verify.py:318-319,333`, `scripts/run_regression.sh`,
|
||||||
|
`scripts/run_lifecycle_*.sh` (reference the retired G-106 fallback).
|
||||||
|
- `acdl_*` temp-dir prefixes: `core/local_emulators.py:71,252`,
|
||||||
|
`core/regression_verify.py:183,234`, `scripts/run_pattern_plan.sh:29`,
|
||||||
|
`scripts/run_primitive_plan.sh:29`, `scripts/run_lifecycle_*.sh:36,41`.
|
||||||
|
|
||||||
|
### R3. Simplification opportunities (verified)
|
||||||
|
|
||||||
|
- `core/regression_verify.py:328-409` — `_check_live_terraform_plan_
|
||||||
|
microservice` + `_check_live_terraform_plan_static_assets` are ~95%
|
||||||
|
identical (resolve → adapt → init → validate → plan). Extract
|
||||||
|
`_check_live_terraform_plan(contract, label)` (~35 lines saved).
|
||||||
|
- `core/regression_verify.py:149-178` — `_check_resolver_static_assets` +
|
||||||
|
`_check_resolver_microservice` identical except contract path. Extract
|
||||||
|
`_check_resolver(contract)`.
|
||||||
|
- `core/regression_verify.py:477-503` — duplicated lifecycle-contract-
|
||||||
|
resolve block. Extract `_assert_contracts_resolve(module_dir)`.
|
||||||
|
- `scripts/run_platform.sh:336-350` + `:452-466` — duplicated HITL
|
||||||
|
attestation block. Extract `run_hitl_gate()` shell fn (~14 lines).
|
||||||
|
- `core/contract_resolver.py:50-68` duplicates `core/environment_check.py:
|
||||||
|
36-54` env loader verbatim. Import instead.
|
||||||
|
- `scripts/run_platform.sh:145-146` — hardcoded `CONTRACT_ID` UUID +
|
||||||
|
`WORK="/tmp/acdl_platform_run_v18"` (`v18` stale). Make config/env-
|
||||||
|
derived.
|
||||||
|
- `.gitea/workflows/` ↔ `.github/workflows/` — 3 byte-identical pairs
|
||||||
|
(`ci.yml`, `deploy.yml`, `modules-lifecycle.yml`, ~20 KB) maintained
|
||||||
|
by hand + a test asserting identity. Generator (D-115) eliminates
|
||||||
|
manual-sync risk.
|
||||||
|
- `core/contract_resolver.py:540` — `is_l2 = "l2" in interface_path or
|
||||||
|
"composition" in interface_path` fragile string heuristic. Add `kind`
|
||||||
|
to registry entries (P7).
|
||||||
|
- `scripts/run_platform.sh` (610 lines) — decommission block (`:180-237`)
|
||||||
|
+ uptime block (`:520-606`) are self-contained. Extract to
|
||||||
|
`scripts/run_decommission.sh` + `scripts/run_uptime.sh` (P9).
|
||||||
|
|
||||||
|
### R4. Security gaps (verified, NEW — not v1.14 duplicates)
|
||||||
|
|
||||||
|
- `core/lambda/contract_ingestor.py:251-252` — `if not caller_arn: pass`
|
||||||
|
silently skips identity validation when IAM identity absent; relies on
|
||||||
|
ABAC layer only (no defense-in-depth). Fail closed instead (P10).
|
||||||
|
- `core/lambda/contract_ingestor.py:269` — `valid_envs = {"dev","qa",
|
||||||
|
"prod","dr"}` hardcoded; the `core/environments/` dir is the source of
|
||||||
|
truth. Derive from the directory (P10).
|
||||||
|
- `core/lambda/contract_ingestor.py` — `submit_contract` checks the
|
||||||
|
`contract` key exists but never validates the blob's size or schema.
|
||||||
|
Unbounded payload → DynamoDB write amplification. Size cap + schema
|
||||||
|
validation (P11).
|
||||||
|
- `scripts/migrate_ssm_paths.py:113` — `except Exception: pass` (claims
|
||||||
|
`ParameterNotFound` but catches all). Last true broad-swallow.
|
||||||
|
Narrow to `ParameterNotFound` (P4).
|
||||||
|
- `core/output_publisher.py:112,182` — `except Exception` in
|
||||||
|
`publish_to_ssm` + `post_github_comment` swallow all (not narrowed by
|
||||||
|
REQ-141 which targeted 6 other sites). Narrow to specific exceptions.
|
||||||
|
|
||||||
|
### R5. Onboarding request-path scaffolding (already present)
|
||||||
|
|
||||||
|
The infrastructure for a zero-human *request* path already exists:
|
||||||
|
- `terraform/platform/main.tf:153-183` deploys `contract_ingestor` Lambda
|
||||||
|
+ Function URL (IAM auth).
|
||||||
|
- `core/lambda/contract_ingestor.py:325-362` dispatches
|
||||||
|
`submit_contract | report_error | validate_change_request`. Adding
|
||||||
|
`onboard_consumer` is a small extension (P18, D-119: writes a
|
||||||
|
`pending` CMDB row, no provisioning).
|
||||||
|
- `terraform/platform/consumer_invoke_policy.json` is the ABAC policy
|
||||||
|
template (`aws:PrincipalTag/nova:owner == ${consumerRepo}` scoped
|
||||||
|
`lambda:InvokeFunctionUrl`).
|
||||||
|
- `core/environments/*.json` are static JSON templates with placeholder
|
||||||
|
`account_id: "000000000000"` — auto-generation from a request is
|
||||||
|
straightforward (P19).
|
||||||
|
|
||||||
|
Missing for "no humans": (a) `onboard_consumer` action + onboarding
|
||||||
|
schema (P18), (b) `core/onboarding.py` to auto-generate `<env>.json` +
|
||||||
|
emit a PR (P19), (c) cross-account deploy-role + ABAC tag Terraform,
|
||||||
|
offline-proven (P20, D-114). Real AWS account/network/state creation
|
||||||
|
stays a future feature (D-113).
|
||||||
|
|
||||||
|
### R6. Developer experience gaps
|
||||||
|
|
||||||
|
- `scripts/run_platform.sh` has no `--help` (`:82` rejects `--*` flags).
|
||||||
|
`--deploy-uptime` (`:532`) is undocumented in the header. `--local`
|
||||||
|
is absent from the README (P15).
|
||||||
|
- No `.github/workflows/README.md` cataloging the 7 workflows' inputs/
|
||||||
|
secrets/triggers (P16).
|
||||||
|
- No single getting-started path; README "How to run" lists 3 manual
|
||||||
|
bootstrap steps. The offline happy path (`run_ci.sh` +
|
||||||
|
`run_platform.sh --check-only`/`--local`) is not surfaced first (P17).
|
||||||
|
|
||||||
|
### Assumptions logged (v1.16)
|
||||||
|
|
||||||
|
- A1 (0.9): No live AWS access during execution (consistent with
|
||||||
|
v1.11–v1.15). `NOVA_LIFECYCLE_MODE` defaults to plan-only; terraform
|
||||||
|
changes validated via `terraform validate`. The state-bucket drift
|
||||||
|
(P1) is latent in plan-only mode but must still be fixed for
|
||||||
|
correctness.
|
||||||
|
- A2 (0.85): The `onboard_consumer` Lambda action (P18) is offline-
|
||||||
|
testable via `moto` / the local Lambda stub (D-092), consistent with
|
||||||
|
the existing `submit_contract`/`report_error` test pattern.
|
||||||
|
- A3 (0.8): The workflow generator (P8, D-115) must preserve the
|
||||||
|
byte-identity property *as a test assertion* (generated outputs match
|
||||||
|
committed files), not lose it — the dedup is mechanical, not a
|
||||||
|
semantic change to the workflows.
|
||||||
|
- A4 (0.85): The regression gate (D-091, D-118) at P9 and P21 confirms
|
||||||
|
"simplify without regressions" — 22/22 capabilities must stay Verified.
|
||||||
|
The gate is the credible control for the simplification wave.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# ACDL v1.11 — Multi-Persona Code Review (P60–P65 retrofit + new work)
|
# Nova v1.11 — Multi-Persona Code Review (P60–P65 retrofit + new work)
|
||||||
|
|
||||||
**Reviewer:** ci-code-reviewer (model: glm-5.2)
|
**Reviewer:** ci-code-reviewer (model: glm-5.2)
|
||||||
**Scope:** v1.11 milestone, branch `milestone/v1.11-restart` — 22 commits
|
**Scope:** v1.11 milestone, branch `milestone/v1.11-restart` — 22 commits
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# ACDL — Roadmap
|
# Nova — Roadmap
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
@@ -25,6 +25,8 @@
|
|||||||
- **v1.11 (complete, tag `v1.11.0`):** RESTART — stateless adapter + pipeline-driven module lifecycle testing. Closes G-005 (CAP-017..022 deploy-unverified) and G-008 (no cost docs) via a corrected architecture, not the failed v1.11 first attempt (which produced 4 drifted VPCs, ran terraform apply from Python, and had no module lifecycle tests). The restart branches off `v1.10.2` and rebuilds v1.11 on three corrections: (1) the terraform adapter becomes a stateless assembler — each L1 module ships a real `terraform/` module dir (variables/locals/main/outputs) owning its resource shape, nested blocks, and defaults; the adapter deletes `TYPE_MAP`/`INPUT_MAP`/`OUTPUT_MAP` and all 39 type-specific branches, becoming a ~80-line assembler that emits `module "x" { source = ... }` blocks; (2) lifecycle is owned by terraform via the shell orchestrator (`run_platform.sh --apply`/`--destroy`), never by Python — `verify_deploy_microservice.py` is deleted; (3) testing is pipeline-driven — a `modules-lifecycle` pipeline (Gitea + GitHub, byte-identical) matrix-runs each L1 module's `examples/{simple,complex}.yml` contracts through apply→modify→destroy against live AWS; no per-module Python. A single platform VPC (`terraform/platform`) is shared by all stacks via `data` source — no per-contract VPC. State keys are deterministic and env-aware (`spike/{id}/{env}/terraform.tfstate`), stable across lifecycle changes. 13 phases (P56a–P65). See the v1.11 section below for the phase breakdown.
|
- **v1.11 (complete, tag `v1.11.0`):** RESTART — stateless adapter + pipeline-driven module lifecycle testing. Closes G-005 (CAP-017..022 deploy-unverified) and G-008 (no cost docs) via a corrected architecture, not the failed v1.11 first attempt (which produced 4 drifted VPCs, ran terraform apply from Python, and had no module lifecycle tests). The restart branches off `v1.10.2` and rebuilds v1.11 on three corrections: (1) the terraform adapter becomes a stateless assembler — each L1 module ships a real `terraform/` module dir (variables/locals/main/outputs) owning its resource shape, nested blocks, and defaults; the adapter deletes `TYPE_MAP`/`INPUT_MAP`/`OUTPUT_MAP` and all 39 type-specific branches, becoming a ~80-line assembler that emits `module "x" { source = ... }` blocks; (2) lifecycle is owned by terraform via the shell orchestrator (`run_platform.sh --apply`/`--destroy`), never by Python — `verify_deploy_microservice.py` is deleted; (3) testing is pipeline-driven — a `modules-lifecycle` pipeline (Gitea + GitHub, byte-identical) matrix-runs each L1 module's `examples/{simple,complex}.yml` contracts through apply→modify→destroy against live AWS; no per-module Python. A single platform VPC (`terraform/platform`) is shared by all stacks via `data` source — no per-contract VPC. State keys are deterministic and env-aware (`spike/{id}/{env}/terraform.tfstate`), stable across lifecycle changes. 13 phases (P56a–P65). See the v1.11 section below for the phase breakdown.
|
||||||
- **v1.12 (complete, tag `v1.12.0`):** Presentation Refinement — the leadership decks synced to the v1.11-verified reality (22/22 Verified, stateless adapter, lifecycle pipeline, cost figures, pre-mortem). Includes the CAP-013 adapter dedup fix + 2 probe fixes (required to make the deck claims true) + the ACDL_LIFECYCLE_MODE CI flag (lifecycle tests default to plan-only, full on override). 6 phases (P66–P70). See the v1.12 section below.
|
- **v1.12 (complete, tag `v1.12.0`):** Presentation Refinement — the leadership decks synced to the v1.11-verified reality (22/22 Verified, stateless adapter, lifecycle pipeline, cost figures, pre-mortem). Includes the CAP-013 adapter dedup fix + 2 probe fixes (required to make the deck claims true) + the ACDL_LIFECYCLE_MODE CI flag (lifecycle tests default to plan-only, full on override). 6 phases (P66–P70). See the v1.12 section below.
|
||||||
- **v1.13 (complete, tag `v1.13.0`):** Presentation Polish — both leadership decks polished across all 4 pipeline layers (source .md → -marp.md → .html → -talking-points.md). Action headlines replace category names; story-arc restructure (Intro ~10% / Body ~80% / Conclusion ~10%); removed all transition story lines; bullets ≤12 words, 3–4 per main slide; larger fonts (body 26px, h1 40px, h2 32px); 6 new mermaid diagrams (frictions 2×2, north-star before/after, zero-trust flow, catalog primitives→modules, decommission gates, semver timeline). Code review: 0 P0, 2 P1 auto-fixed (slide-count metadata + README directory layout). 522 tests pass. Docs-only NFR patch. 1 phase (P71). See the v1.13 section below.
|
- **v1.13 (complete, tag `v1.13.0`):** Presentation Polish — both leadership decks polished across all 4 pipeline layers (source .md → -marp.md → .html → -talking-points.md). Action headlines replace category names; story-arc restructure (Intro ~10% / Body ~80% / Conclusion ~10%); removed all transition story lines; bullets ≤12 words, 3–4 per main slide; larger fonts (body 26px, h1 40px, h2 32px); 6 new mermaid diagrams (frictions 2×2, north-star before/after, zero-trust flow, catalog primitives→modules, decommission gates, semver timeline). Code review: 0 P0, 2 P1 auto-fixed (slide-count metadata + README directory layout). 522 tests pass. Docs-only NFR patch. 1 phase (P71). See the v1.13 section below.
|
||||||
|
- **v1.13.1 (complete, tag `v1.13.1`):** config.json schema migration — regenerate `.ciagent/config.json` to the updated CIAgent v2 config structure (drop removed fields, migrate `gitea`→`release.gitea`, add `secrets`/`ship`/`backend`/`ideation`/`personas`/`logging`/`telemetry` sections). Code review: 0 P0, 2 P1/P2 auto-fixed. Docs-only NFR patch (no code changes). Gitea release id 253.
|
||||||
|
- **v1.13.2 (complete, tag `v1.13.2`):** presentation badge cleanup + platform architecture diagram — removed all `testing`/`agentic` maturity badges from both decks (only `planned` retained); added a new Slide 3 "The platform at a glance" with a shared high-level logical architecture diagram (consumer surfaces → contract → central pipeline → cross-cutting components → AWS) to both decks; renumbered subsequent slides 4–11; synced talking points + README. Docs-only NFR patch (no code changes).
|
||||||
- **v1.0 demo URL:** https://git.cloudinit.dev/continuous-intelligence/acdl-evidence/raw/branch/main/index.html
|
- **v1.0 demo URL:** https://git.cloudinit.dev/continuous-intelligence/acdl-evidence/raw/branch/main/index.html
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -1019,3 +1021,612 @@ verbatim from v1.12. No factual drift.
|
|||||||
Ship tag at milestone COMPLETE: `v1.13.0` (v1.12.0 → v1.13.0; docs-only NFR
|
Ship tag at milestone COMPLETE: `v1.13.0` (v1.12.0 → v1.13.0; docs-only NFR
|
||||||
patch — final patch IS the deliverable, no separate milestone tag).
|
patch — final patch IS the deliverable, no separate milestone tag).
|
||||||
**DONE.**
|
**DONE.**
|
||||||
|
|
||||||
|
### v1.13.1 (complete, tag `v1.13.1`): config.json schema migration
|
||||||
|
|
||||||
|
NFR patch: regenerated `.ciagent/config.json` to the updated CIAgent v2 config
|
||||||
|
structure. The old config used the pre-v2 schema (`mode` field,
|
||||||
|
`projects[].milestone/status/branch/tag`, top-level `gitea` block, missing
|
||||||
|
`secrets`/`release`/`ship`/`backend`/`ideation`/`personas`/`logging`/`telemetry`
|
||||||
|
sections). The new config conforms to `CIAgentConfig` (config.ts:156) and passes
|
||||||
|
`validateConfig()` + `loadConfig()` deep-merge.
|
||||||
|
|
||||||
|
Multi-persona code review: 0 P0, 2 P1/P2 auto-fixed (`.ciagent/logs/` gitignored,
|
||||||
|
trailing newline restored). Gitea release id 253. Docs-only NFR patch (no code
|
||||||
|
changes).
|
||||||
|
|
||||||
|
### v1.13.2 (complete, tag `v1.13.2`): presentation badge cleanup + platform architecture diagram
|
||||||
|
|
||||||
|
NFR patch (docs-only). Two presentation changes across both leadership decks
|
||||||
|
(`how-the-platform-works` + `the-developer-experience`):
|
||||||
|
|
||||||
|
1. **Badge cleanup** — removed all `testing` and `agentic` maturity badges from
|
||||||
|
every deck layer (source `.md`, Marp `-marp.md`, rendered `.html`,
|
||||||
|
talking-points). Only the `planned` badges are retained where relevant. The
|
||||||
|
Marp inline `style:` CSS dropped the `.testing` / `.agentic` rules (kept
|
||||||
|
`.planned`). The README maturity-framing section updated to describe only the
|
||||||
|
`Planned` badge. Empty table cells (dev environment Maturity row) normalized
|
||||||
|
to `—`.
|
||||||
|
2. **Platform architecture diagram** — added a new Slide 3 "The platform at a
|
||||||
|
glance" to both decks, right after the problem statement. A shared mermaid
|
||||||
|
source (`assets/mmd/platform-architecture.mmd`) renders to
|
||||||
|
`assets/png/platform-architecture.png` and is embedded in both Marp decks.
|
||||||
|
The diagram shows the full logical topology: consumer surfaces (technical dev
|
||||||
|
+ citizen dev) → contract schema → central pipeline (8 fixed stages) →
|
||||||
|
cross-cutting components (module catalog, stateless engine adapter,
|
||||||
|
platform-managed environments, HITL gates, hash-chained evidence stream) →
|
||||||
|
downstream AWS resources. All subsequent slides renumbered 4–11; talking
|
||||||
|
points + README directory layout + slide counts (10→11 main, 19→20 / 18→19
|
||||||
|
total) synced. Both HTML decks re-rendered via Marp.
|
||||||
|
|
||||||
|
Docs-only NFR patch (no code changes).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## v1.14 (complete — NFR Refinement: bug fixes, security, stubs, tests, docs, tag `v1.13.24`)
|
||||||
|
|
||||||
|
The v1.14 milestone is a 20-phase NFR sweep — no new features. It clears
|
||||||
|
the open P1/P2 backlog from the v1.11 review, hardens the security
|
||||||
|
posture (swallowed errors, hardcoded account ID, IAM wildcards, schema
|
||||||
|
validation, credential hygiene), resolves stub/missing functionality
|
||||||
|
(Kyverno `--kube-version`, orphan artifacts), adds test coverage for 7
|
||||||
|
untested scripts, and refines all documentation (ARCHITECTURE.md
|
||||||
|
v1.11–v1.14 addenda, stale `@v1.6–1.9` → `@v1.13` refs, COST.md/GRILL/
|
||||||
|
IAM_POLICY.md sync, STANDARDS.md reconciliation).
|
||||||
|
|
||||||
|
**Milestone type:** NFR (all phases fix/test/docs/chore/refactor). The
|
||||||
|
final phase's patch IS the release — no separate milestone tag. Tags run
|
||||||
|
on the v1.13.x line: `v1.13.3` (P0) → `v1.13.4..v1.13.23` (P1–P20) →
|
||||||
|
`v1.13.24` (P21 final = milestone release).
|
||||||
|
|
||||||
|
**Wave ordering:**
|
||||||
|
- Wave 1 (P1–P6): bug fixes — P1 before P2 (composition depends on dedup
|
||||||
|
correctness); P3–P6 independent.
|
||||||
|
- Wave 2 (P7–P12): security — P8 before P9 (externalized account ID for
|
||||||
|
IAM ARNs); rest independent.
|
||||||
|
- Wave 3 (P13–P17): stub/test/CI/hygiene — P15 benefits from P7 landing
|
||||||
|
first; P17 after P14 (both touch config.json).
|
||||||
|
- Wave 4 (P18–P20): standards/docs/VPC — P19 last (reflects all prior
|
||||||
|
phases).
|
||||||
|
|
||||||
|
### Phase P1 — adapter-dedup-diagnostic (Wave 1)
|
||||||
|
- **Description:** Fix P1-1 from the v1.11 review. The adapter dedup loop
|
||||||
|
(`adapters/terraform/adapter.py:159-170`) silently drops resources whose
|
||||||
|
module is not in the registry — a typo'd `module` field vanishes without
|
||||||
|
diagnostic. Raise `ValueError` (preserving the pre-dedup contract) so the
|
||||||
|
misconfiguration surfaces instead of being silently omitted.
|
||||||
|
- **Status:** pending
|
||||||
|
- **Depends on:** —
|
||||||
|
- **Requirements:** REQ-135
|
||||||
|
- **Success Criteria:**
|
||||||
|
- A resource with `module: nonexistent@1.0.0` raises `ValueError` with a
|
||||||
|
descriptive message, not a silent drop.
|
||||||
|
- Existing registered-module dedup behavior preserved (multi-resource L1s
|
||||||
|
still merge into one `module "x" { ... }` block).
|
||||||
|
- `pytest` passes; `run_ci.sh` exits 0.
|
||||||
|
|
||||||
|
### Phase P2 — static-assets-wiring-fix (Wave 1)
|
||||||
|
- **Description:** Fix P1-2. `modules/l2/static-assets/composition.json`
|
||||||
|
drops `default_ttl`/`max_ttl`/`price_class`/`viewer_protocol_policy`
|
||||||
|
(accepted by `cloudfront/interface.json` but never wired) and WAF is
|
||||||
|
unconditionally present (no `features`/conditional). Wire the cloudfront
|
||||||
|
inputs; make WAF conditional via a `waf_enabled` feature flag so
|
||||||
|
`examples/complex.yml` is a real modify (adds CDN + WAF), not a no-op
|
||||||
|
re-apply.
|
||||||
|
- **Status:** pending
|
||||||
|
- **Depends on:** [P1]
|
||||||
|
- **Requirements:** REQ-136
|
||||||
|
- **Success Criteria:**
|
||||||
|
- `complex.yml` resolves to a resource set that differs from `simple.yml`
|
||||||
|
(WAF + CDN TTLs present when `waf_enabled: true`, absent when false).
|
||||||
|
- The L2 static-assets lifecycle cell's "modify" step exercises a real
|
||||||
|
terraform diff, not idempotent re-apply.
|
||||||
|
- `pytest` passes; `run_ci.sh` exits 0.
|
||||||
|
|
||||||
|
### Phase P3 — lifecycle-script-arg-cleanup (Wave 1)
|
||||||
|
- **Description:** Fix P1-3. `scripts/run_l2_lifecycle_test.sh` and
|
||||||
|
`run_l2_lifecycle_destroy.sh` advertise `[ci-vpc-outputs.json]` ($3) in
|
||||||
|
their usage strings but never read it (the L2 path uses
|
||||||
|
`terraform_remote_state`, not the file). Remove the vestigial arg or
|
||||||
|
document that the L2 path uses remote state and the arg is
|
||||||
|
accepted-but-ignored for workflow-argument parity with the L1 scripts.
|
||||||
|
- **Status:** pending
|
||||||
|
- **Depends on:** —
|
||||||
|
- **Requirements:** REQ-137
|
||||||
|
- **Success Criteria:**
|
||||||
|
- Usage strings no longer advertise a feature the scripts don't provide,
|
||||||
|
OR a comment explains the L2-uses-remote-state design + parity reason.
|
||||||
|
- `pytest` passes; `run_ci.sh` exits 0.
|
||||||
|
|
||||||
|
### Phase P4 — regression-gate-evidence-hardening (Wave 1)
|
||||||
|
- **Description:** Fix P1-5. `core/regression_verify.py:432-519`
|
||||||
|
CAP-017..022 checks are offline proxies (files exist + contracts
|
||||||
|
resolve) — a module with broken HCL would pass as long as files exist.
|
||||||
|
Add a `terraform validate` step to
|
||||||
|
`_check_lifecycle_module_terraform` so at least HCL syntax is verified
|
||||||
|
at the gate. Tighten the CAPABILITY_INVENTORY wording to "offline proxy;
|
||||||
|
live apply/modify/destroy verified by the modules-lifecycle workflow
|
||||||
|
run, not by this gate."
|
||||||
|
- **Status:** pending
|
||||||
|
- **Depends on:** —
|
||||||
|
- **Requirements:** REQ-138
|
||||||
|
- **Success Criteria:**
|
||||||
|
- `_check_lifecycle_module_terraform` runs `terraform validate` (or
|
||||||
|
documents why it's too slow + falls back to a syntax probe).
|
||||||
|
- CAPABILITY_INVENTORY + docstrings reflect the offline-proxy caveat
|
||||||
|
honestly.
|
||||||
|
- `pytest` passes; `run_ci.sh` exits 0.
|
||||||
|
|
||||||
|
### Phase P5 — adapter-behavior-tests (Wave 1)
|
||||||
|
- **Description:** Fix P2-2. Add `test_adapter_dedup_merges_same_module`
|
||||||
|
(two resources with the same `module` collapse to one
|
||||||
|
`module "<first_id>" { ... }` block with merged inputs) and
|
||||||
|
`test_adapter_remote_state_key_override` (`ACDL_REMOTE_STATE_KEY`
|
||||||
|
overrides the default `platform/terraform.tfstate` key in the emitted
|
||||||
|
`data terraform_remote_state` block).
|
||||||
|
- **Status:** pending
|
||||||
|
- **Depends on:** [P1]
|
||||||
|
- **Requirements:** REQ-139
|
||||||
|
- **Success Criteria:**
|
||||||
|
- Both unit tests exist in `tests/test_adapter.py` and pass.
|
||||||
|
- `pytest` count increases; `run_ci.sh` exits 0.
|
||||||
|
|
||||||
|
### Phase P6 — alb-name-prefix-fix (Wave 1)
|
||||||
|
- **Description:** Fix P2-1. `modules/l1/alb/terraform/main.tf:9` uses
|
||||||
|
`name_prefix = "tg-ci-"` (hardcoded literal) which discards `var.name`
|
||||||
|
entirely — the target group name is non-configurable and inconsistent
|
||||||
|
with the LB name. Change to `name_prefix = "${var.name}-"` so the
|
||||||
|
consumer's name prefixes the target group while preserving uniqueness.
|
||||||
|
- **Status:** pending
|
||||||
|
- **Depends on:** —
|
||||||
|
- **Requirements:** REQ-140
|
||||||
|
- **Success Criteria:**
|
||||||
|
- Target group `name_prefix` derives from `var.name`.
|
||||||
|
- `terraform validate` passes for the alb module standalone.
|
||||||
|
- `pytest` passes; `run_ci.sh` exits 0.
|
||||||
|
|
||||||
|
### Phase P7 — swallowed-error-hardening (Wave 2)
|
||||||
|
- **Description:** Narrow 6 over-broad `except ...: pass`/`except
|
||||||
|
Exception:` sites: `core/local_emulators.py:374` (fake_urlopen swallow
|
||||||
|
→ network egress risk if patching fails), `core/lambda/contract_ingestor.py:157`
|
||||||
|
(GitHub search failure → duplicate issues),
|
||||||
|
`terraform/bootstrap/create_state_backend.py:51` (over-broad → spurious
|
||||||
|
create_bucket), `core/output_publisher.py:100,168`,
|
||||||
|
`terraform/bootstrap/apply_iam_baseline.py:78`. Catch specific
|
||||||
|
`ClientError`/`NoSuch*` exceptions; log + re-raise where silent failure
|
||||||
|
masks a real defect.
|
||||||
|
- **Status:** pending
|
||||||
|
- **Depends on:** —
|
||||||
|
- **Requirements:** REQ-141
|
||||||
|
- **Success Criteria:**
|
||||||
|
- No bare `except Exception: pass` remains in the targeted files (grep
|
||||||
|
clean for the 6 sites).
|
||||||
|
- Specific exception types caught; errors logged with context.
|
||||||
|
- `pytest` passes; `run_ci.sh` exits 0.
|
||||||
|
|
||||||
|
### Phase P8 — account-id-externalization (Wave 2)
|
||||||
|
- **Description:** Externalize the hardcoded account ID `581513795199`
|
||||||
|
from 15+ sites: `adapters/terraform/adapter.py:125,140`,
|
||||||
|
`terraform/bootstrap/apply_iam_baseline.py:33`,
|
||||||
|
`terraform/bootstrap/create_state_backend.py:33,35`,
|
||||||
|
`scripts/push_consumer_image.py:32`, terraform state-bucket names, ECR
|
||||||
|
image refs. Read from `ACDL_AWS_ACCOUNT_ID` env (code) /
|
||||||
|
`data.aws_caller_identity` (terraform); fall back to env for offline.
|
||||||
|
Keep the COST.md account ID (accepted exposure per P2-4) but centralize
|
||||||
|
the code-side.
|
||||||
|
- **Status:** pending
|
||||||
|
- **Depends on:** —
|
||||||
|
- **Requirements:** REQ-142
|
||||||
|
- **Success Criteria:**
|
||||||
|
- `grep -rn "581513795199" adapters/ scripts/ terraform/ core/` returns
|
||||||
|
0 hits (excluding tests + docs).
|
||||||
|
- `ACDL_AWS_ACCOUNT_ID` env read with a clear default/fallback.
|
||||||
|
- `pytest` passes; `run_ci.sh` exits 0.
|
||||||
|
|
||||||
|
### Phase P9 — iam-policy-least-privilege (Wave 2)
|
||||||
|
- **Description:** Scope 6 `Resource: "*"` statements in
|
||||||
|
`terraform/bootstrap/spike_runner_policy.json` (cloudfront, wafv2, kms,
|
||||||
|
iam) to `acdl-*` ARNs. Scope `iam:CreateRole` etc. to
|
||||||
|
`arn:aws:iam::...:role/acdl-*`; scope KMS to
|
||||||
|
`arn:aws:kms:...:key/acdl-*`; narrow CloudFront/WAF where possible.
|
||||||
|
Add a regression test asserting no new `Resource:"*"` on non-global
|
||||||
|
actions.
|
||||||
|
- **Status:** pending
|
||||||
|
- **Depends on:** [P8]
|
||||||
|
- **Requirements:** REQ-143
|
||||||
|
- **Success Criteria:**
|
||||||
|
- `Resource: "*"` remains only on actions that require it (sts, ce).
|
||||||
|
- IAM/KMS/CloudFront/WAF scoped to `acdl-*` ARNs.
|
||||||
|
- Regression test in `tests/test_iam_policy_baseline.py` asserts the
|
||||||
|
scoping.
|
||||||
|
- `pytest` passes; `run_ci.sh` exits 0.
|
||||||
|
|
||||||
|
### Phase P10 — contract-ingestor-identity-validation (Wave 2)
|
||||||
|
- **Description:** Harden `core/lambda/contract_ingestor.py:221-245`
|
||||||
|
`_validate_caller_identity` — currently best-effort (validates
|
||||||
|
`consumerRepo` format only, doesn't verify the caller owns the repo).
|
||||||
|
Add `contractId` format validation, `environment` enum validation,
|
||||||
|
`error` length cap. Document the ABAC reliance explicitly. Add a
|
||||||
|
spoofing-resistance test.
|
||||||
|
- **Status:** pending
|
||||||
|
- **Depends on:** —
|
||||||
|
- **Requirements:** REQ-144
|
||||||
|
- **Success Criteria:**
|
||||||
|
- `contractId`, `environment`, `error` validated; malformed input
|
||||||
|
rejected with 400.
|
||||||
|
- ABAC reliance documented in the function docstring + ARCHITECTURE.md.
|
||||||
|
- Spoofing-resistance test in `tests/test_contract_ingestor.py` passes.
|
||||||
|
- `pytest` passes; `run_ci.sh` exits 0.
|
||||||
|
|
||||||
|
### Phase P11 — schema-input-validation-hardening (Wave 2)
|
||||||
|
- **Description:** Add `additionalProperties: false` to
|
||||||
|
`schemas/contract.schema.json` + `schemas/environment.schema.json`
|
||||||
|
(currently allows undocumented fields silently). Add `maxItems`/
|
||||||
|
`maxProperties` bounds. Validate `state_backend.bucket` S3 naming
|
||||||
|
rules, `runner_role_arn` ARN format, `vpc_cidr` CIDR format. Add tests
|
||||||
|
asserting rejection of malformed input.
|
||||||
|
- **Status:** pending
|
||||||
|
- **Depends on:** —
|
||||||
|
- **Requirements:** REQ-145
|
||||||
|
- **Success Criteria:**
|
||||||
|
- Both schemas reject undocumented top-level fields.
|
||||||
|
- Format validation (bucket/ARN/CIDR) rejects malformed values.
|
||||||
|
- New tests in `tests/test_environment_schema.py` +
|
||||||
|
`tests/test_contract_schema.py` pass.
|
||||||
|
- `pytest` passes; `run_ci.sh` exits 0.
|
||||||
|
|
||||||
|
### Phase P12 — gitignore-credential-hygiene (Wave 2)
|
||||||
|
- **Description:** `.gitignore` covers `.env*`/`*.tfstate*` but lacks a
|
||||||
|
credential-pattern catch-all (`*.pem`/`*.key`/`*.p12`/`*.pfx`). Add
|
||||||
|
credential patterns. Add `tests/test_no_secrets_tracked.py` asserting no
|
||||||
|
credential-looking file is tracked by git.
|
||||||
|
- **Status:** pending
|
||||||
|
- **Depends on:** —
|
||||||
|
- **Requirements:** REQ-146
|
||||||
|
- **Success Criteria:**
|
||||||
|
- `.gitignore` has credential-pattern catch-all.
|
||||||
|
- `test_no_secrets_tracked.py` passes (grep `git ls-files` for
|
||||||
|
credential patterns → 0 hits).
|
||||||
|
- `pytest` passes; `run_ci.sh` exits 0.
|
||||||
|
|
||||||
|
### Phase P13 — kyverno-kube-version-resolution (Wave 3)
|
||||||
|
- **Description:** Resolve the discarded `--kube-version` flag in
|
||||||
|
`adapters/kyverno/kyverno_adapter.py:11,115-116` (`_ = kube_version`).
|
||||||
|
Either implement version-aware policy selection (select policies by k8s
|
||||||
|
version) or remove the flag and document why it's deferred to the
|
||||||
|
GitOps reconciler roadmap. Resolve the ambiguity either way.
|
||||||
|
- **Status:** pending
|
||||||
|
- **Depends on:** —
|
||||||
|
- **Requirements:** REQ-147
|
||||||
|
- **Success Criteria:**
|
||||||
|
- `--kube-version` is either used (version-aware policy selection) or
|
||||||
|
removed with a documented deferral rationale.
|
||||||
|
- `tests/test_kyverno_adapter.py` updated to match.
|
||||||
|
- `pytest` passes; `run_ci.sh` exits 0.
|
||||||
|
|
||||||
|
### Phase P14 — orphan-artifact-and-dead-config-cleanup (Wave 3)
|
||||||
|
- **Description:** Clean up orphan artifacts + dead config: the orphan
|
||||||
|
`scripts/__pycache__/verify_deploy_microservice.cpython-312.pyc` (source
|
||||||
|
deleted in v1.11); stale `pyproject.toml` coverage source
|
||||||
|
`acdl_platform` → `core` (renamed in v1.6); `pyproject.toml` version
|
||||||
|
`1.3.0` → current; dead JS allowlist entries in `config.json`
|
||||||
|
(npm/node/jest/eslint/tsc — no package.json).
|
||||||
|
- **Status:** pending
|
||||||
|
- **Depends on:** —
|
||||||
|
- **Requirements:** REQ-148
|
||||||
|
- **Success Criteria:**
|
||||||
|
- No orphan `.pyc` for a deleted source file.
|
||||||
|
- `pyproject.toml` coverage source = `core`; version = current.
|
||||||
|
- `config.json` bash_allowlist has no JS-only entries.
|
||||||
|
- `pytest` passes; `run_ci.sh` exits 0.
|
||||||
|
|
||||||
|
### Phase P15 — untested-scripts-coverage (Wave 3)
|
||||||
|
- **Description:** Add unit tests for 7 scripts with no test coverage:
|
||||||
|
`scripts/seed_uptime_monitors.py`, `scripts/push_consumer_image.py`,
|
||||||
|
`scripts/sync_to_gl.sh`, `scripts/post_stage_comment.sh`,
|
||||||
|
`scripts/rotate_spike_key.sh`, `terraform/bootstrap/create_state_backend.py`,
|
||||||
|
`terraform/bootstrap/create_iam_user.py`. Mock boto3/subprocess for
|
||||||
|
offline-testable coverage. Add `--check-only`/dry-run modes where
|
||||||
|
missing.
|
||||||
|
- **Status:** pending
|
||||||
|
- **Depends on:** [P7]
|
||||||
|
- **Requirements:** REQ-149
|
||||||
|
- **Success Criteria:**
|
||||||
|
- Each of the 7 scripts has a corresponding test file with ≥1 passing
|
||||||
|
test.
|
||||||
|
- `pytest` count increases by ≥7; `run_ci.sh` exits 0.
|
||||||
|
|
||||||
|
### Phase P16 — workflow-parity-and-script-flags (Wave 3)
|
||||||
|
- **Description:** 4 GitHub-only workflows (patterns-plan, platform-test,
|
||||||
|
primitives-plan, release) have no Gitea mirror — either mirror them or
|
||||||
|
document the Gitea limitation. Fix `scripts/rotate_spike_key.sh` (only
|
||||||
|
`set -u`, no `-e`/`pipefail`) and `scripts/sync_to_gl.sh` (no `set`
|
||||||
|
flags at all) — add `set -euo pipefail`.
|
||||||
|
- **Status:** pending
|
||||||
|
- **Depends on:** —
|
||||||
|
- **Requirements:** REQ-150
|
||||||
|
- **Success Criteria:**
|
||||||
|
- Gitea workflow parity resolved (mirrored or documented).
|
||||||
|
- `rotate_spike_key.sh` + `sync_to_gl.sh` have `set -euo pipefail`.
|
||||||
|
- `pytest` passes; `run_ci.sh` exits 0.
|
||||||
|
|
||||||
|
### Phase P17 — config-and-persona-hygiene (Wave 3)
|
||||||
|
- **Description:** Fix `config.json` hygiene: `branching_strategy: "phase"`
|
||||||
|
mismatch with flat-workflow practice; empty `ollama-cloud` base_url (no
|
||||||
|
`glm` model configured); `frontend-engineer` persona `active: false` in
|
||||||
|
config.json (PERSONAS.md:80 already says inactive). Align config.json
|
||||||
|
with PERSONAS.md + actual runtime.
|
||||||
|
- **Status:** pending
|
||||||
|
- **Depends on:** [P14]
|
||||||
|
- **Requirements:** REQ-151
|
||||||
|
- **Success Criteria:**
|
||||||
|
- `config.json` persona block matches PERSONAS.md (frontend-engineer
|
||||||
|
inactive).
|
||||||
|
- `branching_strategy` reflects actual practice (or documented).
|
||||||
|
- `ollama-cloud` backend configured or documented as intentionally
|
||||||
|
unset.
|
||||||
|
- `pytest` passes; `run_ci.sh` exits 0.
|
||||||
|
|
||||||
|
### Phase P18 — module-standards-consistency (Wave 4)
|
||||||
|
- **Description:** 3 L1 modules (`ecr`, `ecs-cluster`, `rds`) lack
|
||||||
|
`locals.tf`; `modules/STANDARDS.md` §9.4 requires the full 5-file split
|
||||||
|
but §489-492 allows inlining — internally inconsistent. Either add
|
||||||
|
`locals.tf` to all 3 or reconcile STANDARDS §9.4 with the inline
|
||||||
|
allowance. Remove the stale `TYPE_MAP` reference in §8 (deleted in the
|
||||||
|
v1.11 stateless rewrite).
|
||||||
|
- **Status:** pending
|
||||||
|
- **Depends on:** —
|
||||||
|
- **Requirements:** REQ-152
|
||||||
|
- **Success Criteria:**
|
||||||
|
- STANDARDS.md internally consistent (§8 + §9.4 agree).
|
||||||
|
- No stale `TYPE_MAP` reference.
|
||||||
|
- `pytest` passes; `run_ci.sh` exits 0.
|
||||||
|
|
||||||
|
### Phase P19 — documentation-sync-v1.14 (Wave 4)
|
||||||
|
- **Description:** ARCHITECTURE.md: add v1.11/v1.12/v1.13/v1.14 addenda
|
||||||
|
(stateless adapter, platform VPC, ACDL_LIFECYCLE_MODE, all v1.14
|
||||||
|
changes; record D-083 deferral explicitly). Bump stale `@v1.6–1.9` →
|
||||||
|
`@v1.13` across `README.md`, `docs/consumer-guide.md` (12 sites),
|
||||||
|
`docs/architecture.md`, `docs/pipeline/`. Sync decks to v1.13.2 reality.
|
||||||
|
Update COST.md window to v1.11–v1.14. Resolve G-005/G-008 in GRILL.md
|
||||||
|
(CAP-017..022 now Verified via lifecycle pipeline; COST.md now exists +
|
||||||
|
covers v1.11+). Update IAM_POLICY.md for v1.12/v1.13/v1.14.
|
||||||
|
- **Status:** pending
|
||||||
|
- **Depends on:** [P1-P18]
|
||||||
|
- **Requirements:** REQ-153
|
||||||
|
- **Success Criteria:**
|
||||||
|
- ARCHITECTURE.md has v1.11–v1.14 addenda; D-083 deferral recorded.
|
||||||
|
- `grep -rn "@v1\.[6-9]" docs/ README.md` returns 0 hits (bumped to
|
||||||
|
@v1.13).
|
||||||
|
- GRILL G-005/G-008 marked resolved with evidence.
|
||||||
|
- COST.md window covers v1.11–v1.14.
|
||||||
|
- `pytest` passes; `run_ci.sh` exits 0.
|
||||||
|
|
||||||
|
### Phase P20 — platform-vpc-parameterization (Wave 4)
|
||||||
|
- **Description:** `terraform/platform/main.tf:255` hardcodes
|
||||||
|
`cidr_block = "10.0.0.0/16"` (not `var.vpc_cidr`); `count = 2` subnets
|
||||||
|
hardcoded (not data-driven AZs). Parameterize; document the
|
||||||
|
`0.0.0.0/0` ingress on port 80 (ALB-fronted, acceptable but should be
|
||||||
|
explicit).
|
||||||
|
- **Status:** pending
|
||||||
|
- **Depends on:** —
|
||||||
|
- **Requirements:** REQ-154
|
||||||
|
- **Success Criteria:**
|
||||||
|
- VPC CIDR is a variable (default `10.0.0.0/16`); subnet count is
|
||||||
|
data-driven (`length(data.aws_availability_zones.available)`).
|
||||||
|
- `0.0.0.0/0` ingress documented.
|
||||||
|
- `terraform validate` passes; `pytest` passes; `run_ci.sh` exits 0.
|
||||||
|
|
||||||
|
### Phase P21 — final-review-ship (Final Phase)
|
||||||
|
- **Description:** Multi-persona code review across all v1.14 phases.
|
||||||
|
Audit (reconstruction test, file discipline, branch hygiene, commit
|
||||||
|
discipline). Complete: update REQUIREMENTS.md (REQ-135..154 marked
|
||||||
|
complete), ROADMAP.md (v1.14 complete), PROJECT.md. Tag final patch
|
||||||
|
`v1.13.24` (IS the milestone release). Merge `milestone/v1.14` → `main`.
|
||||||
|
- **Status:** pending
|
||||||
|
- **Depends on:** [P1-P20]
|
||||||
|
- **Requirements:** —
|
||||||
|
- **Success Criteria:**
|
||||||
|
- Review: 0 new P0; all P1-1..P1-5 + P2-1..P2-4 resolved.
|
||||||
|
- Audit: clean; reconstruction test passes.
|
||||||
|
- Tag `v1.13.24` created; milestone merged to main.
|
||||||
|
|
||||||
|
After Phase P21: milestone COMPLETE — `v1.13.24` IS the v1.14 release.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## v1.15 (complete — Nova Rebrand, tag `v1.15.4`)
|
||||||
|
|
||||||
|
A full rebrand from **ACDL** / "Agentic Cloud Delivery Platform" →
|
||||||
|
**Nova** / "The New Dawn of DevSecOps — security as a seamless enabler
|
||||||
|
of fast deployments." The rebrand applies across docs, decks, code,
|
||||||
|
configs, CI, env var prefixes, the consumer contract path, SSM
|
||||||
|
parameter paths, AWS tag keys, and AWS resource names — with a staged
|
||||||
|
infrastructure migration to avoid breakage. The Nova tagline is added
|
||||||
|
alongside (not replacing) the existing "North Star" / "consumers
|
||||||
|
declare intent" framing; the S&P Global Energy visual theme
|
||||||
|
(`sp-theme.json`) is a client brand and is **not** touched.
|
||||||
|
|
||||||
|
**Milestone type:** Major (breaking — consumer-facing path, env var
|
||||||
|
prefixes, SSM path, AWS tag keys, and AWS resource names all change).
|
||||||
|
Per the branch-strategy precedent (breaking/feature milestones tag on
|
||||||
|
their OWN minor line), v1.15 tags run on the **v1.15.x minor line**:
|
||||||
|
`v1.15.0` (P0) → `v1.15.1..v1.15.4` (P1–P4) → `v1.15.4` (P5 final =
|
||||||
|
milestone release). (G-104 binding.)
|
||||||
|
|
||||||
|
**Brand mapping:**
|
||||||
|
- Name: `ACDL` / `Agentic Cloud Delivery Platform` → `Nova`
|
||||||
|
- Tagline (added): "The New Dawn of DevSecOps — security as a seamless
|
||||||
|
enabler of fast deployments"
|
||||||
|
- Env var prefix: `ACDL_*` → `NOVA_*` (dual-read fallback in P2;
|
||||||
|
removed in P5)
|
||||||
|
- Consumer path: `.acdl/contract.yml` → `.nova/contract.yml`
|
||||||
|
- SSM path: `/acdl/{env}/{contractId}/{output}` →
|
||||||
|
`/nova/{env}/{contractId}/{output}`
|
||||||
|
- AWS tag keys: `acdl:owner|environment|contract|cost-center|ref` →
|
||||||
|
`nova:*`
|
||||||
|
- AWS resource names: `acdl-*` → `nova-*` (with migration, P4)
|
||||||
|
- Illustrative URLs in docs: `github.com/acdl/...` →
|
||||||
|
`github.com/nova/...` (prose only; real repo name unchanged)
|
||||||
|
- Gitea release titles going forward: `ACDL vX.Y.Z` → `Nova vX.Y.Z`
|
||||||
|
- S&P visual theme: unchanged (client branding)
|
||||||
|
|
||||||
|
**Wave ordering:**
|
||||||
|
- Wave 1 (P1): docs/decks/prose — no runtime impact; establishes new
|
||||||
|
vocabulary. REQ-155, REQ-156, REQ-157.
|
||||||
|
- Wave 2 (P2): code + env vars + consumer path — rename in code with a
|
||||||
|
dual-read env fallback so deployments don't break during the
|
||||||
|
transition window. REQ-158, REQ-159, REQ-160.
|
||||||
|
- Wave 3 (P3): SSM path + tag keys — SSM: copy `/acdl/...` →
|
||||||
|
`/nova/...`, update readers, delete old. Tag keys: parallel-tag
|
||||||
|
period (`nova:*` added, ABAC policy swapped, `acdl:*` removed).
|
||||||
|
REQ-161, REQ-162.
|
||||||
|
- Wave 4 (P4): AWS resource names — the big migration (KMS alias, SNS,
|
||||||
|
SG, Lambda, DynamoDB data migration, ECR re-push, IAM re-bootstrap,
|
||||||
|
state bucket migration, ALB recreate). Maintenance window + rollback
|
||||||
|
runbook. REQ-163.
|
||||||
|
- Wave 5 (P5): final-review-ship — remove dual-read fallback, consumer
|
||||||
|
migration guide finalized, review + audit + milestone ship. REQ-164.
|
||||||
|
|
||||||
|
### Phase P1 — docs-decks-prose (Wave 1)
|
||||||
|
- **Description:** Rebrand all prose, titles, headers, comments,
|
||||||
|
deck markdown sources, mermaid `.mmd` sources, `pyproject.toml`
|
||||||
|
name/description, and `release.yml` release-title prefix from
|
||||||
|
`ACDL`/`Agentic Cloud Delivery Platform` → `Nova`. Add the Nova
|
||||||
|
tagline ("The New Dawn of DevSecOps — security as a seamless enabler
|
||||||
|
of fast deployments") to the README header, both deck title slides,
|
||||||
|
and `docs/vision.md` — alongside the existing "North Star" framing.
|
||||||
|
Re-export the mermaid PNG diagrams so committed PNGs match new
|
||||||
|
labels. Re-render the deck HTML. Update illustrative URLs in docs
|
||||||
|
(`github.com/acdl/...` → `github.com/nova/...`,
|
||||||
|
`git.cloudinit.dev/continuous-intelligence/acdl*` → `.../nova*` for
|
||||||
|
prose). Ship a consumer migration guide (`docs/NOVA_MIGRATION.md`)
|
||||||
|
announcing the `.acdl/`→`.nova/` path, `ACDL_*`→`NOVA_*` env vars,
|
||||||
|
`/acdl/`→`/nova/` SSM path, `acdl:*`→`nova:*` tag keys, and
|
||||||
|
`acdl-*`→`nova-*` AWS resource names changes coming in P2–P4.
|
||||||
|
- **Status:** complete (v1.15.1)
|
||||||
|
- **Depends on:** —
|
||||||
|
- **Requirements:** REQ-155, REQ-156, REQ-157
|
||||||
|
- **Success Criteria:**
|
||||||
|
- `grep -rni "ACDL\|Agentic Cloud Delivery" README.md docs/ .ciagent/*.md`
|
||||||
|
returns 0 hits (except historical narrative marked as historical).
|
||||||
|
- `pyproject.toml` `name` = `nova`; `description` mentions Nova.
|
||||||
|
- `release.yml` release title prefix is `Nova `.
|
||||||
|
- Both decks' title-slide subtitle is
|
||||||
|
`Nova — The New Dawn of DevSecOps`; mermaid `.mmd` sources use
|
||||||
|
`Nova`; PNGs re-exported; HTML re-rendered.
|
||||||
|
- `docs/vision.md` and README header carry the Nova tagline
|
||||||
|
alongside the North Star.
|
||||||
|
- `docs/NOVA_MIGRATION.md` exists and lists the 5 breaking changes.
|
||||||
|
- `pytest` passes; `run_ci.sh` exits 0.
|
||||||
|
|
||||||
|
### Phase P2 — code-envvars-consumer-path (Wave 2)
|
||||||
|
- **Description:** Rename
|
||||||
|
`adapters/terraform/policy/custom_rules/acdl_tagging.py` →
|
||||||
|
`nova_tagging.py` (+ Checkov custom-rule registration in
|
||||||
|
`schemas/tagging-standard.json` + adapter config). Rename all 21
|
||||||
|
`ACDL_*` env var prefixes → `NOVA_*` across `scripts/`, `core/`,
|
||||||
|
`adapters/`, `tests/`, workflows (`.gitea/`, `.github/`), `.env`,
|
||||||
|
`.env.secrets` (key names only — values stay), and consumer docs.
|
||||||
|
Implement a **dual-read fallback** (`NOVA_X` preferred, fall back to
|
||||||
|
`ACDL_X`) in the env/config loader so deployments don't break during
|
||||||
|
the transition window. Rename the consumer on-disk contract path
|
||||||
|
`.acdl/contract.yml` → `.nova/contract.yml` (and
|
||||||
|
`.acdl/static-assets.*.yml`, `.acdl/contract.yaml`) across the
|
||||||
|
contract resolver, deploy workflow checkout path, consumer docs, and
|
||||||
|
the contract schema description. Rotate Gitea repo secrets via API
|
||||||
|
(rename keys `ACDL_*` → `NOVA_*`, values stay).
|
||||||
|
- **Status:** complete (v1.15.2)
|
||||||
|
- **Depends on:** [P1]
|
||||||
|
- **Requirements:** REQ-158, REQ-159, REQ-160
|
||||||
|
- **Success Criteria:**
|
||||||
|
- `nova_tagging.py` exists; `acdl_tagging.py` removed; Checkov
|
||||||
|
registration updated; rule enforces `nova:*` tag keys (tag-key
|
||||||
|
enforcement of `nova:*` lands here; existing resources still carry
|
||||||
|
`acdl:*` until P3 parallel-tag — rule warns during P2).
|
||||||
|
- No `ACDL_` env var references remain in code/scripts/workflows/tests
|
||||||
|
except the dual-read fallback in the loader + `.env.secrets` legacy
|
||||||
|
comment.
|
||||||
|
- Dual-read fallback implemented and unit-tested.
|
||||||
|
- Contract resolver reads `.nova/contract.yml`; deploy workflow
|
||||||
|
checks out `.nova/`; docs updated.
|
||||||
|
- `pytest` passes; `run_ci.sh` exits 0.
|
||||||
|
|
||||||
|
### Phase P3 — ssm-tagkeys (Wave 3)
|
||||||
|
- **Description:** SSM path migration: rename the parameter path prefix
|
||||||
|
`/acdl/{env}/{contractId}/{output}` →
|
||||||
|
`/nova/{env}/{contractId}/{output}` across `core/output_publisher`,
|
||||||
|
the contract resolver, and consumer docs. Add a migration script
|
||||||
|
(`scripts/migrate_ssm_paths.py`) that copies existing `/acdl/...`
|
||||||
|
parameters → `/nova/...`, then readers are updated, then old
|
||||||
|
parameters are deleted. Tag key migration: add `nova:*` tags to all
|
||||||
|
AWS resources (parallel-tag period), update the ABAC session policies
|
||||||
|
to match `nova:*`, update `nova_tagging.py` to enforce `nova:*`
|
||||||
|
(hard, no warn), then remove `acdl:*` tags once consumers are
|
||||||
|
verified. Terraform tagging updated to emit `nova:*`.
|
||||||
|
- **Status:** complete (v1.15.3)
|
||||||
|
- **Depends on:** [P2]
|
||||||
|
- **Requirements:** REQ-161, REQ-162
|
||||||
|
- **Success Criteria:**
|
||||||
|
- SSM readers use `/nova/...`; migration script copies + deletes;
|
||||||
|
test asserts new path.
|
||||||
|
- `nova_tagging.py` enforces `nova:*` (hard fail on `acdl:*`).
|
||||||
|
- ABAC session policies match `nova:*`; terraform emits `nova:*` tags.
|
||||||
|
- `acdl:*` tags removed from all resources (verified via `aws` CLI or
|
||||||
|
documented deferred if no live AWS access).
|
||||||
|
- `pytest` passes; `run_ci.sh` exits 0.
|
||||||
|
|
||||||
|
### Phase P4 — aws-resource-migration (Wave 4)
|
||||||
|
- **Description:** Rename all `acdl-*` AWS resources → `nova-*` via
|
||||||
|
terraform with a staged migration: KMS alias `alias/acdl-platform` →
|
||||||
|
`alias/nova-platform` (repoint), SNS `acdl-sod-halt` →
|
||||||
|
`nova-sod-halt` (recreate), SG `acdl-ecs-sg` → `nova-ecs-sg`
|
||||||
|
(recreate), Lambda `acdl-contract-ingestor` →
|
||||||
|
`nova-contract-ingestor` (recreate), DynamoDB `acdl-contracts`/
|
||||||
|
`acdl-change-requests` → `nova-contracts`/`nova-change-requests`
|
||||||
|
(scan+copy data migration, verify row counts, keep old tables until
|
||||||
|
verified), ECR `acdl-microservice` → `nova-microservice` (re-push
|
||||||
|
images), IAM user/policy `acdl-spike-runner` → `nova-spike-runner`
|
||||||
|
(re-bootstrap with new key), state bucket `acdl-tfstate-...` →
|
||||||
|
`nova-tfstate-...` (`terraform init -migrate-state` to new backend,
|
||||||
|
state JSON backed up first), ALB name prefix `acdl-alb` → `nova-alb`
|
||||||
|
(recreate, brief downtime). Publish a maintenance window + rollback
|
||||||
|
runbook (`docs/NOVA_AWS_MIGRATION.md`). For the offline/local tier,
|
||||||
|
the terraform `name`/`resource` labels change so `terraform validate`
|
||||||
|
passes; live apply/modify/destroy is exercised by the
|
||||||
|
modules-lifecycle workflow when `ACDL_LIFECYCLE_MODE` (now
|
||||||
|
`NOVA_LIFECYCLE_MODE`) is set to full.
|
||||||
|
- **Status:** complete (v1.15.4)
|
||||||
|
- **Depends on:** [P3]
|
||||||
|
- **Requirements:** REQ-163
|
||||||
|
- **Success Criteria:**
|
||||||
|
- All terraform resource names/labels use `nova-*`; `terraform
|
||||||
|
validate` passes for platform/microservice/ci-vpc.
|
||||||
|
- State bucket name → `nova-tfstate-...`; `terraform init
|
||||||
|
-migrate-state` documented + tested offline.
|
||||||
|
- DynamoDB data-migration script exists (scan+copy, row-count
|
||||||
|
verify).
|
||||||
|
- `docs/NOVA_AWS_MIGRATION.md` runbook exists (maintenance window,
|
||||||
|
rollback steps).
|
||||||
|
- `grep -rn "acdl-" terraform/` returns 0 hits.
|
||||||
|
- `pytest` passes; `run_ci.sh` exits 0.
|
||||||
|
|
||||||
|
### Phase P5 — final-review-ship (Final Phase)
|
||||||
|
- **Description:** Multi-persona code review across all v1.15 phases.
|
||||||
|
Audit (reconstruction test, file discipline, branch hygiene, commit
|
||||||
|
discipline). Remove the dual-read env var fallback (`ACDL_*`→`NOVA_*`)
|
||||||
|
once all consumers are migrated; finalize the consumer migration
|
||||||
|
guide; `nova_tagging.py` no longer accepts `acdl:*` tag keys. Complete:
|
||||||
|
update REQUIREMENTS.md (REQ-155..164 marked complete), ROADMAP.md
|
||||||
|
(v1.15 complete), PROJECT.md. Tag final patch `v1.14.5` (IS the
|
||||||
|
milestone release). Merge `milestone/v1.15-nova` → `main`.
|
||||||
|
- **Status:** complete (v1.15.4, milestone release)
|
||||||
|
- **Depends on:** [P1-P4]
|
||||||
|
- **Requirements:** REQ-164
|
||||||
|
- **Success Criteria:**
|
||||||
|
- Review: 0 new P0; all P1+ flagged or auto-fixed.
|
||||||
|
- Audit: clean; reconstruction test passes.
|
||||||
|
- Dual-read fallback removed; `nova_tagging.py` hard-fails `acdl:*`.
|
||||||
|
- Tag `v1.15.4` created; milestone merged to main.
|
||||||
|
|
||||||
|
After Phase P5: milestone COMPLETE — `v1.15.4` IS the v1.15 release.
|
||||||
|
|||||||
@@ -2,12 +2,13 @@
|
|||||||
"projects": [
|
"projects": [
|
||||||
{
|
{
|
||||||
"slug": "acdl",
|
"slug": "acdl",
|
||||||
"name": "Agentic Cloud Delivery Platform",
|
"name": "Nova — The New Dawn of DevSecOps",
|
||||||
"default": true
|
"default": true
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"active_project": "acdl",
|
"active_project": "acdl",
|
||||||
"active_projects": ["acdl"],
|
"active_projects": ["acdl"],
|
||||||
|
"active_milestone": "v1.16",
|
||||||
"autonomy": {
|
"autonomy": {
|
||||||
"level": "full",
|
"level": "full",
|
||||||
"escalation_hooks": ["deploy", "delete_data", "merge_to_main"],
|
"escalation_hooks": ["deploy", "delete_data", "merge_to_main"],
|
||||||
@@ -36,14 +37,13 @@
|
|||||||
"escalate_high_severity": true,
|
"escalate_high_severity": true,
|
||||||
"bash_allowlist": {
|
"bash_allowlist": {
|
||||||
"allowed_commands": [
|
"allowed_commands": [
|
||||||
"npm", "node", "npx", "pnpm", "yarn",
|
|
||||||
"git", "ls", "cat", "head", "tail", "wc",
|
"git", "ls", "cat", "head", "tail", "wc",
|
||||||
"echo", "mkdir", "cp", "mv", "rm", "touch",
|
"echo", "mkdir", "cp", "mv", "rm", "touch",
|
||||||
"pwd", "which", "env", "printenv",
|
"pwd", "which", "env", "printenv",
|
||||||
"jest", "eslint", "tsc", "prettier",
|
"python3", "pytest", "pip",
|
||||||
|
"terraform", "checkov",
|
||||||
"curl", "wget",
|
"curl", "wget",
|
||||||
"docker", "docker-compose",
|
"docker", "docker-compose"
|
||||||
"ts-node", "tsx"
|
|
||||||
],
|
],
|
||||||
"max_output_bytes": 1048576,
|
"max_output_bytes": 1048576,
|
||||||
"timeout_ms": 30000,
|
"timeout_ms": 30000,
|
||||||
@@ -58,7 +58,8 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"git": {
|
"git": {
|
||||||
"branching_strategy": "phase",
|
"branching_strategy": "flat",
|
||||||
|
"_branching_strategy_note": "ACDL uses flat workflow (committed directly to main per established convention since v1.0). The 'phase' strategy is advisory; CIAgent uses milestone/phase branches for v1.14 but the project convention is flat.",
|
||||||
"auto_commit": true,
|
"auto_commit": true,
|
||||||
"auto_push": true
|
"auto_push": true
|
||||||
},
|
},
|
||||||
@@ -124,6 +125,7 @@
|
|||||||
},
|
},
|
||||||
"ollama-cloud": {
|
"ollama-cloud": {
|
||||||
"base_url": "",
|
"base_url": "",
|
||||||
|
"_base_url_note": "Intentionally unset. The runtime uses the glm-5.2 model via the opencode backend (not the llm_backends config). This entry is for reference only.",
|
||||||
"api_key_env": "OLLAMA_CLOUD_API_KEY",
|
"api_key_env": "OLLAMA_CLOUD_API_KEY",
|
||||||
"model_profile": "quality",
|
"model_profile": "quality",
|
||||||
"timeout_ms": 60000
|
"timeout_ms": 60000
|
||||||
@@ -190,9 +192,11 @@
|
|||||||
{
|
{
|
||||||
"name": "frontend-engineer",
|
"name": "frontend-engineer",
|
||||||
"domain": "frontend",
|
"domain": "frontend",
|
||||||
|
"active": false,
|
||||||
"frameworks": ["react", "next.js"],
|
"frameworks": ["react", "next.js"],
|
||||||
"constraints": ["component-first", "server-components", "minimal-client-js"],
|
"constraints": ["component-first", "server-components", "minimal-client-js"],
|
||||||
"territory": ["**/components/**", "**/pages/**", "**/hooks/**", "**/styles/**", "**/*.tsx", "**/*.css", "**/*.vue"]
|
"territory": ["**/components/**", "**/pages/**", "**/hooks/**", "**/styles/**", "**/*.tsx", "**/*.css", "**/*.vue"],
|
||||||
|
"reason": "ACDL has no frontend (no package.json); decks are markdown (lead-developer territory). Deactivated per PERSONAS.md:80."
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
# Gitea Workflows — Limitation Documentation (v1.14, REQ-150)
|
||||||
|
|
||||||
|
## Shared workflows (byte-identical Gitea + GitHub)
|
||||||
|
|
||||||
|
These 3 workflows exist in both `.gitea/workflows/` and `.github/workflows/`
|
||||||
|
and are byte-identical (asserted by `tests/test_pipeline_contract.py`):
|
||||||
|
|
||||||
|
- `ci.yml` — lint + test + check-only (runs on every PR)
|
||||||
|
- `deploy.yml` — reusable deploy workflow (invoked by consumer repos)
|
||||||
|
- `modules-lifecycle.yml` — L1 + L2 module lifecycle pipeline (plan-only
|
||||||
|
default, full on workflow_dispatch override)
|
||||||
|
|
||||||
|
## GitHub-only workflows (no Gitea mirror)
|
||||||
|
|
||||||
|
These 4 workflows exist only in `.github/workflows/`:
|
||||||
|
|
||||||
|
- `platform-test.yml` — PR pipeline: lint + unit + integration + schema
|
||||||
|
validation. Uses GitHub Actions features (reusable workflow composition,
|
||||||
|
environment protection) not available in Gitea Actions.
|
||||||
|
- `primitives-plan.yml` — PR plan-only matrix over all L1 primitives. Uses
|
||||||
|
GitHub matrix strategy + `terraform plan` against live AWS.
|
||||||
|
- `patterns-plan.yml` — PR plan-only matrix over all L2 modules. Same
|
||||||
|
pattern as primitives-plan.
|
||||||
|
- `release.yml` — release job on merge to main: computes next semver,
|
||||||
|
creates + updates MAJOR.MINOR.PATCH / MAJOR.MINOR / MAJOR floating tags,
|
||||||
|
creates a GitHub release. GitHub-only by design (Gitea releases are
|
||||||
|
created via the ship workflow's API call, not a workflow).
|
||||||
|
|
||||||
|
## Why no Gitea mirror
|
||||||
|
|
||||||
|
Gitea Actions (act_runner) has limited support for reusable workflow
|
||||||
|
composition, environment protection, and the `gh` CLI used by the release
|
||||||
|
job. The 3 shared workflows are the ones that need to run on both forges
|
||||||
|
(CI + deploy + lifecycle). The 4 GitHub-only workflows are the
|
||||||
|
production-grade platform pipelines that run on GitHub Actions; Gitea is
|
||||||
|
the dev/integration forge. Mirroring them would require feature parity
|
||||||
|
that Gitea Actions does not currently provide.
|
||||||
|
|
||||||
|
This is a documented limitation, not a defect. A future milestone may
|
||||||
|
add Gitea mirrors if act_runner gains the required features.
|
||||||
@@ -26,7 +26,7 @@
|
|||||||
# platform log) for auditability.
|
# platform log) for auditability.
|
||||||
#
|
#
|
||||||
# Inputs:
|
# Inputs:
|
||||||
# contract — path to the consumer's contract YAML (default .acdl/contract.yml)
|
# contract — path to the consumer's contract YAML (default .nova/contract.yml)
|
||||||
# mode — full | plan-only | check-only (default full; dev = full apply,
|
# mode — full | plan-only | check-only (default full; dev = full apply,
|
||||||
# higher environments hold for HITL — the calling repo or the
|
# higher environments hold for HITL — the calling repo or the
|
||||||
# forge environment gate enforces that)
|
# forge environment gate enforces that)
|
||||||
@@ -39,11 +39,11 @@
|
|||||||
# policy restricts view/update to resources tagged acdl:owner=<consumer-repo>.
|
# policy restricts view/update to resources tagged acdl:owner=<consumer-repo>.
|
||||||
#
|
#
|
||||||
# Override (where OIDC is unavailable, e.g. Gitea pending
|
# Override (where OIDC is unavailable, e.g. Gitea pending
|
||||||
# go-gitea/gitea#36988): set ACDL_AWS_ACCESS_KEY_ID + ACDL_AWS_SECRET_ACCESS_KEY
|
# go-gitea/gitea#36988): set NOVA_AWS_ACCESS_KEY_ID + NOVA_AWS_SECRET_ACCESS_KEY
|
||||||
# as repository secrets. The platform-managed scheduled pipeline rotates
|
# as repository secrets. The platform-managed scheduled pipeline rotates
|
||||||
# the key on a daily cadence. When .env.secrets is used locally instead,
|
# the key on a daily cadence. When .env.secrets is used locally instead,
|
||||||
# rotating the key out of band is the consumer's responsibility.
|
# rotating the key out of band is the consumer's responsibility.
|
||||||
name: acdl-deploy
|
name: nova-deploy
|
||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_call:
|
workflow_call:
|
||||||
@@ -51,7 +51,7 @@ on:
|
|||||||
contract:
|
contract:
|
||||||
description: Path to the consumer contract YAML (in the consumer repo)
|
description: Path to the consumer contract YAML (in the consumer repo)
|
||||||
type: string
|
type: string
|
||||||
default: .acdl/contract.yml
|
default: .nova/contract.yml
|
||||||
mode:
|
mode:
|
||||||
description: Pipeline mode — full (apply), plan-only, check-only, or decommission
|
description: Pipeline mode — full (apply), plan-only, check-only, or decommission
|
||||||
type: string
|
type: string
|
||||||
@@ -102,10 +102,11 @@ jobs:
|
|||||||
- name: Configure AWS credentials (OIDC default + static-key override)
|
- name: Configure AWS credentials (OIDC default + static-key override)
|
||||||
uses: aws-actions/configure-aws-credentials@v4
|
uses: aws-actions/configure-aws-credentials@v4
|
||||||
with:
|
with:
|
||||||
role-to-assume: ${{ secrets.ACDL_AWS_ACCESS_KEY_ID == '' && format('arn:aws:iam::{0}:role/acdl-deploy-{1}', secrets.ACDL_AWS_ACCOUNT_ID, github.repository_id) || '' }}
|
# P4 (REQ-163): IAM role renamed acdl-deploy- → nova-deploy-.
|
||||||
|
role-to-assume: ${{ secrets.NOVA_AWS_ACCESS_KEY_ID == '' && format('arn:aws:iam::{0}:role/nova-deploy-{1}', secrets.NOVA_AWS_ACCOUNT_ID, github.repository_id) || '' }}
|
||||||
aws-region: us-east-1
|
aws-region: us-east-1
|
||||||
access-key-id: ${{ secrets.ACDL_AWS_ACCESS_KEY_ID }}
|
access-key-id: ${{ secrets.NOVA_AWS_ACCESS_KEY_ID }}
|
||||||
secret-access-key: ${{ secrets.ACDL_AWS_SECRET_ACCESS_KEY }}
|
secret-access-key: ${{ secrets.NOVA_AWS_SECRET_ACCESS_KEY }}
|
||||||
|
|
||||||
- name: Run the platform pipeline
|
- name: Run the platform pipeline
|
||||||
working-directory: ${{ github.workspace }}
|
working-directory: ${{ github.workspace }}
|
||||||
@@ -145,7 +146,7 @@ jobs:
|
|||||||
AWS_DEFAULT_REGION: us-east-1
|
AWS_DEFAULT_REGION: us-east-1
|
||||||
run: |
|
run: |
|
||||||
aws lambda invoke-function-url \
|
aws lambda invoke-function-url \
|
||||||
--function-url "${{ secrets.ACDL_LAMBDA_URL }}" \
|
--function-url "${{ secrets.NOVA_LAMBDA_URL }}" \
|
||||||
--cli-binary-format raw-in-base64-out \
|
--cli-binary-format raw-in-base64-out \
|
||||||
--payload "$(python3 -c "import json,os; print(json.dumps({'action':'report_error','consumerRepo':os.environ.get('GITHUB_REPOSITORY',''),'contractId':'${{ github.run_id }}','error':'Deploy pipeline failed. See run logs.','runUrl':'${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}','environment':'dev'}))")" \
|
--payload "$(python3 -c "import json,os; print(json.dumps({'action':'report_error','consumerRepo':os.environ.get('GITHUB_REPOSITORY',''),'contractId':'${{ github.run_id }}','error':'Deploy pipeline failed. See run logs.','runUrl':'${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}','environment':'dev'}))")" \
|
||||||
/dev/null || true
|
/dev/null || true
|
||||||
@@ -153,13 +154,13 @@ jobs:
|
|||||||
- name: Upload emitted Terraform
|
- name: Upload emitted Terraform
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: acdl-terraform
|
name: nova-terraform
|
||||||
path: /tmp/acdl_platform_run_v18/tf/*.tf
|
path: /tmp/acdl_platform_run_v18/tf/*.tf
|
||||||
if-no-files-found: warn
|
if-no-files-found: warn
|
||||||
|
|
||||||
- name: Upload platform log
|
- name: Upload platform log
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: acdl-platform-log
|
name: nova-platform-log
|
||||||
path: platform/logs/
|
path: platform/logs/
|
||||||
if-no-files-found: warn
|
if-no-files-found: warn
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
# "plan" — the lifecycle scripts run `run_platform.sh --plan-only` (fast,
|
# "plan" — the lifecycle scripts run `run_platform.sh --plan-only` (fast,
|
||||||
# no AWS mutation, validates the contract->resolver->adapter->plan chain
|
# no AWS mutation, validates the contract->resolver->adapter->plan chain
|
||||||
# for every module on every PR, with no AWS credentials or cost). Set to
|
# for every module on every PR, with no AWS credentials or cost). Set to
|
||||||
# "full" via workflow_dispatch (or the ACDL_LIFECYCLE_MODE repo variable)
|
# "full" via workflow_dispatch (or the NOVA_LIFECYCLE_MODE repo variable)
|
||||||
# to run the real apply→modify→destroy against live AWS. In plan mode the
|
# to run the real apply→modify→destroy against live AWS. In plan mode the
|
||||||
# short-lived CI VPC apply/destroy jobs are skipped (nothing is applied).
|
# short-lived CI VPC apply/destroy jobs are skipped (nothing is applied).
|
||||||
#
|
#
|
||||||
@@ -49,7 +49,7 @@ jobs:
|
|||||||
ci-vpc-apply:
|
ci-vpc-apply:
|
||||||
name: CI VPC apply
|
name: CI VPC apply
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: ${{ github.event.inputs.lifecycle_mode != 'plan' && vars.ACDL_LIFECYCLE_MODE != 'plan' }}
|
if: ${{ github.event.inputs.lifecycle_mode != 'plan' && vars.NOVA_LIFECYCLE_MODE != 'plan' }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: Install Terraform 1.9.*
|
- name: Install Terraform 1.9.*
|
||||||
@@ -60,8 +60,8 @@ jobs:
|
|||||||
- name: Apply CI VPC
|
- name: Apply CI VPC
|
||||||
working-directory: terraform/ci-vpc
|
working-directory: terraform/ci-vpc
|
||||||
env:
|
env:
|
||||||
AWS_ACCESS_KEY_ID: ${{ secrets.ACDL_AWS_ACCESS_KEY_ID }}
|
AWS_ACCESS_KEY_ID: ${{ secrets.NOVA_AWS_ACCESS_KEY_ID }}
|
||||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.ACDL_AWS_SECRET_ACCESS_KEY }}
|
AWS_SECRET_ACCESS_KEY: ${{ secrets.NOVA_AWS_SECRET_ACCESS_KEY }}
|
||||||
AWS_DEFAULT_REGION: us-east-1
|
AWS_DEFAULT_REGION: us-east-1
|
||||||
run: |
|
run: |
|
||||||
terraform init -input=false -lock=false
|
terraform init -input=false -lock=false
|
||||||
@@ -78,7 +78,7 @@ jobs:
|
|||||||
matrix:
|
matrix:
|
||||||
module: [s3, kms-key, ecr, ecs-cluster, iam-role, cloudfront, waf, vpc, alb, ecs-service, rds, uptime]
|
module: [s3, kms-key, ecr, ecs-cluster, iam-role, cloudfront, waf, vpc, alb, ecs-service, rds, uptime]
|
||||||
env:
|
env:
|
||||||
ACDL_LIFECYCLE_MODE: ${{ github.event.inputs.lifecycle_mode || vars.ACDL_LIFECYCLE_MODE || 'plan' }}
|
NOVA_LIFECYCLE_MODE: ${{ github.event.inputs.lifecycle_mode || vars.NOVA_LIFECYCLE_MODE || 'plan' }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: Free disk space
|
- name: Free disk space
|
||||||
@@ -97,31 +97,31 @@ jobs:
|
|||||||
echo "deb [signed-by=/usr/share/keyrings/hashicorp.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list
|
echo "deb [signed-by=/usr/share/keyrings/hashicorp.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list
|
||||||
sudo apt-get update && sudo apt-get install -y terraform=1.9.*
|
sudo apt-get update && sudo apt-get install -y terraform=1.9.*
|
||||||
- name: Read CI VPC outputs
|
- name: Read CI VPC outputs
|
||||||
if: ${{ env.ACDL_LIFECYCLE_MODE == 'full' }}
|
if: ${{ env.NOVA_LIFECYCLE_MODE == 'full' }}
|
||||||
working-directory: terraform/ci-vpc
|
working-directory: terraform/ci-vpc
|
||||||
env:
|
env:
|
||||||
AWS_ACCESS_KEY_ID: ${{ secrets.ACDL_AWS_ACCESS_KEY_ID }}
|
AWS_ACCESS_KEY_ID: ${{ secrets.NOVA_AWS_ACCESS_KEY_ID }}
|
||||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.ACDL_AWS_SECRET_ACCESS_KEY }}
|
AWS_SECRET_ACCESS_KEY: ${{ secrets.NOVA_AWS_SECRET_ACCESS_KEY }}
|
||||||
AWS_DEFAULT_REGION: us-east-1
|
AWS_DEFAULT_REGION: us-east-1
|
||||||
run: |
|
run: |
|
||||||
terraform init -input=false -lock=false
|
terraform init -input=false -lock=false
|
||||||
terraform output -json > /tmp/ci-vpc-outputs.json
|
terraform output -json > /tmp/ci-vpc-outputs.json
|
||||||
- name: Apply (simple)
|
- name: Apply (simple)
|
||||||
env:
|
env:
|
||||||
AWS_ACCESS_KEY_ID: ${{ secrets.ACDL_AWS_ACCESS_KEY_ID }}
|
AWS_ACCESS_KEY_ID: ${{ secrets.NOVA_AWS_ACCESS_KEY_ID }}
|
||||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.ACDL_AWS_SECRET_ACCESS_KEY }}
|
AWS_SECRET_ACCESS_KEY: ${{ secrets.NOVA_AWS_SECRET_ACCESS_KEY }}
|
||||||
AWS_DEFAULT_REGION: us-east-1
|
AWS_DEFAULT_REGION: us-east-1
|
||||||
run: bash scripts/run_lifecycle_test.sh ${{ matrix.module }} simple /tmp/ci-vpc-outputs.json
|
run: bash scripts/run_lifecycle_test.sh ${{ matrix.module }} simple /tmp/ci-vpc-outputs.json
|
||||||
- name: Modify (complex)
|
- name: Modify (complex)
|
||||||
env:
|
env:
|
||||||
AWS_ACCESS_KEY_ID: ${{ secrets.ACDL_AWS_ACCESS_KEY_ID }}
|
AWS_ACCESS_KEY_ID: ${{ secrets.NOVA_AWS_ACCESS_KEY_ID }}
|
||||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.ACDL_AWS_SECRET_ACCESS_KEY }}
|
AWS_SECRET_ACCESS_KEY: ${{ secrets.NOVA_AWS_SECRET_ACCESS_KEY }}
|
||||||
AWS_DEFAULT_REGION: us-east-1
|
AWS_DEFAULT_REGION: us-east-1
|
||||||
run: bash scripts/run_lifecycle_test.sh ${{ matrix.module }} complex /tmp/ci-vpc-outputs.json
|
run: bash scripts/run_lifecycle_test.sh ${{ matrix.module }} complex /tmp/ci-vpc-outputs.json
|
||||||
- name: Destroy
|
- name: Destroy
|
||||||
env:
|
env:
|
||||||
AWS_ACCESS_KEY_ID: ${{ secrets.ACDL_AWS_ACCESS_KEY_ID }}
|
AWS_ACCESS_KEY_ID: ${{ secrets.NOVA_AWS_ACCESS_KEY_ID }}
|
||||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.ACDL_AWS_SECRET_ACCESS_KEY }}
|
AWS_SECRET_ACCESS_KEY: ${{ secrets.NOVA_AWS_SECRET_ACCESS_KEY }}
|
||||||
AWS_DEFAULT_REGION: us-east-1
|
AWS_DEFAULT_REGION: us-east-1
|
||||||
run: bash scripts/run_lifecycle_destroy.sh ${{ matrix.module }} /tmp/ci-vpc-outputs.json
|
run: bash scripts/run_lifecycle_destroy.sh ${{ matrix.module }} /tmp/ci-vpc-outputs.json
|
||||||
|
|
||||||
@@ -136,7 +136,7 @@ jobs:
|
|||||||
matrix:
|
matrix:
|
||||||
module: [static-assets, microservice]
|
module: [static-assets, microservice]
|
||||||
env:
|
env:
|
||||||
ACDL_LIFECYCLE_MODE: ${{ github.event.inputs.lifecycle_mode || vars.ACDL_LIFECYCLE_MODE || 'plan' }}
|
NOVA_LIFECYCLE_MODE: ${{ github.event.inputs.lifecycle_mode || vars.NOVA_LIFECYCLE_MODE || 'plan' }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: Free disk space
|
- name: Free disk space
|
||||||
@@ -155,31 +155,31 @@ jobs:
|
|||||||
echo "deb [signed-by=/usr/share/keyrings/hashicorp.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list
|
echo "deb [signed-by=/usr/share/keyrings/hashicorp.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list
|
||||||
sudo apt-get update && sudo apt-get install -y terraform=1.9.*
|
sudo apt-get update && sudo apt-get install -y terraform=1.9.*
|
||||||
- name: Read CI VPC outputs
|
- name: Read CI VPC outputs
|
||||||
if: ${{ env.ACDL_LIFECYCLE_MODE == 'full' }}
|
if: ${{ env.NOVA_LIFECYCLE_MODE == 'full' }}
|
||||||
working-directory: terraform/ci-vpc
|
working-directory: terraform/ci-vpc
|
||||||
env:
|
env:
|
||||||
AWS_ACCESS_KEY_ID: ${{ secrets.ACDL_AWS_ACCESS_KEY_ID }}
|
AWS_ACCESS_KEY_ID: ${{ secrets.NOVA_AWS_ACCESS_KEY_ID }}
|
||||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.ACDL_AWS_SECRET_ACCESS_KEY }}
|
AWS_SECRET_ACCESS_KEY: ${{ secrets.NOVA_AWS_SECRET_ACCESS_KEY }}
|
||||||
AWS_DEFAULT_REGION: us-east-1
|
AWS_DEFAULT_REGION: us-east-1
|
||||||
run: |
|
run: |
|
||||||
terraform init -input=false -lock=false
|
terraform init -input=false -lock=false
|
||||||
terraform output -json > /tmp/ci-vpc-outputs.json
|
terraform output -json > /tmp/ci-vpc-outputs.json
|
||||||
- name: Apply (simple)
|
- name: Apply (simple)
|
||||||
env:
|
env:
|
||||||
AWS_ACCESS_KEY_ID: ${{ secrets.ACDL_AWS_ACCESS_KEY_ID }}
|
AWS_ACCESS_KEY_ID: ${{ secrets.NOVA_AWS_ACCESS_KEY_ID }}
|
||||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.ACDL_AWS_SECRET_ACCESS_KEY }}
|
AWS_SECRET_ACCESS_KEY: ${{ secrets.NOVA_AWS_SECRET_ACCESS_KEY }}
|
||||||
AWS_DEFAULT_REGION: us-east-1
|
AWS_DEFAULT_REGION: us-east-1
|
||||||
run: bash scripts/run_l2_lifecycle_test.sh ${{ matrix.module }} simple /tmp/ci-vpc-outputs.json
|
run: bash scripts/run_l2_lifecycle_test.sh ${{ matrix.module }} simple /tmp/ci-vpc-outputs.json
|
||||||
- name: Modify (complex)
|
- name: Modify (complex)
|
||||||
env:
|
env:
|
||||||
AWS_ACCESS_KEY_ID: ${{ secrets.ACDL_AWS_ACCESS_KEY_ID }}
|
AWS_ACCESS_KEY_ID: ${{ secrets.NOVA_AWS_ACCESS_KEY_ID }}
|
||||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.ACDL_AWS_SECRET_ACCESS_KEY }}
|
AWS_SECRET_ACCESS_KEY: ${{ secrets.NOVA_AWS_SECRET_ACCESS_KEY }}
|
||||||
AWS_DEFAULT_REGION: us-east-1
|
AWS_DEFAULT_REGION: us-east-1
|
||||||
run: bash scripts/run_l2_lifecycle_test.sh ${{ matrix.module }} complex /tmp/ci-vpc-outputs.json
|
run: bash scripts/run_l2_lifecycle_test.sh ${{ matrix.module }} complex /tmp/ci-vpc-outputs.json
|
||||||
- name: Destroy
|
- name: Destroy
|
||||||
env:
|
env:
|
||||||
AWS_ACCESS_KEY_ID: ${{ secrets.ACDL_AWS_ACCESS_KEY_ID }}
|
AWS_ACCESS_KEY_ID: ${{ secrets.NOVA_AWS_ACCESS_KEY_ID }}
|
||||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.ACDL_AWS_SECRET_ACCESS_KEY }}
|
AWS_SECRET_ACCESS_KEY: ${{ secrets.NOVA_AWS_SECRET_ACCESS_KEY }}
|
||||||
AWS_DEFAULT_REGION: us-east-1
|
AWS_DEFAULT_REGION: us-east-1
|
||||||
run: bash scripts/run_l2_lifecycle_destroy.sh ${{ matrix.module }} /tmp/ci-vpc-outputs.json
|
run: bash scripts/run_l2_lifecycle_destroy.sh ${{ matrix.module }} /tmp/ci-vpc-outputs.json
|
||||||
|
|
||||||
@@ -188,7 +188,7 @@ jobs:
|
|||||||
name: CI VPC destroy
|
name: CI VPC destroy
|
||||||
needs: [lifecycle, l2-lifecycle]
|
needs: [lifecycle, l2-lifecycle]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: ${{ always() && github.event.inputs.lifecycle_mode != 'plan' && vars.ACDL_LIFECYCLE_MODE != 'plan' }}
|
if: ${{ always() && github.event.inputs.lifecycle_mode != 'plan' && vars.NOVA_LIFECYCLE_MODE != 'plan' }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: Install Terraform 1.9.*
|
- name: Install Terraform 1.9.*
|
||||||
@@ -199,8 +199,8 @@ jobs:
|
|||||||
- name: Destroy CI VPC
|
- name: Destroy CI VPC
|
||||||
working-directory: terraform/ci-vpc
|
working-directory: terraform/ci-vpc
|
||||||
env:
|
env:
|
||||||
AWS_ACCESS_KEY_ID: ${{ secrets.ACDL_AWS_ACCESS_KEY_ID }}
|
AWS_ACCESS_KEY_ID: ${{ secrets.NOVA_AWS_ACCESS_KEY_ID }}
|
||||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.ACDL_AWS_SECRET_ACCESS_KEY }}
|
AWS_SECRET_ACCESS_KEY: ${{ secrets.NOVA_AWS_SECRET_ACCESS_KEY }}
|
||||||
AWS_DEFAULT_REGION: us-east-1
|
AWS_DEFAULT_REGION: us-east-1
|
||||||
run: |
|
run: |
|
||||||
terraform init -input=false -lock=false
|
terraform init -input=false -lock=false
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
# platform log) for auditability.
|
# platform log) for auditability.
|
||||||
#
|
#
|
||||||
# Inputs:
|
# Inputs:
|
||||||
# contract — path to the consumer's contract YAML (default .acdl/contract.yml)
|
# contract — path to the consumer's contract YAML (default .nova/contract.yml)
|
||||||
# mode — full | plan-only | check-only (default full; dev = full apply,
|
# mode — full | plan-only | check-only (default full; dev = full apply,
|
||||||
# higher environments hold for HITL — the calling repo or the
|
# higher environments hold for HITL — the calling repo or the
|
||||||
# forge environment gate enforces that)
|
# forge environment gate enforces that)
|
||||||
@@ -39,11 +39,11 @@
|
|||||||
# policy restricts view/update to resources tagged acdl:owner=<consumer-repo>.
|
# policy restricts view/update to resources tagged acdl:owner=<consumer-repo>.
|
||||||
#
|
#
|
||||||
# Override (where OIDC is unavailable, e.g. Gitea pending
|
# Override (where OIDC is unavailable, e.g. Gitea pending
|
||||||
# go-gitea/gitea#36988): set ACDL_AWS_ACCESS_KEY_ID + ACDL_AWS_SECRET_ACCESS_KEY
|
# go-gitea/gitea#36988): set NOVA_AWS_ACCESS_KEY_ID + NOVA_AWS_SECRET_ACCESS_KEY
|
||||||
# as repository secrets. The platform-managed scheduled pipeline rotates
|
# as repository secrets. The platform-managed scheduled pipeline rotates
|
||||||
# the key on a daily cadence. When .env.secrets is used locally instead,
|
# the key on a daily cadence. When .env.secrets is used locally instead,
|
||||||
# rotating the key out of band is the consumer's responsibility.
|
# rotating the key out of band is the consumer's responsibility.
|
||||||
name: acdl-deploy
|
name: nova-deploy
|
||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_call:
|
workflow_call:
|
||||||
@@ -51,7 +51,7 @@ on:
|
|||||||
contract:
|
contract:
|
||||||
description: Path to the consumer contract YAML (in the consumer repo)
|
description: Path to the consumer contract YAML (in the consumer repo)
|
||||||
type: string
|
type: string
|
||||||
default: .acdl/contract.yml
|
default: .nova/contract.yml
|
||||||
mode:
|
mode:
|
||||||
description: Pipeline mode — full (apply), plan-only, check-only, or decommission
|
description: Pipeline mode — full (apply), plan-only, check-only, or decommission
|
||||||
type: string
|
type: string
|
||||||
@@ -102,10 +102,11 @@ jobs:
|
|||||||
- name: Configure AWS credentials (OIDC default + static-key override)
|
- name: Configure AWS credentials (OIDC default + static-key override)
|
||||||
uses: aws-actions/configure-aws-credentials@v4
|
uses: aws-actions/configure-aws-credentials@v4
|
||||||
with:
|
with:
|
||||||
role-to-assume: ${{ secrets.ACDL_AWS_ACCESS_KEY_ID == '' && format('arn:aws:iam::{0}:role/acdl-deploy-{1}', secrets.ACDL_AWS_ACCOUNT_ID, github.repository_id) || '' }}
|
# P4 (REQ-163): IAM role renamed acdl-deploy- → nova-deploy-.
|
||||||
|
role-to-assume: ${{ secrets.NOVA_AWS_ACCESS_KEY_ID == '' && format('arn:aws:iam::{0}:role/nova-deploy-{1}', secrets.NOVA_AWS_ACCOUNT_ID, github.repository_id) || '' }}
|
||||||
aws-region: us-east-1
|
aws-region: us-east-1
|
||||||
access-key-id: ${{ secrets.ACDL_AWS_ACCESS_KEY_ID }}
|
access-key-id: ${{ secrets.NOVA_AWS_ACCESS_KEY_ID }}
|
||||||
secret-access-key: ${{ secrets.ACDL_AWS_SECRET_ACCESS_KEY }}
|
secret-access-key: ${{ secrets.NOVA_AWS_SECRET_ACCESS_KEY }}
|
||||||
|
|
||||||
- name: Run the platform pipeline
|
- name: Run the platform pipeline
|
||||||
working-directory: ${{ github.workspace }}
|
working-directory: ${{ github.workspace }}
|
||||||
@@ -145,7 +146,7 @@ jobs:
|
|||||||
AWS_DEFAULT_REGION: us-east-1
|
AWS_DEFAULT_REGION: us-east-1
|
||||||
run: |
|
run: |
|
||||||
aws lambda invoke-function-url \
|
aws lambda invoke-function-url \
|
||||||
--function-url "${{ secrets.ACDL_LAMBDA_URL }}" \
|
--function-url "${{ secrets.NOVA_LAMBDA_URL }}" \
|
||||||
--cli-binary-format raw-in-base64-out \
|
--cli-binary-format raw-in-base64-out \
|
||||||
--payload "$(python3 -c "import json,os; print(json.dumps({'action':'report_error','consumerRepo':os.environ.get('GITHUB_REPOSITORY',''),'contractId':'${{ github.run_id }}','error':'Deploy pipeline failed. See run logs.','runUrl':'${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}','environment':'dev'}))")" \
|
--payload "$(python3 -c "import json,os; print(json.dumps({'action':'report_error','consumerRepo':os.environ.get('GITHUB_REPOSITORY',''),'contractId':'${{ github.run_id }}','error':'Deploy pipeline failed. See run logs.','runUrl':'${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}','environment':'dev'}))")" \
|
||||||
/dev/null || true
|
/dev/null || true
|
||||||
@@ -153,13 +154,13 @@ jobs:
|
|||||||
- name: Upload emitted Terraform
|
- name: Upload emitted Terraform
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: acdl-terraform
|
name: nova-terraform
|
||||||
path: /tmp/acdl_platform_run_v18/tf/*.tf
|
path: /tmp/acdl_platform_run_v18/tf/*.tf
|
||||||
if-no-files-found: warn
|
if-no-files-found: warn
|
||||||
|
|
||||||
- name: Upload platform log
|
- name: Upload platform log
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: acdl-platform-log
|
name: nova-platform-log
|
||||||
path: platform/logs/
|
path: platform/logs/
|
||||||
if-no-files-found: warn
|
if-no-files-found: warn
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
# "plan" — the lifecycle scripts run `run_platform.sh --plan-only` (fast,
|
# "plan" — the lifecycle scripts run `run_platform.sh --plan-only` (fast,
|
||||||
# no AWS mutation, validates the contract->resolver->adapter->plan chain
|
# no AWS mutation, validates the contract->resolver->adapter->plan chain
|
||||||
# for every module on every PR, with no AWS credentials or cost). Set to
|
# for every module on every PR, with no AWS credentials or cost). Set to
|
||||||
# "full" via workflow_dispatch (or the ACDL_LIFECYCLE_MODE repo variable)
|
# "full" via workflow_dispatch (or the NOVA_LIFECYCLE_MODE repo variable)
|
||||||
# to run the real apply→modify→destroy against live AWS. In plan mode the
|
# to run the real apply→modify→destroy against live AWS. In plan mode the
|
||||||
# short-lived CI VPC apply/destroy jobs are skipped (nothing is applied).
|
# short-lived CI VPC apply/destroy jobs are skipped (nothing is applied).
|
||||||
#
|
#
|
||||||
@@ -49,7 +49,7 @@ jobs:
|
|||||||
ci-vpc-apply:
|
ci-vpc-apply:
|
||||||
name: CI VPC apply
|
name: CI VPC apply
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: ${{ github.event.inputs.lifecycle_mode != 'plan' && vars.ACDL_LIFECYCLE_MODE != 'plan' }}
|
if: ${{ github.event.inputs.lifecycle_mode != 'plan' && vars.NOVA_LIFECYCLE_MODE != 'plan' }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: Install Terraform 1.9.*
|
- name: Install Terraform 1.9.*
|
||||||
@@ -60,8 +60,8 @@ jobs:
|
|||||||
- name: Apply CI VPC
|
- name: Apply CI VPC
|
||||||
working-directory: terraform/ci-vpc
|
working-directory: terraform/ci-vpc
|
||||||
env:
|
env:
|
||||||
AWS_ACCESS_KEY_ID: ${{ secrets.ACDL_AWS_ACCESS_KEY_ID }}
|
AWS_ACCESS_KEY_ID: ${{ secrets.NOVA_AWS_ACCESS_KEY_ID }}
|
||||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.ACDL_AWS_SECRET_ACCESS_KEY }}
|
AWS_SECRET_ACCESS_KEY: ${{ secrets.NOVA_AWS_SECRET_ACCESS_KEY }}
|
||||||
AWS_DEFAULT_REGION: us-east-1
|
AWS_DEFAULT_REGION: us-east-1
|
||||||
run: |
|
run: |
|
||||||
terraform init -input=false -lock=false
|
terraform init -input=false -lock=false
|
||||||
@@ -78,7 +78,7 @@ jobs:
|
|||||||
matrix:
|
matrix:
|
||||||
module: [s3, kms-key, ecr, ecs-cluster, iam-role, cloudfront, waf, vpc, alb, ecs-service, rds, uptime]
|
module: [s3, kms-key, ecr, ecs-cluster, iam-role, cloudfront, waf, vpc, alb, ecs-service, rds, uptime]
|
||||||
env:
|
env:
|
||||||
ACDL_LIFECYCLE_MODE: ${{ github.event.inputs.lifecycle_mode || vars.ACDL_LIFECYCLE_MODE || 'plan' }}
|
NOVA_LIFECYCLE_MODE: ${{ github.event.inputs.lifecycle_mode || vars.NOVA_LIFECYCLE_MODE || 'plan' }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: Free disk space
|
- name: Free disk space
|
||||||
@@ -97,31 +97,31 @@ jobs:
|
|||||||
echo "deb [signed-by=/usr/share/keyrings/hashicorp.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list
|
echo "deb [signed-by=/usr/share/keyrings/hashicorp.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list
|
||||||
sudo apt-get update && sudo apt-get install -y terraform=1.9.*
|
sudo apt-get update && sudo apt-get install -y terraform=1.9.*
|
||||||
- name: Read CI VPC outputs
|
- name: Read CI VPC outputs
|
||||||
if: ${{ env.ACDL_LIFECYCLE_MODE == 'full' }}
|
if: ${{ env.NOVA_LIFECYCLE_MODE == 'full' }}
|
||||||
working-directory: terraform/ci-vpc
|
working-directory: terraform/ci-vpc
|
||||||
env:
|
env:
|
||||||
AWS_ACCESS_KEY_ID: ${{ secrets.ACDL_AWS_ACCESS_KEY_ID }}
|
AWS_ACCESS_KEY_ID: ${{ secrets.NOVA_AWS_ACCESS_KEY_ID }}
|
||||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.ACDL_AWS_SECRET_ACCESS_KEY }}
|
AWS_SECRET_ACCESS_KEY: ${{ secrets.NOVA_AWS_SECRET_ACCESS_KEY }}
|
||||||
AWS_DEFAULT_REGION: us-east-1
|
AWS_DEFAULT_REGION: us-east-1
|
||||||
run: |
|
run: |
|
||||||
terraform init -input=false -lock=false
|
terraform init -input=false -lock=false
|
||||||
terraform output -json > /tmp/ci-vpc-outputs.json
|
terraform output -json > /tmp/ci-vpc-outputs.json
|
||||||
- name: Apply (simple)
|
- name: Apply (simple)
|
||||||
env:
|
env:
|
||||||
AWS_ACCESS_KEY_ID: ${{ secrets.ACDL_AWS_ACCESS_KEY_ID }}
|
AWS_ACCESS_KEY_ID: ${{ secrets.NOVA_AWS_ACCESS_KEY_ID }}
|
||||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.ACDL_AWS_SECRET_ACCESS_KEY }}
|
AWS_SECRET_ACCESS_KEY: ${{ secrets.NOVA_AWS_SECRET_ACCESS_KEY }}
|
||||||
AWS_DEFAULT_REGION: us-east-1
|
AWS_DEFAULT_REGION: us-east-1
|
||||||
run: bash scripts/run_lifecycle_test.sh ${{ matrix.module }} simple /tmp/ci-vpc-outputs.json
|
run: bash scripts/run_lifecycle_test.sh ${{ matrix.module }} simple /tmp/ci-vpc-outputs.json
|
||||||
- name: Modify (complex)
|
- name: Modify (complex)
|
||||||
env:
|
env:
|
||||||
AWS_ACCESS_KEY_ID: ${{ secrets.ACDL_AWS_ACCESS_KEY_ID }}
|
AWS_ACCESS_KEY_ID: ${{ secrets.NOVA_AWS_ACCESS_KEY_ID }}
|
||||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.ACDL_AWS_SECRET_ACCESS_KEY }}
|
AWS_SECRET_ACCESS_KEY: ${{ secrets.NOVA_AWS_SECRET_ACCESS_KEY }}
|
||||||
AWS_DEFAULT_REGION: us-east-1
|
AWS_DEFAULT_REGION: us-east-1
|
||||||
run: bash scripts/run_lifecycle_test.sh ${{ matrix.module }} complex /tmp/ci-vpc-outputs.json
|
run: bash scripts/run_lifecycle_test.sh ${{ matrix.module }} complex /tmp/ci-vpc-outputs.json
|
||||||
- name: Destroy
|
- name: Destroy
|
||||||
env:
|
env:
|
||||||
AWS_ACCESS_KEY_ID: ${{ secrets.ACDL_AWS_ACCESS_KEY_ID }}
|
AWS_ACCESS_KEY_ID: ${{ secrets.NOVA_AWS_ACCESS_KEY_ID }}
|
||||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.ACDL_AWS_SECRET_ACCESS_KEY }}
|
AWS_SECRET_ACCESS_KEY: ${{ secrets.NOVA_AWS_SECRET_ACCESS_KEY }}
|
||||||
AWS_DEFAULT_REGION: us-east-1
|
AWS_DEFAULT_REGION: us-east-1
|
||||||
run: bash scripts/run_lifecycle_destroy.sh ${{ matrix.module }} /tmp/ci-vpc-outputs.json
|
run: bash scripts/run_lifecycle_destroy.sh ${{ matrix.module }} /tmp/ci-vpc-outputs.json
|
||||||
|
|
||||||
@@ -136,7 +136,7 @@ jobs:
|
|||||||
matrix:
|
matrix:
|
||||||
module: [static-assets, microservice]
|
module: [static-assets, microservice]
|
||||||
env:
|
env:
|
||||||
ACDL_LIFECYCLE_MODE: ${{ github.event.inputs.lifecycle_mode || vars.ACDL_LIFECYCLE_MODE || 'plan' }}
|
NOVA_LIFECYCLE_MODE: ${{ github.event.inputs.lifecycle_mode || vars.NOVA_LIFECYCLE_MODE || 'plan' }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: Free disk space
|
- name: Free disk space
|
||||||
@@ -155,31 +155,31 @@ jobs:
|
|||||||
echo "deb [signed-by=/usr/share/keyrings/hashicorp.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list
|
echo "deb [signed-by=/usr/share/keyrings/hashicorp.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list
|
||||||
sudo apt-get update && sudo apt-get install -y terraform=1.9.*
|
sudo apt-get update && sudo apt-get install -y terraform=1.9.*
|
||||||
- name: Read CI VPC outputs
|
- name: Read CI VPC outputs
|
||||||
if: ${{ env.ACDL_LIFECYCLE_MODE == 'full' }}
|
if: ${{ env.NOVA_LIFECYCLE_MODE == 'full' }}
|
||||||
working-directory: terraform/ci-vpc
|
working-directory: terraform/ci-vpc
|
||||||
env:
|
env:
|
||||||
AWS_ACCESS_KEY_ID: ${{ secrets.ACDL_AWS_ACCESS_KEY_ID }}
|
AWS_ACCESS_KEY_ID: ${{ secrets.NOVA_AWS_ACCESS_KEY_ID }}
|
||||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.ACDL_AWS_SECRET_ACCESS_KEY }}
|
AWS_SECRET_ACCESS_KEY: ${{ secrets.NOVA_AWS_SECRET_ACCESS_KEY }}
|
||||||
AWS_DEFAULT_REGION: us-east-1
|
AWS_DEFAULT_REGION: us-east-1
|
||||||
run: |
|
run: |
|
||||||
terraform init -input=false -lock=false
|
terraform init -input=false -lock=false
|
||||||
terraform output -json > /tmp/ci-vpc-outputs.json
|
terraform output -json > /tmp/ci-vpc-outputs.json
|
||||||
- name: Apply (simple)
|
- name: Apply (simple)
|
||||||
env:
|
env:
|
||||||
AWS_ACCESS_KEY_ID: ${{ secrets.ACDL_AWS_ACCESS_KEY_ID }}
|
AWS_ACCESS_KEY_ID: ${{ secrets.NOVA_AWS_ACCESS_KEY_ID }}
|
||||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.ACDL_AWS_SECRET_ACCESS_KEY }}
|
AWS_SECRET_ACCESS_KEY: ${{ secrets.NOVA_AWS_SECRET_ACCESS_KEY }}
|
||||||
AWS_DEFAULT_REGION: us-east-1
|
AWS_DEFAULT_REGION: us-east-1
|
||||||
run: bash scripts/run_l2_lifecycle_test.sh ${{ matrix.module }} simple /tmp/ci-vpc-outputs.json
|
run: bash scripts/run_l2_lifecycle_test.sh ${{ matrix.module }} simple /tmp/ci-vpc-outputs.json
|
||||||
- name: Modify (complex)
|
- name: Modify (complex)
|
||||||
env:
|
env:
|
||||||
AWS_ACCESS_KEY_ID: ${{ secrets.ACDL_AWS_ACCESS_KEY_ID }}
|
AWS_ACCESS_KEY_ID: ${{ secrets.NOVA_AWS_ACCESS_KEY_ID }}
|
||||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.ACDL_AWS_SECRET_ACCESS_KEY }}
|
AWS_SECRET_ACCESS_KEY: ${{ secrets.NOVA_AWS_SECRET_ACCESS_KEY }}
|
||||||
AWS_DEFAULT_REGION: us-east-1
|
AWS_DEFAULT_REGION: us-east-1
|
||||||
run: bash scripts/run_l2_lifecycle_test.sh ${{ matrix.module }} complex /tmp/ci-vpc-outputs.json
|
run: bash scripts/run_l2_lifecycle_test.sh ${{ matrix.module }} complex /tmp/ci-vpc-outputs.json
|
||||||
- name: Destroy
|
- name: Destroy
|
||||||
env:
|
env:
|
||||||
AWS_ACCESS_KEY_ID: ${{ secrets.ACDL_AWS_ACCESS_KEY_ID }}
|
AWS_ACCESS_KEY_ID: ${{ secrets.NOVA_AWS_ACCESS_KEY_ID }}
|
||||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.ACDL_AWS_SECRET_ACCESS_KEY }}
|
AWS_SECRET_ACCESS_KEY: ${{ secrets.NOVA_AWS_SECRET_ACCESS_KEY }}
|
||||||
AWS_DEFAULT_REGION: us-east-1
|
AWS_DEFAULT_REGION: us-east-1
|
||||||
run: bash scripts/run_l2_lifecycle_destroy.sh ${{ matrix.module }} /tmp/ci-vpc-outputs.json
|
run: bash scripts/run_l2_lifecycle_destroy.sh ${{ matrix.module }} /tmp/ci-vpc-outputs.json
|
||||||
|
|
||||||
@@ -188,7 +188,7 @@ jobs:
|
|||||||
name: CI VPC destroy
|
name: CI VPC destroy
|
||||||
needs: [lifecycle, l2-lifecycle]
|
needs: [lifecycle, l2-lifecycle]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: ${{ always() && github.event.inputs.lifecycle_mode != 'plan' && vars.ACDL_LIFECYCLE_MODE != 'plan' }}
|
if: ${{ always() && github.event.inputs.lifecycle_mode != 'plan' && vars.NOVA_LIFECYCLE_MODE != 'plan' }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: Install Terraform 1.9.*
|
- name: Install Terraform 1.9.*
|
||||||
@@ -199,8 +199,8 @@ jobs:
|
|||||||
- name: Destroy CI VPC
|
- name: Destroy CI VPC
|
||||||
working-directory: terraform/ci-vpc
|
working-directory: terraform/ci-vpc
|
||||||
env:
|
env:
|
||||||
AWS_ACCESS_KEY_ID: ${{ secrets.ACDL_AWS_ACCESS_KEY_ID }}
|
AWS_ACCESS_KEY_ID: ${{ secrets.NOVA_AWS_ACCESS_KEY_ID }}
|
||||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.ACDL_AWS_SECRET_ACCESS_KEY }}
|
AWS_SECRET_ACCESS_KEY: ${{ secrets.NOVA_AWS_SECRET_ACCESS_KEY }}
|
||||||
AWS_DEFAULT_REGION: us-east-1
|
AWS_DEFAULT_REGION: us-east-1
|
||||||
run: |
|
run: |
|
||||||
terraform init -input=false -lock=false
|
terraform init -input=false -lock=false
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# ACDL Release Pipeline — GitHub Actions (production)
|
# Nova Release Pipeline — GitHub Actions (production)
|
||||||
#
|
#
|
||||||
# Runs on push to main. Computes the next semver tag from the latest tag +
|
# Runs on push to main. Computes the next semver tag from the latest tag +
|
||||||
# commit history, creates the tag, updates floating MAJOR.MINOR and MAJOR tags,
|
# commit history, creates the tag, updates floating MAJOR.MINOR and MAJOR tags,
|
||||||
@@ -8,7 +8,7 @@
|
|||||||
# - Regular phase commit -> bump PATCH (v1.6.0 -> v1.6.1)
|
# - Regular phase commit -> bump PATCH (v1.6.0 -> v1.6.1)
|
||||||
# - Milestone completion ("docs(milestone): complete") -> bump MINOR (v1.6.1 -> v1.7.0)
|
# - Milestone completion ("docs(milestone): complete") -> bump MINOR (v1.6.1 -> v1.7.0)
|
||||||
# - Major bumps are manual (not implemented here).
|
# - Major bumps are manual (not implemented here).
|
||||||
name: acdl-release
|
name: nova-release
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
@@ -87,6 +87,6 @@ jobs:
|
|||||||
BODY=$(git log --format='- %s' HEAD)
|
BODY=$(git log --format='- %s' HEAD)
|
||||||
fi
|
fi
|
||||||
gh release create ${{ steps.version.outputs.new_tag }} \
|
gh release create ${{ steps.version.outputs.new_tag }} \
|
||||||
--title "ACDL ${{ steps.version.outputs.new_tag }}" \
|
--title "Nova ${{ steps.version.outputs.new_tag }}" \
|
||||||
--notes "$BODY" \
|
--notes "$BODY" \
|
||||||
--generate-notes || true
|
--generate-notes || true
|
||||||
@@ -18,4 +18,14 @@ terraform/bootstrap/.bootstrap_state.json
|
|||||||
**/.terraform/
|
**/.terraform/
|
||||||
**/.terraform.lock.hcl
|
**/.terraform.lock.hcl
|
||||||
**/tfplan
|
**/tfplan
|
||||||
**/*.tfstate*
|
**/*.tfstate*
|
||||||
|
|
||||||
|
# Credential patterns (v1.14, REQ-146)
|
||||||
|
*.pem
|
||||||
|
*.key
|
||||||
|
*.p12
|
||||||
|
*.pfx
|
||||||
|
*.cer
|
||||||
|
*.crt
|
||||||
|
*.jks
|
||||||
|
*.keystore
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
# ACDL — Agentic Cloud Delivery Platform
|
# Nova
|
||||||
|
|
||||||
|
> **Nova — The New Dawn of DevSecOps.** Security as a seamless enabler of fast deployments — not a bottleneck, not a "no" department.
|
||||||
|
|
||||||
Consumers declare intent; the platform delivers safe production deployment
|
Consumers declare intent; the platform delivers safe production deployment
|
||||||
through an agentic stack — automatically, safely, and with a complete audit
|
through an agentic stack — automatically, safely, and with a complete audit
|
||||||
@@ -18,7 +20,7 @@ a configuration file, or an infrastructure module.
|
|||||||
|
|
||||||
## Repository roles
|
## Repository roles
|
||||||
|
|
||||||
There are two kinds of repository in the ACDL model:
|
There are two kinds of repository in the Nova model:
|
||||||
|
|
||||||
- **Platform repo (this one).** This is the **source code of the platform**.
|
- **Platform repo (this one).** This is the **source code of the platform**.
|
||||||
It owns `modules/`, `adapters/`, `core/`, `schemas/`, `pipelines/`,
|
It owns `modules/`, `adapters/`, `core/`, `schemas/`, `pipelines/`,
|
||||||
@@ -26,7 +28,7 @@ There are two kinds of repository in the ACDL model:
|
|||||||
A **consumer never clones it.**
|
A **consumer never clones it.**
|
||||||
- **Consumer repo (yours).** A consumer repo contains only:
|
- **Consumer repo (yours).** A consumer repo contains only:
|
||||||
1. **Its application code** — the service or site being deployed.
|
1. **Its application code** — the service or site being deployed.
|
||||||
2. **One or more contracts** — small YAML files at `.acdl/contract.yml`
|
2. **One or more contracts** — small YAML files at `.nova/contract.yml`
|
||||||
that declare infrastructure (one or more modules by name + version),
|
that declare infrastructure (one or more modules by name + version),
|
||||||
select an environment, and supply module-specific inputs.
|
select an environment, and supply module-specific inputs.
|
||||||
3. **One or more CI definitions** — thin `.github/workflows/*.yml` files
|
3. **One or more CI definitions** — thin `.github/workflows/*.yml` files
|
||||||
@@ -93,7 +95,7 @@ intent via a contract; the platform delivers the deployment through the
|
|||||||
same contract schema, the same policy envelope, and the same evidence
|
same contract schema, the same policy envelope, and the same evidence
|
||||||
stream.
|
stream.
|
||||||
|
|
||||||
Consumers have their own repos and consume ACDL by writing a contract that
|
Consumers have their own repos and consume Nova by writing a contract that
|
||||||
declares infrastructure. A consumer declares a contract (id + name +
|
declares infrastructure. A consumer declares a contract (id + name +
|
||||||
environment + infrastructure); the platform resolves it to a stack instance,
|
environment + infrastructure); the platform resolves it to a stack instance,
|
||||||
compiles it, runs security + policy checks, computes a confidence signal,
|
compiles it, runs security + policy checks, computes a confidence signal,
|
||||||
@@ -142,7 +144,7 @@ engine-specific code. `modules/`, `schemas/`, `contracts/`,
|
|||||||
```bash
|
```bash
|
||||||
# 1. Bootstrap the AWS state backend + runner IAM user (one-time, idempotent)
|
# 1. Bootstrap the AWS state backend + runner IAM user (one-time, idempotent)
|
||||||
# (requires the bootstrap root key in env — skip if the state bucket +
|
# (requires the bootstrap root key in env — skip if the state bucket +
|
||||||
# acdl-spike-runner already exist)
|
# nova-spike-runner already exist)
|
||||||
ACDL_BOOTSTRAP_AWS_ACCESS_KEY_ID=... ACDL_BOOTSTRAP_AWS_SECRET_ACCESS_KEY=... \
|
ACDL_BOOTSTRAP_AWS_ACCESS_KEY_ID=... ACDL_BOOTSTRAP_AWS_SECRET_ACCESS_KEY=... \
|
||||||
python3 terraform/bootstrap/create_state_backend.py
|
python3 terraform/bootstrap/create_state_backend.py
|
||||||
ACDL_BOOTSTRAP_AWS_ACCESS_KEY_ID=... ACDL_BOOTSTRAP_AWS_SECRET_ACCESS_KEY=... \
|
ACDL_BOOTSTRAP_AWS_ACCESS_KEY_ID=... ACDL_BOOTSTRAP_AWS_SECRET_ACCESS_KEY=... \
|
||||||
@@ -222,8 +224,8 @@ The workflow implements the same stages as `pipelines/contract.yml`
|
|||||||
(validate-contract → resolve-stack → security checks → infrastructure plan
|
(validate-contract → resolve-stack → security checks → infrastructure plan
|
||||||
→ policy checks → confidence → evidence event → apply). A consumer repo
|
→ policy checks → confidence → evidence event → apply). A consumer repo
|
||||||
invokes the reusable workflow via a **versioned tag** (floating MAJOR +
|
invokes the reusable workflow via a **versioned tag** (floating MAJOR +
|
||||||
MINOR, e.g. `acdl/.github/workflows/deploy.yml@v1.6`). The workflow checks
|
MINOR, e.g. `acdl/.github/workflows/deploy.yml@v1.13`). The workflow checks
|
||||||
out the consumer repo, then checks out the ACDL platform repo into the
|
out the consumer repo, then checks out the Nova platform repo into the
|
||||||
runner workspace, and runs `scripts/run_platform.sh` against the consumer's
|
runner workspace, and runs `scripts/run_platform.sh` against the consumer's
|
||||||
contract — the consumer never clones the platform repo or invokes its
|
contract — the consumer never clones the platform repo or invokes its
|
||||||
scripts locally. See the [Consumer guide](docs/consumer-guide.md) for the
|
scripts locally. See the [Consumer guide](docs/consumer-guide.md) for the
|
||||||
@@ -247,7 +249,7 @@ backwards-compatible log-only mode.
|
|||||||
## Consumer guide
|
## Consumer guide
|
||||||
|
|
||||||
A step-by-step guide for a consumer to create their pipeline and define a
|
A step-by-step guide for a consumer to create their pipeline and define a
|
||||||
contract that deploys any ACDL module to AWS is at
|
contract that deploys any Nova module to AWS is at
|
||||||
[`docs/consumer-guide.md`](docs/consumer-guide.md). The guide is generic
|
[`docs/consumer-guide.md`](docs/consumer-guide.md). The guide is generic
|
||||||
across all modules; `static-assets` is the worked example.
|
across all modules; `static-assets` is the worked example.
|
||||||
|
|
||||||
@@ -283,8 +285,8 @@ no static credentials in repo secrets.
|
|||||||
`repo:org/consumer-repo:ref:refs/heads/main`) binds the role's trust
|
`repo:org/consumer-repo:ref:refs/heads/main`) binds the role's trust
|
||||||
policy to the exact consumer repo + branch that invoked the workflow.
|
policy to the exact consumer repo + branch that invoked the workflow.
|
||||||
- **Resource-creation attributes** — every resource the pipeline creates
|
- **Resource-creation attributes** — every resource the pipeline creates
|
||||||
is tagged with `acdl:owner=<consumer-repo>` and
|
is tagged with `nova:owner=<consumer-repo>` and
|
||||||
`acdl:contract=<contract-id>`. The session policy grants
|
`nova:contract=<contract-id>`. The session policy grants
|
||||||
view/update/delete **only on resources whose tags match the calling
|
view/update/delete **only on resources whose tags match the calling
|
||||||
repo**.
|
repo**.
|
||||||
|
|
||||||
|
|||||||
@@ -55,8 +55,8 @@ manifests (documentation-only today — the platform does not run them):
|
|||||||
|
|
||||||
- `disallow-privileged-containers.yml` — fail pods with
|
- `disallow-privileged-containers.yml` — fail pods with
|
||||||
`securityContext.privileged: true`.
|
`securityContext.privileged: true`.
|
||||||
- `require-resource-labels.yml` — require `acdl:owner` and
|
- `require-resource-labels.yml` — require `nova:owner` and
|
||||||
`acdl:environment` labels on all pods (mirrors the ACDL tagging standard
|
`nova:environment` labels on all pods (mirrors the Nova tagging standard
|
||||||
in [`schemas/tagging-standard.json`](../../schemas/tagging-standard.json)).
|
in [`schemas/tagging-standard.json`](../../schemas/tagging-standard.json)).
|
||||||
- `require-image-digests.yml` — require container images to reference a
|
- `require-image-digests.yml` — require container images to reference a
|
||||||
digest (`image@sha256:...`), not a mutable tag.
|
digest (`image@sha256:...`), not a mutable tag.
|
||||||
|
|||||||
@@ -8,13 +8,16 @@ v1.9 (REQ-111): the translator is fleshed out — full PolicyReport →
|
|||||||
PolicyCheckResult mapping with severity + skip-with-reason handling. It
|
PolicyCheckResult mapping with severity + skip-with-reason handling. It
|
||||||
remains inactive for Terraform-only stacks (guard preserved — emits a
|
remains inactive for Terraform-only stacks (guard preserved — emits a
|
||||||
single SKIPPED `KYVERNO_INACTIVE_TF_STACK` record when no K8s manifests).
|
single SKIPPED `KYVERNO_INACTIVE_TF_STACK` record when no K8s manifests).
|
||||||
A `--kube-version` stub is parsed but not yet used (for future GitOps).
|
A `--kube-version` flag was previously parsed but never used. It has been
|
||||||
|
removed (v1.14, G-103) to resolve the stub. Version-aware policy selection
|
||||||
|
will be added when the GitOps reconciler emits K8s manifests (D-053
|
||||||
|
roadmap). The adapter is inactive for Terraform-only stacks today.
|
||||||
|
|
||||||
D-053: the platform emits Terraform, not K8s manifests. This adapter
|
D-053: the platform emits Terraform, not K8s manifests. This adapter
|
||||||
activates when the GitOps reconciler (roadmap) emits K8s manifests.
|
activates when the GitOps reconciler (roadmap) emits K8s manifests.
|
||||||
Sample policies are included as documentation at adapters/kyverno/policies/.
|
Sample policies are included as documentation at adapters/kyverno/policies/.
|
||||||
|
|
||||||
CLI: kyverno_adapter.py <policyreport.json> <contract-id> [--kube-version <ver>]
|
CLI: kyverno_adapter.py <policyreport.json> <contract-id>
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
@@ -100,7 +103,7 @@ def _emit_inactive_tf(contract_id):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def adapt(policyreport_json_path, contract_id, kube_version=None):
|
def adapt(policyreport_json_path, contract_id):
|
||||||
with open(policyreport_json_path, "r", encoding="utf-8") as fh:
|
with open(policyreport_json_path, "r", encoding="utf-8") as fh:
|
||||||
data = json.load(fh)
|
data = json.load(fh)
|
||||||
out = []
|
out = []
|
||||||
@@ -112,8 +115,6 @@ def adapt(policyreport_json_path, contract_id, kube_version=None):
|
|||||||
out.append(_to_pcr(entry, contract_id))
|
out.append(_to_pcr(entry, contract_id))
|
||||||
if not out:
|
if not out:
|
||||||
out.append(_emit_inactive_tf(contract_id))
|
out.append(_emit_inactive_tf(contract_id))
|
||||||
# kube_version is parsed but not yet used (future GitOps reconciler).
|
|
||||||
_ = kube_version
|
|
||||||
return out
|
return out
|
||||||
|
|
||||||
|
|
||||||
@@ -123,14 +124,8 @@ def adapt_inactive(contract_id):
|
|||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
kube_ver = None
|
|
||||||
args = sys.argv[1:]
|
args = sys.argv[1:]
|
||||||
if "--kube-version" in args:
|
|
||||||
idx = args.index("--kube-version")
|
|
||||||
if idx + 1 < len(args):
|
|
||||||
kube_ver = args[idx + 1]
|
|
||||||
args = args[:idx] + args[idx + 2:]
|
|
||||||
if len(args) != 2:
|
if len(args) != 2:
|
||||||
print("usage: kyverno_adapter.py <policyreport.json> <contract-id> [--kube-version <ver>]", file=sys.stderr)
|
print("usage: kyverno_adapter.py <policyreport.json> <contract-id>", file=sys.stderr)
|
||||||
sys.exit(2)
|
sys.exit(2)
|
||||||
print(json.dumps(adapt(args[0], args[1], kube_version=kube_ver), indent=2))
|
print(json.dumps(adapt(args[0], args[1]), indent=2))
|
||||||
@@ -3,7 +3,7 @@ kind: ClusterPolicy
|
|||||||
metadata:
|
metadata:
|
||||||
name: require-resource-labels
|
name: require-resource-labels
|
||||||
annotations:
|
annotations:
|
||||||
policies.kyverno.io/title: Require ACDL Resource Labels
|
policies.kyverno.io/title: Require Nova Resource Labels
|
||||||
policies.kyverno.io/category: Governance
|
policies.kyverno.io/category: Governance
|
||||||
policies.kyverno.io/severity: medium
|
policies.kyverno.io/severity: medium
|
||||||
policies.kyverno.io/subject: Pod
|
policies.kyverno.io/subject: Pod
|
||||||
@@ -11,27 +11,27 @@ spec:
|
|||||||
validationFailureAction: audit
|
validationFailureAction: audit
|
||||||
background: true
|
background: true
|
||||||
rules:
|
rules:
|
||||||
- name: require-acdl-owner-label
|
- name: require-nova-owner-label
|
||||||
match:
|
match:
|
||||||
any:
|
any:
|
||||||
- resources:
|
- resources:
|
||||||
kinds:
|
kinds:
|
||||||
- Pod
|
- Pod
|
||||||
validate:
|
validate:
|
||||||
message: "Pods must carry the acdl:owner label (ACDL tagging standard)."
|
message: "Pods must carry the nova:owner label (Nova tagging standard)."
|
||||||
pattern:
|
pattern:
|
||||||
metadata:
|
metadata:
|
||||||
labels:
|
labels:
|
||||||
acdl:owner: "?*"
|
nova:owner: "?*"
|
||||||
- name: require-acdl-environment-label
|
- name: require-nova-environment-label
|
||||||
match:
|
match:
|
||||||
any:
|
any:
|
||||||
- resources:
|
- resources:
|
||||||
kinds:
|
kinds:
|
||||||
- Pod
|
- Pod
|
||||||
validate:
|
validate:
|
||||||
message: "Pods must carry the acdl:environment label (ACDL tagging standard)."
|
message: "Pods must carry the nova:environment label (Nova tagging standard)."
|
||||||
pattern:
|
pattern:
|
||||||
metadata:
|
metadata:
|
||||||
labels:
|
labels:
|
||||||
acdl:environment: "?*"
|
nova:environment: "?*"
|
||||||
@@ -10,9 +10,10 @@ lives in the per-module terraform/ subdir, NOT in this file.
|
|||||||
CLI: adapter.py <instance.json> <out_dir>
|
CLI: adapter.py <instance.json> <out_dir>
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import json
|
import json, os, sys
|
||||||
import os
|
_R = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||||
import sys
|
sys.path.insert(0, _R) if _R not in sys.path else None
|
||||||
|
from core import env
|
||||||
|
|
||||||
|
|
||||||
def _load_registry(repo_root):
|
def _load_registry(repo_root):
|
||||||
@@ -112,6 +113,8 @@ def adapt(stack_instance, out_dir):
|
|||||||
|
|
||||||
stack_name = stack.get("name", "spike")
|
stack_name = stack.get("name", "spike")
|
||||||
environment = stack.get("environment", "dev")
|
environment = stack.get("environment", "dev")
|
||||||
|
account_id = env.get_env("AWS_ACCOUNT_ID", "581513795199")
|
||||||
|
state_bucket = f"nova-tfstate-{account_id}-us-east-1"
|
||||||
terraform_tf = (
|
terraform_tf = (
|
||||||
'terraform {\n'
|
'terraform {\n'
|
||||||
' required_version = ">= 1.9, < 1.10"\n'
|
' required_version = ">= 1.9, < 1.10"\n'
|
||||||
@@ -122,7 +125,7 @@ def adapt(stack_instance, out_dir):
|
|||||||
' }\n'
|
' }\n'
|
||||||
' }\n'
|
' }\n'
|
||||||
' backend "s3" {\n'
|
' backend "s3" {\n'
|
||||||
' bucket = "acdl-tfstate-581513795199-us-east-1"\n'
|
f' bucket = "{state_bucket}"\n'
|
||||||
f' key = "spike/{stack_name}/{environment}/terraform.tfstate"\n'
|
f' key = "spike/{stack_name}/{environment}/terraform.tfstate"\n'
|
||||||
' region = "us-east-1"\n'
|
' region = "us-east-1"\n'
|
||||||
' }\n'
|
' }\n'
|
||||||
@@ -132,12 +135,12 @@ def adapt(stack_instance, out_dir):
|
|||||||
data_source_names = stack_instance.get("data_sources", [])
|
data_source_names = stack_instance.get("data_sources", [])
|
||||||
parts = []
|
parts = []
|
||||||
if data_source_names:
|
if data_source_names:
|
||||||
remote_state_key = os.environ.get("ACDL_REMOTE_STATE_KEY", "platform/terraform.tfstate")
|
remote_state_key = env.get_env("REMOTE_STATE_KEY", "platform/terraform.tfstate")
|
||||||
parts.append(
|
parts.append(
|
||||||
'data "terraform_remote_state" "platform" {\n'
|
'data "terraform_remote_state" "platform" {\n'
|
||||||
' backend = "s3"\n'
|
' backend = "s3"\n'
|
||||||
' config = {\n'
|
' config = {\n'
|
||||||
' bucket = "acdl-tfstate-581513795199-us-east-1"\n'
|
f' bucket = "{state_bucket}"\n'
|
||||||
f' key = "{remote_state_key}"\n'
|
f' key = "{remote_state_key}"\n'
|
||||||
' region = "us-east-1"\n'
|
' region = "us-east-1"\n'
|
||||||
' }\n'
|
' }\n'
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
"""Translate Checkov JSON output to ACDL PolicyCheckResult records.
|
"""Translate Checkov JSON output to Nova PolicyCheckResult records.
|
||||||
|
|
||||||
Reads Checkov's JSON output (one framework key, e.g. terraform_plan),
|
Reads Checkov's JSON output (one framework key, e.g. terraform_plan),
|
||||||
emits a list of PolicyCheckResult dicts conforming to
|
emits a list of PolicyCheckResult dicts conforming to
|
||||||
@@ -6,10 +6,13 @@ schemas/policy_check_result.schema.json. Run Checkov with --soft-fail so
|
|||||||
Checkov never exits non-zero; the confidence signal decides the gate, not
|
Checkov never exits non-zero; the confidence signal decides the gate, not
|
||||||
Checkov's exit code.
|
Checkov's exit code.
|
||||||
|
|
||||||
The ACDL tagging standard (D-054, D-043 closure) is enforced by a custom
|
The Nova tagging standard (D-054, D-043 closure, D-109 hard mode in P3)
|
||||||
Checkov rule at adapters/terraform/policy/custom_rules/acdl_tagging.py,
|
is enforced by a custom Checkov rule at
|
||||||
loaded via --external-checks-dir. The adapter therefore maps
|
adapters/terraform/policy/custom_rules/nova_tagging.py, loaded via
|
||||||
ACDL_TAG_NAMING as a real rule (no synthetic SKIPPED record is emitted).
|
--external-checks-dir. The adapter therefore maps NOVA_TAG_NAMING as a
|
||||||
|
real rule (no synthetic SKIPPED record is emitted). Renamed from
|
||||||
|
ACDL_TAG_NAMING in P2 (REQ-158); the rule is in hard mode as of P3
|
||||||
|
(REQ-162: hard-fail on missing nova:* or acdl:*-only tags).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
@@ -29,10 +32,12 @@ RULE_MAP = {
|
|||||||
"CKV_AWS_40": ("iam-wildcard", "medium"),
|
"CKV_AWS_40": ("iam-wildcard", "medium"),
|
||||||
"CKV_AWS_7": ("kms-key-reference", "medium"),
|
"CKV_AWS_7": ("kms-key-reference", "medium"),
|
||||||
"CKV_AWS_33": ("kms-key-reference", "medium"),
|
"CKV_AWS_33": ("kms-key-reference", "medium"),
|
||||||
# D-054 / D-043 closure: ACDL_TAG_NAMING is now a real custom Checkov
|
# D-054 / D-043 closure, D-109 hard mode (P3): NOVA_TAG_NAMING is a real
|
||||||
# rule (adapters/terraform/policy/custom_rules/acdl_tagging.py), loaded
|
# custom Checkov rule (adapters/terraform/policy/custom_rules/nova_tagging.py),
|
||||||
# via --external-checks-dir. No synthetic SKIPPED record is emitted.
|
# loaded via --external-checks-dir. No synthetic SKIPPED record is emitted.
|
||||||
"ACDL_TAG_NAMING": ("tagging-standard", "medium"),
|
# Renamed from ACDL_TAG_NAMING in P2 (REQ-158). Hard mode as of P3
|
||||||
|
# (REQ-162: hard-fail on missing nova:* or acdl:*-only tags).
|
||||||
|
"NOVA_TAG_NAMING": ("tagging-standard", "medium"),
|
||||||
}
|
}
|
||||||
|
|
||||||
_RESULT_MAP = {"PASSED": "pass", "FAILED": "fail", "SKIPPED": "skipped"}
|
_RESULT_MAP = {"PASSED": "pass", "FAILED": "fail", "SKIPPED": "skipped"}
|
||||||
|
|||||||
@@ -1,16 +1,24 @@
|
|||||||
# ACDL Custom Checkov Rules
|
# Nova Custom Checkov Rules
|
||||||
|
|
||||||
This directory holds ACDL-authored Checkov custom rules, written in the
|
This directory holds Nova-authored Checkov custom rules, written in the
|
||||||
[Checkov Python custom-rule framework](https://www.checkov.io/4.Contributing/Custom%20Policies.html).
|
[Checkov Python custom-rule framework](https://www.checkov.io/4.Contributing/Custom%20Policies.html).
|
||||||
|
|
||||||
## Files
|
## Files
|
||||||
|
|
||||||
- `acdl_tagging.py` — `ACDL_TAG_NAMING` (D-054): ensures every taggable AWS
|
- `nova_tagging.py` — `NOVA_TAG_NAMING` (D-054, D-109 warn mode in P2):
|
||||||
resource carries the four required ACDL tags
|
ensures every taggable AWS resource carries the four required Nova tags
|
||||||
(`acdl:owner`, `acdl:contract`, `acdl:environment`, `acdl:cost-center`).
|
(`nova:owner`, `nova:contract`, `nova:environment`, `nova:cost-center`).
|
||||||
This rule replaces the synthetic SKIPPED `ACDL_TAG_NAMING` record that the
|
This rule replaces the synthetic SKIPPED `NOVA_TAG_NAMING` record that the
|
||||||
Checkov adapter previously emitted (D-043 closure). The canonical tag set
|
Checkov adapter previously emitted (D-043 closure). Renamed from
|
||||||
is declared in [`schemas/tagging-standard.json`](../../../schemas/tagging-standard.json).
|
`acdl_tagging.py` / `ACDL_TAG_NAMING` in P2 (REQ-158). The canonical tag
|
||||||
|
set is declared in [`schemas/tagging-standard.json`](../../../schemas/tagging-standard.json).
|
||||||
|
|
||||||
|
**P2 warn mode (D-109):** existing resources still carry `acdl:*` tag-key
|
||||||
|
values (left for P3). When a resource has only `acdl:*`-style tags and no
|
||||||
|
`nova:*` tags, the rule logs a WARNING instead of failing, so the
|
||||||
|
regression gate stays green during the parallel-tag transition window.
|
||||||
|
P3 flips to hard-fail once `nova:*` tags are emitted in parallel and the
|
||||||
|
ABAC policy is swapped.
|
||||||
|
|
||||||
## How Checkov loads them
|
## How Checkov loads them
|
||||||
|
|
||||||
@@ -23,12 +31,12 @@ checkov -f terraform/spike/main.tf --framework terraform -o json --soft-fail \
|
|||||||
```
|
```
|
||||||
|
|
||||||
Checkov imports each `*.py` file in the directory and instantiates the
|
Checkov imports each `*.py` file in the directory and instantiates the
|
||||||
module-level `check` object (see the `check = AcdlTaggingStandard()` line at
|
module-level `check` object (see the `check = NovaTaggingStandard()` line at
|
||||||
the bottom of `acdl_tagging.py`).
|
the bottom of `nova_tagging.py`).
|
||||||
|
|
||||||
## Severity / result mapping
|
## Severity / result mapping
|
||||||
|
|
||||||
The Checkov adapter (`adapters/terraform/policy/checkov_adapter.py`)
|
The Checkov adapter (`adapters/terraform/policy/checkov_adapter.py`)
|
||||||
maps `ACDL_TAG_NAMING` to `(tagging-standard, medium)` in `RULE_MAP`. The
|
maps `NOVA_TAG_NAMING` to `(tagging-standard, medium)` in `RULE_MAP`. The
|
||||||
custom rule therefore produces real `PASS`/`FAIL` PolicyCheckResult records,
|
custom rule therefore produces real `PASS`/`FAIL` PolicyCheckResult records,
|
||||||
feeding the confidence signal instead of the old SKIPPED placeholder.
|
feeding the confidence signal instead of the old SKIPPED placeholder.
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
"""ACDL tagging standard custom Checkov rule (D-054).
|
|
||||||
|
|
||||||
Checks that all taggable AWS resources have the required ACDL tags:
|
|
||||||
acdl:owner, acdl:contract, acdl:environment, acdl:cost-center
|
|
||||||
|
|
||||||
Fails (severity medium) when any required tag is missing.
|
|
||||||
Closes the D-043 deferral (the SKIPPED ACDL_TAG_NAMING placeholder
|
|
||||||
becomes a real check).
|
|
||||||
"""
|
|
||||||
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from checkov.terraform.checks.resource.base_resource_check import BaseResourceCheck
|
|
||||||
from checkov.common.models.enums import CheckResult, CheckCategories
|
|
||||||
|
|
||||||
REQUIRED_TAGS = ("acdl:owner", "acdl:contract", "acdl:environment", "acdl:cost-center")
|
|
||||||
|
|
||||||
# Resources that support tags (exclude resources that have no tags attribute)
|
|
||||||
NON_TAGGABLE_TYPES = (
|
|
||||||
"aws_cloudfront_origin_access_control",
|
|
||||||
"aws_lambda_function_url",
|
|
||||||
"aws_route_table_association",
|
|
||||||
"aws_internet_gateway",
|
|
||||||
)
|
|
||||||
|
|
||||||
class AcdlTaggingStandard(BaseResourceCheck):
|
|
||||||
def __init__(self):
|
|
||||||
name = "Ensure all taggable AWS resources have required ACDL tags"
|
|
||||||
check_id = "ACDL_TAG_NAMING"
|
|
||||||
supported_resources = ["*"] # all resources
|
|
||||||
categories = [CheckCategories.GENERAL_SECURITY]
|
|
||||||
super().__init__(name=name, check_id=check_id, categories=categories, supported_resources=supported_resources)
|
|
||||||
|
|
||||||
def scan_resource_conf(self, conf, entity_type):
|
|
||||||
# Skip non-taggable resources
|
|
||||||
if entity_type in NON_TAGGABLE_TYPES:
|
|
||||||
return CheckResult.PASSED
|
|
||||||
# Check for a tags block
|
|
||||||
tags = conf.get("tags")
|
|
||||||
if not tags:
|
|
||||||
return CheckResult.FAILED
|
|
||||||
tag_keys = set()
|
|
||||||
if isinstance(tags, list) and tags:
|
|
||||||
tag_block = tags[0]
|
|
||||||
if isinstance(tag_block, dict):
|
|
||||||
tag_keys = set(tag_block.keys())
|
|
||||||
elif isinstance(tags, dict):
|
|
||||||
tag_keys = set(tags.keys())
|
|
||||||
missing = [t for t in REQUIRED_TAGS if t not in tag_keys]
|
|
||||||
if missing:
|
|
||||||
return CheckResult.FAILED
|
|
||||||
return CheckResult.PASSED
|
|
||||||
|
|
||||||
check = AcdlTaggingStandard()
|
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
"""Nova tagging standard custom Checkov rule (D-054, D-109 hard mode).
|
||||||
|
|
||||||
|
Checks that all taggable AWS resources have the required Nova tags:
|
||||||
|
nova:owner, nova:contract, nova:environment, nova:cost-center
|
||||||
|
|
||||||
|
In **hard mode** (P3, REQ-162): the rule hard-fails when a taggable resource
|
||||||
|
is missing any required `nova:*` tag, OR when a resource carries only the
|
||||||
|
legacy `acdl:*` tag keys (and no `nova:*` keys). P2 shipped warn mode
|
||||||
|
(`_WARN_MODE = True`) so the regression gate stayed green during the
|
||||||
|
parallel-tag transition window; P3 flips to hard-fail (`_WARN_MODE = False`)
|
||||||
|
once `nova:*` tags are emitted in terraform and the ABAC policy is swapped
|
||||||
|
to match `nova:*`. P5 keeps hard mode and additionally hard-fails on any
|
||||||
|
`acdl:*` tag key present at all (no legacy tolerated post-cutoff).
|
||||||
|
|
||||||
|
Closes the D-043 deferral (the SKIPPED NOVA_TAG_NAMING placeholder
|
||||||
|
becomes a real check). Renamed from acdl_tagging.py in P2 (REQ-158);
|
||||||
|
the Checkov rule ID ACDL_TAG_NAMING → NOVA_TAG_NAMING.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from checkov.terraform.checks.resource.base_resource_check import BaseResourceCheck
|
||||||
|
from checkov.common.models.enums import CheckResult, CheckCategories
|
||||||
|
|
||||||
|
REQUIRED_TAGS = ("nova:owner", "nova:contract", "nova:environment", "nova:cost-center")
|
||||||
|
|
||||||
|
# Legacy acdl:* tag keys — the parallel-tag period (P3) emits both nova:*
|
||||||
|
# and acdl:*; P2 warn mode treats acdl:*-only tags as a warning, not a
|
||||||
|
# failure. The acdl:* VALUES in tagging-standard.json are left for P3.
|
||||||
|
LEGACY_TAGS = ("acdl:owner", "acdl:contract", "acdl:environment", "acdl:cost-center")
|
||||||
|
|
||||||
|
# Resources that support tags (exclude resources that have no tags attribute)
|
||||||
|
NON_TAGGABLE_TYPES = (
|
||||||
|
"aws_cloudfront_origin_access_control",
|
||||||
|
"aws_lambda_function_url",
|
||||||
|
"aws_route_table_association",
|
||||||
|
"aws_internet_gateway",
|
||||||
|
)
|
||||||
|
|
||||||
|
# P5 hard mode (D-109, REQ-164): `_WARN_MODE = False` (set in P3) AND
|
||||||
|
# any `acdl:*` tag key present at all is a hard FAIL (P5 tightens from
|
||||||
|
# P3's "acdl:*-only fails" to "any acdl:* key fails"). The legacy tag
|
||||||
|
# keys are fully removed from terraform (P3); any remaining `acdl:*` key
|
||||||
|
# is a rebrand regression.
|
||||||
|
_WARN_MODE = False
|
||||||
|
|
||||||
|
|
||||||
|
class NovaTaggingStandard(BaseResourceCheck):
|
||||||
|
def __init__(self):
|
||||||
|
name = "Ensure all taggable AWS resources have required Nova tags"
|
||||||
|
check_id = "NOVA_TAG_NAMING"
|
||||||
|
supported_resources = ["*"] # all resources
|
||||||
|
categories = [CheckCategories.GENERAL_SECURITY]
|
||||||
|
super().__init__(name=name, check_id=check_id, categories=categories, supported_resources=supported_resources)
|
||||||
|
|
||||||
|
def scan_resource_conf(self, conf, entity_type):
|
||||||
|
# Skip non-taggable resources
|
||||||
|
if entity_type in NON_TAGGABLE_TYPES:
|
||||||
|
return CheckResult.PASSED
|
||||||
|
# Check for a tags block
|
||||||
|
tags = conf.get("tags")
|
||||||
|
if not tags:
|
||||||
|
return CheckResult.FAILED
|
||||||
|
tag_keys = set()
|
||||||
|
if isinstance(tags, list) and tags:
|
||||||
|
tag_block = tags[0]
|
||||||
|
if isinstance(tag_block, dict):
|
||||||
|
tag_keys = set(tag_block.keys())
|
||||||
|
elif isinstance(tags, dict):
|
||||||
|
tag_keys = set(tags.keys())
|
||||||
|
# P5 (REQ-164): any legacy acdl:* tag key present = hard FAIL.
|
||||||
|
legacy_present = tag_keys & set(LEGACY_TAGS)
|
||||||
|
if legacy_present:
|
||||||
|
return CheckResult.FAILED
|
||||||
|
missing = [t for t in REQUIRED_TAGS if t not in tag_keys]
|
||||||
|
if not missing:
|
||||||
|
return CheckResult.PASSED
|
||||||
|
return CheckResult.FAILED
|
||||||
|
|
||||||
|
check = NovaTaggingStandard()
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
# ACDL sample consumer contract — microservice module (dev)
|
# Nova sample consumer contract — microservice module (dev)
|
||||||
# Per-environment contract (REQ-105). Promotion = running the dev job;
|
# Per-environment contract (REQ-105). Promotion = running the dev job;
|
||||||
# no environment field editing. Interpolation resolves against dev.json.
|
# no environment field editing. Interpolation resolves against dev.json.
|
||||||
id: msvc
|
id: msvc
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# ACDL sample consumer contract — microservice module (dr)
|
# Nova sample consumer contract — microservice module (dr)
|
||||||
# Per-environment contract (REQ-105). Promotion = running the dr job;
|
# Per-environment contract (REQ-105). Promotion = running the dr job;
|
||||||
# no environment field editing. Interpolation resolves against dr.json.
|
# no environment field editing. Interpolation resolves against dr.json.
|
||||||
id: msvc
|
id: msvc
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# ACDL sample consumer contract — microservice module (prod)
|
# Nova sample consumer contract — microservice module (prod)
|
||||||
# Per-environment contract (REQ-105). Promotion = running the prod job;
|
# Per-environment contract (REQ-105). Promotion = running the prod job;
|
||||||
# no environment field editing. Interpolation resolves against prod.json.
|
# no environment field editing. Interpolation resolves against prod.json.
|
||||||
id: msvc
|
id: msvc
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# ACDL sample consumer contract — microservice module (qa)
|
# Nova sample consumer contract — microservice module (qa)
|
||||||
# Per-environment contract (REQ-105). Promotion = running the qa job;
|
# Per-environment contract (REQ-105). Promotion = running the qa job;
|
||||||
# no environment field editing. Interpolation resolves against qa.json.
|
# no environment field editing. Interpolation resolves against qa.json.
|
||||||
id: msvc
|
id: msvc
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# ACDL sample consumer contract — microservice module (dev)
|
# Nova sample consumer contract — microservice module (dev)
|
||||||
#
|
#
|
||||||
# Reference example for an ECS Fargate microservice deployment.
|
# Reference example for an ECS Fargate microservice deployment.
|
||||||
# Interpolation (D-081): bucket_name uses the naming pattern that includes
|
# Interpolation (D-081): bucket_name uses the naming pattern that includes
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# ACDL sample consumer contract — static-assets module (dev)
|
# Nova sample consumer contract — static-assets module (dev)
|
||||||
# Per-environment contract (REQ-105). The dev default
|
# Per-environment contract (REQ-105). The dev default
|
||||||
# (contracts/static-assets.yml) remains for backwards compat; this file
|
# (contracts/static-assets.yml) remains for backwards compat; this file
|
||||||
# is the explicit per-env dev contract. Interpolation resolves against dev.json.
|
# is the explicit per-env dev contract. Interpolation resolves against dev.json.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# ACDL sample consumer contract — static-assets module (dr)
|
# Nova sample consumer contract — static-assets module (dr)
|
||||||
# Per-environment contract (REQ-105). Promotion = running the dr job;
|
# Per-environment contract (REQ-105). Promotion = running the dr job;
|
||||||
# no environment field editing. Interpolation resolves against dr.json.
|
# no environment field editing. Interpolation resolves against dr.json.
|
||||||
id: assets
|
id: assets
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# ACDL sample consumer contract — static-assets module (prod)
|
# Nova sample consumer contract — static-assets module (prod)
|
||||||
# Per-environment contract (REQ-105). Promotion = running the prod job;
|
# Per-environment contract (REQ-105). Promotion = running the prod job;
|
||||||
# no environment field editing. Interpolation resolves against prod.json.
|
# no environment field editing. Interpolation resolves against prod.json.
|
||||||
id: assets
|
id: assets
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# ACDL sample consumer contract — static-assets module (qa)
|
# Nova sample consumer contract — static-assets module (qa)
|
||||||
# Per-environment contract (REQ-105). Promotion = running the qa job;
|
# Per-environment contract (REQ-105). Promotion = running the qa job;
|
||||||
# no environment field editing. Interpolation resolves against qa.json.
|
# no environment field editing. Interpolation resolves against qa.json.
|
||||||
id: assets
|
id: assets
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# ACDL sample consumer contract — static-assets module (dev)
|
# Nova sample consumer contract — static-assets module (dev)
|
||||||
#
|
#
|
||||||
# This is the reference example for a consumer contract. It declares:
|
# This is the reference example for a consumer contract. It declares:
|
||||||
# id: short operational acronym (becomes stack.name for state, tags, evidence)
|
# id: short operational acronym (becomes stack.name for state, tags, evidence)
|
||||||
|
|||||||
@@ -14,7 +14,8 @@ concerns split into two tiers:
|
|||||||
The operator-supplied evidence artifact is a JSON blob with `timestamp`,
|
The operator-supplied evidence artifact is a JSON blob with `timestamp`,
|
||||||
`type`, `payload`, and an optional `signature` (JWS detached). Freshness
|
`type`, `payload`, and an optional `signature` (JWS detached). Freshness
|
||||||
is validated against the window from §10.4. Signature verification runs
|
is validated against the window from §10.4. Signature verification runs
|
||||||
when `ACDL_ATTESTATION_SIGNING_KEY_ID` is set; it is skipped + logged
|
when `NOVA_ATTESTATION_SIGNING_KEY_ID` is set (dual-read via core/env.py:
|
||||||
|
NOVA_* preferred, ACDL_* fallback until P5); it is skipped + logged
|
||||||
when unset (dev/CI — D-089). The matrix fails loud if an operator-supplied
|
when unset (dev/CI — D-089). The matrix fails loud if an operator-supplied
|
||||||
concern is missing or expired for prod/dr.
|
concern is missing or expired for prod/dr.
|
||||||
"""
|
"""
|
||||||
@@ -24,6 +25,14 @@ import os
|
|||||||
import sys
|
import sys
|
||||||
from typing import Optional, Tuple
|
from typing import Optional, Tuple
|
||||||
|
|
||||||
|
# Repo root on sys.path so `from core import env` resolves to THIS package
|
||||||
|
# when run as a script (avoids editable-installed third-party `core` shadow).
|
||||||
|
_REPO_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||||
|
if _REPO_ROOT not in sys.path:
|
||||||
|
sys.path.insert(0, _REPO_ROOT)
|
||||||
|
|
||||||
|
from core import env
|
||||||
|
|
||||||
|
|
||||||
# Freshness windows (days) from hitl_matrix_design.md §10.4.
|
# Freshness windows (days) from hitl_matrix_design.md §10.4.
|
||||||
FRESHNESS_DAYS = {
|
FRESHNESS_DAYS = {
|
||||||
@@ -81,14 +90,15 @@ def _is_fresh(artifact: dict, concern: str) -> bool:
|
|||||||
|
|
||||||
|
|
||||||
def _verify_signature(artifact: dict) -> bool:
|
def _verify_signature(artifact: dict) -> bool:
|
||||||
"""Verify the JWS detached signature when ACDL_ATTESTATION_SIGNING_KEY_ID is set.
|
"""Verify the JWS detached signature when NOVA_ATTESTATION_SIGNING_KEY_ID is set.
|
||||||
|
|
||||||
When unset (dev/CI — D-089), signature verification is skipped + logged.
|
When unset (dev/CI — D-089), signature verification is skipped + logged.
|
||||||
|
Dual-read via core/env.py: NOVA_* preferred, ACDL_* fallback until P5.
|
||||||
"""
|
"""
|
||||||
key_id = os.environ.get("ACDL_ATTESTATION_SIGNING_KEY_ID", "")
|
key_id = env.get_env("ATTESTATION_SIGNING_KEY_ID", "") or ""
|
||||||
if not key_id:
|
if not key_id:
|
||||||
sys.stderr.write(
|
sys.stderr.write(
|
||||||
"[attestation] ACDL_ATTESTATION_SIGNING_KEY_ID unset — "
|
"[attestation] NOVA_ATTESTATION_SIGNING_KEY_ID unset — "
|
||||||
"signature verification skipped (dev/CI, D-089)\n"
|
"signature verification skipped (dev/CI, D-089)\n"
|
||||||
)
|
)
|
||||||
return True
|
return True
|
||||||
|
|||||||
@@ -36,6 +36,16 @@ import sys
|
|||||||
import yaml
|
import yaml
|
||||||
import jsonschema
|
import jsonschema
|
||||||
|
|
||||||
|
# Ensure the repo root (parent of core/) is on sys.path so `from core
|
||||||
|
# import env` resolves to THIS package when contract_resolver.py is run
|
||||||
|
# as a script (python3 core/contract_resolver.py) — otherwise an
|
||||||
|
# editable-installed third-party `core` package can shadow it.
|
||||||
|
_REPO_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||||
|
if _REPO_ROOT not in sys.path:
|
||||||
|
sys.path.insert(0, _REPO_ROOT)
|
||||||
|
|
||||||
|
from core import env
|
||||||
|
|
||||||
|
|
||||||
def _load_env(env_name, repo_root):
|
def _load_env(env_name, repo_root):
|
||||||
"""Load the environment onboarding JSON for env_name.
|
"""Load the environment onboarding JSON for env_name.
|
||||||
@@ -620,9 +630,10 @@ if __name__ == "__main__":
|
|||||||
idx = sys.argv.index("--environment")
|
idx = sys.argv.index("--environment")
|
||||||
if idx + 1 < len(sys.argv):
|
if idx + 1 < len(sys.argv):
|
||||||
env_override = sys.argv[idx + 1]
|
env_override = sys.argv[idx + 1]
|
||||||
# Also honor the ACDL_ENVIRONMENT_OVERRIDE env var (used by run_platform.sh).
|
# Also honor the NOVA_ENVIRONMENT_OVERRIDE env var (used by run_platform.sh).
|
||||||
if env_override is None and os.environ.get("ACDL_ENVIRONMENT_OVERRIDE"):
|
# Dual-read via core/env.py: NOVA_* preferred, ACDL_* fallback until P5.
|
||||||
env_override = os.environ["ACDL_ENVIRONMENT_OVERRIDE"]
|
if env_override is None and env.get_env("ENVIRONMENT_OVERRIDE"):
|
||||||
|
env_override = env.get_env("ENVIRONMENT_OVERRIDE")
|
||||||
result = resolve(contract_path, environment_override=env_override)
|
result = resolve(contract_path, environment_override=env_override)
|
||||||
with open(out_path, "w") as fh:
|
with open(out_path, "w") as fh:
|
||||||
json.dump(result, fh, indent=2)
|
json.dump(result, fh, indent=2)
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
"""Environment helper (D-108, REQ-159, REQ-164).
|
||||||
|
|
||||||
|
During the Nova rebrand transition window (P2–P4), `get_env` read
|
||||||
|
`NOVA_*` preferred with the legacy `ACDL_*` name as the fallback. **P5
|
||||||
|
(REQ-164) removed the fallback** — `get_env` now reads `NOVA_*` only.
|
||||||
|
|
||||||
|
`get_env(name, default=None)` resolves `NOVA_<name>`, then returns
|
||||||
|
`default` if unset. Direct-read paths that bypass this helper (the
|
||||||
|
`.env.secrets` shell export in `scripts/run_platform.sh` and the Python
|
||||||
|
parser in `core/regression_verify.py`) were updated to NOVA-only in P5
|
||||||
|
(the G-106 dual-read contract was retired with the fallback).
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import os
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
__all__ = ["get_env"]
|
||||||
|
|
||||||
|
|
||||||
|
def get_env(name: str, default: Optional[str] = None) -> Optional[str]:
|
||||||
|
"""Resolve a config value from the `NOVA_*` environment.
|
||||||
|
|
||||||
|
`name` is the bare key WITHOUT the prefix (e.g. ``"AWS_ACCOUNT_ID"``).
|
||||||
|
Returns ``NOVA_<name>`` if set and non-empty, else ``default``.
|
||||||
|
"""
|
||||||
|
val = os.environ.get(f"NOVA_{name}")
|
||||||
|
if val:
|
||||||
|
return val
|
||||||
|
return default
|
||||||
@@ -93,7 +93,7 @@ The full table (lifted verbatim from §10.4):
|
|||||||
The operator-supplied evidence artifact is a JSON blob with `timestamp`,
|
The operator-supplied evidence artifact is a JSON blob with `timestamp`,
|
||||||
`type`, `payload`, and an optional `signature` (JWS detached). Freshness
|
`type`, `payload`, and an optional `signature` (JWS detached). Freshness
|
||||||
is validated against the window above. Signature verification runs when
|
is validated against the window above. Signature verification runs when
|
||||||
`ACDL_ATTESTATION_SIGNING_KEY_ID` is set; it is skipped + logged when
|
`NOVA_ATTESTATION_SIGNING_KEY_ID` is set; it is skipped + logged when
|
||||||
unset (dev/CI — D-089). The matrix fails loud if an operator-supplied
|
unset (dev/CI — D-089). The matrix fails loud if an operator-supplied
|
||||||
concern is missing or expired for prod/dr.
|
concern is missing or expired for prod/dr.
|
||||||
|
|
||||||
@@ -140,7 +140,7 @@ not Kyverno (in v1). Sequence:
|
|||||||
in the same process that has authority to block the promotion.
|
in the same process that has authority to block the promotion.
|
||||||
|
|
||||||
v1.9 implements `route_halt_artifact` as a real SNS publish (topic
|
v1.9 implements `route_halt_artifact` as a real SNS publish (topic
|
||||||
`acdl-sod-halt`, ARN from `ACDL_SOD_HALT_TOPIC_ARN`) with an outbox-event
|
`acdl-sod-halt`, ARN from `NOVA_SOD_HALT_TOPIC_ARN`) with an outbox-event
|
||||||
fallback when the topic ARN is unset (REQ-107). The attestation gate
|
fallback when the topic ARN is unset (REQ-107). The attestation gate
|
||||||
itself is `core/hitl_gates.py` (`attest(contract_id, env, approver,
|
itself is `core/hitl_gates.py` (`attest(contract_id, env, approver,
|
||||||
evidence)`), which records the approver to the outbox, runs the SoD
|
evidence)`), which records the approver to the outbox, runs the SoD
|
||||||
@@ -171,5 +171,5 @@ v1.9 (Phase 41 + Phase 42) wires the gates end-to-end:
|
|||||||
concerns run for real; operator-supplied concerns accept signed
|
concerns run for real; operator-supplied concerns accept signed
|
||||||
evidence artifacts validated for freshness + schema.
|
evidence artifacts validated for freshness + schema.
|
||||||
- **D-089** (v1.9) — attestation artifact signature verification is
|
- **D-089** (v1.9) — attestation artifact signature verification is
|
||||||
skipped when `ACDL_ATTESTATION_SIGNING_KEY_ID` is unset (dev/CI);
|
skipped when `NOVA_ATTESTATION_SIGNING_KEY_ID` is unset (dev/CI);
|
||||||
required for prod/dr.
|
required for prod/dr.
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
Invoked via a Function URL (IAM auth) by consumer pipelines (one-way
|
Invoked via a Function URL (IAM auth) by consumer pipelines (one-way
|
||||||
communication, D-051). Accepts { consumerRepo, contractId, contract,
|
communication, D-051). Accepts { consumerRepo, contractId, contract,
|
||||||
environment, action } and writes contracts to DynamoDB table acdl-contracts
|
environment, action } and writes contracts to DynamoDB table nova-contracts
|
||||||
(PK consumerRepo, SK contractId#submittedAt).
|
(PK consumerRepo, SK contractId#submittedAt).
|
||||||
|
|
||||||
The report_error action (D-055) creates a GitHub issue on the platform repo
|
The report_error action (D-055) creates a GitHub issue on the platform repo
|
||||||
@@ -17,14 +17,15 @@ requests. The invoke policy is scoped via ABAC (consumer repo identity).
|
|||||||
import datetime
|
import datetime
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
|
import urllib.error
|
||||||
import urllib.parse
|
import urllib.parse
|
||||||
|
|
||||||
import boto3
|
import boto3
|
||||||
|
|
||||||
TABLE_NAME = os.environ.get("CONTRACTS_TABLE", "acdl-contracts")
|
TABLE_NAME = os.environ.get("CONTRACTS_TABLE", "nova-contracts")
|
||||||
CHANGE_REQUESTS_TABLE = os.environ.get("CHANGE_REQUESTS_TABLE", "acdl-change-requests")
|
CHANGE_REQUESTS_TABLE = os.environ.get("CHANGE_REQUESTS_TABLE", "nova-change-requests")
|
||||||
GITHUB_TOKEN_SECRET_ID = os.environ.get("GITHUB_TOKEN_SECRET_ID", "acdl/github-token")
|
GITHUB_TOKEN_SECRET_ID = os.environ.get("GITHUB_TOKEN_SECRET_ID", "nova/github-token")
|
||||||
PLATFORM_REPO = os.environ.get("PLATFORM_REPO", "acdl/acdl")
|
PLATFORM_REPO = os.environ.get("PLATFORM_REPO", "nova/acdl")
|
||||||
# P1-9: Forge-agnostic API base URL. Defaults to GitHub; set GITHUB_API_BASE
|
# P1-9: Forge-agnostic API base URL. Defaults to GitHub; set GITHUB_API_BASE
|
||||||
# to a Gitea API root (e.g. https://git.cloudinit.dev/api/v1) for Gitea.
|
# to a Gitea API root (e.g. https://git.cloudinit.dev/api/v1) for Gitea.
|
||||||
GITHUB_API_BASE = os.environ.get("GITHUB_API_BASE", "https://api.github.com")
|
GITHUB_API_BASE = os.environ.get("GITHUB_API_BASE", "https://api.github.com")
|
||||||
@@ -154,7 +155,16 @@ def _report_error(payload):
|
|||||||
with urllib.request.urlopen(req, timeout=10) as resp:
|
with urllib.request.urlopen(req, timeout=10) as resp:
|
||||||
search_result = json.loads(resp.read())
|
search_result = json.loads(resp.read())
|
||||||
existing = search_result.get("items", [])
|
existing = search_result.get("items", [])
|
||||||
except Exception:
|
except urllib.error.HTTPError as e:
|
||||||
|
if e.code == 404:
|
||||||
|
existing = []
|
||||||
|
else:
|
||||||
|
import sys
|
||||||
|
print(f"WARNING: GitHub issue search failed (HTTP {e.code}): {e}", file=sys.stderr)
|
||||||
|
existing = []
|
||||||
|
except urllib.error.URLError as e:
|
||||||
|
import sys
|
||||||
|
print(f"WARNING: GitHub issue search network error: {e}", file=sys.stderr)
|
||||||
existing = []
|
existing = []
|
||||||
|
|
||||||
body = f"""## Deploy Failure Report
|
body = f"""## Deploy Failure Report
|
||||||
@@ -228,27 +238,48 @@ def _validate_caller_identity(event, payload):
|
|||||||
|
|
||||||
If the identity is not available (e.g. local testing or non-IAM auth), the
|
If the identity is not available (e.g. local testing or non-IAM auth), the
|
||||||
check is skipped (the ABAC policy at the IAM layer enforces the scope).
|
check is skipped (the ABAC policy at the IAM layer enforces the scope).
|
||||||
|
|
||||||
|
v1.14 (REQ-144): also validates contractId format, environment enum, and
|
||||||
|
error length. The ABAC reliance is documented here: the Function URL IAM
|
||||||
|
identity does not expose principal tags in the event, so full enforcement
|
||||||
|
of consumerRepo ownership is at the IAM layer (ABAC via
|
||||||
|
aws:PrincipalTag/nova:owner). This function validates format only, not
|
||||||
|
ownership.
|
||||||
"""
|
"""
|
||||||
identity = event.get("requestContext", {}).get("identity", {})
|
identity = event.get("requestContext", {}).get("identity", {})
|
||||||
caller_arn = identity.get("userArn", "")
|
caller_arn = identity.get("userArn", "")
|
||||||
if not caller_arn:
|
if not caller_arn:
|
||||||
return # no identity available — rely on IAM ABAC enforcement
|
pass # no identity available — rely on IAM ABAC enforcement
|
||||||
payload_repo = payload.get("consumerRepo", "")
|
payload_repo = payload.get("consumerRepo", "")
|
||||||
if not payload_repo:
|
if payload_repo:
|
||||||
return
|
# consumerRepo must be org/repo format, <=128 chars
|
||||||
# Extract the session name or principal tag from the ARN. The ABAC policy
|
if "/" not in payload_repo or len(payload_repo) > 128:
|
||||||
# scopes via aws:PrincipalTag/acdl:owner = <consumerRepo>. The Function URL
|
raise ValueError(f"invalid consumerRepo format: {payload_repo!r}")
|
||||||
# IAM identity does not expose principal tags in the event, so we do a
|
|
||||||
# best-effort check: the consumerRepo must not be empty and must be a valid
|
# v1.14 (REQ-144): contractId format validation
|
||||||
# repo identifier (org/repo format). Full enforcement is at the IAM layer.
|
contract_id = payload.get("contractId", "")
|
||||||
if "/" not in payload_repo or len(payload_repo) > 128:
|
if contract_id:
|
||||||
raise ValueError(f"invalid consumerRepo format: {payload_repo!r}")
|
import re
|
||||||
|
if not re.match(r'^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$', contract_id):
|
||||||
|
raise ValueError(f"invalid contractId format: {contract_id!r} (alphanumeric, hyphen, underscore; max 64 chars)")
|
||||||
|
|
||||||
|
# v1.14 (REQ-144): environment enum validation
|
||||||
|
environment = payload.get("environment", "")
|
||||||
|
if environment:
|
||||||
|
valid_envs = {"dev", "qa", "prod", "dr"}
|
||||||
|
if environment not in valid_envs:
|
||||||
|
raise ValueError(f"invalid environment: {environment!r} (must be one of {valid_envs})")
|
||||||
|
|
||||||
|
# v1.14 (REQ-144): error length cap (for report_error action)
|
||||||
|
error_msg = payload.get("error", "")
|
||||||
|
if error_msg and len(str(error_msg)) > 10000:
|
||||||
|
payload["error"] = str(error_msg)[:10000]
|
||||||
|
|
||||||
|
|
||||||
def _validate_change_request(payload):
|
def _validate_change_request(payload):
|
||||||
"""REQ-93: Validate a change request ID against the CMDB (DynamoDB).
|
"""REQ-93: Validate a change request ID against the CMDB (DynamoDB).
|
||||||
|
|
||||||
Queries the acdl-change-requests table for the given changeRequestId.
|
Queries the nova-change-requests table for the given changeRequestId.
|
||||||
Returns the CR details if status is 'approved' and the consumerRepo matches.
|
Returns the CR details if status is 'approved' and the consumerRepo matches.
|
||||||
Raises ValueError if the CR is not found, not approved, or the repo doesn't match.
|
Raises ValueError if the CR is not found, not approved, or the repo doesn't match.
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -12,7 +12,8 @@ evidence event) runs end-to-end against the local tier with no AWS:
|
|||||||
|
|
||||||
Each adapter exposes the same interface as the live counterpart so the
|
Each adapter exposes the same interface as the live counterpart so the
|
||||||
caller code path is unchanged; only the I/O target swaps. Selection is
|
caller code path is unchanged; only the I/O target swaps. Selection is
|
||||||
gated on the ACDL_LOCAL_TIER env var (set by run_platform.sh --local).
|
gated on the NOVA_LOCAL_TIER env var (set by run_platform.sh --local).
|
||||||
|
Dual-read via core/env.py: NOVA_* preferred, ACDL_* fallback until P5.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
@@ -32,12 +33,20 @@ from dataclasses import dataclass, field
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, Dict, List, Optional, Tuple
|
from typing import Any, Dict, List, Optional, Tuple
|
||||||
|
|
||||||
|
# Repo root on sys.path so `from core import env` resolves to THIS package
|
||||||
|
# when run as a script (avoids editable-installed third-party `core` shadow).
|
||||||
|
_REPO_ROOT = str(Path(__file__).resolve().parent.parent)
|
||||||
|
if _REPO_ROOT not in sys.path:
|
||||||
|
sys.path.insert(0, _REPO_ROOT)
|
||||||
|
|
||||||
|
from core import env
|
||||||
|
|
||||||
ROOT = Path(__file__).resolve().parent.parent
|
ROOT = Path(__file__).resolve().parent.parent
|
||||||
|
|
||||||
|
|
||||||
def is_local_tier() -> bool:
|
def is_local_tier() -> bool:
|
||||||
"""True when the local emulating tier is active."""
|
"""True when the local emulating tier is active."""
|
||||||
return os.environ.get("ACDL_LOCAL_TIER", "") == "1"
|
return env.get_env("LOCAL_TIER", "") == "1"
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
@@ -78,7 +87,7 @@ class FlatFileOutbox:
|
|||||||
return hashlib.sha256(canonical.encode("utf-8")).hexdigest()
|
return hashlib.sha256(canonical.encode("utf-8")).hexdigest()
|
||||||
|
|
||||||
def write_event(self, event: Dict[str, Any],
|
def write_event(self, event: Dict[str, Any],
|
||||||
outbox_table: str = "acdl-outbox-local",
|
outbox_table: str = "nova-outbox-local",
|
||||||
region: str = "local") -> Dict[str, Any]:
|
region: str = "local") -> Dict[str, Any]:
|
||||||
"""Write an evidence event to the flat-file outbox.
|
"""Write an evidence event to the flat-file outbox.
|
||||||
|
|
||||||
@@ -286,7 +295,7 @@ class LocalLambdaStub:
|
|||||||
|
|
||||||
Returns the handler's response dict
|
Returns the handler's response dict
|
||||||
({statusCode, body}). The handler's DynamoDB calls are
|
({statusCode, body}). The handler's DynamoDB calls are
|
||||||
intercepted via the ACDL_LOCAL_TIER env var (the handler checks
|
intercepted via the NOVA_LOCAL_TIER env var (the handler checks
|
||||||
_get_dynamodb(); under local tier it would need patching - we
|
_get_dynamodb(); under local tier it would need patching - we
|
||||||
patch the module's _get_dynamodb to return a local stub)."""
|
patch the module's _get_dynamodb to return a local stub)."""
|
||||||
# Import the handler module (the dir is named `lambda`, a Python
|
# Import the handler module (the dir is named `lambda`, a Python
|
||||||
@@ -371,8 +380,9 @@ class LocalLambdaStub:
|
|||||||
return _FakeResponse(
|
return _FakeResponse(
|
||||||
json.dumps([{"number": 1, "title": "stub"}]).encode())
|
json.dumps([{"number": 1, "title": "stub"}]).encode())
|
||||||
urllib.request.urlopen = _fake_urlopen
|
urllib.request.urlopen = _fake_urlopen
|
||||||
except Exception:
|
except (AttributeError, TypeError) as e:
|
||||||
pass
|
import sys
|
||||||
|
print(f"WARNING: could not patch urlopen for local Lambda stub: {e}", file=sys.stderr)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
event = {
|
event = {
|
||||||
@@ -418,7 +428,7 @@ def run_local_e2e(contract_path: str, repo_root: Optional[Path] = None) -> Dict[
|
|||||||
|
|
||||||
stack = resolve(contract_path, str(root))
|
stack = resolve(contract_path, str(root))
|
||||||
stack_name = stack["stack"]["name"]
|
stack_name = stack["stack"]["name"]
|
||||||
work = Path(tempfile.mkdtemp(prefix="acdl_local_e2e_"))
|
work = Path(tempfile.mkdtemp(prefix="nova_local_e2e_"))
|
||||||
tf_dir = work / "tf"
|
tf_dir = work / "tf"
|
||||||
tf_dir.mkdir(exist_ok=True)
|
tf_dir.mkdir(exist_ok=True)
|
||||||
adapter.adapt(stack, str(tf_dir))
|
adapter.adapt(stack, str(tf_dir))
|
||||||
@@ -489,6 +499,9 @@ def run_local_e2e(contract_path: str, repo_root: Optional[Path] = None) -> Dict[
|
|||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
contract = sys.argv[1] if len(sys.argv) > 1 else "contracts/microservice.yml"
|
contract = sys.argv[1] if len(sys.argv) > 1 else "contracts/microservice.yml"
|
||||||
os.environ["ACDL_LOCAL_TIER"] = "1"
|
# Set both so the dual-read in is_local_tier() finds NOVA_* (preferred);
|
||||||
|
# the ACDL_* alias stays for any unmigrated reader until P5.
|
||||||
|
os.environ["NOVA_LOCAL_TIER"] = "1"
|
||||||
|
# P5 (REQ-164): ACDL_LOCAL_TIER legacy alias removed (NOVA_* only)
|
||||||
result = run_local_e2e(contract)
|
result = run_local_e2e(contract)
|
||||||
print(json.dumps(result, indent=2))
|
print(json.dumps(result, indent=2))
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
"""ACDL Outbox Writer — write an evidence event to the DynamoDB outbox.
|
"""Nova Outbox Writer — write an evidence event to the DynamoDB outbox.
|
||||||
|
|
||||||
ARCHITECTURE.md §9: DynamoDB outbox, RPO=0 (synchronous write before
|
ARCHITECTURE.md §9: DynamoDB outbox, RPO=0 (synchronous write before
|
||||||
ack). The event is hash-chained (SHA-256 over canonical JSON); the first
|
ack). The event is hash-chained (SHA-256 over canonical JSON); the first
|
||||||
event has prev_event_hash="GENESIS". D-P10-3: the spike writes ONE
|
event has prev_event_hash="GENESIS". D-P10-3: the spike writes ONE
|
||||||
CONFIDENCE_COMPUTED event.
|
CONFIDENCE_COMPUTED event.
|
||||||
|
|
||||||
The outbox table (Phase 08): acdl-outbox, PAY_PER_REQUEST, PK contractId,
|
The outbox table (Phase 08): nova-outbox, PAY_PER_REQUEST, PK contractId,
|
||||||
SK eventType#eventTs, TTL expire_at = now + 365d (D-044).
|
SK eventType#eventTs, TTL expire_at = now + 365d (D-044).
|
||||||
|
|
||||||
CLI: outbox_writer.py <event.json> (uses AWS creds from env)
|
CLI: outbox_writer.py <event.json> (uses AWS creds from env)
|
||||||
@@ -20,7 +20,7 @@ import sys
|
|||||||
import boto3
|
import boto3
|
||||||
|
|
||||||
|
|
||||||
OUTBOX_TABLE = "acdl-outbox"
|
OUTBOX_TABLE = "nova-outbox"
|
||||||
REGION = os.environ.get("AWS_DEFAULT_REGION", "us-east-1")
|
REGION = os.environ.get("AWS_DEFAULT_REGION", "us-east-1")
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -8,8 +8,10 @@ Two canonical mechanisms:
|
|||||||
strings, ALB DNS, S3 bucket URL, CloudFront domain). No raw secrets in
|
strings, ALB DNS, S3 bucket URL, CloudFront domain). No raw secrets in
|
||||||
the comment — only non-sensitive outputs (DNS names, ARNs, bucket names).
|
the comment — only non-sensitive outputs (DNS names, ARNs, bucket names).
|
||||||
|
|
||||||
The namespace is /acdl/{environment}/{contractId}/{output_name} so consumers
|
The namespace is /nova/{environment}/{contractId}/{output_name} so consumers
|
||||||
can query their own outputs via aws ssm get-parameter --name /acdl/dev/<id>/...
|
can query their own outputs via aws ssm get-parameter --name /nova/dev/<id>/...
|
||||||
|
(REQ-161, P3: migrated from /acdl/... ; scripts/migrate_ssm_paths.py copies
|
||||||
|
existing /acdl/... parameters to /nova/... and deletes the old ones.)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import json
|
import json
|
||||||
@@ -21,8 +23,16 @@ try:
|
|||||||
except ImportError:
|
except ImportError:
|
||||||
boto3 = None
|
boto3 = None
|
||||||
|
|
||||||
SSM_PREFIX = "/acdl"
|
# Repo root on sys.path so `from core import env` resolves to THIS package
|
||||||
KMS_KEY_ID_ENV = "ACDL_KMS_KEY_ID"
|
# when run as a script (avoids editable-installed third-party `core` shadow).
|
||||||
|
_REPO_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||||
|
if _REPO_ROOT not in sys.path:
|
||||||
|
sys.path.insert(0, _REPO_ROOT)
|
||||||
|
|
||||||
|
from core import env as _envhelper
|
||||||
|
|
||||||
|
SSM_PREFIX = "/nova"
|
||||||
|
KMS_KEY_ID_ENV = "NOVA_KMS_KEY_ID"
|
||||||
|
|
||||||
# Outputs that are safe to display in a PR comment (no secrets).
|
# Outputs that are safe to display in a PR comment (no secrets).
|
||||||
SAFE_OUTPUT_NAMES = {
|
SAFE_OUTPUT_NAMES = {
|
||||||
@@ -54,20 +64,22 @@ def _ssm_client():
|
|||||||
def _kms_key_id():
|
def _kms_key_id():
|
||||||
"""Return the KMS key ID for SSM SecureString encryption.
|
"""Return the KMS key ID for SSM SecureString encryption.
|
||||||
|
|
||||||
P1-3: Fail loud when ACDL_KMS_KEY_ID is not set — silently falling back
|
P1-3: Fail loud when NOVA_KMS_KEY_ID is not set — silently falling back
|
||||||
to the AWS-managed key (`alias/aws/ssm`) was a security gap. The platform
|
to the AWS-managed key (`alias/aws/ssm`) was a security gap. The platform
|
||||||
CMK must be explicitly configured. Set ACDL_ALLOW_DEFAULT_KMS=1 to use
|
CMK must be explicitly configured. Set NOVA_ALLOW_DEFAULT_KMS=1 to use
|
||||||
the AWS-managed key as an escape hatch for local testing.
|
the AWS-managed key as an escape hatch for local testing. (Dual-read
|
||||||
|
via core/env.py: NOVA_* preferred, ACDL_* fallback until P5.)
|
||||||
"""
|
"""
|
||||||
key_id = os.environ.get(KMS_KEY_ID_ENV)
|
key_id = _envhelper.get_env("KMS_KEY_ID")
|
||||||
if key_id:
|
if key_id:
|
||||||
return key_id
|
return key_id
|
||||||
if os.environ.get("ACDL_ALLOW_DEFAULT_KMS") == "1":
|
if _envhelper.get_env("ALLOW_DEFAULT_KMS") == "1":
|
||||||
return "alias/aws/ssm"
|
return "alias/aws/ssm"
|
||||||
raise RuntimeError(
|
raise RuntimeError(
|
||||||
f"{KMS_KEY_ID_ENV} is not set — refusing to use the AWS-managed SSM key "
|
f"{KMS_KEY_ID_ENV} is not set — refusing to use the AWS-managed SSM key "
|
||||||
f"silently. Set {KMS_KEY_ID_ENV} to your platform CMK ARN, or set "
|
f"silently. Set {KMS_KEY_ID_ENV} to your platform CMK ARN, or set "
|
||||||
f"ACDL_ALLOW_DEFAULT_KMS=1 to use alias/aws/ssm (escape hatch for local testing)."
|
f"NOVA_ALLOW_DEFAULT_KMS=1 (ACDL_ALLOW_DEFAULT_KMS=1 fallback) to use "
|
||||||
|
f"alias/aws/ssm (escape hatch for local testing)."
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -97,8 +109,10 @@ def publish_to_ssm(outputs, environment, contract_id):
|
|||||||
Overwrite=True,
|
Overwrite=True,
|
||||||
)
|
)
|
||||||
results[name] = param_name
|
results[name] = param_name
|
||||||
except Exception:
|
except Exception as e:
|
||||||
# Don't fail the pipeline if one output fails to publish
|
# Don't fail the pipeline if one output fails to publish, but log it
|
||||||
|
import sys
|
||||||
|
print(f"WARNING: SSM put_parameter failed for {name}: {e}", file=sys.stderr)
|
||||||
results[name] = None
|
results[name] = None
|
||||||
return results
|
return results
|
||||||
|
|
||||||
@@ -110,7 +124,7 @@ def format_comment(outputs, environment, contract_id, ssm_results=None):
|
|||||||
outputs are noted as 'published to SSM' without their values.
|
outputs are noted as 'published to SSM' without their values.
|
||||||
"""
|
"""
|
||||||
lines = [
|
lines = [
|
||||||
f"### ACDL Deploy Outputs ({environment})",
|
f"### Nova Deploy Outputs ({environment})",
|
||||||
"",
|
"",
|
||||||
f"**Contract:** `{contract_id}`",
|
f"**Contract:** `{contract_id}`",
|
||||||
f"**Environment:** `{environment}`",
|
f"**Environment:** `{environment}`",
|
||||||
@@ -132,7 +146,7 @@ def format_comment(outputs, environment, contract_id, ssm_results=None):
|
|||||||
ssm_path = "—"
|
ssm_path = "—"
|
||||||
lines.append(f"| `{name}` | {display} | {ssm_path} |")
|
lines.append(f"| `{name}` | {display} | {ssm_path} |")
|
||||||
lines.append("")
|
lines.append("")
|
||||||
lines.append("> Sensitive outputs are available via `aws ssm get-parameter --name /acdl/" + environment + "/" + contract_id + "/<output_name>` (KMS-encrypted SecureString).")
|
lines.append("> Sensitive outputs are available via `aws ssm get-parameter --name /nova/" + environment + "/" + contract_id + "/<output_name>` (KMS-encrypted SecureString).")
|
||||||
return "\n".join(lines)
|
return "\n".join(lines)
|
||||||
|
|
||||||
|
|
||||||
@@ -165,7 +179,9 @@ def post_github_comment(comment_text, token=None, repo=None, pr_number=None):
|
|||||||
req.add_header("Accept", "application/vnd.github+json")
|
req.add_header("Accept", "application/vnd.github+json")
|
||||||
urllib.request.urlopen(req, timeout=10)
|
urllib.request.urlopen(req, timeout=10)
|
||||||
return True
|
return True
|
||||||
except Exception:
|
except Exception as e:
|
||||||
|
import sys
|
||||||
|
print(f"WARNING: GitHub PR comment failed: {e}", file=sys.stderr)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,15 @@ from dataclasses import dataclass, field, asdict
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Callable, Dict, List, Optional, Tuple
|
from typing import Callable, Dict, List, Optional, Tuple
|
||||||
|
|
||||||
|
# Repo root on sys.path so `from core import env` resolves to THIS package
|
||||||
|
# when regression_verify.py is run as a script (avoids editable-installed
|
||||||
|
# third-party `core` shadow).
|
||||||
|
_REPO_ROOT = str(Path(__file__).resolve().parent.parent)
|
||||||
|
if _REPO_ROOT not in sys.path:
|
||||||
|
sys.path.insert(0, _REPO_ROOT)
|
||||||
|
|
||||||
|
from core import env as _envhelper
|
||||||
|
|
||||||
ROOT = Path(__file__).resolve().parent.parent
|
ROOT = Path(__file__).resolve().parent.parent
|
||||||
CIAgent = ROOT / ".ciagent"
|
CIAgent = ROOT / ".ciagent"
|
||||||
|
|
||||||
@@ -306,9 +315,10 @@ def _load_aws_env() -> Dict[str, str]:
|
|||||||
continue
|
continue
|
||||||
if "=" in line:
|
if "=" in line:
|
||||||
k, v = line.split("=", 1)
|
k, v = line.split("=", 1)
|
||||||
if k == "ACDL_AWS_ACCESS_KEY_ID":
|
# P5 (REQ-164): dual-read fallback removed — NOVA_* only.
|
||||||
|
if k == "NOVA_AWS_ACCESS_KEY_ID":
|
||||||
env["AWS_ACCESS_KEY_ID"] = v
|
env["AWS_ACCESS_KEY_ID"] = v
|
||||||
elif k == "ACDL_AWS_SECRET_ACCESS_KEY":
|
elif k == "NOVA_AWS_SECRET_ACCESS_KEY":
|
||||||
env["AWS_SECRET_ACCESS_KEY"] = v
|
env["AWS_SECRET_ACCESS_KEY"] = v
|
||||||
elif k == "AWS_DEFAULT_REGION":
|
elif k == "AWS_DEFAULT_REGION":
|
||||||
env["AWS_DEFAULT_REGION"] = v
|
env["AWS_DEFAULT_REGION"] = v
|
||||||
@@ -319,10 +329,11 @@ def _check_live_terraform_plan_microservice() -> Tuple[Status, str]:
|
|||||||
"""CAP-013: terraform init+validate+plan against live AWS for the
|
"""CAP-013: terraform init+validate+plan against live AWS for the
|
||||||
microservice stack (D-093 live-AWS tier of the headline E2E).
|
microservice stack (D-093 live-AWS tier of the headline E2E).
|
||||||
|
|
||||||
Requires AWS credentials (ACDL_AWS_ACCESS_KEY_ID etc. in .env.secrets).
|
Requires AWS credentials (NOVA_AWS_ACCESS_KEY_ID etc. in .env.secrets;
|
||||||
|
dual-read NOVA_* first, ACDL_* fallback per G-106).
|
||||||
Runs in a temp dir; does NOT apply (plan only)."""
|
Runs in a temp dir; does NOT apply (plan only)."""
|
||||||
import tempfile, os
|
import tempfile, os
|
||||||
work = tempfile.mkdtemp(prefix="acdl_regr_live_")
|
work = tempfile.mkdtemp(prefix="nova_regr_live_")
|
||||||
stack_path = os.path.join(work, "stack.json")
|
stack_path = os.path.join(work, "stack.json")
|
||||||
tf_dir = os.path.join(work, "tf")
|
tf_dir = os.path.join(work, "tf")
|
||||||
os.makedirs(tf_dir, exist_ok=True)
|
os.makedirs(tf_dir, exist_ok=True)
|
||||||
@@ -362,7 +373,7 @@ def _check_live_terraform_plan_static_assets() -> Tuple[Status, str]:
|
|||||||
"""CAP-014: terraform init+validate+plan against live AWS for the
|
"""CAP-014: terraform init+validate+plan against live AWS for the
|
||||||
static-assets stack (CloudFront + WAF + S3)."""
|
static-assets stack (CloudFront + WAF + S3)."""
|
||||||
import tempfile, os
|
import tempfile, os
|
||||||
work = tempfile.mkdtemp(prefix="acdl_regr_live_sa_")
|
work = tempfile.mkdtemp(prefix="nova_regr_live_sa_")
|
||||||
stack_path = os.path.join(work, "stack.json")
|
stack_path = os.path.join(work, "stack.json")
|
||||||
tf_dir = os.path.join(work, "tf")
|
tf_dir = os.path.join(work, "tf")
|
||||||
os.makedirs(tf_dir, exist_ok=True)
|
os.makedirs(tf_dir, exist_ok=True)
|
||||||
@@ -406,9 +417,9 @@ def _check_dynamodb_outbox_table() -> Tuple[Status, str]:
|
|||||||
dyn = boto3.client("dynamodb", region_name=env.get("AWS_DEFAULT_REGION", "us-east-1"),
|
dyn = boto3.client("dynamodb", region_name=env.get("AWS_DEFAULT_REGION", "us-east-1"),
|
||||||
aws_access_key_id=env.get("AWS_ACCESS_KEY_ID"),
|
aws_access_key_id=env.get("AWS_ACCESS_KEY_ID"),
|
||||||
aws_secret_access_key=env.get("AWS_SECRET_ACCESS_KEY"))
|
aws_secret_access_key=env.get("AWS_SECRET_ACCESS_KEY"))
|
||||||
r = dyn.describe_table(TableName="acdl-outbox")
|
r = dyn.describe_table(TableName="nova-outbox")
|
||||||
count = r["Table"].get("ItemCount", "unknown")
|
count = r["Table"].get("ItemCount", "unknown")
|
||||||
return "Verified", f"acdl-outbox exists, item_count={count}"
|
return "Verified", f"nova-outbox exists, item_count={count}"
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return "Decayed", f"describe_table failed: {type(e).__name__}: {str(e)[:150]}"
|
return "Decayed", f"describe_table failed: {type(e).__name__}: {str(e)[:150]}"
|
||||||
|
|
||||||
@@ -421,8 +432,10 @@ def _check_s3_state_bucket() -> Tuple[Status, str]:
|
|||||||
s3 = boto3.client("s3", region_name=env.get("AWS_DEFAULT_REGION", "us-east-1"),
|
s3 = boto3.client("s3", region_name=env.get("AWS_DEFAULT_REGION", "us-east-1"),
|
||||||
aws_access_key_id=env.get("AWS_ACCESS_KEY_ID"),
|
aws_access_key_id=env.get("AWS_ACCESS_KEY_ID"),
|
||||||
aws_secret_access_key=env.get("AWS_SECRET_ACCESS_KEY"))
|
aws_secret_access_key=env.get("AWS_SECRET_ACCESS_KEY"))
|
||||||
s3.head_bucket(Bucket="acdl-tfstate-581513795199-us-east-1")
|
account_id = _envhelper.get_env("AWS_ACCOUNT_ID", "581513795199")
|
||||||
r = s3.list_objects_v2(Bucket="acdl-tfstate-581513795199-us-east-1", MaxKeys=5)
|
state_bucket = f"nova-tfstate-{account_id}-us-east-1"
|
||||||
|
s3.head_bucket(Bucket=state_bucket)
|
||||||
|
r = s3.list_objects_v2(Bucket=state_bucket, MaxKeys=5)
|
||||||
keys = [o["Key"] for o in r.get("Contents", [])]
|
keys = [o["Key"] for o in r.get("Contents", [])]
|
||||||
return "Verified", f"state bucket exists, keys={keys}"
|
return "Verified", f"state bucket exists, keys={keys}"
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -431,13 +444,19 @@ def _check_s3_state_bucket() -> Tuple[Status, str]:
|
|||||||
|
|
||||||
def _check_lifecycle_module_terraform(module: str) -> Tuple[Status, str]:
|
def _check_lifecycle_module_terraform(module: str) -> Tuple[Status, str]:
|
||||||
"""Helper: verify an L1 module's terraform dir exists with the required
|
"""Helper: verify an L1 module's terraform dir exists with the required
|
||||||
files + its example contracts resolve. This is the offline proxy for
|
files + its example contracts resolve + terraform fmt syntax check
|
||||||
'lifecycle pipeline green' — the pipeline cell going green requires
|
passes. This is the offline proxy for 'lifecycle pipeline green' — the
|
||||||
terraform init+validate+apply+modify+destroy to succeed against live
|
pipeline cell going green requires terraform init+validate+apply+modify+
|
||||||
AWS, which requires the terraform files to exist and contracts to
|
destroy to succeed against live AWS, which requires the terraform files
|
||||||
resolve first. We avoid terraform init here (too slow for the
|
to exist, contracts to resolve, and HCL syntax to be valid first.
|
||||||
regression gate); terraform validate is run by the lifecycle pipeline
|
|
||||||
itself."""
|
We run `terraform fmt -check` (fast, no init required) as a syntax probe.
|
||||||
|
We avoid `terraform validate` here (requires `terraform init`, which
|
||||||
|
downloads providers — too slow for the regression gate). Full
|
||||||
|
`terraform validate` is run by the lifecycle pipeline itself. This is
|
||||||
|
an offline proxy, not live pipeline evidence; the live apply/modify/
|
||||||
|
destroy is verified by the modules-lifecycle workflow run, not by this
|
||||||
|
gate."""
|
||||||
tf_dir = ROOT / "modules" / "l1" / module / "terraform"
|
tf_dir = ROOT / "modules" / "l1" / module / "terraform"
|
||||||
if not tf_dir.is_dir():
|
if not tf_dir.is_dir():
|
||||||
return "Broken", f"modules/l1/{module}/terraform/ does not exist"
|
return "Broken", f"modules/l1/{module}/terraform/ does not exist"
|
||||||
@@ -450,6 +469,11 @@ def _check_lifecycle_module_terraform(module: str) -> Tuple[Status, str]:
|
|||||||
tf_text = "".join((tf_dir / f).read_text() for f in ["variables.tf", "main.tf", "outputs.tf"] if (tf_dir / f).is_file())
|
tf_text = "".join((tf_dir / f).read_text() for f in ["variables.tf", "main.tf", "outputs.tf"] if (tf_dir / f).is_file())
|
||||||
if "local." in tf_text and not (tf_dir / "locals.tf").is_file():
|
if "local." in tf_text and not (tf_dir / "locals.tf").is_file():
|
||||||
return "Broken", "missing terraform files: ['locals.tf'] (referenced by module)"
|
return "Broken", "missing terraform files: ['locals.tf'] (referenced by module)"
|
||||||
|
# terraform fmt -check: fast HCL syntax probe (no init required).
|
||||||
|
rc, out, err = _run_subprocess(
|
||||||
|
["terraform", "fmt", "-check", "-diff", str(tf_dir)], timeout=30)
|
||||||
|
if rc != 0:
|
||||||
|
return "Broken", f"terraform fmt -check failed: {err.strip()[-200:]}"
|
||||||
for ex in ["simple", "complex"]:
|
for ex in ["simple", "complex"]:
|
||||||
contract = ROOT / "modules" / "l1" / module / "examples" / f"{ex}.yml"
|
contract = ROOT / "modules" / "l1" / module / "examples" / f"{ex}.yml"
|
||||||
if not contract.is_file():
|
if not contract.is_file():
|
||||||
@@ -459,12 +483,15 @@ def _check_lifecycle_module_terraform(module: str) -> Tuple[Status, str]:
|
|||||||
], timeout=30)
|
], timeout=30)
|
||||||
if rc != 0:
|
if rc != 0:
|
||||||
return "Broken", f"{ex}.yml resolver failed: {err.strip()[-200:]}"
|
return "Broken", f"{ex}.yml resolver failed: {err.strip()[-200:]}"
|
||||||
return "Verified", f"terraform files present + simple/complex contracts resolve"
|
return "Verified", f"terraform files present + fmt -check passes + simple/complex contracts resolve"
|
||||||
|
|
||||||
|
|
||||||
def _check_lifecycle_l2_module(module: str) -> Tuple[Status, str]:
|
def _check_lifecycle_l2_module(module: str) -> Tuple[Status, str]:
|
||||||
"""Helper: verify an L2 module's composition resolves + its example
|
"""Helper: verify an L2 module's composition resolves + its example
|
||||||
contracts resolve. Offline proxy for 'L2 lifecycle pipeline green'."""
|
contracts resolve. Offline proxy for 'L2 lifecycle pipeline green'.
|
||||||
|
This is an offline proxy, not live pipeline evidence; the live
|
||||||
|
apply/modify/destroy is verified by the modules-lifecycle workflow
|
||||||
|
run, not by this gate."""
|
||||||
for ex in ["simple", "complex"]:
|
for ex in ["simple", "complex"]:
|
||||||
contract = ROOT / "modules" / "l2" / module / "examples" / f"{ex}.yml"
|
contract = ROOT / "modules" / "l2" / module / "examples" / f"{ex}.yml"
|
||||||
if not contract.is_file():
|
if not contract.is_file():
|
||||||
@@ -474,11 +501,11 @@ def _check_lifecycle_l2_module(module: str) -> Tuple[Status, str]:
|
|||||||
], timeout=30)
|
], timeout=30)
|
||||||
if rc != 0:
|
if rc != 0:
|
||||||
return "Broken", f"{ex}.yml resolver failed: {err.strip()[-200:]}"
|
return "Broken", f"{ex}.yml resolver failed: {err.strip()[-200:]}"
|
||||||
return "Verified", f"L2 composition resolves (simple + complex contracts)"
|
return "Verified", f"L2 composition resolves (simple + complex contracts; offline proxy)"
|
||||||
|
|
||||||
|
|
||||||
def _check_cap_017_dynamodb() -> Tuple[Status, str]:
|
def _check_cap_017_dynamodb() -> Tuple[Status, str]:
|
||||||
"""CAP-017: DynamoDB acdl-contracts table. Evidence = L1 rds module
|
"""CAP-017: DynamoDB nova-contracts table. Evidence = L1 rds module
|
||||||
lifecycle pipeline green (terraform validate + contracts resolve).
|
lifecycle pipeline green (terraform validate + contracts resolve).
|
||||||
The DynamoDB table is created via the microservice stack (L2 lifecycle).
|
The DynamoDB table is created via the microservice stack (L2 lifecycle).
|
||||||
"""
|
"""
|
||||||
@@ -493,7 +520,7 @@ def _check_cap_018_lambda() -> Tuple[Status, str]:
|
|||||||
"python3", "-c",
|
"python3", "-c",
|
||||||
"from core.local_emulators import LocalLambdaStub, FlatFileOutbox; "
|
"from core.local_emulators import LocalLambdaStub, FlatFileOutbox; "
|
||||||
"import tempfile; "
|
"import tempfile; "
|
||||||
"stub = LocalLambdaStub(outbox=FlatFileOutbox(tempfile.mkdtemp(prefix='acdl_stub_'))); "
|
"stub = LocalLambdaStub(outbox=FlatFileOutbox(tempfile.mkdtemp(prefix='nova_stub_'))); "
|
||||||
"print('LocalLambdaStub instantiates OK')",
|
"print('LocalLambdaStub instantiates OK')",
|
||||||
])
|
])
|
||||||
if rc != 0:
|
if rc != 0:
|
||||||
@@ -562,7 +589,7 @@ CAPABILITY_REGISTRY: List[Tuple[str, str, str, Callable[[], Tuple[Status, str]]]
|
|||||||
_check_dynamodb_outbox_table),
|
_check_dynamodb_outbox_table),
|
||||||
("CAP-016", "S3 state bucket exists + readable (live AWS)", "live-aws",
|
("CAP-016", "S3 state bucket exists + readable (live AWS)", "live-aws",
|
||||||
_check_s3_state_bucket),
|
_check_s3_state_bucket),
|
||||||
("CAP-017", "DynamoDB acdl-contracts table (lifecycle pipeline evidence)", "lifecycle-pipeline",
|
("CAP-017", "DynamoDB nova-contracts table (lifecycle pipeline evidence)", "lifecycle-pipeline",
|
||||||
_check_cap_017_dynamodb),
|
_check_cap_017_dynamodb),
|
||||||
("CAP-018", "Lambda contract-ingestor (local stub + lifecycle evidence)", "lifecycle-pipeline",
|
("CAP-018", "Lambda contract-ingestor (local stub + lifecycle evidence)", "lifecycle-pipeline",
|
||||||
_check_cap_018_lambda),
|
_check_cap_018_lambda),
|
||||||
@@ -627,8 +654,8 @@ def write_report(report: RegressionReport,
|
|||||||
|
|
||||||
|
|
||||||
def main() -> int:
|
def main() -> int:
|
||||||
milestone = os.environ.get("ACDL_REGRESSION_MILESTONE", "v1.10")
|
milestone = _envhelper.get_env("REGRESSION_MILESTONE", "v1.10") or "v1.10"
|
||||||
phase = int(os.environ.get("ACDL_REGRESSION_PHASE", "52"))
|
phase = int(_envhelper.get_env("REGRESSION_PHASE", "52") or "52")
|
||||||
report = run_regression(milestone=milestone, phase=phase)
|
report = run_regression(milestone=milestone, phase=phase)
|
||||||
md, js = write_report(report)
|
md, js = write_report(report)
|
||||||
print(f"regression: {report.summary} -> {md}")
|
print(f"regression: {report.summary} -> {md}")
|
||||||
|
|||||||
@@ -5,16 +5,27 @@ Blocks on equality, emits `SEPARATION_OF_DUTIES_VIOLATION`, routes a halt
|
|||||||
artifact to SRE on-call.
|
artifact to SRE on-call.
|
||||||
|
|
||||||
v1.9 (REQ-107, D-085): route_halt_artifact is a real implementation —
|
v1.9 (REQ-107, D-085): route_halt_artifact is a real implementation —
|
||||||
publishes to SNS topic `acdl-sod-halt` (ARN from ACDL_SOD_HALT_TOPIC_ARN)
|
publishes to SNS topic `acdl-sod-halt` (ARN from NOVA_SOD_HALT_TOPIC_ARN)
|
||||||
when set; falls back to a structured stderr emission + a
|
when set; falls back to a structured stderr emission + a
|
||||||
SEPARATION_OF_DUTIES_VIOLATION event write to the DynamoDB outbox when
|
SEPARATION_OF_DUTIES_VIOLATION event write to the DynamoDB outbox when
|
||||||
unset. No silent print-only stub.
|
unset. No silent print-only stub. (Dual-read via core/env.py: NOVA_*
|
||||||
|
preferred, ACDL_* fallback until P5; the SNS topic ARN is the AWS
|
||||||
|
resource `acdl-sod-halt` → renamed `nova-sod-halt` in P4.)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
from typing import Optional, Tuple
|
from typing import Optional, Tuple
|
||||||
|
|
||||||
|
# Repo root on sys.path so `from core import env` resolves to THIS package
|
||||||
|
# when imported/run in a context where an editable-installed third-party
|
||||||
|
# `core` package would otherwise shadow it.
|
||||||
|
_REPO_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||||
|
if _REPO_ROOT not in sys.path:
|
||||||
|
sys.path.insert(0, _REPO_ROOT)
|
||||||
|
|
||||||
|
from core import env
|
||||||
|
|
||||||
|
|
||||||
def check(outbox_client, contract_id: str,
|
def check(outbox_client, contract_id: str,
|
||||||
current_prod_approver: Optional[str]) -> Tuple[bool, str]:
|
current_prod_approver: Optional[str]) -> Tuple[bool, str]:
|
||||||
@@ -40,13 +51,13 @@ def route_halt_artifact(contract_id: str, violation_reason: str,
|
|||||||
oncall_client=None) -> None:
|
oncall_client=None) -> None:
|
||||||
"""Route a halt artifact to SRE on-call (REQ-107, D-085).
|
"""Route a halt artifact to SRE on-call (REQ-107, D-085).
|
||||||
|
|
||||||
When ACDL_SOD_HALT_TOPIC_ARN is set, publish to the SNS topic via
|
When NOVA_SOD_HALT_TOPIC_ARN is set, publish to the SNS topic via
|
||||||
boto3. When unset (dev/CI), fall back to a structured stderr emission
|
boto3. When unset (dev/CI), fall back to a structured stderr emission
|
||||||
+ a SEPARATION_OF_DUTIES_VIOLATION event write to the DynamoDB outbox
|
+ a SEPARATION_OF_DUTIES_VIOLATION event write to the DynamoDB outbox
|
||||||
via outbox_writer.write_event (so the halt is in the audit chain).
|
via outbox_writer.write_event (so the halt is in the audit chain).
|
||||||
The oncall_client, when provided, is the SNS client (test injection).
|
The oncall_client, when provided, is the SNS client (test injection).
|
||||||
"""
|
"""
|
||||||
topic_arn = os.environ.get("ACDL_SOD_HALT_TOPIC_ARN", "")
|
topic_arn = env.get_env("SOD_HALT_TOPIC_ARN", "") or ""
|
||||||
halt_payload = {
|
halt_payload = {
|
||||||
"contractId": contract_id,
|
"contractId": contract_id,
|
||||||
"reason": violation_reason,
|
"reason": violation_reason,
|
||||||
@@ -63,7 +74,7 @@ def route_halt_artifact(contract_id: str, violation_reason: str,
|
|||||||
sns.publish(
|
sns.publish(
|
||||||
TopicArn=topic_arn,
|
TopicArn=topic_arn,
|
||||||
Message=json.dumps(halt_payload),
|
Message=json.dumps(halt_payload),
|
||||||
Subject="ACDL SoD halt",
|
Subject="Nova SoD halt",
|
||||||
)
|
)
|
||||||
print(f"[halt-artifact] SNS published contract={contract_id} "
|
print(f"[halt-artifact] SNS published contract={contract_id} "
|
||||||
f"topic={topic_arn}", flush=True)
|
f"topic={topic_arn}", flush=True)
|
||||||
|
|||||||
@@ -0,0 +1,270 @@
|
|||||||
|
# Nova AWS Resource Migration Runbook (REQ-163, P4)
|
||||||
|
|
||||||
|
> **Milestone:** v1.15-Nova (Wave 4, P4). Renames every `acdl-*` AWS
|
||||||
|
> resource name → `nova-*` via Terraform. This is the heaviest Terraform
|
||||||
|
> phase of the rebrand and requires a **maintenance window**.
|
||||||
|
>
|
||||||
|
> **Plan-validated only.** Per A1, `NOVA_LIFECYCLE_MODE` defaults to
|
||||||
|
> `plan` (no live AWS mutation from CI). `terraform validate` passes; the
|
||||||
|
> live apply steps below are executed by a platform operator during the
|
||||||
|
> scheduled maintenance window. Each step has a verification + rollback.
|
||||||
|
|
||||||
|
## Scope (renamed resources)
|
||||||
|
|
||||||
|
| AWS resource | Before | After | Strategy |
|
||||||
|
|---|---|---|---|
|
||||||
|
| KMS alias | `alias/acdl-platform` | `alias/nova-platform` | cheap rename |
|
||||||
|
| SNS topic | `acdl-sod-halt` | `nova-sod-halt` | recreate |
|
||||||
|
| Security group | `acdl-ecs-sg` | `nova-ecs-sg` | recreate |
|
||||||
|
| Lambda (role/policy/function) | `acdl-contract-ingestor` | `nova-contract-ingestor` | recreate |
|
||||||
|
| DynamoDB contracts | `acdl-contracts` | `nova-contracts` | scan + copy |
|
||||||
|
| DynamoDB change-requests | `acdl-change-requests` | `nova-change-requests` | scan + copy |
|
||||||
|
| Secrets Manager secret | `acdl/github-token` | `nova/github-token` | recreate + re-store |
|
||||||
|
| ECR repo | `acdl-microservice` | `nova-microservice` | re-push |
|
||||||
|
| ECS cluster/service/task/role | `acdl-microservice` | `nova-microservice` | recreate |
|
||||||
|
| IAM user + policy | `acdl-spike-runner` (+ `-policy`) | `nova-spike-runner` (+ `-policy`) | re-bootstrap |
|
||||||
|
| IAM act-runner role | `acdl-act-runner-role` | `nova-act-runner-role` | re-bootstrap |
|
||||||
|
| IAM deploy role | `acdl-deploy-<repo>` | `nova-deploy-<repo>` | re-bootstrap |
|
||||||
|
| S3 state bucket | `acdl-tfstate-581513795199-us-east-1` | `nova-tfstate-581513795199-us-east-1` | `-migrate-state` |
|
||||||
|
| DynamoDB outbox | `acdl-outbox` | `nova-outbox` | scan + copy |
|
||||||
|
| Platform VPC/subnet/IGW/RT | `acdl-shared*` | `nova-shared*` | recreate (brief downtime) |
|
||||||
|
| CI VPC/subnet/SG/cluster | `acdl-ci-*` | `nova-ci-*` | recreate (CI-only) |
|
||||||
|
| ALB name prefix | `acdl-alb` | `nova-alb` | recreate (brief downtime, LAST) |
|
||||||
|
|
||||||
|
## Migration ordering (binding)
|
||||||
|
|
||||||
|
Order: **KMS alias → SNS/SG → Lambda → DynamoDB → ECR → IAM → state bucket → ALB**.
|
||||||
|
Each step is independently rollback-able. The ALB is last because it
|
||||||
|
requires the briefest downtime window.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Pre-flight
|
||||||
|
|
||||||
|
1. **Announce the maintenance window** (consumers are notified via the
|
||||||
|
P1 migration guide `docs/NOVA_MIGRATION.md`).
|
||||||
|
2. **Back up state** for every stack (see §State bucket — back up the
|
||||||
|
state JSON *before* `-migrate-state`).
|
||||||
|
3. Confirm `NOVA_LIFECYCLE_MODE=plan` (default) so CI does not mutate
|
||||||
|
AWS during the window.
|
||||||
|
4. Confirm the new `nova-*` destination tables/repos will be created by
|
||||||
|
the same Terraform apply (no manual pre-creation needed).
|
||||||
|
|
||||||
|
## Step 1 — KMS alias (`alias/acdl-platform` → `alias/nova-platform`)
|
||||||
|
|
||||||
|
- **Command (in `terraform/platform/`):**
|
||||||
|
```bash
|
||||||
|
terraform init -upgrade
|
||||||
|
terraform apply -replace=aws_kms_alias.nova_platform
|
||||||
|
```
|
||||||
|
(Terraform destroys the old alias + creates the new one — aliases are
|
||||||
|
cheap; the underlying key ID is unchanged.)
|
||||||
|
- **Verify:** `aws kms list-aliases --query 'Aliases[?AliasName==`alias/nova-platform`]'` returns the new alias; `alias/acdl-platform` is gone.
|
||||||
|
- **Rollback:** `terraform apply -replace=aws_kms_alias.nova_platform` against the prior revision (re-creates `alias/acdl-platform`). Resources encrypted by the key are unaffected (key ID unchanged).
|
||||||
|
|
||||||
|
## Step 2 — SNS topic + Security group (recreate)
|
||||||
|
|
||||||
|
- **Command:** `terraform apply` in `terraform/platform/`.
|
||||||
|
- SNS `acdl-sod-halt` → `nova-sod-halt` (the topic ARN changes; update `NOVA_SOD_HALT_TOPIC_ARN` wherever it is set).
|
||||||
|
- SG `acdl-ecs-sg` → `nova-ecs-sg` (the security group is re-attached to running ECS tasks; brief task restart).
|
||||||
|
- **Verify:** `aws sns list-topics` shows `nova-sod-halt`; `aws ec2 describe-security-groups` shows `nova-ecs-sg`.
|
||||||
|
- **Rollback:** `terraform apply` the prior revision re-creates the `acdl-*` names. The SNS topic has no message backlog (halt artifacts are fire-and-forget); the SG drift resolves on next task deploy.
|
||||||
|
|
||||||
|
## Step 3 — Lambda (recreate)
|
||||||
|
|
||||||
|
- **Command:** `terraform apply` in `terraform/platform/`.
|
||||||
|
- Lambda function `acdl-contract-ingestor` → `nova-contract-ingestor`.
|
||||||
|
- Execution role `acdl-contract-ingestor-role` → `nova-contract-ingestor-role`.
|
||||||
|
- Inline policy `acdl-contract-ingestor-policy` → `nova-contract-ingestor-policy`.
|
||||||
|
- The Lambda env vars (`CONTRACTS_TABLE`, `GITHUB_TOKEN_SECRET_ID`) now resolve to `nova-*` defaults.
|
||||||
|
- **Verify:** `aws lambda list-functions` shows `nova-contract-ingestor`; the Function URL returns 200 on a SigV4-signed invoke. The `consumer_invoke_policy.json` rendered output (Terraform `consumer_invoke_policy_rendered`) now references `function:nova-contract-ingestor` — re-distribute to consumer deploy roles.
|
||||||
|
- **Rollback:** `terraform apply` the prior revision re-creates `acdl-contract-ingestor`. Consumer deploy roles must point back at the old Function ARN (re-distribute the prior `consumer_invoke_policy.json`).
|
||||||
|
|
||||||
|
## Step 4 — DynamoDB (scan + copy)
|
||||||
|
|
||||||
|
DynamoDB table names are immutable post-creation, so the migration is a
|
||||||
|
**scan + copy** (not a rename). The new `nova-*` tables are created by
|
||||||
|
the same Terraform apply (Step 3). The data-migration script copies
|
||||||
|
every item and verifies row counts.
|
||||||
|
|
||||||
|
- **Command (from repo root):**
|
||||||
|
```bash
|
||||||
|
# Dry-run first (no writes):
|
||||||
|
python3 scripts/migrate_dynamodb_data.py
|
||||||
|
# Execute the copy:
|
||||||
|
python3 scripts/migrate_dynamodb_data.py --apply
|
||||||
|
# A single table:
|
||||||
|
python3 scripts/migrate_dynamodb_data.py --table contracts --apply
|
||||||
|
```
|
||||||
|
The script scans `acdl-contracts` → copies to `nova-contracts`, and
|
||||||
|
`acdl-change-requests` → `nova-change-requests`, then verifies the
|
||||||
|
destination row count == source row count (re-scan, not
|
||||||
|
`DescribeTable.ItemCount` which lags ~6h).
|
||||||
|
- **Verify:**
|
||||||
|
```bash
|
||||||
|
# Row counts must match (printed by the script). Manual cross-check:
|
||||||
|
aws dynamodb scan --table-name nova-contracts --select COUNT
|
||||||
|
aws dynamodb scan --table-name acdl-contracts --select COUNT
|
||||||
|
```
|
||||||
|
Then **point consumers at the new tables** (the Lambda already reads
|
||||||
|
`nova-*` defaults; any direct DynamoDB consumers update their env).
|
||||||
|
- **Keep the old tables** (`acdl-contracts`, `acdl-change-requests`)
|
||||||
|
until consumers are verified reading from `nova-*`. **Deletion is a
|
||||||
|
manual post-verification step:**
|
||||||
|
```bash
|
||||||
|
aws dynamodb delete-table --table-name acdl-contracts
|
||||||
|
aws dynamodb delete-table --table-name acdl-change-requests
|
||||||
|
```
|
||||||
|
Only delete after a full soak period confirms `nova-*` reads succeed.
|
||||||
|
- **Rollback:** Re-point consumers at `acdl-*` (the old tables are
|
||||||
|
retained). The copy is additive (no data loss). To roll back a partial
|
||||||
|
copy, re-run `--apply` (idempotent — `PutItem` overwrites).
|
||||||
|
|
||||||
|
### Outbox table (`acdl-outbox` → `nova-outbox`)
|
||||||
|
|
||||||
|
The evidence outbox table follows the same scan+copy pattern (it is
|
||||||
|
created by `terraform/bootstrap/create_state_backend.py`).
|
||||||
|
- **Command:** `python3 scripts/migrate_dynamodb_data.py --source acdl-outbox --dest nova-outbox --apply`
|
||||||
|
- The `core/outbox_writer.py` default + `core/regression_verify.py`
|
||||||
|
CAP-015 probe now reference `nova-outbox` (P4 updated both). The
|
||||||
|
regression gate's live-AWS CAP-015 will return `Verified` once the
|
||||||
|
`nova-outbox` table exists live; until then it is `Decayed` (the gate
|
||||||
|
is re-run at milestone complete after the live migration).
|
||||||
|
|
||||||
|
## Step 5 — ECR (re-push)
|
||||||
|
|
||||||
|
- **Command:** `terraform apply` in `terraform/microservice/` creates
|
||||||
|
the new `nova-microservice` ECR repo. Re-push the image:
|
||||||
|
```bash
|
||||||
|
python3 scripts/push_consumer_image.py # creates nova-microservice + prints docker tag/push
|
||||||
|
```
|
||||||
|
(The script's `ECR_REPO_NAME` is now `nova-microservice`.)
|
||||||
|
- **Verify:** `aws ecr describe-repositories` shows `nova-microservice`; `docker pull <acct>.dkr.ecr.us-east-1.amazonaws.com/nova-microservice:latest` succeeds.
|
||||||
|
- **Rollback:** The old `acdl-microservice` repo is retained until the
|
||||||
|
soak passes. Re-push to it if a rollback is needed. Delete it manually:
|
||||||
|
`aws ecr delete-repository --repository-name acdl-microservice --force`.
|
||||||
|
|
||||||
|
## Step 6 — IAM (re-bootstrap)
|
||||||
|
|
||||||
|
- **Command:**
|
||||||
|
```bash
|
||||||
|
export NOVA_BOOTSTRAP_AWS_ACCESS_KEY_ID="<root key>"
|
||||||
|
export NOVA_BOOTSTRAP_AWS_SECRET_ACCESS_KEY="<root secret>"
|
||||||
|
python3 terraform/bootstrap/create_state_backend.py # creates nova-outbox (idempotent)
|
||||||
|
python3 terraform/bootstrap/create_iam_user.py # creates nova-spike-runner
|
||||||
|
python3 terraform/bootstrap/apply_iam_baseline.py # creates nova-spike-runner-policy + nova-act-runner-role
|
||||||
|
bash scripts/rotate_spike_key.sh # rotates the nova-spike-runner key
|
||||||
|
```
|
||||||
|
The deploy role `acdl-deploy-<repo>` → `nova-deploy-<repo>` is
|
||||||
|
created by the bootstrap (the deploy workflow
|
||||||
|
`.gitea/.github/workflows/deploy.yml` now references
|
||||||
|
`role/nova-deploy-{1}`).
|
||||||
|
- **Verify:** `aws iam get-user --user-name nova-spike-runner`;
|
||||||
|
`aws iam list-attached-user-policies --user-name nova-spike-runner`
|
||||||
|
shows `nova-spike-runner-policy`;
|
||||||
|
`aws iam get-role --role-name nova-act-runner-role`.
|
||||||
|
- **Rollback:** Re-run the prior bootstrap scripts (they create
|
||||||
|
`acdl-spike-runner` + `acdl-act-runner-role`). The deploy workflow's
|
||||||
|
`role-to-assume` must be reverted to `acdl-deploy-` (prior revision).
|
||||||
|
|
||||||
|
## Step 7 — State bucket (`acdl-tfstate-*` → `nova-tfstate-*`, `-migrate-state`)
|
||||||
|
|
||||||
|
The S3 state backend is renamed. Terraform's `-migrate-state` copies the
|
||||||
|
state objects to the new bucket. **Back up the state JSON first.**
|
||||||
|
|
||||||
|
- **Back up state (per stack):**
|
||||||
|
```bash
|
||||||
|
for stack in platform microservice ci-vpc; do
|
||||||
|
aws s3 cp s3://acdl-tfstate-581513795199-us-east-1/$stack/terraform.tfstate \
|
||||||
|
./backup-$stack.tfstate
|
||||||
|
done
|
||||||
|
```
|
||||||
|
- **Command (per stack):** the backend config in each
|
||||||
|
`terraform/*/terraform.tf` now points at `nova-tfstate-...`.
|
||||||
|
```bash
|
||||||
|
cd terraform/platform
|
||||||
|
terraform init -migrate-state # copies state acdl-tfstate → nova-tfstate
|
||||||
|
cd ../microservice
|
||||||
|
terraform init -migrate-state
|
||||||
|
cd ../ci-vpc
|
||||||
|
terraform init -migrate-state
|
||||||
|
```
|
||||||
|
- **Verify:** `aws s3 ls s3://nova-tfstate-581513795199-us-east-1/`
|
||||||
|
shows the state keys; `terraform state list` in each dir lists the
|
||||||
|
expected resources.
|
||||||
|
- **Rollback:** Point the backend back at `acdl-tfstate-*` and re-run
|
||||||
|
`terraform init -migrate-state` (restores from the backup bucket). The
|
||||||
|
old `acdl-tfstate-*` bucket is retained until the soak passes. Delete
|
||||||
|
it manually:
|
||||||
|
`aws s3 rb s3://acdl-tfstate-581513795199-us-east-1 --force`.
|
||||||
|
|
||||||
|
## Step 8 — ALB (recreate, brief downtime, LAST)
|
||||||
|
|
||||||
|
The ALB is last because its recreation requires the briefest downtime
|
||||||
|
window (the ECS service is re-attached to the new target group).
|
||||||
|
|
||||||
|
- **Command:** `terraform apply` in `terraform/microservice/`. The ALB
|
||||||
|
`acdl-microservice` / `acdl-alb` → `nova-microservice` / `nova-alb`.
|
||||||
|
- **Verify:** `aws elbv2 describe-load-balancers` shows the new ALB;
|
||||||
|
`curl http://<new-alb-dns>/` returns 200.
|
||||||
|
- **Rollback:** `terraform apply` the prior revision re-creates the
|
||||||
|
`acdl-*` ALB (brief downtime again). The old ALB DNS is retained until
|
||||||
|
consumers are re-pointed.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Post-migration
|
||||||
|
|
||||||
|
1. **Soak:** run consumers against `nova-*` for a full verification
|
||||||
|
window (deploy a test contract end-to-end).
|
||||||
|
2. **Delete old resources** (manual, only after soak):
|
||||||
|
- DynamoDB: `acdl-contracts`, `acdl-change-requests`, `acdl-outbox`
|
||||||
|
- ECR: `acdl-microservice`
|
||||||
|
- IAM: `acdl-spike-runner` (+ policy), `acdl-act-runner-role`,
|
||||||
|
`acdl-deploy-<repo>`
|
||||||
|
- S3: `acdl-tfstate-581513795199-us-east-1`
|
||||||
|
- SNS: `acdl-sod-halt`
|
||||||
|
- SG: `acdl-ecs-sg`
|
||||||
|
- Secrets Manager: `acdl/github-token`
|
||||||
|
- KMS alias: `alias/acdl-platform`
|
||||||
|
- ALB: `acdl-alb` / `acdl-microservice`
|
||||||
|
3. **Regression gate:** re-run `bash scripts/run_regression.sh`. The
|
||||||
|
live-AWS CAP-013..016 probes should return `Verified` (the `nova-*`
|
||||||
|
tables + state bucket exist). CAP-015 (outbox) flips from `Decayed`
|
||||||
|
→ `Verified` once `nova-outbox` is live.
|
||||||
|
|
||||||
|
## What P5 owns (not P4)
|
||||||
|
|
||||||
|
- **Remove dual-read fallback:** `core/env.py` `get_env()` drops the
|
||||||
|
`ACDL_*` fallback; shell scripts drop `:-$ACDL_X`. P4 keeps the
|
||||||
|
dual-read (deployments don't break mid-window).
|
||||||
|
- **`nova_tagging.py` hard-fail on `acdl:*`:** P3 set hard mode (no
|
||||||
|
`acdl:*`-only tags); P5 tightens to fail on any `acdl:*` presence. P4
|
||||||
|
leaves P3's behavior.
|
||||||
|
- **Delete `ACDL_*` Gitea secrets:** the `NOVA_*` aliases created in P2
|
||||||
|
are now the only source.
|
||||||
|
- **Finalize `docs/NOVA_MIGRATION.md`:** mark the migration complete
|
||||||
|
(cutoff passed).
|
||||||
|
- **Milestone ship:** tag `v1.15.4`, merge to `main`, Gitea release.
|
||||||
|
|
||||||
|
## Files touched in P4
|
||||||
|
|
||||||
|
- `terraform/platform/main.tf`, `terraform/microservice/main.tf`,
|
||||||
|
`terraform/ci-vpc/main.tf` — resource renames + backend bucket.
|
||||||
|
- `terraform/{platform,microservice,ci-vpc}/terraform.tf` — state bucket.
|
||||||
|
- `terraform/platform/consumer_invoke_policy.json` — Lambda ARN.
|
||||||
|
- `terraform/bootstrap/{create_state_backend,create_iam_user,apply_iam_baseline}.py`,
|
||||||
|
`spike_runner_policy.json`, `.bootstrap_state.json`, `README.md` —
|
||||||
|
IAM/outbox/state-bucket renames.
|
||||||
|
- `modules/l1/*/terraform/**` + `modules/l1/alb/instance.json` — L1
|
||||||
|
resource-name defaults.
|
||||||
|
- `modules/l2/microservice/composition.json` — `nova-app-role` default.
|
||||||
|
- `core/lambda/contract_ingestor.py` — default table names (D-111).
|
||||||
|
- `core/outbox_writer.py`, `core/regression_verify.py`,
|
||||||
|
`core/local_emulators.py` — outbox table consistency (cross-territory,
|
||||||
|
minimal).
|
||||||
|
- `.gitea/workflows/deploy.yml` + `.github/workflows/deploy.yml` —
|
||||||
|
`nova-deploy-` role ARN + artifact names.
|
||||||
|
- `scripts/migrate_dynamodb_data.py` (NEW), `scripts/rotate_spike_key.sh`,
|
||||||
|
`scripts/push_consumer_image.py`.
|
||||||
|
- `tests/**` — fixtures updated to assert `nova-*`.
|
||||||
@@ -0,0 +1,177 @@
|
|||||||
|
# Nova Migration Guide — What Consumers Must Know
|
||||||
|
|
||||||
|
> **STATUS: COMPLETE (milestone v1.15.4, 2026-07-30).** The Nova rebrand
|
||||||
|
> is fully rolled out. The dual-read / parallel-write grace period has
|
||||||
|
> ended (P5 cutoff passed). All `ACDL_*` env var fallbacks, `.acdl/`
|
||||||
|
> consumer-path fallbacks, `/acdl/` SSM-path fallbacks, `acdl:*` tag-key
|
||||||
|
> fallbacks, and `acdl-*` AWS resource names are removed. Consumers must
|
||||||
|
> use the `NOVA_*` / `.nova/` / `/nova/` / `nova:*` / `nova-*` names
|
||||||
|
> exclusively. If you have not yet migrated, follow the steps below.
|
||||||
|
|
||||||
|
> **Nova** is the new product brand for the platform formerly known as
|
||||||
|
> **ACDL** (Agentic Cloud Delivery Platform). This guide documents the
|
||||||
|
> breaking changes from the rebrand rollout (Phases P2–P4, cutoff P5)
|
||||||
|
> and tells you exactly what to do.
|
||||||
|
|
||||||
|
## What is NOT changing
|
||||||
|
|
||||||
|
- **The Gitea repository name** (`continuous-intelligence/acdl`) is **not**
|
||||||
|
changing. Only the product brand is changing. The `uses:` reference
|
||||||
|
(`acdl/.github/workflows/deploy.yml@vX.Y`) and the GitHub `acdl/acdl` repo
|
||||||
|
path are unchanged for the duration of the rebrand; the workflow
|
||||||
|
`uses:` reference will be migrated in a later, separately-announced step.
|
||||||
|
- **The platform behavior** is unchanged. Same pipeline stages, same
|
||||||
|
contract schema, same confidence model, same evidence stream, same
|
||||||
|
modules. Only the brand, the on-disk path, the env var names, the SSM
|
||||||
|
path, the AWS tag keys, and the AWS resource names are changing.
|
||||||
|
|
||||||
|
## The 5 breaking changes
|
||||||
|
|
||||||
|
Five things that consumers may reference are being renamed. Each is
|
||||||
|
scheduled into a phase, ships with a grace period, and has a cutoff.
|
||||||
|
|
||||||
|
### 1. Consumer contract path — Phase P2
|
||||||
|
|
||||||
|
- **Old:** `.acdl/contract.yml`
|
||||||
|
- **New:** `.nova/contract.yml`
|
||||||
|
- **Phase:** P2 (env vars + consumer path)
|
||||||
|
- **Grace period:** during P2–P4 the deploy workflow reads **both** paths
|
||||||
|
(`.nova/contract.yml` first, falling back to `.acdl/contract.yml` if the
|
||||||
|
new path is absent). Your existing contracts keep working until P5.
|
||||||
|
- **Cutoff:** P5 removes the `.acdl/` fallback. Move your contract file
|
||||||
|
before P5.
|
||||||
|
- **What you must do:** rename the directory in your consumer repo from
|
||||||
|
`.acdl/` to `.nova/` and update any `contract:` workflow input that
|
||||||
|
points at the old path. Nothing else changes in the contract content.
|
||||||
|
|
||||||
|
### 2. Environment variables — Phase P2
|
||||||
|
|
||||||
|
- **Old:** `ACDL_*` (e.g. `ACDL_LIFECYCLE_MODE`, `ACDL_AWS_ACCOUNT_ID`,
|
||||||
|
`ACDL_BOOTSTRAP_AWS_ACCESS_KEY_ID`, …)
|
||||||
|
- **New:** `NOVA_*` (e.g. `NOVA_LIFECYCLE_MODE`, `NOVA_AWS_ACCOUNT_ID`,
|
||||||
|
`NOVA_BOOTSTRAP_AWS_ACCESS_KEY_ID`, …)
|
||||||
|
- **Phase:** P2 (env vars + consumer path)
|
||||||
|
- **Grace period — dual-read fallback:** during P2–P4 the platform reads
|
||||||
|
**`NOVA_*` first, then falls back to `ACDL_*`** if the Nova variable is
|
||||||
|
unset. This means your CI secrets, workflow env blocks, and local
|
||||||
|
`.env.secrets` keep working unchanged through P4. You do not need to
|
||||||
|
rename everything in one shot — rename a variable and the dual-read picks
|
||||||
|
it up; leave one old and it still resolves.
|
||||||
|
- **Cutoff:** P5 removes the `ACDL_*` fallback. After P5, only `NOVA_*`
|
||||||
|
is read.
|
||||||
|
- **What you must do:** rename your `ACDL_*` CI secrets, workflow `env:`
|
||||||
|
blocks, and any local `.env.secrets` entries to `NOVA_*`. Because of the
|
||||||
|
dual-read, you can do this incrementally across P2–P4 — but it must be
|
||||||
|
complete before P5.
|
||||||
|
|
||||||
|
### 3. SSM parameter path — Phase P3 (DONE)
|
||||||
|
|
||||||
|
- **Old:** `/acdl/{env}/{contractId}/{output}`
|
||||||
|
- **New:** `/nova/{env}/{contractId}/{output}`
|
||||||
|
- **Phase:** P3 (SSM paths + tag keys) — **shipped in P3**
|
||||||
|
- **Grace period — parallel-write:** during P3–P4 the platform **writes
|
||||||
|
every output to both** the `/acdl/…` and `/nova/…` SSM paths, and reads
|
||||||
|
from `/nova/…` first (falling back to `/acdl/…`). Any hardcoded SSM path
|
||||||
|
reads in your application code keep resolving through P4. The P3
|
||||||
|
migration script (`scripts/migrate_ssm_paths.py`) copies existing
|
||||||
|
`/acdl/…` parameters to `/nova/…`, verifies the copy, and deletes the
|
||||||
|
old ones.
|
||||||
|
- **Cutoff:** P5 stops writing to `/acdl/…` and removes the read fallback.
|
||||||
|
After P5 only `/nova/…` exists.
|
||||||
|
- **What you must do:** if your application code or runbooks read deploy
|
||||||
|
outputs from SSM by hardcoded path, update the path prefix from `/acdl/`
|
||||||
|
to `/nova/`. If you consume outputs only via the PR-comment / GitHub
|
||||||
|
issue surface, you do nothing — the platform republishes under the new
|
||||||
|
path automatically.
|
||||||
|
|
||||||
|
### 4. AWS tag keys — Phase P3 (DONE)
|
||||||
|
|
||||||
|
- **Old:** `acdl:owner`, `acdl:environment`, `acdl:contract`,
|
||||||
|
`acdl:cost-center`, `acdl:ref`
|
||||||
|
- **New:** `nova:owner`, `nova:environment`, `nova:contract`,
|
||||||
|
`nova:cost-center`, `nova:ref`
|
||||||
|
- **Phase:** P3 (SSM paths + tag keys) — **shipped in P3**
|
||||||
|
- **Grace period — parallel-tag period:** during P3–P4 the platform
|
||||||
|
**tags every resource with both** the `acdl:*` and `nova:*` keys (same
|
||||||
|
values). The ABAC session policy matches on **either** key set, so your
|
||||||
|
existing scoped permissions keep working. The default cost-center value
|
||||||
|
moves from `acdl-default` to `nova-default` (both written during the
|
||||||
|
parallel-tag period). Terraform now emits `nova:*` keys; old `acdl:*`
|
||||||
|
tags on pre-P3 live resources are removed by the P4 runbook's
|
||||||
|
`scripts/untag_acdl_keys.py` step after the `nova:*` tags are applied
|
||||||
|
live.
|
||||||
|
- **Cutoff:** P5 stops writing the `acdl:*` keys and the ABAC policy matches
|
||||||
|
only on `nova:*`. After P5, resources created before P5 still carry the
|
||||||
|
old `acdl:*` tags (tags are not retroactively rewritten) but **new**
|
||||||
|
resources are tagged `nova:*` only, and the policy no longer grants
|
||||||
|
access via `acdl:*`.
|
||||||
|
- **What you must do:** if you have IAM policies, Cost Explorer filters,
|
||||||
|
or billing groupings that key off `acdl:*` tag keys, add a parallel
|
||||||
|
`nova:*` condition (or migrate to `nova:*`) before P5. The platform
|
||||||
|
handles the dual-tagging; you only need to update your own tag-key
|
||||||
|
references.
|
||||||
|
|
||||||
|
### 5. AWS resource names — Phase P4
|
||||||
|
|
||||||
|
- **Old:** `acdl-*` (DynamoDB tables `acdl-contracts`,
|
||||||
|
`acdl-change-requests`; Lambda `acdl-contract-ingestor`; SNS
|
||||||
|
`acdl-sod-halt`; security group `acdl-ecs-sg`; KMS alias
|
||||||
|
`alias/acdl-platform`; ECS services, ECR repos, IAM user
|
||||||
|
`acdl-spike-runner`, state bucket `acdl-tfstate-*`, ALB `acdl-alb`,
|
||||||
|
`acdl-deploy-*`)
|
||||||
|
- **New:** `nova-*` (the same resources, prefixed `nova-`)
|
||||||
|
- **Phase:** P4 (resource names) — **maintenance window**
|
||||||
|
- **Grace period:** P4 is a **planned maintenance window**. AWS resources
|
||||||
|
cannot be renamed in place, so P4 provisions the `nova-*` resources,
|
||||||
|
migrates data (DynamoDB tables, S3 state), repoints the platform, and
|
||||||
|
tears down the `acdl-*` resources. The platform team schedules and
|
||||||
|
announces the window; consumers do not provision or rename anything
|
||||||
|
themselves.
|
||||||
|
- **Cutoff:** the `acdl-*` resources are decommissioned at the end of the
|
||||||
|
P4 maintenance window. After P4, only `nova-*` resources exist.
|
||||||
|
- **What you must do:** nothing for the resource names themselves — the
|
||||||
|
platform owns the rename. If your application code or runbooks reference
|
||||||
|
a specific `acdl-*` resource by name (e.g. a hardcoded DynamoDB table
|
||||||
|
name or ECR URI), update it to the `nova-*` name during P4. The platform
|
||||||
|
publishes the exact old → new name mapping with the P4 announcement.
|
||||||
|
|
||||||
|
## Timeline at a glance
|
||||||
|
|
||||||
|
| Phase | What ships | Grace period | Cutoff |
|
||||||
|
|-------|------------|--------------|--------|
|
||||||
|
| **P1** (this phase) | Brand prose, docs, decks, schema `$id`, release titles | n/a (prose only) | n/a |
|
||||||
|
| **P2** | `.nova/` contract path + `NOVA_*` env vars | dual-read: `.nova/`→`.acdl/`, `NOVA_*`→`ACDL_*` | **P5** removes fallback |
|
||||||
|
| **P3** | `/nova/` SSM path + `nova:*` tag keys | parallel-write (SSM) + parallel-tag (ABAC matches either) | **P5** removes old path/tags |
|
||||||
|
| **P4** | `nova-*` AWS resource names | maintenance window (platform-owned migration) | end of P4 window |
|
||||||
|
| **P5** | Fallback removal | — | `ACDL_*` env vars, `.acdl/` path, `/acdl/` SSM, `acdl:*` tags stop working |
|
||||||
|
|
||||||
|
## What consumers must do (checklist)
|
||||||
|
|
||||||
|
1. **Before P5 — contract path:** move `.acdl/contract.yml` →
|
||||||
|
`.nova/contract.yml` in your consumer repo; update the `contract:`
|
||||||
|
workflow input. *(Can be done any time in P2–P4.)*
|
||||||
|
2. **Before P5 — env vars:** rename `ACDL_*` CI secrets / workflow `env:`
|
||||||
|
blocks / local `.env.secrets` to `NOVA_*`. *(Incremental during P2–P4;
|
||||||
|
dual-read keeps you green.)*
|
||||||
|
3. **Before P5 — SSM reads:** if you read deploy outputs from SSM by
|
||||||
|
hardcoded `/acdl/…` path, update to `/nova/…`. *(Skip if you consume
|
||||||
|
outputs via PR comments only.)*
|
||||||
|
4. **Before P5 — tag-key references:** if you have IAM policies, Cost
|
||||||
|
Explorer filters, or billing groupings keyed off `acdl:*`, add or
|
||||||
|
migrate to `nova:*`. *(Platform handles dual-tagging.)*
|
||||||
|
5. **During P4 — resource-name references:** if your code or runbooks
|
||||||
|
reference a specific `acdl-*` AWS resource by name, update to the
|
||||||
|
`nova-*` name per the P4 mapping announcement. *(Platform owns the
|
||||||
|
rename itself.)*
|
||||||
|
|
||||||
|
## Questions
|
||||||
|
|
||||||
|
If anything in this guide is unclear, or you are unsure whether your
|
||||||
|
consumer repo references a renamed value, open an issue on the platform
|
||||||
|
repo. The platform team will confirm what you need to change and when.
|
||||||
|
|
||||||
|
> **Note:** the real Gitea repository name (`continuous-intelligence/acdl`)
|
||||||
|
> is **not** changing — only the product brand. The `uses:` workflow
|
||||||
|
> reference and repo path are migrated in a separately-announced later step;
|
||||||
|
> until then, keep your `uses: acdl/.github/workflows/deploy.yml@vX.Y`
|
||||||
|
> reference as-is.
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
title: ACDL — Agentic Cloud Delivery Platform
|
title: Nova
|
||||||
description: Consumer + platform-engineer documentation for the ACDL platform.
|
description: Consumer + platform-engineer documentation for the Nova platform (formerly ACDL — Agentic Cloud Delivery Platform).
|
||||||
remote_theme: mmistakes/minimal-mistakes@9.0.4
|
remote_theme: mmistakes/minimal-mistakes@9.0.4
|
||||||
|
|
||||||
exclude:
|
exclude:
|
||||||
|
|||||||
@@ -230,7 +230,7 @@ change to the modules/stack/confidence/audit.
|
|||||||
- A MAJOR bump requires a new registry entry (immutable publication); the
|
- A MAJOR bump requires a new registry entry (immutable publication); the
|
||||||
old entry enters a 12-month deprecation window.
|
old entry enters a 12-month deprecation window.
|
||||||
- The central deploy pipeline is referenced by a floating MAJOR + MINOR tag
|
- The central deploy pipeline is referenced by a floating MAJOR + MINOR tag
|
||||||
(e.g. `@v1.6`); patch fixes flow within the tag, breaking changes land
|
(e.g. `@v1.13`); patch fixes flow within the tag, breaking changes land
|
||||||
under the next MINOR tag.
|
under the next MINOR tag.
|
||||||
|
|
||||||
See [Versioning](pipeline/versioning) for the consumer-facing details.
|
See [Versioning](pipeline/versioning) for the consumer-facing details.
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
# Consumer Guide — Declare intent, deploy to AWS
|
# Consumer Guide — Declare intent, deploy to AWS
|
||||||
|
|
||||||
This guide walks a consumer through creating their pipeline and defining a
|
This guide walks a consumer through creating their pipeline and defining a
|
||||||
contract that deploys any ACDL module to AWS. It is **generic** across all
|
contract that deploys any Nova module to AWS. It is **generic** across all
|
||||||
modules in the registry; `static-assets` is the worked example, but every
|
modules in the registry; `static-assets` is the worked example, but every
|
||||||
step applies to `microservice` and any future module.
|
step applies to `microservice` and any future module.
|
||||||
|
|
||||||
## The model
|
## The model
|
||||||
|
|
||||||
Consumers have their own repos and consume ACDL by writing a contract
|
Consumers have their own repos and consume Nova by writing a contract
|
||||||
that declares infrastructure (one or more modules), an environment, and inputs. The consumer declares a **contract** (which infrastructure, which
|
that declares infrastructure (one or more modules), an environment, and inputs. The consumer declares a **contract** (which infrastructure, which
|
||||||
environment, which inputs); the ACDL platform owns the pipelines, modules,
|
environment, which inputs); the Nova platform owns the pipelines, modules,
|
||||||
engine adapter, and evidence stream.
|
engine adapter, and evidence stream.
|
||||||
|
|
||||||
You do not write infrastructure modules, workflow YAML, or adapter code.
|
You do not write infrastructure modules, workflow YAML, or adapter code.
|
||||||
@@ -19,7 +19,7 @@ definitions.
|
|||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
flowchart LR
|
flowchart LR
|
||||||
A["your repo<br/>(app code + contracts + CI definitions)"] -->|uses: acdl/.github/workflows/deploy.yml@v1.9| B
|
A["your repo<br/>(app code + contracts + CI definitions)"] -->|uses: acdl/.github/workflows/deploy.yml@v1.13| B
|
||||||
B["platform runners<br/>(modules + pipelines + adapters + schemas)"] -->|contract -> resolver -> stack -> adapter<br/>-> security checks -> infrastructure plan -> policy checks<br/>-> confidence -> apply -> evidence event| C
|
B["platform runners<br/>(modules + pipelines + adapters + schemas)"] -->|contract -> resolver -> stack -> adapter<br/>-> security checks -> infrastructure plan -> policy checks<br/>-> confidence -> apply -> evidence event| C
|
||||||
C["your resources in AWS"]
|
C["your resources in AWS"]
|
||||||
```
|
```
|
||||||
@@ -27,7 +27,7 @@ flowchart LR
|
|||||||
## Versioning the `uses:` reference
|
## Versioning the `uses:` reference
|
||||||
|
|
||||||
The central deployment pipeline is **always versioned with floating MAJOR
|
The central deployment pipeline is **always versioned with floating MAJOR
|
||||||
and MINOR tags** (e.g. `acdl/pipelines/contract.yml@v1.9`). Version
|
and MINOR tags** (e.g. `acdl/pipelines/contract.yml@v1.13`). Version
|
||||||
constraints cannot be expressed inside the contract, so the tag in
|
constraints cannot be expressed inside the contract, so the tag in
|
||||||
`uses:` is the only immutability lever a consumer has. See
|
`uses:` is the only immutability lever a consumer has. See
|
||||||
[Versioning](pipeline/versioning) for the full rationale.
|
[Versioning](pipeline/versioning) for the full rationale.
|
||||||
@@ -47,13 +47,13 @@ platform-managed. See [Environments](environments/).
|
|||||||
environment is bound, your first pipeline run emits a friendly onboarding
|
environment is bound, your first pipeline run emits a friendly onboarding
|
||||||
prompt. See [Environments](environments/).
|
prompt. See [Environments](environments/).
|
||||||
- **Authorization to reference the central pipeline.** Onboarding grants
|
- **Authorization to reference the central pipeline.** Onboarding grants
|
||||||
your repo the right to `uses: acdl/.github/workflows/deploy.yml@v1.9`.
|
your repo the right to `uses: acdl/.github/workflows/deploy.yml@v1.13`.
|
||||||
Contact the platform team if you have not been onboarded.
|
Contact the platform team if you have not been onboarded.
|
||||||
|
|
||||||
## Step 1 — Create a consumer repo
|
## Step 1 — Create a consumer repo
|
||||||
|
|
||||||
Create a repository for your application. The top level holds your app
|
Create a repository for your application. The top level holds your app
|
||||||
code; your contract lives at `.acdl/contract.yml`. Example for a static
|
code; your contract lives at `.nova/contract.yml`. Example for a static
|
||||||
site:
|
site:
|
||||||
|
|
||||||
```
|
```
|
||||||
@@ -62,7 +62,7 @@ my-static-site/
|
|||||||
assets/
|
assets/
|
||||||
style.css
|
style.css
|
||||||
logo.png
|
logo.png
|
||||||
.acdl/
|
.nova/
|
||||||
contract.yaml
|
contract.yaml
|
||||||
.github/
|
.github/
|
||||||
workflows/
|
workflows/
|
||||||
@@ -75,7 +75,7 @@ Example for a microservice:
|
|||||||
my-microservice/
|
my-microservice/
|
||||||
app.py
|
app.py
|
||||||
Dockerfile
|
Dockerfile
|
||||||
.acdl/
|
.nova/
|
||||||
contract.yaml
|
contract.yaml
|
||||||
.github/
|
.github/
|
||||||
workflows/
|
workflows/
|
||||||
@@ -83,20 +83,20 @@ my-microservice/
|
|||||||
```
|
```
|
||||||
|
|
||||||
Your app code lives at the top level. Your contract lives at
|
Your app code lives at the top level. Your contract lives at
|
||||||
`.acdl/contract.yml` regardless of the module you deploy. Your CI
|
`.nova/contract.yml` regardless of the module you deploy. Your CI
|
||||||
definition lives at `.github/workflows/deploy.yml`.
|
definition lives at `.github/workflows/deploy.yml`.
|
||||||
|
|
||||||
## Step 2 — Reference the central pipeline
|
## Step 2 — Reference the central pipeline
|
||||||
|
|
||||||
In your CI workflow (`.github/workflows/deploy.yml`), reference the central
|
In your CI workflow (`.github/workflows/deploy.yml`), reference the central
|
||||||
ACDL deployment workflow with a **versioned tag** (floating MAJOR + MINOR):
|
Nova deployment workflow with a **versioned tag** (floating MAJOR + MINOR):
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
jobs:
|
jobs:
|
||||||
deploy:
|
deploy:
|
||||||
uses: acdl/.github/workflows/deploy.yml@v1.9
|
uses: acdl/.github/workflows/deploy.yml@v1.13
|
||||||
with:
|
with:
|
||||||
contract: .acdl/contract.yml
|
contract: .nova/contract.yml
|
||||||
environment: dev
|
environment: dev
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -106,7 +106,7 @@ field; the version pin lives in the CI workflow reference.
|
|||||||
|
|
||||||
## Step 3 — Define the contract
|
## Step 3 — Define the contract
|
||||||
|
|
||||||
Write `.acdl/contract.yml`. The `static-assets` example:
|
Write `.nova/contract.yml`. The `static-assets` example:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
environment: dev
|
environment: dev
|
||||||
@@ -140,7 +140,7 @@ name: microservice
|
|||||||
|
|
||||||
| Field | Type | Required | Description |
|
| Field | Type | Required | Description |
|
||||||
|-------|------|----------|-------------|
|
|-------|------|----------|-------------|
|
||||||
| `uses` | string | yes | Reference to the central deployment pipeline, **versioned** with a floating MAJOR+MINOR tag (e.g. `acdl/pipelines/contract.yml@v1.9`). Bare or `@main` references are discouraged. See [Versioning](pipeline/versioning). |
|
| `uses` | string | yes | Reference to the central deployment pipeline, **versioned** with a floating MAJOR+MINOR tag (e.g. `acdl/pipelines/contract.yml@v1.13`). Bare or `@main` references are discouraged. See [Versioning](pipeline/versioning). |
|
||||||
| `module` | string | yes | Module name from the registry — any primitive or module (e.g. `static-assets`, `microservice`, `s3`). See the [module catalog](modules/). |
|
| `module` | string | yes | Module name from the registry — any primitive or module (e.g. `static-assets`, `microservice`, `s3`). See the [module catalog](modules/). |
|
||||||
| `environment` | string | yes | The platform-managed environment to deploy to (e.g. `dev`). See [Environments](environments/). |
|
| `environment` | string | yes | The platform-managed environment to deploy to (e.g. `dev`). See [Environments](environments/). |
|
||||||
| `inputs` | object | yes | Module-specific inputs (see the module's README). |
|
| `inputs` | object | yes | Module-specific inputs (see the module's README). |
|
||||||
@@ -167,7 +167,7 @@ and execute for you.
|
|||||||
|
|
||||||
### The consumer CI definition
|
### The consumer CI definition
|
||||||
|
|
||||||
Add a thin workflow file to **your** repo that invokes the reusable ACDL
|
Add a thin workflow file to **your** repo that invokes the reusable Nova
|
||||||
deploy workflow with a **versioned tag** (`.github/workflows/deploy.yml`):
|
deploy workflow with a **versioned tag** (`.github/workflows/deploy.yml`):
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
@@ -177,22 +177,22 @@ on:
|
|||||||
branches: [main]
|
branches: [main]
|
||||||
jobs:
|
jobs:
|
||||||
deploy:
|
deploy:
|
||||||
uses: acdl/.github/workflows/deploy.yml@v1.9
|
uses: acdl/.github/workflows/deploy.yml@v1.13
|
||||||
with:
|
with:
|
||||||
contract: .acdl/contract.yml
|
contract: .nova/contract.yml
|
||||||
```
|
```
|
||||||
|
|
||||||
That is the entire consumer-side workflow. When you push to `main`:
|
That is the entire consumer-side workflow. When you push to `main`:
|
||||||
|
|
||||||
1. The platform runner resolves `uses: acdl/.github/workflows/deploy.yml@v1.9`
|
1. The platform runner resolves `uses: acdl/.github/workflows/deploy.yml@v1.13`
|
||||||
to the reusable workflow **at the pinned tag**.
|
to the reusable workflow **at the pinned tag**.
|
||||||
2. A **platform-provided runner** checks out **your** repo.
|
2. A **platform-provided runner** checks out **your** repo.
|
||||||
3. The runner checks out the **ACDL platform repo** into the workspace —
|
3. The runner checks out the **Nova platform repo** into the workspace —
|
||||||
this is how the pipeline fetches the platform code at run time. You
|
this is how the pipeline fetches the platform code at run time. You
|
||||||
never clone the platform repo yourself.
|
never clone the platform repo yourself.
|
||||||
4. The runner installs the runtime dependencies the platform requires.
|
4. The runner installs the runtime dependencies the platform requires.
|
||||||
5. The runner invokes `scripts/run_platform.sh` against your
|
5. The runner invokes `scripts/run_platform.sh` against your
|
||||||
`.acdl/contract.yml`.
|
`.nova/contract.yml`.
|
||||||
|
|
||||||
You see the streamed output (infrastructure plan, policy-check results,
|
You see the streamed output (infrastructure plan, policy-check results,
|
||||||
confidence signal) in your run logs. The `--check-only` and `--plan-only`
|
confidence signal) in your run logs. The `--check-only` and `--plan-only`
|
||||||
@@ -203,7 +203,7 @@ hold for attestation).
|
|||||||
|
|
||||||
### Local validation (optional)
|
### Local validation (optional)
|
||||||
|
|
||||||
A consumer *may* clone the ACDL platform repo to run `--check-only` against
|
A consumer *may* clone the Nova platform repo to run `--check-only` against
|
||||||
their contract before pushing — this is optional and not required for the
|
their contract before pushing — this is optional and not required for the
|
||||||
happy path. If you do this, the runtime dependencies must be installed
|
happy path. If you do this, the runtime dependencies must be installed
|
||||||
locally, and any AWS credentials follow the
|
locally, and any AWS credentials follow the
|
||||||
@@ -213,7 +213,7 @@ static key in `.env.secrets` (gitignored) is rotated **out of band by you**
|
|||||||
locally-held copies.
|
locally-held copies.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
bash scripts/run_platform.sh --check-only path/to/your/.acdl/contract.yml
|
bash scripts/run_platform.sh --check-only path/to/your/.nova/contract.yml
|
||||||
```
|
```
|
||||||
|
|
||||||
## Step 5 — What the pipeline does
|
## Step 5 — What the pipeline does
|
||||||
@@ -326,8 +326,8 @@ per-module extension points. Common examples:
|
|||||||
| Contract schema | `schemas/contract.schema.json` | JSON Schema for consumer contracts. |
|
| Contract schema | `schemas/contract.schema.json` | JSON Schema for consumer contracts. |
|
||||||
| Stack schema | `schemas/stack.schema.json` | JSON Schema for the resolved stack instance. |
|
| Stack schema | `schemas/stack.schema.json` | JSON Schema for the resolved stack instance. |
|
||||||
| Module catalog | [modules/](modules/) | All primitives and modules. |
|
| Module catalog | [modules/](modules/) | All primitives and modules. |
|
||||||
| Sample contract | `contracts/static-assets.yaml` | The reference example contract (uses `@v1.9`). |
|
| Sample contract | `contracts/static-assets.yaml` | The reference example contract (uses `@v1.13`). |
|
||||||
| Sample contract | `contracts/microservice.yaml` | The microservice example contract (uses `@v1.9`). |
|
| Sample contract | `contracts/microservice.yaml` | The microservice example contract (uses `@v1.13`). |
|
||||||
| Module examples | `modules/<name>/examples/` | Validated per-module example contracts (`simple.yaml` + `complex.yaml`). |
|
| Module examples | `modules/<name>/examples/` | Validated per-module example contracts (`simple.yaml` + `complex.yaml`). |
|
||||||
| Contract resolver | `core/contract_resolver.py` | Resolves contracts to stack instances. |
|
| Contract resolver | `core/contract_resolver.py` | Resolves contracts to stack instances. |
|
||||||
| Angine adapter | `adapters/terraform/adapter.py` | Compiles stack instances to infrastructure. |
|
| Angine adapter | `adapters/terraform/adapter.py` | Compiles stack instances to infrastructure. |
|
||||||
@@ -345,7 +345,7 @@ process is a 2-step pipeline with **HITL SRE gates** to prevent accidental
|
|||||||
destruction:
|
destruction:
|
||||||
|
|
||||||
1. **Request a change request (CR):** Contact the platform team to create a
|
1. **Request a change request (CR):** Contact the platform team to create a
|
||||||
change request in the platform CMDB (DynamoDB `acdl-change-requests`
|
change request in the platform CMDB (DynamoDB `nova-change-requests`
|
||||||
table). The CR must be approved before decommission can proceed. The CR
|
table). The CR must be approved before decommission can proceed. The CR
|
||||||
includes the consumer repo, contract ID, and the reason for decommission.
|
includes the consumer repo, contract ID, and the reason for decommission.
|
||||||
|
|
||||||
@@ -353,9 +353,9 @@ destruction:
|
|||||||
use `mode: decommission` with the `changeRequestId` input:
|
use `mode: decommission` with the `changeRequestId` input:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
uses: acdl/.github/workflows/deploy.yml@v1.8
|
uses: acdl/.github/workflows/deploy.yml@v1.13
|
||||||
with:
|
with:
|
||||||
contract: .acdl/contract.yml
|
contract: .nova/contract.yml
|
||||||
mode: decommission
|
mode: decommission
|
||||||
changeRequestId: "CHG0678912"
|
changeRequestId: "CHG0678912"
|
||||||
```
|
```
|
||||||
@@ -395,7 +395,7 @@ separately (or left running to monitor the decommissioned stack's
|
|||||||
endpoints going dark).
|
endpoints going dark).
|
||||||
## Per-environment deployment
|
## Per-environment deployment
|
||||||
|
|
||||||
ACDL supports a **promotion-without-editing** model: you do not edit the
|
Nova supports a **promotion-without-editing** model: you do not edit the
|
||||||
`environment:` field in a contract to promote dev → qa → prod → dr.
|
`environment:` field in a contract to promote dev → qa → prod → dr.
|
||||||
Instead, there is **one CI job per environment**, each pointing at its
|
Instead, there is **one CI job per environment**, each pointing at its
|
||||||
respective contract (or the same contract + the `environment` workflow
|
respective contract (or the same contract + the `environment` workflow
|
||||||
@@ -404,8 +404,8 @@ input). Promotion = running the matching job.
|
|||||||
### Two shapes (both supported)
|
### Two shapes (both supported)
|
||||||
|
|
||||||
**Shape 1 — per-environment contract files:** a consumer repo has one
|
**Shape 1 — per-environment contract files:** a consumer repo has one
|
||||||
contract per environment (e.g. `.acdl/static-assets.dev.yml`,
|
contract per environment (e.g. `.nova/static-assets.dev.yml`,
|
||||||
`.acdl/static-assets.qa.yml`, …). Each sets `environment:` to its own
|
`.nova/static-assets.qa.yml`, …). Each sets `environment:` to its own
|
||||||
name and uses interpolation so env-specific values differ automatically:
|
name and uses interpolation so env-specific values differ automatically:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
@@ -421,7 +421,7 @@ name: static-assets
|
|||||||
```
|
```
|
||||||
|
|
||||||
**Shape 2 — single contract + `environment` workflow input:** the
|
**Shape 2 — single contract + `environment` workflow input:** the
|
||||||
reusable deploy workflow (`acdl/.github/workflows/deploy.yml@v1.9`)
|
reusable deploy workflow (`acdl/.github/workflows/deploy.yml@v1.13`)
|
||||||
declares an `environment` input. When non-empty, it overrides the
|
declares an `environment` input. When non-empty, it overrides the
|
||||||
contract's `environment` field at load time (before interpolation), so
|
contract's `environment` field at load time (before interpolation), so
|
||||||
the same contract can be promoted by passing a different environment:
|
the same contract can be promoted by passing a different environment:
|
||||||
@@ -436,10 +436,10 @@ on: workflow_dispatch:
|
|||||||
required: true
|
required: true
|
||||||
jobs:
|
jobs:
|
||||||
deploy-qa:
|
deploy-qa:
|
||||||
uses: acdl/.github/workflows/deploy.yml@v1.9
|
uses: acdl/.github/workflows/deploy.yml@v1.13
|
||||||
with:
|
with:
|
||||||
environment: qa
|
environment: qa
|
||||||
contract: .acdl/contract.yml
|
contract: .nova/contract.yml
|
||||||
```
|
```
|
||||||
|
|
||||||
### One job per environment
|
### One job per environment
|
||||||
@@ -467,7 +467,7 @@ duties check blocks a prod promotion when `approver_qa == approver_prod`
|
|||||||
| `${env.environment}` | the environment name (dev/qa/prod/dr) | `qa` |
|
| `${env.environment}` | the environment name (dev/qa/prod/dr) | `qa` |
|
||||||
| `${env.region}` | the environment's AWS region | `us-east-1` |
|
| `${env.region}` | the environment's AWS region | `us-east-1` |
|
||||||
| `${env.account_id}` | the environment's AWS account id | `123456789012` |
|
| `${env.account_id}` | the environment's AWS account id | `123456789012` |
|
||||||
| `${env.state_backend.bucket}` | the environment's state bucket | `acdl-qa-state` |
|
| `${env.state_backend.bucket}` | the environment's state bucket | `nova-qa-state` |
|
||||||
| `${env.network.vpc_cidr}` | the environment's VPC CIDR | `10.1.0.0/16` |
|
| `${env.network.vpc_cidr}` | the environment's VPC CIDR | `10.1.0.0/16` |
|
||||||
| `${contract.id}` | the contract's operational acronym | `assets` |
|
| `${contract.id}` | the contract's operational acronym | `assets` |
|
||||||
| `${contract.environment}` | the contract's environment field | `qa` |
|
| `${contract.environment}` | the contract's environment field | `qa` |
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ deploys it.
|
|||||||
|
|
||||||
## The contract file
|
## The contract file
|
||||||
|
|
||||||
A consumer repo keeps its contract at `.acdl/contract.yml`. A minimal
|
A consumer repo keeps its contract at `.nova/contract.yml`. A minimal
|
||||||
example (the `static-assets` module):
|
example (the `static-assets` module):
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
@@ -57,7 +57,7 @@ infrastructure:
|
|||||||
## Validation
|
## Validation
|
||||||
|
|
||||||
The contract is validated against
|
The contract is validated against
|
||||||
[`schemas/contract.schema.json`](https://github.com/acdl/acdl/blob/main/schemas/contract.schema.json).
|
[`schemas/contract.schema.json`](https://github.com/nova/nova/blob/main/schemas/contract.schema.json).
|
||||||
An invalid contract (missing field, unknown module, wrong type) fails at the
|
An invalid contract (missing field, unknown module, wrong type) fails at the
|
||||||
validate-contract stage with a clear error.
|
validate-contract stage with a clear error.
|
||||||
|
|
||||||
@@ -65,9 +65,9 @@ validate-contract stage with a clear error.
|
|||||||
|
|
||||||
Two reference examples exist in `contracts/`:
|
Two reference examples exist in `contracts/`:
|
||||||
|
|
||||||
- [`contracts/static-assets.yml`](https://github.com/acdl/acdl/blob/main/contracts/static-assets.yml)
|
- [`contracts/static-assets.yml`](https://github.com/nova/nova/blob/main/contracts/static-assets.yml)
|
||||||
— the `static-assets` module.
|
— the `static-assets` module.
|
||||||
- [`contracts/microservice.yml`](https://github.com/acdl/acdl/blob/main/contracts/microservice.yml)
|
- [`contracts/microservice.yml`](https://github.com/nova/nova/blob/main/contracts/microservice.yml)
|
||||||
— the `microservice` module.
|
— the `microservice` module.
|
||||||
|
|
||||||
Additionally, every module has a `modules/<name>/examples/` directory with
|
Additionally, every module has a `modules/<name>/examples/` directory with
|
||||||
|
|||||||
@@ -56,13 +56,13 @@ threshold. Staging does not exist.
|
|||||||
## Cross-account contract ingestion grant (D-051)
|
## Cross-account contract ingestion grant (D-051)
|
||||||
|
|
||||||
Onboarding now also grants the consumer repo's deploy role permission to
|
Onboarding now also grants the consumer repo's deploy role permission to
|
||||||
invoke the **platform Lambda** — `acdl-contract-ingestor` — across
|
invoke the **platform Lambda** — `nova-contract-ingestor` — across
|
||||||
accounts. The Lambda is invoked via a Function URL with IAM auth, so the
|
accounts. The Lambda is invoked via a Function URL with IAM auth, so the
|
||||||
grant is an inline IAM policy applied to the consumer's deploy role. The
|
grant is an inline IAM policy applied to the consumer's deploy role. The
|
||||||
policy template lives at
|
policy template lives at
|
||||||
[`terraform/platform/consumer_invoke_policy.json`](https://github.com/acdl/acdl/blob/main/terraform/platform/consumer_invoke_policy.json)
|
[`terraform/platform/consumer_invoke_policy.json`](https://github.com/nova/nova/blob/main/terraform/platform/consumer_invoke_policy.json)
|
||||||
and is scoped via **ABAC**: the condition
|
and is scoped via **ABAC**: the condition
|
||||||
`aws:PrincipalTag/acdl:owner == ${consumerRepo}` ensures a repo can only
|
`aws:PrincipalTag/nova:owner == ${consumerRepo}` ensures a repo can only
|
||||||
invoke the Lambda when its principal tag matches its claimed identity.
|
invoke the Lambda when its principal tag matches its claimed identity.
|
||||||
|
|
||||||
The consumer's deploy workflow signs the Function URL request with
|
The consumer's deploy workflow signs the Function URL request with
|
||||||
@@ -75,7 +75,7 @@ is used for two purposes:
|
|||||||
|
|
||||||
1. **Contract ingestion** — the consumer submits its resolved deployment
|
1. **Contract ingestion** — the consumer submits its resolved deployment
|
||||||
contract (`action: "submit_contract"`) so the platform has a durable
|
contract (`action: "submit_contract"`) so the platform has a durable
|
||||||
record in the `acdl-contracts` DynamoDB table (PK `consumerRepo`, SK
|
record in the `nova-contracts` DynamoDB table (PK `consumerRepo`, SK
|
||||||
`contractId#submittedAt`).
|
`contractId#submittedAt`).
|
||||||
2. **Error reporting** (D-055) — the consumer reports a deployment error
|
2. **Error reporting** (D-055) — the consumer reports a deployment error
|
||||||
(`action: "report_error"`) which the platform turns into a GitHub
|
(`action: "report_error"`) which the platform turns into a GitHub
|
||||||
@@ -83,16 +83,16 @@ is used for two purposes:
|
|||||||
prepared-status stub until then).
|
prepared-status stub until then).
|
||||||
|
|
||||||
The Lambda handler and the Terraform that deploys it live in
|
The Lambda handler and the Terraform that deploys it live in
|
||||||
[`core/lambda/contract_ingestor.py`](https://github.com/acdl/acdl/blob/main/core/lambda/contract_ingestor.py)
|
[`core/lambda/contract_ingestor.py`](https://github.com/nova/nova/blob/main/core/lambda/contract_ingestor.py)
|
||||||
and
|
and
|
||||||
[`terraform/platform/main.tf`](https://github.com/acdl/acdl/blob/main/terraform/platform/main.tf)
|
[`terraform/platform/main.tf`](https://github.com/nova/nova/blob/main/terraform/platform/main.tf)
|
||||||
respectively.
|
respectively.
|
||||||
|
|
||||||
## Onboarding scaffold (current state)
|
## Onboarding scaffold (current state)
|
||||||
|
|
||||||
The platform repo ships a minimal onboarding scaffold:
|
The platform repo ships a minimal onboarding scaffold:
|
||||||
|
|
||||||
- [`core/environments/`](https://github.com/acdl/acdl/blob/main/core/environments/)
|
- [`core/environments/`](https://github.com/nova/nova/blob/main/core/environments/)
|
||||||
— environment definitions (a sample `dev.json`).
|
— environment definitions (a sample `dev.json`).
|
||||||
- `core/environment_check.py` — checks whether an environment is defined for
|
- `core/environment_check.py` — checks whether an environment is defined for
|
||||||
a given contract's repo + environment name; prints the friendly onboarding
|
a given contract's repo + environment name; prints the friendly onboarding
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
# ACDL — Agentic Cloud Delivery Platform
|
# Nova
|
||||||
|
|
||||||
|
> **Nova — The New Dawn of DevSecOps.** Security as a seamless enabler of fast deployments — not a bottleneck, not a "no" department.
|
||||||
|
|
||||||
Consumers declare intent; the platform delivers safe production deployment
|
Consumers declare intent; the platform delivers safe production deployment
|
||||||
through an agentic stack — automatically, safely, and with a complete audit
|
through an agentic stack — automatically, safely, and with a complete audit
|
||||||
@@ -9,14 +11,14 @@ a configuration file, or an infrastructure module.
|
|||||||
|
|
||||||
## Two repositories
|
## Two repositories
|
||||||
|
|
||||||
There are two kinds of repository in the ACDL model:
|
There are two kinds of repository in the Nova model:
|
||||||
|
|
||||||
- **Platform repo (this one).** The source code of the platform. It owns
|
- **Platform repo (this one).** The source code of the platform. It owns
|
||||||
`modules/`, `adapters/`, `core/`, `schemas/`, `pipelines/`, `scripts/`,
|
`modules/`, `adapters/`, `core/`, `schemas/`, `pipelines/`, `scripts/`,
|
||||||
and the reusable workflow files. Platform engineers work here. A consumer
|
and the reusable workflow files. Platform engineers work here. A consumer
|
||||||
never clones it.
|
never clones it.
|
||||||
- **Consumer repo (yours).** A consumer repo contains only its application
|
- **Consumer repo (yours).** A consumer repo contains only its application
|
||||||
code, one or more contracts (`.acdl/contract.yml`), and one or more CI
|
code, one or more contracts (`.nova/contract.yml`), and one or more CI
|
||||||
definitions (a thin `.github/workflows/deploy.yml` that `uses:` the central
|
definitions (a thin `.github/workflows/deploy.yml` that `uses:` the central
|
||||||
reusable workflow, pointing at the appropriate environment + contract).
|
reusable workflow, pointing at the appropriate environment + contract).
|
||||||
The consumer does not write infrastructure modules, workflow YAML, or
|
The consumer does not write infrastructure modules, workflow YAML, or
|
||||||
@@ -75,4 +77,11 @@ Planned future features (no dates; tracked in the internal roadmap):
|
|||||||
|
|
||||||
- [Consumer Guide](consumer-guide) — start here if you are a consumer.
|
- [Consumer Guide](consumer-guide) — start here if you are a consumer.
|
||||||
- [Architecture](architecture) — start here if you are a platform engineer.
|
- [Architecture](architecture) — start here if you are a platform engineer.
|
||||||
- The [README](https://github.com/acdl/acdl) describes the platform repo.
|
- The [README](https://github.com/nova/nova) describes the platform repo.
|
||||||
|
|
||||||
|
> **Note:** The product brand is **Nova** (formerly ACDL — Agentic Cloud
|
||||||
|
> Delivery Platform). The Gitea repository name (`continuous-intelligence/acdl`)
|
||||||
|
> and the GitHub `uses:` reference (`acdl/.github/workflows/deploy.yml@…`)
|
||||||
|
> are unchanged during the rebrand transition; only the product name is
|
||||||
|
> changing. See the [Nova migration guide](NOVA_MIGRATION) for the
|
||||||
|
> scheduled breaking changes.
|
||||||
@@ -16,28 +16,28 @@ module's README documents which resources it creates.
|
|||||||
|
|
||||||
| Module | What it creates | Source |
|
| Module | What it creates | Source |
|
||||||
|--------|----------------|--------|
|
|--------|----------------|--------|
|
||||||
| `s3` | `aws_s3_bucket` — a single S3 bucket | [modules/l1/s3/README.md](https://github.com/acdl/acdl/blob/main/modules/l1/s3/README.md) |
|
| `s3` | `aws_s3_bucket` — a single S3 bucket | [modules/l1/s3/README.md](https://github.com/nova/nova/blob/main/modules/l1/s3/README.md) |
|
||||||
| `vpc` | `aws_vpc` + `aws_subnet` + `aws_route_table` + `aws_internet_gateway` — VPC with subnets and routing | [modules/l1/vpc/README.md](https://github.com/acdl/acdl/blob/main/modules/l1/vpc/README.md) |
|
| `vpc` | `aws_vpc` + `aws_subnet` + `aws_route_table` + `aws_internet_gateway` — VPC with subnets and routing | [modules/l1/vpc/README.md](https://github.com/nova/nova/blob/main/modules/l1/vpc/README.md) |
|
||||||
| `ecs-cluster` | `aws_ecs_cluster` — ECS Fargate cluster | [modules/l1/ecs-cluster/README.md](https://github.com/acdl/acdl/blob/main/modules/l1/ecs-cluster/README.md) |
|
| `ecs-cluster` | `aws_ecs_cluster` — ECS Fargate cluster | [modules/l1/ecs-cluster/README.md](https://github.com/nova/nova/blob/main/modules/l1/ecs-cluster/README.md) |
|
||||||
| `ecs-service` | `aws_ecs_task_definition` + `aws_ecs_service` — Fargate service with task definition | [modules/l1/ecs-service/README.md](https://github.com/acdl/acdl/blob/main/modules/l1/ecs-service/README.md) |
|
| `ecs-service` | `aws_ecs_task_definition` + `aws_ecs_service` — Fargate service with task definition | [modules/l1/ecs-service/README.md](https://github.com/nova/nova/blob/main/modules/l1/ecs-service/README.md) |
|
||||||
| `iam-role` | `aws_iam_role` — IAM role with assume-role policy | [modules/l1/iam-role/README.md](https://github.com/acdl/acdl/blob/main/modules/l1/iam-role/README.md) |
|
| `iam-role` | `aws_iam_role` — IAM role with assume-role policy | [modules/l1/iam-role/README.md](https://github.com/nova/nova/blob/main/modules/l1/iam-role/README.md) |
|
||||||
| `alb` | `aws_lb` + `aws_lb_target_group` + `aws_lb_listener` — Application Load Balancer | [modules/l1/alb/README.md](https://github.com/acdl/acdl/blob/main/modules/l1/alb/README.md) |
|
| `alb` | `aws_lb` + `aws_lb_target_group` + `aws_lb_listener` — Application Load Balancer | [modules/l1/alb/README.md](https://github.com/nova/nova/blob/main/modules/l1/alb/README.md) |
|
||||||
| `ecr` | `aws_ecr_repository` — ECR container image repository | [modules/l1/ecr/README.md](https://github.com/acdl/acdl/blob/main/modules/l1/ecr/README.md) |
|
| `ecr` | `aws_ecr_repository` — ECR container image repository | [modules/l1/ecr/README.md](https://github.com/nova/nova/blob/main/modules/l1/ecr/README.md) |
|
||||||
| `cloudfront` | `aws_cloudfront_distribution` + `aws_cloudfront_origin_access_control` — CloudFront distribution with S3 origin via OAC | [modules/l1/cloudfront/README.md](https://github.com/acdl/acdl/blob/main/modules/l1/cloudfront/README.md) |
|
| `cloudfront` | `aws_cloudfront_distribution` + `aws_cloudfront_origin_access_control` — CloudFront distribution with S3 origin via OAC | [modules/l1/cloudfront/README.md](https://github.com/nova/nova/blob/main/modules/l1/cloudfront/README.md) |
|
||||||
| `waf` | `aws_wafv2_web_acl` — WAFv2 Web ACL (CloudFront-scoped) | [modules/l1/waf/README.md](https://github.com/acdl/acdl/blob/main/modules/l1/waf/README.md) |
|
| `waf` | `aws_wafv2_web_acl` — WAFv2 Web ACL (CloudFront-scoped) | [modules/l1/waf/README.md](https://github.com/nova/nova/blob/main/modules/l1/waf/README.md) |
|
||||||
| `rds` | `aws_db_instance` — RDS database instance (multi-engine: postgres, mysql, etc.) | [modules/l1/rds/README.md](https://github.com/acdl/acdl/blob/main/modules/l1/rds/README.md) |
|
| `rds` | `aws_db_instance` — RDS database instance (multi-engine: postgres, mysql, etc.) | [modules/l1/rds/README.md](https://github.com/nova/nova/blob/main/modules/l1/rds/README.md) |
|
||||||
|
|
||||||
## Modules
|
## Modules
|
||||||
|
|
||||||
| Module | What it references | Source |
|
| Module | What it references | Source |
|
||||||
|--------|--------------------|--------|
|
|--------|--------------------|--------|
|
||||||
| `static-assets` | 3 primitives (s3, cloudfront, waf) — a production static asset stack | [modules/l2/static-assets/README.md](https://github.com/acdl/acdl/blob/main/modules/l2/static-assets/README.md) |
|
| `static-assets` | 3 primitives (s3, cloudfront, waf) — a production static asset stack | [modules/l2/static-assets/README.md](https://github.com/nova/nova/blob/main/modules/l2/static-assets/README.md) |
|
||||||
| `microservice` | 6 primitives (vpc, cluster, ecr, iam-role, alb, ecs-service) — an ECS Fargate microservice | [modules/l2/microservice/README.md](https://github.com/acdl/acdl/blob/main/modules/l2/microservice/README.md) |
|
| `microservice` | 6 primitives (vpc, cluster, ecr, iam-role, alb, ecs-service) — an ECS Fargate microservice | [modules/l2/microservice/README.md](https://github.com/nova/nova/blob/main/modules/l2/microservice/README.md) |
|
||||||
|
|
||||||
## Registry
|
## Registry
|
||||||
|
|
||||||
Module versions are tracked in
|
Module versions are tracked in
|
||||||
[`registry.json`](https://github.com/acdl/acdl/blob/main/modules/registry.json).
|
[`registry.json`](https://github.com/nova/nova/blob/main/modules/registry.json).
|
||||||
Both primitives and modules are registered.
|
Both primitives and modules are registered.
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
@@ -45,7 +45,7 @@ Both primitives and modules are registered.
|
|||||||
Each module has a `examples/` directory containing validated consumer
|
Each module has a `examples/` directory containing validated consumer
|
||||||
contract examples (`simple.yaml` + `complex.yaml` + variation files). The
|
contract examples (`simple.yaml` + `complex.yaml` + variation files). The
|
||||||
platform-test pipeline validates them against
|
platform-test pipeline validates them against
|
||||||
[`schemas/contract.schema.json`](https://github.com/acdl/acdl/blob/main/schemas/contract.schema.json).
|
[`schemas/contract.schema.json`](https://github.com/nova/nova/blob/main/schemas/contract.schema.json).
|
||||||
See each module's `## Examples` section for the excerpts.
|
See each module's `## Examples` section for the excerpts.
|
||||||
|
|
||||||
## Versioning
|
## Versioning
|
||||||
|
|||||||
@@ -6,9 +6,9 @@ are the single source of truth for the workflow files.
|
|||||||
## CI pipeline
|
## CI pipeline
|
||||||
|
|
||||||
The CI pipeline runs on every push and pull request to `main`. It is defined
|
The CI pipeline runs on every push and pull request to `main`. It is defined
|
||||||
by [`pipelines/ci.yml`](https://github.com/acdl/acdl/blob/main/pipelines/ci.yml),
|
by [`pipelines/ci.yml`](https://github.com/nova/nova/blob/main/pipelines/ci.yml),
|
||||||
validated against
|
validated against
|
||||||
[`schemas/pipeline.schema.json`](https://github.com/acdl/acdl/blob/main/schemas/pipeline.schema.json).
|
[`schemas/pipeline.schema.json`](https://github.com/nova/nova/blob/main/schemas/pipeline.schema.json).
|
||||||
Both platform-runner workflow files implement the same contract and are
|
Both platform-runner workflow files implement the same contract and are
|
||||||
byte-identical:
|
byte-identical:
|
||||||
|
|
||||||
@@ -31,16 +31,16 @@ bash scripts/run_ci.sh --quiet # suppress per-stage banners
|
|||||||
## Deployment pipeline
|
## Deployment pipeline
|
||||||
|
|
||||||
The deployment pipeline runs when a consumer submits a contract. It is
|
The deployment pipeline runs when a consumer submits a contract. It is
|
||||||
defined by [`pipelines/contract.yml`](https://github.com/acdl/acdl/blob/main/pipelines/contract.yml),
|
defined by [`pipelines/contract.yml`](https://github.com/nova/nova/blob/main/pipelines/contract.yml),
|
||||||
validated against
|
validated against
|
||||||
[`schemas/deploy-pipeline.schema.json`](https://github.com/acdl/acdl/blob/main/schemas/deploy-pipeline.schema.json).
|
[`schemas/deploy-pipeline.schema.json`](https://github.com/nova/nova/blob/main/schemas/deploy-pipeline.schema.json).
|
||||||
It is exposed to consumer repos as a **reusable workflow**:
|
It is exposed to consumer repos as a **reusable workflow**:
|
||||||
|
|
||||||
- `.github/workflows/deploy.yml` — GitHub Actions (production)
|
- `.github/workflows/deploy.yml` — GitHub Actions (production)
|
||||||
|
|
||||||
A consumer repo invokes the reusable workflow via a **versioned tag**
|
A consumer repo invokes the reusable workflow via a **versioned tag**
|
||||||
(floating MAJOR + MINOR, e.g. `acdl/.github/workflows/deploy.yml@v1.6`).
|
(floating MAJOR + MINOR, e.g. `acdl/.github/workflows/deploy.yml@v1.13`).
|
||||||
The workflow checks out the consumer repo, then checks out the ACDL platform
|
The workflow checks out the consumer repo, then checks out the Nova platform
|
||||||
repo into the runner workspace, and runs `scripts/run_platform.sh` against
|
repo into the runner workspace, and runs `scripts/run_platform.sh` against
|
||||||
the consumer's contract. The consumer never clones the platform repo or
|
the consumer's contract. The consumer never clones the platform repo or
|
||||||
invokes its scripts locally. See the [Consumer Guide](../consumer-guide/)
|
invokes its scripts locally. See the [Consumer Guide](../consumer-guide/)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Versioning
|
# Versioning
|
||||||
|
|
||||||
ACDL uses two versioning schemes: one for modules, one for the deploy
|
Nova uses two versioning schemes: one for modules, one for the deploy
|
||||||
pipeline. Both matter to a consumer.
|
pipeline. Both matter to a consumer.
|
||||||
|
|
||||||
## Module versioning
|
## Module versioning
|
||||||
@@ -16,7 +16,7 @@ old entry enters a **12-month deprecation window**. A module pins its
|
|||||||
primitives by `name@semver`; the resolver picks the highest compatible.
|
primitives by `name@semver`; the resolver picks the highest compatible.
|
||||||
|
|
||||||
Module versions are tracked in
|
Module versions are tracked in
|
||||||
[`registry.json`](https://github.com/acdl/acdl/blob/main/modules/registry.json).
|
[`registry.json`](https://github.com/nova/nova/blob/main/modules/registry.json).
|
||||||
|
|
||||||
## Deploy-pipeline versioning (the CI workflow `uses:` tag)
|
## Deploy-pipeline versioning (the CI workflow `uses:` tag)
|
||||||
|
|
||||||
@@ -26,9 +26,9 @@ tag** in a consumer's CI workflow definition:
|
|||||||
```yaml
|
```yaml
|
||||||
jobs:
|
jobs:
|
||||||
deploy:
|
deploy:
|
||||||
uses: acdl/.github/workflows/deploy.yml@v1.6
|
uses: acdl/.github/workflows/deploy.yml@v1.13
|
||||||
with:
|
with:
|
||||||
contract: .acdl/contract.yml
|
contract: .nova/contract.yml
|
||||||
```
|
```
|
||||||
|
|
||||||
The version pin lives in the CI workflow reference (not in the contract
|
The version pin lives in the CI workflow reference (not in the contract
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Presentations
|
# Presentations
|
||||||
|
|
||||||
Leadership-facing presentation decks for the ACDL platform.
|
Leadership-facing presentation decks for the Nova platform.
|
||||||
|
|
||||||
## The 4-step slide creation process
|
## The 4-step slide creation process
|
||||||
|
|
||||||
@@ -61,9 +61,7 @@ Synthesize the full markdown into a lean Marp deck:
|
|||||||
- **`<!-- _class: title -->` + `<!-- _paginate: false -->`** on title and
|
- **`<!-- _class: title -->` + `<!-- _paginate: false -->`** on title and
|
||||||
closing slides for the dark-background title style.
|
closing slides for the dark-background title style.
|
||||||
- **Maturity badges** using inline spans:
|
- **Maturity badges** using inline spans:
|
||||||
`<span class="badge testing">Testing</span>`
|
|
||||||
`<span class="badge planned">Planned</span>`
|
`<span class="badge planned">Planned</span>`
|
||||||
`<span class="badge agentic">Agentic</span>`
|
|
||||||
- **Tighter prose** than Step 1 — strip the speaker-note nuance; keep the
|
- **Tighter prose** than Step 1 — strip the speaker-note nuance; keep the
|
||||||
leadership-relevant selling points.
|
leadership-relevant selling points.
|
||||||
|
|
||||||
@@ -115,7 +113,7 @@ Distill the source of truth (Step 1) into presenter-ready cues, indexed by
|
|||||||
the Marp deck (Step 2) slide structure:
|
the Marp deck (Step 2) slide structure:
|
||||||
|
|
||||||
- **One section per Marp slide** — `## Slide N — Title`, matching the Marp
|
- **One section per Marp slide** — `## Slide N — Title`, matching the Marp
|
||||||
deck's 10 main + Appendix TOC + appendix slide structure exactly. The Marp deck
|
deck's 11 main + Appendix TOC + appendix slide structure exactly. The Marp deck
|
||||||
provides the indexing and context (what the audience sees); the source
|
provides the indexing and context (what the audience sees); the source
|
||||||
markdown provides the content (the speaker notes, the detail, the nuance).
|
markdown provides the content (the speaker notes, the detail, the nuance).
|
||||||
- **3-6 talking point bullets per slide** — punchy, actionable cues distilled
|
- **3-6 talking point bullets per slide** — punchy, actionable cues distilled
|
||||||
@@ -145,13 +143,13 @@ and re-distill.
|
|||||||
docs/presentations/
|
docs/presentations/
|
||||||
├── README.md ← this file
|
├── README.md ← this file
|
||||||
├── how-the-platform-works.md ← Step 1: full source of truth
|
├── how-the-platform-works.md ← Step 1: full source of truth
|
||||||
├── how-the-platform-works-marp.md ← Step 2: Marp deck (10 main + TOC + 8 appendix = 19)
|
├── how-the-platform-works-marp.md ← Step 2: Marp deck (11 main + TOC + 8 appendix = 20)
|
||||||
├── how-the-platform-works.html ← Step 3: rendered HTML (committed)
|
├── how-the-platform-works.html ← Step 3: rendered HTML (committed)
|
||||||
├── how-the-platform-works-talking-points.md ← Step 4: presenter cues (19 sections)
|
├── how-the-platform-works-talking-points.md ← Step 4: presenter cues (20 sections)
|
||||||
├── the-developer-experience.md ← Step 1: full source of truth
|
├── the-developer-experience.md ← Step 1: full source of truth
|
||||||
├── the-developer-experience-marp.md ← Step 2: Marp deck (10 main + TOC + 7 appendix = 18)
|
├── the-developer-experience-marp.md ← Step 2: Marp deck (11 main + TOC + 7 appendix = 19)
|
||||||
├── the-developer-experience.html ← Step 3: rendered HTML (committed)
|
├── the-developer-experience.html ← Step 3: rendered HTML (committed)
|
||||||
├── the-developer-experience-talking-points.md ← Step 4: presenter cues (18 sections)
|
├── the-developer-experience-talking-points.md ← Step 4: presenter cues (19 sections)
|
||||||
└── assets/
|
└── assets/
|
||||||
├── puppeteer-config.json ← no-sandbox config for mmdc
|
├── puppeteer-config.json ← no-sandbox config for mmdc
|
||||||
├── mmd/ ← mermaid source files (Step 2 input)
|
├── mmd/ ← mermaid source files (Step 2 input)
|
||||||
@@ -171,6 +169,7 @@ docs/presentations/
|
|||||||
│ ├── developer-experience-05-catalog.mmd
|
│ ├── developer-experience-05-catalog.mmd
|
||||||
│ ├── developer-experience-07-decommission.mmd
|
│ ├── developer-experience-07-decommission.mmd
|
||||||
│ ├── developer-experience-08-semver.mmd
|
│ ├── developer-experience-08-semver.mmd
|
||||||
|
│ ├── platform-architecture.mmd ← shared high-level logical architecture (both decks)
|
||||||
│ └── road-to-north-star.mmd
|
│ └── road-to-north-star.mmd
|
||||||
└── png/ ← rendered PNGs (embedded in Marp)
|
└── png/ ← rendered PNGs (embedded in Marp)
|
||||||
├── platform-works-01-contract-driven.png
|
├── platform-works-01-contract-driven.png
|
||||||
@@ -188,6 +187,7 @@ docs/presentations/
|
|||||||
├── developer-experience-05-catalog.png
|
├── developer-experience-05-catalog.png
|
||||||
├── developer-experience-07-decommission.png
|
├── developer-experience-07-decommission.png
|
||||||
├── developer-experience-08-semver.png
|
├── developer-experience-08-semver.png
|
||||||
|
├── platform-architecture.png ← shared high-level logical architecture (both decks)
|
||||||
└── road-to-north-star.png
|
└── road-to-north-star.png
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -195,12 +195,12 @@ docs/presentations/
|
|||||||
|
|
||||||
### Appendix structure
|
### Appendix structure
|
||||||
|
|
||||||
Each Marp deck has **10 main slides + an Appendix TOC + appendix slides**. The
|
Each Marp deck has **11 main slides + an Appendix TOC + appendix slides**. The
|
||||||
main 10 are the presentation; the appendix is for deep dives and Q&A backup.
|
main 11 are the presentation; the appendix is for deep dives and Q&A backup.
|
||||||
The platform-works deck has 8 appendix slides (A1–A8); the developer-experience
|
The platform-works deck has 8 appendix slides (A1–A8); the developer-experience
|
||||||
deck has 7 appendix slides (A1–A7). Both include an Appendix TOC slide.
|
deck has 7 appendix slides (A1–A7). Both include an Appendix TOC slide.
|
||||||
|
|
||||||
- **Main slides** (1-10): the story arc, high-impact, minimal text,
|
- **Main slides** (1-11): the story arc, high-impact, minimal text,
|
||||||
visual-heavy. These are what the audience sees during the talk.
|
visual-heavy. These are what the audience sees during the talk.
|
||||||
- **Appendix slides** (TOC + A1..An): detail-heavy slides moved out of the
|
- **Appendix slides** (TOC + A1..An): detail-heavy slides moved out of the
|
||||||
main 10 to preserve the narrative flow. The appendix starts with a TOC
|
main 10 to preserve the narrative flow. The appendix starts with a TOC
|
||||||
@@ -213,13 +213,11 @@ deck has 7 appendix slides (A1–A7). Both include an Appendix TOC slide.
|
|||||||
|
|
||||||
### Maturity framing
|
### Maturity framing
|
||||||
|
|
||||||
Every capability claim in a deck is tagged with one of three badges:
|
Every capability claim in a deck is tagged with a `Planned` badge when the item is on the roadmap but not yet implemented:
|
||||||
|
|
||||||
| Badge | Meaning |
|
| Badge | Meaning |
|
||||||
|---|---|
|
|---|---|
|
||||||
| `Testing` | Works internally, not yet released to consumers (0 adoption) |
|
|
||||||
| `Planned` | On the roadmap, not yet implemented |
|
| `Planned` | On the roadmap, not yet implemented |
|
||||||
| `Agentic` | Involves AI agents, autonomous decision-making, or the citizen developer flow |
|
|
||||||
|
|
||||||
This is non-negotiable for a leadership audience: never present a roadmap
|
This is non-negotiable for a leadership audience: never present a roadmap
|
||||||
item as a current capability, and never bury a tested capability's
|
item as a current capability, and never bury a tested capability's
|
||||||
@@ -345,5 +343,5 @@ attachments to the Gitea release.
|
|||||||
|
|
||||||
| Deck | Source of truth (Step 1) | Marp deck (Step 2) | Rendered HTML (Step 3) | Talking points (Step 4) | Slides | Audience |
|
| Deck | Source of truth (Step 1) | Marp deck (Step 2) | Rendered HTML (Step 3) | Talking points (Step 4) | Slides | Audience |
|
||||||
|---|---|---|---|---|---|---|
|
|---|---|---|---|---|---|---|
|
||||||
| How the Platform Works | `how-the-platform-works.md` | `how-the-platform-works-marp.md` | `how-the-platform-works.html` | `how-the-platform-works-talking-points.md` | 10 main + TOC + 8 appendix (19) | CTO, Head of Cloud, Head of Infra, Head of DevOps |
|
| How the Platform Works | `how-the-platform-works.md` | `how-the-platform-works-marp.md` | `how-the-platform-works.html` | `how-the-platform-works-talking-points.md` | 11 main + TOC + 8 appendix (20) | CTO, Head of Cloud, Head of Infra, Head of DevOps |
|
||||||
| The Developer Experience | `the-developer-experience.md` | `the-developer-experience-marp.md` | `the-developer-experience.html` | `the-developer-experience-talking-points.md` | 10 main + TOC + 7 appendix (18) | CTO, Head of Cloud, Head of Infra, Head of DevOps |
|
| The Developer Experience | `the-developer-experience.md` | `the-developer-experience-marp.md` | `the-developer-experience.html` | `the-developer-experience-talking-points.md` | 11 main + TOC + 7 appendix (19) | CTO, Head of Cloud, Head of Infra, Head of DevOps |
|
||||||
@@ -6,7 +6,7 @@ flowchart LR
|
|||||||
A["Technical dev\n(app code + contract)"]
|
A["Technical dev\n(app code + contract)"]
|
||||||
B["Citizen dev\n(intent → AI agent\n→ contract)"]
|
B["Citizen dev\n(intent → AI agent\n→ contract)"]
|
||||||
end
|
end
|
||||||
subgraph ACDL ["ACDL — infrastructure only"]
|
subgraph ACDL ["Nova — infrastructure only"]
|
||||||
C["Same contract\nSame pipeline\nSame safety"]
|
C["Same contract\nSame pipeline\nSame safety"]
|
||||||
D["Provision\nAWS resources"]
|
D["Provision\nAWS resources"]
|
||||||
E["Evidence\nhash-chained"]
|
E["Evidence\nhash-chained"]
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
flowchart LR
|
flowchart LR
|
||||||
A["1. App code<br/>(top level of the repo)"] --> D["Push to main"]
|
A["1. App code<br/>(top level of the repo)"] --> D["Push to main"]
|
||||||
B["2. Contract<br/>(.acdl/contract.yml)"] --> D
|
B["2. Contract<br/>(.nova/contract.yml)"] --> D
|
||||||
C["3. CI definition<br/>(.github/workflows/deploy.yml<br/>— one 'uses:' line)"] --> D
|
C["3. CI definition<br/>(.github/workflows/deploy.yml<br/>— one 'uses:' line)"] --> D
|
||||||
D --> E["Platform does the rest"]
|
D --> E["Platform does the rest"]
|
||||||
classDef accent fill:#1B1B1B,color:#fff,stroke:#D6002A,stroke-width:2px
|
classDef accent fill:#1B1B1B,color:#fff,stroke:#D6002A,stroke-width:2px
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
flowchart LR
|
flowchart LR
|
||||||
A["Consumer repo<br/>app + contract + 'uses:'"] -->|triggers on push to main| B["Platform runner"]
|
A["Consumer repo<br/>app + contract + 'uses:'"] -->|triggers on push to main| B["Platform runner"]
|
||||||
B -->|checks out the consumer repo| A
|
B -->|checks out the consumer repo| A
|
||||||
B -->|checks out the ACDL platform repo<br/>into the workspace| C["Platform code<br/>(modules, adapters, schemas)"]
|
B -->|checks out the Nova platform repo<br/>into the workspace| C["Platform code<br/>(modules, adapters, schemas)"]
|
||||||
C --> B
|
C --> B
|
||||||
B -->|runs the pipeline against<br/>the consumer's contract| D["Consumer's resources in AWS"]
|
B -->|runs the pipeline against<br/>the consumer's contract| D["Consumer's resources in AWS"]
|
||||||
classDef accent fill:#1B1B1B,color:#fff,stroke:#D6002A,stroke-width:2px
|
classDef accent fill:#1B1B1B,color:#fff,stroke:#D6002A,stroke-width:2px
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
%%{init: {"theme": "base", "themeVariables": {"primaryColor": "#1B1B1B", "primaryBorderColor": "#D6002A", "primaryTextColor": "#fff", "secondaryColor": "#fff", "secondaryBorderColor": "#D6002A", "secondaryTextColor": "#1B1B1B", "tertiaryColor": "#F0F0F0", "clusterBkg": "#F0F0F0", "lineColor": "#1B1B1B", "fontFamily": "\"Akkurat Pro\", \"Helvetica Neue\", \"Arial\", sans-serif"}}}%%
|
||||||
|
|
||||||
|
flowchart TD
|
||||||
|
subgraph UP ["Consumer surfaces — upstream"]
|
||||||
|
direction LR
|
||||||
|
U1["Technical dev\napp code + contract"]
|
||||||
|
U2["Citizen dev\nintent → AI agent → contract"]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph ACDL ["Nova — infrastructure only"]
|
||||||
|
direction TB
|
||||||
|
CS["Contract schema\n(validate + fail-fast)"]
|
||||||
|
subgraph PIPE ["Central pipeline — fixed stages, every deployment"]
|
||||||
|
direction LR
|
||||||
|
P1["Validate"] --> P2["Resolve\ntarget stack"] --> P3["Security\nchecks"] --> P4["Infra plan"] --> P5["Policy\nchecks"] --> P6["Confidence\nsignal"] --> P7["Evidence\nevent"] --> P8["Infra apply"]
|
||||||
|
end
|
||||||
|
CAT["Module catalog\nprimitives + modules\n(security-reviewed)"]
|
||||||
|
ADAPT["Engine adapter\n(stateless → Terraform)"]
|
||||||
|
ENV["Platform-managed\nenvironments\naccount · VPC · state · IAM"]
|
||||||
|
HITL["HITL gates\nqa · prod · dr"]
|
||||||
|
EVID["Evidence stream\nhash-chained outbox\n(RPO = 0)"]
|
||||||
|
CS --> PIPE
|
||||||
|
CAT --> P2
|
||||||
|
ADAPT --> P4
|
||||||
|
ADAPT --> P8
|
||||||
|
ENV --> P8
|
||||||
|
P6 --> HITL
|
||||||
|
HITL --> P8
|
||||||
|
P7 --> EVID
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph DOWN ["Downstream"]
|
||||||
|
direction LR
|
||||||
|
D1["AWS resources\nrunning\n(tagged, encrypted)"]
|
||||||
|
D2["Consumer pipeline\ndeploys image"]
|
||||||
|
end
|
||||||
|
|
||||||
|
U1 --> CS
|
||||||
|
U2 --> CS
|
||||||
|
P8 --> D1
|
||||||
|
D1 --> D2
|
||||||
|
|
||||||
|
classDef accent fill:#1B1B1B,color:#fff,stroke:#D6002A,stroke-width:2px
|
||||||
|
classDef supporting fill:#fff,color:#1B1B1B,stroke:#D6002A,stroke-width:1px
|
||||||
|
classDef clusterTitle fill:#F0F0F0,color:#1B1B1B,stroke:#D6002A,stroke-width:1px
|
||||||
|
class CS,P6,P7,EVID,ADAPT,ENV accent
|
||||||
|
class U1,U2,P1,P2,P3,P4,P5,P8,CAT,HITL,D1,D2 supporting
|
||||||
@@ -9,7 +9,7 @@ flowchart LR
|
|||||||
D["Manual promotion"]
|
D["Manual promotion"]
|
||||||
A --> B --> C --> D
|
A --> B --> C --> D
|
||||||
end
|
end
|
||||||
subgraph ACDL ["With ACDL"]
|
subgraph ACDL ["With Nova"]
|
||||||
direction TB
|
direction TB
|
||||||
E["Declare intent\n(one YAML contract)"]
|
E["Declare intent\n(one YAML contract)"]
|
||||||
F["Platform delivers\nsafely, autonomously"]
|
F["Platform delivers\nsafely, autonomously"]
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ flowchart LR
|
|||||||
B["Agentic SDLC\n(agent writes contract)"]
|
B["Agentic SDLC\n(agent writes contract)"]
|
||||||
C["Citizen dev\n(vibe codes → AI agent\n→ contract)"]
|
C["Citizen dev\n(vibe codes → AI agent\n→ contract)"]
|
||||||
end
|
end
|
||||||
subgraph ACDL ["ACDL — infrastructure only"]
|
subgraph ACDL ["Nova — infrastructure only"]
|
||||||
D["Contract\nvalidated"]
|
D["Contract\nvalidated"]
|
||||||
E["Resolve → Plan\nSecurity + Policy checks\nConfidence signal"]
|
E["Resolve → Plan\nSecurity + Policy checks\nConfidence signal"]
|
||||||
F["Provision\nAWS resources"]
|
F["Provision\nAWS resources"]
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 37 KiB |
|
After Width: | Height: | Size: 76 KiB |
|
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 56 KiB |
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
@@ -23,9 +23,7 @@ style: |
|
|||||||
display: inline-block; padding: 2px 8px; border-radius: 4px;
|
display: inline-block; padding: 2px 8px; border-radius: 4px;
|
||||||
font-size: 16px; font-weight: 600;
|
font-size: 16px; font-weight: 600;
|
||||||
}
|
}
|
||||||
.testing { background: #DBEAFE; color: #1E3A5F; }
|
|
||||||
.planned { background: #fef3c7; color: #78350f; }
|
.planned { background: #fef3c7; color: #78350f; }
|
||||||
.agentic { background: #EDE9FE; color: #4C1D95; }
|
|
||||||
---
|
---
|
||||||
|
|
||||||
<!-- _class: title -->
|
<!-- _class: title -->
|
||||||
@@ -33,7 +31,7 @@ style: |
|
|||||||
|
|
||||||
# How The Platform Works
|
# How The Platform Works
|
||||||
|
|
||||||
### Agentic Cloud Delivery Platform
|
### Nova — The New Dawn of DevSecOps
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
section.title h1 { font-size: 44px; margin-bottom: 0.1em; }
|
section.title h1 { font-size: 44px; margin-bottom: 0.1em; }
|
||||||
@@ -53,6 +51,17 @@ section.title h3 { color: #F0F0F0; font-weight: 400; font-size: 22px; margin-top
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
# The platform at a glance
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
- **Consumer surfaces** — technical dev or citizen dev; both produce a contract
|
||||||
|
- **Central pipeline** — fixed stages, identical for every deployment: validate → resolve → security → plan → policy → confidence → evidence → apply
|
||||||
|
- **Module catalog + engine adapter** — security-reviewed blocks; the adapter is the only engine-specific code (Terraform today)
|
||||||
|
- **HITL gates + evidence stream** — human attestation for qa/prod/dr; every deployment writes a hash-chained event (RPO = 0)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
# Declare intent; the platform delivers safe production
|
# Declare intent; the platform delivers safe production
|
||||||
|
|
||||||

|

|
||||||
@@ -63,12 +72,12 @@ section.title h3 { color: #F0F0F0; font-weight: 400; font-size: 22px; margin-top
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# ACDL owns infrastructure, not your app
|
# Nova owns infrastructure, not your app
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
- **Upstream is anything** — IDE, agentic SDLC, or vibe coding
|
- **Upstream is anything** — IDE, agentic SDLC, or vibe coding
|
||||||
- **ACDL is infrastructure only** — provisions and governs AWS resources
|
- **Nova is infrastructure only** — provisions and governs AWS resources
|
||||||
- **Not a general-purpose AI** — autonomy is narrow, policy-bounded
|
- **Not a general-purpose AI** — autonomy is narrow, policy-bounded
|
||||||
- **Not a permissive highway** — no escape hatches
|
- **Not a permissive highway** — no escape hatches
|
||||||
|
|
||||||
@@ -98,7 +107,7 @@ section.title h3 { color: #F0F0F0; font-weight: 400; font-size: 22px; margin-top
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
- **OIDC federation** — short-lived token per job, no stored credential <span class="badge testing">Testing (GitHub Actions)</span> <span class="badge planned">Planned: all runners</span>
|
- **OIDC federation** — short-lived token per job, no stored credential <span class="badge planned">Planned: all runners</span>
|
||||||
- **ABAC, not role-based** — repo identity + resource tags scope every action
|
- **ABAC, not role-based** — repo identity + resource tags scope every action
|
||||||
- **A consumer can only touch its own tagged resources.** One consumer can never affect another.
|
- **A consumer can only touch its own tagged resources.** One consumer can never affect another.
|
||||||
|
|
||||||
@@ -108,11 +117,11 @@ section.title h3 { color: #F0F0F0; font-weight: 400; font-size: 22px; margin-top
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
- **Six weighted inputs** — manually tuned, auditable per-input breakdown <span class="badge agentic">Agentic</span>
|
- **Six weighted inputs** — manually tuned, auditable per-input breakdown
|
||||||
|
|
||||||
| Environment | Threshold | Attester |
|
| Environment | Threshold | Attester |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| dev | ≥ 0.50 | No one — autonomous <span class="badge testing">Testing</span> |
|
| dev | ≥ 0.50 | No one — autonomous |
|
||||||
| qa | ≥ 0.75 | QA <span class="badge planned">Planned</span> |
|
| qa | ≥ 0.75 | QA <span class="badge planned">Planned</span> |
|
||||||
| prod | ≥ 0.90 | SRE <span class="badge planned">Planned</span> |
|
| prod | ≥ 0.90 | SRE <span class="badge planned">Planned</span> |
|
||||||
|
|
||||||
@@ -124,10 +133,10 @@ section.title h3 { color: #F0F0F0; font-weight: 400; font-size: 22px; margin-top
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
- **Dev is fully autonomous** — confidence signal is the only gate <span class="badge testing">Testing</span> <span class="badge agentic">Agentic</span>
|
- **Dev is fully autonomous** — confidence signal is the only gate
|
||||||
- **qa, prod, dr require human attestation** — contract + plan + evidence <span class="badge planned">Planned</span>
|
- **qa, prod, dr require human attestation** — contract + plan + evidence <span class="badge planned">Planned</span>
|
||||||
- **Separation of duties** — QA approver ≠ prod approver; platform **blocks on a match** <span class="badge planned">Planned</span>
|
- **Separation of duties** — QA approver ≠ prod approver; platform **blocks on a match** <span class="badge planned">Planned</span>
|
||||||
- **Hash-chained evidence event** — tampering breaks the chain. **RPO = 0** <span class="badge testing">Testing</span>
|
- **Hash-chained evidence event** — tampering breaks the chain. **RPO = 0**
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -141,7 +150,7 @@ section.title h3 { color: #F0F0F0; font-weight: 400; font-size: 22px; margin-top
|
|||||||
- **Auditability as a byproduct, not a project** — every change traceable to a human attestation
|
- **Auditability as a byproduct, not a project** — every change traceable to a human attestation
|
||||||
- **Blast radius contained by design** — OIDC + ABAC, only your own tagged resources
|
- **Blast radius contained by design** — OIDC + ABAC, only your own tagged resources
|
||||||
- **Infrastructure as a utility, not a craft** — consume, don't maintain
|
- **Infrastructure as a utility, not a craft** — consume, don't maintain
|
||||||
- **A path to the citizen developer** — same envelope, senior engineer or non-technical <span class="badge agentic">Agentic</span>
|
- **A path to the citizen developer** — same envelope, senior engineer or non-technical
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -176,13 +185,13 @@ A named environment is a platform-owned bundle of:
|
|||||||
|
|
||||||
The consumer selects an environment **by name** in their contract. The platform resolves it at run time. **The consumer never sees raw credentials.**
|
The consumer selects an environment **by name** in their contract. The platform resolves it at run time. **The consumer never sees raw credentials.**
|
||||||
|
|
||||||
**Friendly onboarding:** the first run detects no environment and emits a guided prompt (not an opaque failure). <span class="badge testing">Testing</span> <span class="badge planned">Self-service: planned</span>
|
**Friendly onboarding:** the first run detects no environment and emits a guided prompt (not an opaque failure). <span class="badge planned">Self-service: planned</span>
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# A2 — Observability Built In
|
# A2 — Observability Built In
|
||||||
|
|
||||||
Monitoring is **a platform default, not a per-team project.** <span class="badge testing">Testing</span>
|
Monitoring is **a platform default, not a per-team project.**
|
||||||
|
|
||||||
- **Uptime monitoring deployed automatically with every stack** — separate state, feature flag to disable
|
- **Uptime monitoring deployed automatically with every stack** — separate state, feature flag to disable
|
||||||
- **Monitored endpoints passed from the deployment's own outputs** — no manual endpoint registration
|
- **Monitored endpoints passed from the deployment's own outputs** — no manual endpoint registration
|
||||||
@@ -194,7 +203,7 @@ Monitoring is **a platform default, not a per-team project.** <span class="badge
|
|||||||
|
|
||||||
# A3 — Security by Construction
|
# A3 — Security by Construction
|
||||||
|
|
||||||
Security defaults that **do not require a team to opt in.** Checks run on **every** deployment, normalized to a single schema. <span class="badge testing">Testing</span>
|
Security defaults that **do not require a team to opt in.** Checks run on **every** deployment, normalized to a single schema.
|
||||||
|
|
||||||
- **Policy checks** (Checkov, Wiz, Kyverno) — secrets, public ingress, IAM wildcards, **required tagging** — all run *before* infra is created
|
- **Policy checks** (Checkov, Wiz, Kyverno) — secrets, public ingress, IAM wildcards, **required tagging** — all run *before* infra is created
|
||||||
- **Encryption on every resource** — at-rest on by default; per-stack CMKs with 90-day rotation, **no shared keys across stacks**
|
- **Encryption on every resource** — at-rest on by default; per-stack CMKs with 90-day rotation, **no shared keys across stacks**
|
||||||
@@ -238,7 +247,7 @@ li { margin-bottom: 2px; }
|
|||||||
- Module catalog (primitives + modules) with validated examples
|
- Module catalog (primitives + modules) with validated examples
|
||||||
- Zero-trust OIDC + ABAC on GitHub Actions runners
|
- Zero-trust OIDC + ABAC on GitHub Actions runners
|
||||||
- Security + policy checks before infra creation (Checkov; Wiz + Kyverno ready)
|
- Security + policy checks before infra creation (Checkov; Wiz + Kyverno ready)
|
||||||
- Confidence signal (6 inputs, per-env thresholds) gating promotion <span class="badge agentic">Agentic</span>
|
- Confidence signal (6 inputs, per-env thresholds) gating promotion
|
||||||
- Hash-chained, tamper-evident evidence outbox (RPO = 0)
|
- Hash-chained, tamper-evident evidence outbox (RPO = 0)
|
||||||
- Encryption by default + per-stack customer-managed keys
|
- Encryption by default + per-stack customer-managed keys
|
||||||
- Deletion protection by default + safe decommission with SRE gates
|
- Deletion protection by default + safe decommission with SRE gates
|
||||||
@@ -256,8 +265,8 @@ li { margin-bottom: 2px; }
|
|||||||
- Full regulatory ledger: S3 Object Lock + JWS signatures + daily checkpoints
|
- Full regulatory ledger: S3 Object Lock + JWS signatures + daily checkpoints
|
||||||
- Compliance milestone: GDPR, SOX, SOC2, DORA extension points
|
- Compliance milestone: GDPR, SOX, SOC2, DORA extension points
|
||||||
- Environment self-service provisioning
|
- Environment self-service provisioning
|
||||||
- Dynamic module creation from a contract (agentic citizen-developer flow) <span class="badge agentic">Agentic</span>
|
- Dynamic module creation from a contract (agentic citizen-developer flow)
|
||||||
- Pattern recognition compounds value over time <span class="badge agentic">Agentic</span>
|
- Pattern recognition compounds value over time
|
||||||
- Additional engine adapters (OpenTofu, Pulumi, Kubernetes CRDs)
|
- Additional engine adapters (OpenTofu, Pulumi, Kubernetes CRDs)
|
||||||
- Deeper observability bootstrap (dashboards, runbooks, on-call)
|
- Deeper observability bootstrap (dashboards, runbooks, on-call)
|
||||||
|
|
||||||
@@ -290,7 +299,7 @@ section { font-size: 20px; }
|
|||||||
table { font-size: 18px; }
|
table { font-size: 18px; }
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
ACDL runs at **zero cloud cost** for day-to-day development. AWS spend was measured via Cost Explorer (`COST.md`, 2026-07-28):
|
Nova runs at **zero cloud cost** for day-to-day development. AWS spend was measured via Cost Explorer (`COST.md`, 2026-07-28):
|
||||||
|
|
||||||
| Metric | Value |
|
| Metric | Value |
|
||||||
|--------|-------|
|
|--------|-------|
|
||||||
@@ -300,8 +309,8 @@ ACDL runs at **zero cloud cost** for day-to-day development. AWS spend was measu
|
|||||||
| Peak day | 2026-07-27 ($0.000867) |
|
| Peak day | 2026-07-27 ($0.000867) |
|
||||||
|
|
||||||
- **S3 dominates** (98.8%, terraform state bucket) — no compute ran because v1.0→v1.10 was plan-only for IAM-gated capabilities
|
- **S3 dominates** (98.8%, terraform state bucket) — no compute ran because v1.0→v1.10 was plan-only for IAM-gated capabilities
|
||||||
- **Local emulators are the primary tier** — the full pipeline runs in-process, no AWS credentials <span class="badge testing">Testing</span>
|
- **Local emulators are the primary tier** — the full pipeline runs in-process, no AWS credentials
|
||||||
- **Live-AWS verification is milestone-scoped, then torn down.** The pipeline now **defaults to plan-only** on every PR; `ACDL_LIFECYCLE_MODE=full` overrides to apply→destroy for milestone verification (REQ-134, v1.12).
|
- **Live-AWS verification is milestone-scoped, then torn down.** The pipeline now **defaults to plan-only** on every PR; `NOVA_LIFECYCLE_MODE=full` overrides to apply→destroy for milestone verification (REQ-134, v1.12).
|
||||||
- **Cost drivers** are spike-scoped: Terraform plan reads (free), S3 state storage (cents), DynamoDB outbox (cents). Any spike > $1/day is an anomaly.
|
- **Cost drivers** are spike-scoped: Terraform plan reads (free), S3 state storage (cents), DynamoDB outbox (cents). Any spike > $1/day is an anomaly.
|
||||||
|
|
||||||
**Pre-mortem (`PRE_MORTEM.md`):** the v1.10 decay incident (diff-scoped VERIFY missed 7 adapter defects) is the root pattern: *a claim outruns the verification that backs it.* Four forward failure modes + structural mitigations (regression-tested IAM baseline, mandatory teardown, verified-only deck claims, honest scope).
|
**Pre-mortem (`PRE_MORTEM.md`):** the v1.10 decay incident (diff-scoped VERIFY missed 7 adapter defects) is the root pattern: *a claim outruns the verification that backs it.* Four forward failure modes + structural mitigations (regression-tested IAM baseline, mandatory teardown, verified-only deck claims, honest scope).
|
||||||
@@ -320,6 +329,6 @@ section { font-size: 20px; }
|
|||||||
Two architectural pillars make "Verified" a structural property, not a claim:
|
Two architectural pillars make "Verified" a structural property, not a claim:
|
||||||
|
|
||||||
- **The stateless adapter (918 → ~80 lines).** The Terraform adapter was a 918-line monolith with 3 constant tables and 39 type-specific branches. It is now a ~80-line **stateless assembler**: it owns no module content — no resource shape, no nested HCL blocks, no defaults. Each L1 module ships a real `terraform/` module dir owning its shape, nested blocks, and defaults. The adapter reads the registry and emits `module "x" { source = ... }` blocks. A new module is a new terraform dir, not a code change. *(The v1.12 P67 fix closed a dedup defect for multi-resource L1s — ecs-service, alb; CAP-013 now Verified.)*
|
- **The stateless adapter (918 → ~80 lines).** The Terraform adapter was a 918-line monolith with 3 constant tables and 39 type-specific branches. It is now a ~80-line **stateless assembler**: it owns no module content — no resource shape, no nested HCL blocks, no defaults. Each L1 module ships a real `terraform/` module dir owning its shape, nested blocks, and defaults. The adapter reads the registry and emits `module "x" { source = ... }` blocks. A new module is a new terraform dir, not a code change. *(The v1.12 P67 fix closed a dedup defect for multi-resource L1s — ecs-service, alb; CAP-013 now Verified.)*
|
||||||
- **Pipeline-driven lifecycle testing.** A `modules-lifecycle` pipeline matrix-runs each L1 and L2 module's `examples/{simple,complex}.yml` contracts through apply→modify→destroy against live AWS. **The "test" = the pipeline cell going green.** Defaults to **plan-only** on every PR (fast, no AWS mutation, no cost); `ACDL_LIFECYCLE_MODE=full` overrides to the real apply→destroy for milestone verification (REQ-134, v1.12). The regression gate (D-091) re-runs all 22 capabilities at milestone completion — **22/22 Verified** as of v1.12.
|
- **Pipeline-driven lifecycle testing.** A `modules-lifecycle` pipeline matrix-runs each L1 and L2 module's `examples/{simple,complex}.yml` contracts through apply→modify→destroy against live AWS. **The "test" = the pipeline cell going green.** Defaults to **plan-only** on every PR (fast, no AWS mutation, no cost); `NOVA_LIFECYCLE_MODE=full` overrides to the real apply→destroy for milestone verification (REQ-134, v1.12). The regression gate (D-091) re-runs all 22 capabilities at milestone completion — **22/22 Verified** as of v1.12.
|
||||||
|
|
||||||
The v1.10 lesson is the negative space: a 918-line adapter with type-specific branches decayed silently. The ~80-line stateless adapter + the milestone regression gate are the structural fix.
|
The v1.10 lesson is the negative space: a 918-line adapter with type-specific branches decayed silently. The ~80-line stateless adapter + the milestone regression gate are the structural fix.
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
# How The Platform Works — Talking Points
|
# How The Platform Works — Talking Points
|
||||||
|
|
||||||
> **Companion to:** `how-the-platform-works-marp.md` (10 main + Appendix TOC + 8 appendix = 19 slides)
|
> **Companion to:** `how-the-platform-works-marp.md` (11 main + Appendix TOC + 8 appendix = 20 slides)
|
||||||
> **Content source:** `how-the-platform-works.md` (full source of truth with speaker notes)
|
> **Content source:** `how-the-platform-works.md` (full source of truth with speaker notes)
|
||||||
> **Purpose:** Presenter-ready cues — 3-6 talking points per slide + the one key takeaway the audience should remember.
|
> **Purpose:** Presenter-ready cues — 3-6 talking points per slide + the one key takeaway the audience should remember.
|
||||||
> **Audience:** Senior Leadership — CTO, Head of Cloud, Head of Infrastructure, Head of DevOps
|
> **Audience:** Senior Leadership — CTO, Head of Cloud, Head of Infrastructure, Head of DevOps
|
||||||
@@ -30,10 +30,22 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Slide 3 — Declare intent; the platform delivers safe production
|
## Slide 3 — The platform at a glance
|
||||||
|
|
||||||
**Talking points:**
|
**Talking points:**
|
||||||
- Land the before/after contrast: today's queue vs. ACDL's autonomous flow
|
- One-slide map of the whole platform — use it to orient the audience before diving into any single component
|
||||||
|
- The leadership-relevant beats: (1) two surfaces, one pipeline, one evidence stream — the convergence is the design; (2) the pipeline stages are fixed and identical for every consumer; (3) the engine adapter is the only engine-specific code, which makes the catalog and confidence model portable
|
||||||
|
- Don't walk every node — point to the boundaries and say "the rest of this deck zooms into each of these"
|
||||||
|
- The contract schema is the boundary between upstream and Nova; everything left of it is the consumer's, everything right of it is the platform's
|
||||||
|
|
||||||
|
**Key takeaway:** Two surfaces, one pipeline, one evidence stream. The rest of the deck zooms in.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Slide 4 — Declare intent; the platform delivers safe production
|
||||||
|
|
||||||
|
**Talking points:**
|
||||||
|
- Land the before/after contrast: today's queue vs. Nova's autonomous flow
|
||||||
- The litmus test: if a platform engineer still has to touch a ticket for a dev→qa promotion, we haven't delivered the vision
|
- The litmus test: if a platform engineer still has to touch a ticket for a dev→qa promotion, we haven't delivered the vision
|
||||||
- The North Star is one sentence: "declare intent → safe production deployment"
|
- The North Star is one sentence: "declare intent → safe production deployment"
|
||||||
- A non-technical consumer ships by declaring intent — no workflow, no config file, no module
|
- A non-technical consumer ships by declaring intent — no workflow, no config file, no module
|
||||||
@@ -42,19 +54,19 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Slide 4 — ACDL owns infrastructure, not your app
|
## Slide 5 — Nova owns infrastructure, not your app
|
||||||
|
|
||||||
**Talking points:**
|
**Talking points:**
|
||||||
- The platform is deliberately scoped — it is not trying to be everything
|
- The platform is deliberately scoped — it is not trying to be everything
|
||||||
- The sovereign boundary: the platform team owns delivery and infrastructure, not the upstream development process
|
- The sovereign boundary: the platform team owns delivery and infrastructure, not the upstream development process
|
||||||
- The anti-goals are as important as the goals — they tell leadership what not to expect
|
- The anti-goals are as important as the goals — they tell leadership what not to expect
|
||||||
- Upstream is anything: IDE, agentic SDLC, or vibe coding — ACDL doesn't care how the contract was produced
|
- Upstream is anything: IDE, agentic SDLC, or vibe coding — Nova doesn't care how the contract was produced
|
||||||
|
|
||||||
**Key takeaway:** ACDL is infrastructure only. App build/test/deploy is upstream.
|
**Key takeaway:** Nova is infrastructure only. App build/test/deploy is upstream.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Slide 5 — One YAML file. The platform owns everything else.
|
## Slide 6 — One YAML file. The platform owns everything else.
|
||||||
|
|
||||||
**Talking points:**
|
**Talking points:**
|
||||||
- Hold this slide — emphasize the asymmetry. The consumer's surface is intentionally tiny; the platform's surface is large and opinionated
|
- Hold this slide — emphasize the asymmetry. The consumer's surface is intentionally tiny; the platform's surface is large and opinionated
|
||||||
@@ -66,19 +78,19 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Slide 6 — Same stages, same checks, every deployment
|
## Slide 7 — Same stages, same checks, every deployment
|
||||||
|
|
||||||
**Talking points:**
|
**Talking points:**
|
||||||
- Walk left to right once — don't dwell on internals; the point is the flow is fixed, opinionated, and identical for every consumer
|
- Walk left to right once — don't dwell on internals; the point is the flow is fixed, opinionated, and identical for every consumer
|
||||||
- The two leadership-relevant beats: (1) checks before creation, (2) every stage is evidenced
|
- The two leadership-relevant beats: (1) checks before creation, (2) every stage is evidenced
|
||||||
- No team-specific pipelines, no tribal runbooks — the flow is the contract
|
- No team-specific pipelines, no tribal runbooks — the flow is the contract
|
||||||
- The confidence signal (Slide 8) is where the "safety is computed" story lands
|
- The confidence signal (Slide 9) is where the "safety is computed" story lands
|
||||||
|
|
||||||
**Key takeaway:** Same stages, same checks, every deployment. No "unchecked" path.
|
**Key takeaway:** Same stages, same checks, every deployment. No "unchecked" path.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Slide 7 — No long-lived credentials. Blast radius contained.
|
## Slide 8 — No long-lived credentials. Blast radius contained.
|
||||||
|
|
||||||
**Talking points:**
|
**Talking points:**
|
||||||
- This is the slide for the Head of Cloud/Security — the key phrase is "blast radius contained to the consumer's own stack"
|
- This is the slide for the Head of Cloud/Security — the key phrase is "blast radius contained to the consumer's own stack"
|
||||||
@@ -91,7 +103,7 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Slide 8 — Safety is a measurable signal, not a black box
|
## Slide 9 — Safety is a measurable signal, not a black box
|
||||||
|
|
||||||
**Talking points:**
|
**Talking points:**
|
||||||
- This is the bet that separates this platform from "yet another CI/CD tool" — reliance on operator instinct or tenure is not a substitute
|
- This is the bet that separates this platform from "yet another CI/CD tool" — reliance on operator instinct or tenure is not a substitute
|
||||||
@@ -104,7 +116,7 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Slide 9 — Every change traceable to a human attestation
|
## Slide 10 — Every change traceable to a human attestation
|
||||||
|
|
||||||
**Talking points:**
|
**Talking points:**
|
||||||
- The "lower environments autonomous, higher environments attested" tenet resolves the classic "move fast vs. be safe" false dichotomy
|
- The "lower environments autonomous, higher environments attested" tenet resolves the classic "move fast vs. be safe" false dichotomy
|
||||||
@@ -117,7 +129,7 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Slide 10 — The vision realized
|
## Slide 11 — The vision realized
|
||||||
|
|
||||||
**Talking points:**
|
**Talking points:**
|
||||||
- Close on the strategic frame — the platform is not "a CI/CD tool," it's the organizational lever for shipping safely at the pace the business demands
|
- Close on the strategic frame — the platform is not "a CI/CD tool," it's the organizational lever for shipping safely at the pace the business demands
|
||||||
@@ -170,7 +182,7 @@
|
|||||||
**Talking points:**
|
**Talking points:**
|
||||||
- The phrase to land is "secure by default, not secure by effort"
|
- The phrase to land is "secure by default, not secure by effort"
|
||||||
- The selling point is *normalization* — we can add a new security tool without changing the confidence model or the evidence stream
|
- The selling point is *normalization* — we can add a new security tool without changing the confidence model or the evidence stream
|
||||||
- For the Head of Security: tagging standards are enforced, not advisory — a missing `acdl:owner` tag fails the check, not a warning
|
- For the Head of Security: tagging standards are enforced, not advisory — a missing `nova:owner` tag fails the check, not a warning
|
||||||
- The decommission flow is the counter-argument to "deletion protection makes cleanup impossible" — it's a deliberate, gated, two-approval path
|
- The decommission flow is the counter-argument to "deletion protection makes cleanup impossible" — it's a deliberate, gated, two-approval path
|
||||||
|
|
||||||
**Key takeaway:** Secure by default, not secure by effort. Checks run before infra is created.
|
**Key takeaway:** Secure by default, not secure by effort. Checks run before infra is created.
|
||||||
@@ -195,7 +207,7 @@
|
|||||||
- Close on honesty — the platform delivers real, verifiable value today: 22/22 auto-verifiable capabilities Verified via the v1.11 lifecycle pipeline
|
- Close on honesty — the platform delivers real, verifiable value today: 22/22 auto-verifiable capabilities Verified via the v1.11 lifecycle pipeline
|
||||||
- The roadmap is concrete, not aspirational hand-waving — 9 planned items, each with a defined milestone and a clear reason it isn't shipped yet (usually an upstream dependency, not an engineering gap)
|
- The roadmap is concrete, not aspirational hand-waving — 9 planned items, each with a defined milestone and a clear reason it isn't shipped yet (usually an upstream dependency, not an engineering gap)
|
||||||
- Emphasize: 0 consumer adoption today — "Testing" means it works internally and is dev pilot-ready, not that it's released
|
- Emphasize: 0 consumer adoption today — "Testing" means it works internally and is dev pilot-ready, not that it's released
|
||||||
- The lifecycle pipeline defaults to plan-only on every PR; `ACDL_LIFECYCLE_MODE=full` overrides for milestone verification
|
- The lifecycle pipeline defaults to plan-only on every PR; `NOVA_LIFECYCLE_MODE=full` overrides for milestone verification
|
||||||
|
|
||||||
**Key takeaway:** 22/22 Verified today. 9 planned, each with a clear milestone and reason.
|
**Key takeaway:** 22/22 Verified today. 9 planned, each with a clear milestone and reason.
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
# How The Platform Works
|
# How The Platform Works
|
||||||
|
|
||||||
> **Subtitle:** Agentic Cloud Delivery Platform
|
> **Subtitle:** Nova — The New Dawn of DevSecOps
|
||||||
> **Audience:** Senior Leadership, CTO, Head of Cloud, Head of Infrastructure, Head of DevOps
|
> **Audience:** Senior Leadership, CTO, Head of Cloud, Head of Infrastructure, Head of DevOps
|
||||||
> **Length:** ~15 minutes · 10 main + Appendix TOC + 8 appendix = 19 slides
|
> **Length:** ~16 minutes · 11 main + Appendix TOC + 8 appendix = 20 slides
|
||||||
> **Purpose:** Sell the platform's value to tech leadership — zero-trust, security, observability, auditability, and the shift from "operators guess" to "the platform computes safety."
|
> **Purpose:** Sell the platform's value to tech leadership — zero-trust, security, observability, auditability, and the shift from "operators guess" to "the platform computes safety."
|
||||||
> **Maturity framing:** "Testing" = works internally, dev pilot-ready. "Planned" = on the roadmap, not yet implemented. "Agentic" = involves AI agents or autonomous decision-making.
|
> **Maturity framing:** "Testing" = works internally, dev pilot-ready. "Planned" = on the roadmap, not yet implemented. "Agentic" = involves AI agents or autonomous decision-making.
|
||||||
> **Re-verification (2026-07-29):** Every "Testing" claim in this deck was re-verified in v1.10 Phase 54 (D-093) and again in v1.11 via the pipeline-driven lifecycle tests (P59–P62). The headline E2E (contract → resolver → adapter → terraform init/validate/plan) passes against the live AWS account; the local emulating tier (Phase 53) runs the full E2E with no cloud credentials. **22/22 auto-verifiable capabilities Verified** (CAP-013 fixed in v1.12 P67 — the adapter's multi-resource L1 dedup defect is closed; CAP-017/018 probe bugs fixed). The v1.11 lifecycle pipeline ran apply→modify→destroy against live AWS and was then torn down to zero-cost (D-096). See `.ciagent/CAPABILITY_INVENTORY.md` and `.ciagent/PRE_MORTEM.md`.
|
> **Re-verification (2026-07-29):** Every "Testing" claim in this deck was re-verified in v1.10 Phase 54 (D-093) and again in v1.11 via the pipeline-driven lifecycle tests (P59–P62). The headline E2E (contract → resolver → adapter → terraform init/validate/plan) passes against the live AWS account; the local emulating tier (Phase 53) runs the full E2E with no cloud credentials. **22/22 auto-verifiable capabilities Verified** (CAP-013 fixed in v1.12 P67 — the adapter's multi-resource L1 dedup defect is closed; CAP-017/018 probe bugs fixed). The v1.11 lifecycle pipeline ran apply→modify→destroy against live AWS and was then torn down to zero-cost (D-096). See `.ciagent/CAPABILITY_INVENTORY.md` and `.ciagent/PRE_MORTEM.md`.
|
||||||
@@ -13,7 +13,9 @@
|
|||||||
|
|
||||||
# How The Platform Works
|
# How The Platform Works
|
||||||
|
|
||||||
### Agentic Cloud Delivery Platform
|
### Nova — The New Dawn of DevSecOps
|
||||||
|
|
||||||
|
**Security as a seamless enabler of fast deployments — not a bottleneck, not a "no" department.**
|
||||||
|
|
||||||
> **Speaker notes:** Brief introduction — this deck explains *how* the platform works internally, not what the developer experience is (that's the companion deck). Set the frame: the platform is not a CI/CD tool — it's the organizational lever for shipping safely at the pace the business demands.
|
> **Speaker notes:** Brief introduction — this deck explains *how* the platform works internally, not what the developer experience is (that's the companion deck). Set the frame: the platform is not a CI/CD tool — it's the organizational lever for shipping safely at the pace the business demands.
|
||||||
|
|
||||||
@@ -50,7 +52,66 @@ flowchart LR
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Slide 3 — Declare intent; the platform delivers safe production
|
## Slide 3 — The platform at a glance
|
||||||
|
|
||||||
|
One picture of the whole platform — the components, how they connect, and where the boundaries are. The rest of this deck zooms into each piece.
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
subgraph UP ["Consumer surfaces — upstream"]
|
||||||
|
direction LR
|
||||||
|
U1["Technical dev\napp code + contract"]
|
||||||
|
U2["Citizen dev\nintent → AI agent → contract"]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph ACDL ["Nova — infrastructure only"]
|
||||||
|
direction TB
|
||||||
|
CS["Contract schema\n(validate + fail-fast)"]
|
||||||
|
subgraph PIPE ["Central pipeline — fixed stages, every deployment"]
|
||||||
|
direction LR
|
||||||
|
P1["Validate"] --> P2["Resolve\ntarget stack"] --> P3["Security\nchecks"] --> P4["Infra plan"] --> P5["Policy\nchecks"] --> P6["Confidence\nsignal"] --> P7["Evidence\nevent"] --> P8["Infra apply"]
|
||||||
|
end
|
||||||
|
CAT["Module catalog\nprimitives + modules\n(security-reviewed)"]
|
||||||
|
ADAPT["Engine adapter\n(stateless → Terraform)"]
|
||||||
|
ENV["Platform-managed\nenvironments\naccount · VPC · state · IAM"]
|
||||||
|
HITL["HITL gates\nqa · prod · dr"]
|
||||||
|
EVID["Evidence stream\nhash-chained outbox\n(RPO = 0)"]
|
||||||
|
CS --> PIPE
|
||||||
|
CAT --> P2
|
||||||
|
ADAPT --> P4
|
||||||
|
ADAPT --> P8
|
||||||
|
ENV --> P8
|
||||||
|
P6 --> HITL
|
||||||
|
HITL --> P8
|
||||||
|
P7 --> EVID
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph DOWN ["Downstream"]
|
||||||
|
direction LR
|
||||||
|
D1["AWS resources\nrunning\n(tagged, encrypted)"]
|
||||||
|
D2["Consumer pipeline\ndeploys image"]
|
||||||
|
end
|
||||||
|
|
||||||
|
U1 --> CS
|
||||||
|
U2 --> CS
|
||||||
|
P8 --> D1
|
||||||
|
D1 --> D2
|
||||||
|
```
|
||||||
|
|
||||||
|
- **Consumer surfaces** — technical dev or citizen dev; both produce a contract. Upstream is anything.
|
||||||
|
- **Contract schema** — the boundary between upstream and Nova; validated fail-fast.
|
||||||
|
- **Central pipeline** — fixed stages, identical for every deployment: validate → resolve → security → plan → policy → confidence → evidence → apply.
|
||||||
|
- **Module catalog** — security-reviewed primitives + modules the resolver expands against.
|
||||||
|
- **Engine adapter** — stateless; the only engine-specific code (Terraform today).
|
||||||
|
- **Platform-managed environments** — account, VPC, state, IAM role; the platform owns the blast radius.
|
||||||
|
- **HITL gates** — human attestation for qa/prod/dr; dev is autonomous.
|
||||||
|
- **Evidence stream** — hash-chained outbox, RPO = 0, written by every deployment.
|
||||||
|
|
||||||
|
> **Speaker notes:** This is the one-slide map of the platform. Use it to orient the audience before diving into any single component. The leadership-relevant beats: (1) two surfaces, one pipeline, one evidence stream — the convergence is the design; (2) the pipeline stages are fixed and identical for every consumer — no team-specific pipelines; (3) the engine adapter is the only engine-specific code, which is what makes the catalog and confidence model portable. Don't walk every node; point to the boundaries and say "the rest of this deck zooms into each of these."
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Slide 4 — Declare intent; the platform delivers safe production
|
||||||
|
|
||||||
Consumers **declare intent**; the platform delivers **safe production deployment** — automatically, safely, with a complete audit trail.
|
Consumers **declare intent**; the platform delivers **safe production deployment** — automatically, safely, with a complete audit trail.
|
||||||
|
|
||||||
@@ -64,7 +125,7 @@ flowchart LR
|
|||||||
D["Manual promotion"]
|
D["Manual promotion"]
|
||||||
A --> B --> C --> D
|
A --> B --> C --> D
|
||||||
end
|
end
|
||||||
subgraph ACDL ["With ACDL"]
|
subgraph ACDL ["With Nova"]
|
||||||
direction TB
|
direction TB
|
||||||
E["Declare intent\n(one YAML contract)"]
|
E["Declare intent\n(one YAML contract)"]
|
||||||
F["Platform delivers\nsafely, autonomously"]
|
F["Platform delivers\nsafely, autonomously"]
|
||||||
@@ -78,11 +139,11 @@ flowchart LR
|
|||||||
- A **non-technical consumer** ships by declaring intent — no workflow, no config file, no module.
|
- A **non-technical consumer** ships by declaring intent — no workflow, no config file, no module.
|
||||||
- Every production change is **traceable to a human attestation** and an immutable evidence stream.
|
- Every production change is **traceable to a human attestation** and an immutable evidence stream.
|
||||||
|
|
||||||
> **Speaker notes:** Land the before/after contrast: today's queue vs. ACDL's autonomous flow. The litmus test: if a platform engineer still has to touch a ticket for a dev→qa promotion, we haven't delivered the vision. The North Star is "declare intent → safe production deployment."
|
> **Speaker notes:** Land the before/after contrast: today's queue vs. Nova's autonomous flow. The litmus test: if a platform engineer still has to touch a ticket for a dev→qa promotion, we haven't delivered the vision. The North Star is "declare intent → safe production deployment."
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Slide 4 — ACDL owns infrastructure, not your app
|
## Slide 5 — Nova owns infrastructure, not your app
|
||||||
|
|
||||||
The platform is deliberately scoped — it is not trying to be everything.
|
The platform is deliberately scoped — it is not trying to be everything.
|
||||||
|
|
||||||
@@ -94,7 +155,7 @@ flowchart LR
|
|||||||
B["Agentic SDLC\n(agent writes contract)"]
|
B["Agentic SDLC\n(agent writes contract)"]
|
||||||
C["Citizen dev\n(vibe codes → AI agent\n→ contract)"]
|
C["Citizen dev\n(vibe codes → AI agent\n→ contract)"]
|
||||||
end
|
end
|
||||||
subgraph ACDL ["ACDL — infrastructure only"]
|
subgraph ACDL ["Nova — infrastructure only"]
|
||||||
D["Contract\nvalidated"]
|
D["Contract\nvalidated"]
|
||||||
E["Resolve → Plan\nSecurity + Policy checks\nConfidence signal"]
|
E["Resolve → Plan\nSecurity + Policy checks\nConfidence signal"]
|
||||||
F["Provision\nAWS resources"]
|
F["Provision\nAWS resources"]
|
||||||
@@ -114,8 +175,8 @@ flowchart LR
|
|||||||
H --> I
|
H --> I
|
||||||
```
|
```
|
||||||
|
|
||||||
- **Upstream is anything** — IDE, agentic SDLC, or vibe coding. ACDL doesn't care how the contract was produced.
|
- **Upstream is anything** — IDE, agentic SDLC, or vibe coding. Nova doesn't care how the contract was produced.
|
||||||
- **ACDL is infrastructure only** — it provisions and governs AWS resources. App build/test/deploy is upstream.
|
- **Nova is infrastructure only** — it provisions and governs AWS resources. App build/test/deploy is upstream.
|
||||||
- **Not a general-purpose AI** — autonomy is narrow, scoped to delivery, bounded by strict policy.
|
- **Not a general-purpose AI** — autonomy is narrow, scoped to delivery, bounded by strict policy.
|
||||||
- **Not a permissive highway** — no escape hatches to bypass the confidence framework.
|
- **Not a permissive highway** — no escape hatches to bypass the confidence framework.
|
||||||
|
|
||||||
@@ -123,9 +184,9 @@ flowchart LR
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Slide 5 — One YAML file. The platform owns everything else.
|
## Slide 6 — One YAML file. The platform owns everything else.
|
||||||
|
|
||||||
The contract is the boundary between upstream and ACDL. It's all a consumer writes.
|
The contract is the boundary between upstream and Nova. It's all a consumer writes.
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
flowchart LR
|
flowchart LR
|
||||||
@@ -142,7 +203,7 @@ flowchart LR
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Slide 6 — Same stages, same checks, every deployment
|
## Slide 7 — Same stages, same checks, every deployment
|
||||||
|
|
||||||
Every deployment runs the same stages, in the same order, with the same checks — no team-specific pipelines, no tribal runbooks.
|
Every deployment runs the same stages, in the same order, with the same checks — no team-specific pipelines, no tribal runbooks.
|
||||||
|
|
||||||
@@ -161,11 +222,11 @@ flowchart TD
|
|||||||
- **Security and policy checks run *before* any infrastructure is created** — not as a post-deployment audit.
|
- **Security and policy checks run *before* any infrastructure is created** — not as a post-deployment audit.
|
||||||
- **Every stage produces a record** that feeds the confidence signal and the evidence stream. No "unchecked" path.
|
- **Every stage produces a record** that feeds the confidence signal and the evidence stream. No "unchecked" path.
|
||||||
|
|
||||||
> **Speaker notes:** Walk left to right once. Don't dwell on internals — the point is that the flow is fixed, opinionated, and identical for every consumer. The two leadership-relevant beats: (1) checks before creation, (2) every stage is evidenced. The confidence signal (Slide 8) is where the "safety is computed" story lands.
|
> **Speaker notes:** Walk left to right once. Don't dwell on internals — the point is that the flow is fixed, opinionated, and identical for every consumer. The two leadership-relevant beats: (1) checks before creation, (2) every stage is evidenced. The confidence signal (Slide 9) is where the "safety is computed" story lands.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Slide 7 — No long-lived credentials. Blast radius contained.
|
## Slide 8 — No long-lived credentials. Blast radius contained.
|
||||||
|
|
||||||
Consumer repositories hold **no long-lived cloud credentials.** Ever.
|
Consumer repositories hold **no long-lived cloud credentials.** Ever.
|
||||||
|
|
||||||
@@ -178,19 +239,19 @@ flowchart LR
|
|||||||
A --> B --> C --> D
|
A --> B --> C --> D
|
||||||
```
|
```
|
||||||
|
|
||||||
- **Authentication — OIDC federation.** Each job mints a short-lived token; no credential stored in the consumer repo or runner secret. <span class="badge testing">Testing (GitHub Actions)</span> <span class="badge planned">Planned: all runners</span>
|
- **Authentication — OIDC federation.** Each job mints a short-lived token; no credential stored in the consumer repo or runner secret. <span class="badge planned">Planned: all runners</span>
|
||||||
- **Authorization — attribute-based (ABAC), not role-based.** Two attribute classes scope every action:
|
- **Authorization — attribute-based (ABAC), not role-based.** Two attribute classes scope every action:
|
||||||
- **Repository identity** — trust policy binds to the exact consumer repo + branch.
|
- **Repository identity** — trust policy binds to the exact consumer repo + branch.
|
||||||
- **Resource tags** — every resource tagged `acdl:owner` + `acdl:contract`; session policy grants access **only to matching tags.**
|
- **Resource tags** — every resource tagged `nova:owner` + `nova:contract`; session policy grants access **only to matching tags.**
|
||||||
- **The effect:** a consumer can only touch the resources it created. One consumer can never affect another.
|
- **The effect:** a consumer can only touch the resources it created. One consumer can never affect another.
|
||||||
|
|
||||||
> **Speaker notes:** This is the slide for the Head of Cloud/Security. The key phrase is "blast radius contained to the consumer's own stack." Contrast with the common failure mode of shared CI roles that can touch any account resource. The static-key override exists for edge cases but is rotated daily on platform runners; it is never the default.
|
> **Speaker notes:** This is the slide for the Head of Cloud/Security. The key phrase is "blast radius contained to the consumer's own stack." Contrast with the common failure mode of shared CI roles that can touch any account resource. The static-key override exists for edge cases but is rotated daily on platform runners; it is never the default.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Slide 8 — Safety is a measurable signal, not a black box
|
## Slide 9 — Safety is a measurable signal, not a black box
|
||||||
|
|
||||||
Every delivery action produces a **measurable, explainable confidence signal** — a weighted sum of observable facts, not a black box. <span class="badge agentic">Agentic</span>
|
Every delivery action produces a **measurable, explainable confidence signal** — a weighted sum of observable facts, not a black box.
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
flowchart LR
|
flowchart LR
|
||||||
@@ -208,7 +269,7 @@ flowchart LR
|
|||||||
|
|
||||||
| Environment | Threshold | Attester |
|
| Environment | Threshold | Attester |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| dev | ≥ 0.50 | No one — autonomous <span class="badge testing">Testing</span> |
|
| dev | ≥ 0.50 | No one — autonomous |
|
||||||
| qa | ≥ 0.75 | QA <span class="badge planned">Planned</span> |
|
| qa | ≥ 0.75 | QA <span class="badge planned">Planned</span> |
|
||||||
| prod | ≥ 0.90 | SRE <span class="badge planned">Planned</span> |
|
| prod | ≥ 0.90 | SRE <span class="badge planned">Planned</span> |
|
||||||
|
|
||||||
@@ -218,7 +279,7 @@ flowchart LR
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Slide 9 — Every change traceable to a human attestation
|
## Slide 10 — Every change traceable to a human attestation
|
||||||
|
|
||||||
Computed safety handles the gate. Humans still matter — here's how accountability works.
|
Computed safety handles the gate. Humans still matter — here's how accountability works.
|
||||||
|
|
||||||
@@ -237,23 +298,23 @@ flowchart LR
|
|||||||
GATED --> OUT
|
GATED --> OUT
|
||||||
```
|
```
|
||||||
|
|
||||||
- **Dev is fully autonomous.** The confidence signal (≥ 0.50) is the only gate. <span class="badge testing">Testing</span> <span class="badge agentic">Agentic</span>
|
- **Dev is fully autonomous.** The confidence signal (≥ 0.50) is the only gate.
|
||||||
- **qa, prod, dr require human attestation** — the approver reviews contract, planned Terraform, and accumulated evidence. <span class="badge planned">Planned</span>
|
- **qa, prod, dr require human attestation** — the approver reviews contract, planned Terraform, and accumulated evidence. <span class="badge planned">Planned</span>
|
||||||
- **Separation of duties is enforced** — the QA approver **cannot** be the prod approver. The platform **blocks on a match.** <span class="badge planned">Planned</span>
|
- **Separation of duties is enforced** — the QA approver **cannot** be the prod approver. The platform **blocks on a match.** <span class="badge planned">Planned</span>
|
||||||
- **Every deployment writes a hash-chained evidence event** — tampering breaks the chain. **RPO = 0.** <span class="badge testing">Testing</span>
|
- **Every deployment writes a hash-chained evidence event** — tampering breaks the chain. **RPO = 0.**
|
||||||
|
|
||||||
> **Speaker notes:** The "lower environments autonomous, higher environments attested" tenet is the resolution to the classic "move fast vs. be safe" false dichotomy. Be honest: the separation-of-duties *mechanism* is designed and the dev path is wired; qa/prod/dr wiring is on the roadmap. The audit trail is a byproduct of deployment, not a project. The full regulatory ledger (S3 Object Lock, JWS signatures, daily checkpoints) is planned; what ships today is the outbox + hash chain that makes every event tamper-evident and queryable.
|
> **Speaker notes:** The "lower environments autonomous, higher environments attested" tenet is the resolution to the classic "move fast vs. be safe" false dichotomy. Be honest: the separation-of-duties *mechanism* is designed and the dev path is wired; qa/prod/dr wiring is on the roadmap. The audit trail is a byproduct of deployment, not a project. The full regulatory ledger (S3 Object Lock, JWS signatures, daily checkpoints) is planned; what ships today is the outbox + hash chain that makes every event tamper-evident and queryable.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Slide 10 — The vision realized
|
## Slide 11 — The vision realized
|
||||||
|
|
||||||
- **Velocity without sacrificing safety.** Speed is in the ergonomics; safety is in the gates the consumer cannot bypass.
|
- **Velocity without sacrificing safety.** Speed is in the ergonomics; safety is in the gates the consumer cannot bypass.
|
||||||
- **Security, observability, and compliance as platform defaults** — not per-team effort, not post-hoc remediation.
|
- **Security, observability, and compliance as platform defaults** — not per-team effort, not post-hoc remediation.
|
||||||
- **Auditability as a byproduct, not a project.** Every production change is traceable to a human attestation and a tamper-evident evidence event.
|
- **Auditability as a byproduct, not a project.** Every production change is traceable to a human attestation and a tamper-evident evidence event.
|
||||||
- **Blast radius contained by design.** Zero-trust OIDC + ABAC means a consumer can only touch its own tagged resources.
|
- **Blast radius contained by design.** Zero-trust OIDC + ABAC means a consumer can only touch its own tagged resources.
|
||||||
- **Infrastructure as a utility, not a craft.** Teams consume infrastructure, they don't maintain it.
|
- **Infrastructure as a utility, not a craft.** Teams consume infrastructure, they don't maintain it.
|
||||||
- **A path to the citizen developer.** The same safety envelope serves a senior engineer and a non-technical consumer. <span class="badge agentic">Agentic</span>
|
- **A path to the citizen developer.** The same safety envelope serves a senior engineer and a non-technical consumer.
|
||||||
|
|
||||||
> **Speaker notes:** Close on the strategic frame. The platform is not "a CI/CD tool" — it's the organizational lever for shipping safely at the pace the business demands, with the security and audit posture the regulators require. The investment is in the abstraction, not the tool.
|
> **Speaker notes:** Close on the strategic frame. The platform is not "a CI/CD tool" — it's the organizational lever for shipping safely at the pace the business demands, with the security and audit posture the regulators require. The investment is in the abstraction, not the tool.
|
||||||
|
|
||||||
@@ -315,7 +376,7 @@ Monitoring is **a platform default, not a per-team project.** *(Testing.)*
|
|||||||
|
|
||||||
Security defaults that **do not require a team to opt in.** Checks run on **every** deployment, normalized to a single schema regardless of which engine produced them. *(Testing.)*
|
Security defaults that **do not require a team to opt in.** Checks run on **every** deployment, normalized to a single schema regardless of which engine produced them. *(Testing.)*
|
||||||
|
|
||||||
- **Infrastructure-as-code policy** (Checkov) — secrets in plaintext, public ingress, IAM wildcards, KMS key references, **required tagging standards** (`acdl:owner`, `acdl:contract`, `acdl:environment`, `acdl:cost-center`). All run *before* infra is created.
|
- **Infrastructure-as-code policy** (Checkov) — secrets in plaintext, public ingress, IAM wildcards, KMS key references, **required tagging standards** (`nova:owner`, `nova:contract`, `nova:environment`, `nova:cost-center`). All run *before* infra is created.
|
||||||
- **Cloud security posture** (Wiz adapter) — translates cloud security findings into the same normalized record. *(Adapter testing; activates when a Wiz tenant is configured.)*
|
- **Cloud security posture** (Wiz adapter) — translates cloud security findings into the same normalized record. *(Adapter testing; activates when a Wiz tenant is configured.)*
|
||||||
- **Kubernetes-native policy** (Kyverno adapter) — ready for the GitOps reconciler roadmap item. *(Adapter testing; inactive for Terraform-only stacks.)*
|
- **Kubernetes-native policy** (Kyverno adapter) — ready for the GitOps reconciler roadmap item. *(Adapter testing; inactive for Terraform-only stacks.)*
|
||||||
- **Encryption on every resource** — at-rest encryption is on by default for every primitive (S3, RDS, ECR, ECS, and more). *(Testing.)*
|
- **Encryption on every resource** — at-rest encryption is on by default for every primitive (S3, RDS, ECR, ECS, and more). *(Testing.)*
|
||||||
@@ -324,7 +385,7 @@ Security defaults that **do not require a team to opt in.** Checks run on **ever
|
|||||||
- **Deletion protection on by default** — every resource has `prevent_destroy` on unless a consumer explicitly disables it via a documented feature flag. *(Testing.)*
|
- **Deletion protection on by default** — every resource has `prevent_destroy` on unless a consumer explicitly disables it via a documented feature flag. *(Testing.)*
|
||||||
- **Safe decommission** — a 2-step pipeline (disable protection → zero counts → destroy) with **two SRE human-attestation gates** and a **change-request validated against the platform CMDB** before any destructive action. *(Testing.)* Encryption keys enter a grace window (default 30 days) so encrypted data remains recoverable during decommission.
|
- **Safe decommission** — a 2-step pipeline (disable protection → zero counts → destroy) with **two SRE human-attestation gates** and a **change-request validated against the platform CMDB** before any destructive action. *(Testing.)* Encryption keys enter a grace window (default 30 days) so encrypted data remains recoverable during decommission.
|
||||||
|
|
||||||
> **Speaker notes:** The phrase to land is "secure by default, not secure by effort." The selling point is *normalization* — we can add a new security tool without changing the confidence model or the evidence stream. For the Head of Security: tagging standards are enforced, not advisory — a missing `acdl:owner` tag fails the check, not a warning. The decommission flow is the counter-argument to "deletion protection makes cleanup impossible" — it's a deliberate, gated, two-approval path, not a lock with no key.
|
> **Speaker notes:** The phrase to land is "secure by default, not secure by effort." The selling point is *normalization* — we can add a new security tool without changing the confidence model or the evidence stream. For the Head of Security: tagging standards are enforced, not advisory — a missing `nova:owner` tag fails the check, not a warning. The decommission flow is the counter-argument to "deletion protection makes cleanup impossible" — it's a deliberate, gated, two-approval path, not a lock with no key.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -373,7 +434,7 @@ A phased roadmap from the current Testing baseline to the full North Star:
|
|||||||
- Additional engine adapters (OpenTofu, Pulumi, Kubernetes CRDs).
|
- Additional engine adapters (OpenTofu, Pulumi, Kubernetes CRDs).
|
||||||
- Deeper observability bootstrap (dashboards, runbooks, on-call bindings).
|
- Deeper observability bootstrap (dashboards, runbooks, on-call bindings).
|
||||||
|
|
||||||
> **Speaker notes:** Close on honesty. The platform delivers real, verifiable value today — 22/22 auto-verifiable capabilities are Verified via the v1.11 lifecycle pipeline (apply→modify→destroy against live AWS) + the D-091 regression gate. The roadmap is concrete, not aspirational hand-waving — 9 planned items, each with a defined milestone and a clear reason it isn't shipped yet (usually an upstream dependency, not an engineering gap). Emphasize: 0 consumer adoption today — "Testing" means it works internally and is dev pilot-ready, not that it's released. The lifecycle pipeline defaults to **plan-only** on every PR (fast, no AWS mutation, no cost); a CI variable (`ACDL_LIFECYCLE_MODE=full`) overrides to the real apply→destroy for milestone verification (REQ-134, v1.12).
|
> **Speaker notes:** Close on honesty. The platform delivers real, verifiable value today — 22/22 auto-verifiable capabilities are Verified via the v1.11 lifecycle pipeline (apply→modify→destroy against live AWS) + the D-091 regression gate. The roadmap is concrete, not aspirational hand-waving — 9 planned items, each with a defined milestone and a clear reason it isn't shipped yet (usually an upstream dependency, not an engineering gap). Emphasize: 0 consumer adoption today — "Testing" means it works internally and is dev pilot-ready, not that it's released. The lifecycle pipeline defaults to **plan-only** on every PR (fast, no AWS mutation, no cost); a CI variable (`NOVA_LIFECYCLE_MODE=full`) overrides to the real apply→destroy for milestone verification (REQ-134, v1.12).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -397,7 +458,7 @@ A phased roadmap from the current Testing baseline to the full North Star:
|
|||||||
|
|
||||||
## A7 — Operating Model & Cost (real AWS spend + pre-mortem)
|
## A7 — Operating Model & Cost (real AWS spend + pre-mortem)
|
||||||
|
|
||||||
ACDL runs at **zero cloud cost** for day-to-day development. The v1.0→v1.10 AWS spend was measured directly via Cost Explorer (`COST.md`, 2026-07-28):
|
Nova runs at **zero cloud cost** for day-to-day development. The v1.0→v1.10 AWS spend was measured directly via Cost Explorer (`COST.md`, 2026-07-28):
|
||||||
|
|
||||||
| Metric | Value |
|
| Metric | Value |
|
||||||
|--------|-------|
|
|--------|-------|
|
||||||
@@ -408,7 +469,7 @@ ACDL runs at **zero cloud cost** for day-to-day development. The v1.0→v1.10 AW
|
|||||||
|
|
||||||
- **S3 dominates** (98.8%, terraform state bucket) — no compute (ECS/Lambda) ran because v1.0→v1.10 was plan-only for IAM-gated capabilities.
|
- **S3 dominates** (98.8%, terraform state bucket) — no compute (ECS/Lambda) ran because v1.0→v1.10 was plan-only for IAM-gated capabilities.
|
||||||
- **Local emulators are the primary tier** — the full pipeline runs in-process, no AWS credentials, no Checkov, no DynamoDB. *(Testing.)*
|
- **Local emulators are the primary tier** — the full pipeline runs in-process, no AWS credentials, no Checkov, no DynamoDB. *(Testing.)*
|
||||||
- **Live-AWS verification is milestone-scoped, then torn down.** The v1.11 lifecycle pipeline ran apply→modify→destroy for every module, then tore down to zero-cost steady state (D-096 — teardown mandatory before milestone COMPLETE; no merge to main until `terraform show` confirms no resources). The lifecycle pipeline now **defaults to plan-only** on every PR (fast, no AWS mutation, no cost); a CI variable (`ACDL_LIFECYCLE_MODE=full`) overrides to the real apply→destroy for milestone verification (REQ-134, v1.12).
|
- **Live-AWS verification is milestone-scoped, then torn down.** The v1.11 lifecycle pipeline ran apply→modify→destroy for every module, then tore down to zero-cost steady state (D-096 — teardown mandatory before milestone COMPLETE; no merge to main until `terraform show` confirms no resources). The lifecycle pipeline now **defaults to plan-only** on every PR (fast, no AWS mutation, no cost); a CI variable (`NOVA_LIFECYCLE_MODE=full`) overrides to the real apply→destroy for milestone verification (REQ-134, v1.12).
|
||||||
- **Cost drivers** are spike-scoped: Terraform plan reads (free), S3 state storage (cents), DynamoDB outbox (cents). Any cost spike > $1/day is an anomaly.
|
- **Cost drivers** are spike-scoped: Terraform plan reads (free), S3 state storage (cents), DynamoDB outbox (cents). Any cost spike > $1/day is an anomaly.
|
||||||
|
|
||||||
**Pre-mortem (`PRE_MORTEM.md`):** the project's failure modes were pre-mortemed before the leadership pitch. The v1.10 decay incident (diff-scoped VERIFY missed 7 adapter defects across 8 NFR-patch phases — decks advertised capability that wasn't reproducible) is the root pattern: *a claim outruns the verification that backs it.* Four forward failure modes + structural mitigations: (FM-1) IAM-drift recurrence → IAM policy baseline is regression-tested; (FM-2) cost spike from un-torn-down stacks → D-096 mandatory teardown; (FM-3) deck overstates capability → verified-only claims + decks unfrozen only after re-verification; (FM-4) pilot contract gap → honest scope (microservice + static-assets today; the L2 pattern is extensible). All mitigations are structural, not procedural.
|
**Pre-mortem (`PRE_MORTEM.md`):** the project's failure modes were pre-mortemed before the leadership pitch. The v1.10 decay incident (diff-scoped VERIFY missed 7 adapter defects across 8 NFR-patch phases — decks advertised capability that wasn't reproducible) is the root pattern: *a claim outruns the verification that backs it.* Four forward failure modes + structural mitigations: (FM-1) IAM-drift recurrence → IAM policy baseline is regression-tested; (FM-2) cost spike from un-torn-down stacks → D-096 mandatory teardown; (FM-3) deck overstates capability → verified-only claims + decks unfrozen only after re-verification; (FM-4) pilot contract gap → honest scope (microservice + static-assets today; the L2 pattern is extensible). All mitigations are structural, not procedural.
|
||||||
@@ -422,6 +483,6 @@ ACDL runs at **zero cloud cost** for day-to-day development. The v1.0→v1.10 AW
|
|||||||
v1.11 rebuilt the platform on two architectural pillars that make "Verified" a structural property, not a claim:
|
v1.11 rebuilt the platform on two architectural pillars that make "Verified" a structural property, not a claim:
|
||||||
|
|
||||||
- **The stateless adapter (REQ-123, 918 → ~80 lines).** The Terraform adapter was a 918-line monolith with 3 constant tables and 39 type-specific branches. It is now a ~80-line **stateless assembler**: it owns no module content — no resource shape, no nested HCL blocks, no defaults, no type-specific logic. Each L1 module ships a real `terraform/` module dir owning its resource shape, nested blocks, and defaults (centralized in `locals.tf`). The adapter reads the registry and emits `module "x" { source = ... }` blocks. No type-specific logic in the adapter means a new module is a new terraform dir, not a code change. *(The v1.12 P67 fix closed a dedup defect where multi-resource L1s — ecs-service, alb — produced invalid Terraform; CAP-013 now Verified.)*
|
- **The stateless adapter (REQ-123, 918 → ~80 lines).** The Terraform adapter was a 918-line monolith with 3 constant tables and 39 type-specific branches. It is now a ~80-line **stateless assembler**: it owns no module content — no resource shape, no nested HCL blocks, no defaults, no type-specific logic. Each L1 module ships a real `terraform/` module dir owning its resource shape, nested blocks, and defaults (centralized in `locals.tf`). The adapter reads the registry and emits `module "x" { source = ... }` blocks. No type-specific logic in the adapter means a new module is a new terraform dir, not a code change. *(The v1.12 P67 fix closed a dedup defect where multi-resource L1s — ecs-service, alb — produced invalid Terraform; CAP-013 now Verified.)*
|
||||||
- **Pipeline-driven lifecycle testing (REQ-127/128).** A `modules-lifecycle` pipeline matrix-runs each L1 and L2 module's `examples/{simple,complex}.yml` contracts through apply→modify→destroy against live AWS. No per-module Python. **The "test" = the pipeline cell going green.** Defaults to **plan-only** on every PR (fast, no AWS mutation, no cost); `ACDL_LIFECYCLE_MODE=full` runs the real apply→destroy for milestone verification (REQ-134, v1.12). The regression gate (D-091) re-runs all 22 capabilities at milestone completion — 22/22 Verified as of v1.12.
|
- **Pipeline-driven lifecycle testing (REQ-127/128).** A `modules-lifecycle` pipeline matrix-runs each L1 and L2 module's `examples/{simple,complex}.yml` contracts through apply→modify→destroy against live AWS. No per-module Python. **The "test" = the pipeline cell going green.** Defaults to **plan-only** on every PR (fast, no AWS mutation, no cost); `NOVA_LIFECYCLE_MODE=full` runs the real apply→destroy for milestone verification (REQ-134, v1.12). The regression gate (D-091) re-runs all 22 capabilities at milestone completion — 22/22 Verified as of v1.12.
|
||||||
|
|
||||||
> **Speaker notes:** This is the deep-dive slide for the Head of Engineering / Architecture. The two pillars are the answer to "how do you keep the decks honest?" The adapter is simple enough to reason about (a stateless assembler), and the lifecycle pipeline is the automated verification that backs every "Testing" claim. The v1.10 lesson is the negative space: a 918-line adapter with type-specific branches decayed silently because the VERIFY gate was diff-scoped. The ~80-line stateless adapter + the milestone regression gate are the structural fix. The plan-only default (v1.12) means this verification runs on every PR at zero cost, with the full apply→destroy gated behind a CI variable override.
|
> **Speaker notes:** This is the deep-dive slide for the Head of Engineering / Architecture. The two pillars are the answer to "how do you keep the decks honest?" The adapter is simple enough to reason about (a stateless assembler), and the lifecycle pipeline is the automated verification that backs every "Testing" claim. The v1.10 lesson is the negative space: a 918-line adapter with type-specific branches decayed silently because the VERIFY gate was diff-scoped. The ~80-line stateless adapter + the milestone regression gate are the structural fix. The plan-only default (v1.12) means this verification runs on every PR at zero cost, with the full apply→destroy gated behind a CI variable override.
|
||||||
@@ -25,9 +25,7 @@ style: |
|
|||||||
display: inline-block; padding: 2px 8px; border-radius: 4px;
|
display: inline-block; padding: 2px 8px; border-radius: 4px;
|
||||||
font-size: 16px; font-weight: 600;
|
font-size: 16px; font-weight: 600;
|
||||||
}
|
}
|
||||||
.testing { background: #DBEAFE; color: #1E3A5F; }
|
|
||||||
.planned { background: #fef3c7; color: #78350f; }
|
.planned { background: #fef3c7; color: #78350f; }
|
||||||
.agentic { background: #EDE9FE; color: #4C1D95; }
|
|
||||||
---
|
---
|
||||||
|
|
||||||
<!-- _class: title -->
|
<!-- _class: title -->
|
||||||
@@ -35,7 +33,7 @@ style: |
|
|||||||
|
|
||||||
# The Developer Experience
|
# The Developer Experience
|
||||||
|
|
||||||
### Agentic Cloud Delivery Platform
|
### Nova — The New Dawn of DevSecOps
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
section.title h1 { font-size: 44px; margin-bottom: 0.1em; }
|
section.title h1 { font-size: 44px; margin-bottom: 0.1em; }
|
||||||
@@ -49,9 +47,20 @@ section.title h3 { color: #F0F0F0; font-weight: 400; font-size: 22px; margin-top
|
|||||||

|

|
||||||
|
|
||||||
- **Technical developer** — owns app code + a contract + a thin CI definition
|
- **Technical developer** — owns app code + a contract + a thin CI definition
|
||||||
- **Citizen developer** — declares intent; an AI agent produces a contract that passes the **same** safety envelope <span class="badge agentic">Agentic</span>
|
- **Citizen developer** — declares intent; an AI agent produces a contract that passes the **same** safety envelope
|
||||||
- **Upstream is anything** — IDE, agentic SDLC, or vibe coding. ACDL doesn't care how the contract was produced
|
- **Upstream is anything** — IDE, agentic SDLC, or vibe coding. Nova doesn't care how the contract was produced
|
||||||
- **ACDL is infrastructure only** — provisions and governs AWS resources. Application deployment is upstream
|
- **Nova is infrastructure only** — provisions and governs AWS resources. Application deployment is upstream
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# The platform at a glance
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
- **You own the left edge** — app code and a contract. That is the entire consumer surface
|
||||||
|
- **The platform owns the middle** — pipeline, catalog, adapter, environments, gates, evidence
|
||||||
|
- **Two surfaces, one pipeline, one evidence stream** — senior engineer and citizen dev converge on the same safety envelope
|
||||||
|
- **The bar rises automatically** — confidence signal + HITL gates scale with the target environment, not a ticket
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -87,7 +96,7 @@ infrastructure:
|
|||||||
- **PR comments after every successful pipeline stage** — always know where you stand
|
- **PR comments after every successful pipeline stage** — always know where you stand
|
||||||
- **Clear, explainable halt reasons** — a policy violation, an insufficient signal, or a missing attestation. **Never opaque.**
|
- **Clear, explainable halt reasons** — a policy violation, an insufficient signal, or a missing attestation. **Never opaque.**
|
||||||
- **Connection strings posted as PR comments** — human-readable, no hunting. Runtime secrets go to encrypted Parameter Store, never to logs
|
- **Connection strings posted as PR comments** — human-readable, no hunting. Runtime secrets go to encrypted Parameter Store, never to logs
|
||||||
- **Errors become GitHub issues, automatically** — a failed deploy opens an issue on the platform repo <span class="badge testing">Testing</span>
|
- **Errors become GitHub issues, automatically** — a failed deploy opens an issue on the platform repo
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -98,7 +107,7 @@ infrastructure:
|
|||||||
- **Primitives** — single-purpose resources (S3, VPC, ECS, IAM, ALB, ECR, CloudFront, WAF, RDS)
|
- **Primitives** — single-purpose resources (S3, VPC, ECS, IAM, ALB, ECR, CloudFront, WAF, RDS)
|
||||||
- **Modules** — composed patterns (static site with CDN + WAF; microservice with VPC + ECS + ALB + ECR)
|
- **Modules** — composed patterns (static site with CDN + WAF; microservice with VPC + ECS + ALB + ECR)
|
||||||
- **Validated examples per module** — `simple.yaml` + `complex.yaml`, validated against the contract schema in CI
|
- **Validated examples per module** — `simple.yaml` + `complex.yaml`, validated against the contract schema in CI
|
||||||
- **Auto-promotion of patterns** — after 3 observed usages <span class="badge planned">Planned</span> <span class="badge agentic">Agentic</span>
|
- **Auto-promotion of patterns** — after 3 observed usages <span class="badge planned">Planned</span>
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -108,7 +117,7 @@ infrastructure:
|
|||||||
|
|
||||||
| Environment | What the platform adds | Maturity |
|
| Environment | What the platform adds | Maturity |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| dev | Confidence ≥ 0.50, fully autonomous | <span class="badge testing">Testing</span> |
|
| dev | Confidence ≥ 0.50, fully autonomous | — |
|
||||||
| qa | QA human attestation + confidence ≥ 0.75 | <span class="badge planned">Planned</span> |
|
| qa | QA human attestation + confidence ≥ 0.75 | <span class="badge planned">Planned</span> |
|
||||||
| prod | SRE human attestation + confidence ≥ 0.90 | <span class="badge planned">Planned</span> |
|
| prod | SRE human attestation + confidence ≥ 0.90 | <span class="badge planned">Planned</span> |
|
||||||
| dr | SRE human attestation + confidence ≥ 0.95 + DR drill | <span class="badge planned">Planned</span> |
|
| dr | SRE human attestation + confidence ≥ 0.95 + DR drill | <span class="badge planned">Planned</span> |
|
||||||
@@ -131,14 +140,14 @@ code { font-size: 13px; }
|
|||||||
```yaml
|
```yaml
|
||||||
uses: acdl/.github/workflows/deploy.yml@v1.12
|
uses: acdl/.github/workflows/deploy.yml@v1.12
|
||||||
with:
|
with:
|
||||||
contract: .acdl/contract.yml
|
contract: .nova/contract.yml
|
||||||
mode: decommission
|
mode: decommission
|
||||||
changeRequestId: "CHG0678912"
|
changeRequestId: "CHG0678912"
|
||||||
```
|
```
|
||||||
|
|
||||||
- **Validate the change request** — platform queries the CMDB; CR must be `approved` and match the consumer repo
|
- **Validate the change request** — platform queries the CMDB; CR must be `approved` and match the consumer repo
|
||||||
- **Two SRE human-attestation gates** — disable protection → SRE approves → zero counts + destroy → second SRE approves
|
- **Two SRE human-attestation gates** — disable protection → SRE approves → zero counts + destroy → second SRE approves
|
||||||
- **Per-stack encryption key enters a grace window** (default 30 days) so encrypted data remains recoverable <span class="badge testing">Testing</span>
|
- **Per-stack encryption key enters a grace window** (default 30 days) so encrypted data remains recoverable
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -150,13 +159,13 @@ with:
|
|||||||
- **Semantic versioning with a clear contract:** interface → MAJOR, behavior → MINOR, lifecycle → PATCH
|
- **Semantic versioning with a clear contract:** interface → MAJOR, behavior → MINOR, lifecycle → PATCH
|
||||||
- **Pin to an exact version** for stability, or float on MAJOR only (`@v1`) to absorb new features on your own cadence
|
- **Pin to an exact version** for stability, or float on MAJOR only (`@v1`) to absorb new features on your own cadence
|
||||||
- **Unversioned references (`@main`, bare) are discouraged** — the versioned tag is the only immutability lever
|
- **Unversioned references (`@main`, bare) are discouraged** — the versioned tag is the only immutability lever
|
||||||
- **Automated release job** computes the next semver on merge to main, creates the tag, and updates floating tags <span class="badge testing">Testing</span>
|
- **Automated release job** computes the next semver on merge to main, creates the tag, and updates floating tags
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# Fails gracefully, not opaquely
|
# Fails gracefully, not opaquely
|
||||||
|
|
||||||
First impressions of a platform are made **when it fails for the first time.** The platform fails gracefully. <span class="badge testing">Testing</span>
|
First impressions of a platform are made **when it fails for the first time.** The platform fails gracefully.
|
||||||
|
|
||||||
When no environment is bound, the platform emits a **user-friendly onboarding prompt** instead of failing opaquely:
|
When no environment is bound, the platform emits a **user-friendly onboarding prompt** instead of failing opaquely:
|
||||||
|
|
||||||
@@ -182,7 +191,7 @@ The pipeline then **exits without attempting a deployment** — no partial state
|
|||||||
- **Blast radius contained by design** — OIDC + ABAC, only your own tagged resources
|
- **Blast radius contained by design** — OIDC + ABAC, only your own tagged resources
|
||||||
- **The bottleneck moves off the platform team's ticket queue** — a merged change progresses without a platform engineer joining a thread
|
- **The bottleneck moves off the platform team's ticket queue** — a merged change progresses without a platform engineer joining a thread
|
||||||
- **Infrastructure as a utility, not a craft** — consume, don't maintain
|
- **Infrastructure as a utility, not a craft** — consume, don't maintain
|
||||||
- **A path to the citizen developer** — same envelope, senior engineer or non-technical <span class="badge agentic">Agentic</span>
|
- **A path to the citizen developer** — same envelope, senior engineer or non-technical
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -217,7 +226,7 @@ A non-technical consumer ships a production deployment **by declaring intent**
|
|||||||
- Agents are **stateless** — all state lives in the platform; the platform trusts and **always verifies**
|
- Agents are **stateless** — all state lives in the platform; the platform trusts and **always verifies**
|
||||||
- The agent's trace and submission confidence are captured in the contract for review
|
- The agent's trace and submission confidence are captured in the contract for review
|
||||||
|
|
||||||
<span class="badge planned">Skill catalog + real agent runtime: planned</span> <span class="badge agentic">Agentic</span>
|
<span class="badge planned">Skill catalog + real agent runtime: planned</span>
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -236,7 +245,7 @@ Consumers `uses:` a **versioned** central workflow. The platform fetches itself
|
|||||||
|
|
||||||
# A3 — Local Reproducibility
|
# A3 — Local Reproducibility
|
||||||
|
|
||||||
The entire CI pipeline runs **from the shell**, not just in CI. <span class="badge testing">Testing</span>
|
The entire CI pipeline runs **from the shell**, not just in CI.
|
||||||
|
|
||||||
- `scripts/run_ci.sh` mirrors the CI pipeline locally — the same three stages (lint → test → check-only) in sequence
|
- `scripts/run_ci.sh` mirrors the CI pipeline locally — the same three stages (lint → test → check-only) in sequence
|
||||||
- `scripts/run_platform.sh --check-only` runs the platform **offline** — no AWS, no policy engine, no outbox required. Validates a contract end-to-end before pushing
|
- `scripts/run_platform.sh --check-only` runs the platform **offline** — no AWS, no policy engine, no outbox required. Validates a contract end-to-end before pushing
|
||||||
@@ -278,7 +287,7 @@ section { font-size: 20px; }
|
|||||||
table { font-size: 18px; }
|
table { font-size: 18px; }
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
ACDL runs at **zero cloud cost** for day-to-day development. AWS spend was measured via Cost Explorer (`COST.md`, 2026-07-28):
|
Nova runs at **zero cloud cost** for day-to-day development. AWS spend was measured via Cost Explorer (`COST.md`, 2026-07-28):
|
||||||
|
|
||||||
| Metric | Value |
|
| Metric | Value |
|
||||||
|--------|-------|
|
|--------|-------|
|
||||||
@@ -287,8 +296,8 @@ ACDL runs at **zero cloud cost** for day-to-day development. AWS spend was measu
|
|||||||
| Projected monthly | ~$0.007 |
|
| Projected monthly | ~$0.007 |
|
||||||
| Peak day | 2026-07-27 ($0.000867) |
|
| Peak day | 2026-07-27 ($0.000867) |
|
||||||
|
|
||||||
- **Local emulators are the primary tier** — the full pipeline runs in-process, no AWS credentials <span class="badge testing">Testing</span>
|
- **Local emulators are the primary tier** — the full pipeline runs in-process, no AWS credentials
|
||||||
- **Live-AWS verification is milestone-scoped, then torn down.** The pipeline now **defaults to plan-only** on every PR; `ACDL_LIFECYCLE_MODE=full` overrides to apply→destroy for milestone verification (REQ-134, v1.12).
|
- **Live-AWS verification is milestone-scoped, then torn down.** The pipeline now **defaults to plan-only** on every PR; `NOVA_LIFECYCLE_MODE=full` overrides to apply→destroy for milestone verification (REQ-134, v1.12).
|
||||||
- **Cost drivers** are spike-scoped: Terraform plan reads (free), S3 state storage (cents), DynamoDB outbox (cents). No running infrastructure between milestones.
|
- **Cost drivers** are spike-scoped: Terraform plan reads (free), S3 state storage (cents), DynamoDB outbox (cents). No running infrastructure between milestones.
|
||||||
|
|
||||||
**Pre-mortem (`PRE_MORTEM.md`):** the v1.10 decay incident (diff-scoped VERIFY missed 7 adapter defects) is the root pattern: *a claim outruns the verification that backs it.* Four forward failure modes + structural mitigations (regression-tested IAM baseline, mandatory teardown, verified-only deck claims, honest scope).
|
**Pre-mortem (`PRE_MORTEM.md`):** the v1.10 decay incident (diff-scoped VERIFY missed 7 adapter defects) is the root pattern: *a claim outruns the verification that backs it.* Four forward failure modes + structural mitigations (regression-tested IAM baseline, mandatory teardown, verified-only deck claims, honest scope).
|
||||||
@@ -307,6 +316,6 @@ section { font-size: 20px; }
|
|||||||
Two architectural pillars make "Verified" a structural property, not a claim:
|
Two architectural pillars make "Verified" a structural property, not a claim:
|
||||||
|
|
||||||
- **The stateless adapter (918 → ~80 lines).** The Terraform adapter was a 918-line monolith with 3 constant tables and 39 type-specific branches. It is now a ~80-line **stateless assembler**: it owns no module content — no resource shape, no nested HCL blocks, no defaults. Each L1 module ships a real `terraform/` module dir owning its shape, nested blocks, and defaults. The adapter reads the registry and emits `module "x" { source = ... }` blocks. A new module is a new terraform dir, not a code change. *(The v1.12 P67 fix closed a dedup defect for multi-resource L1s — ecs-service, alb; CAP-013 now Verified.)*
|
- **The stateless adapter (918 → ~80 lines).** The Terraform adapter was a 918-line monolith with 3 constant tables and 39 type-specific branches. It is now a ~80-line **stateless assembler**: it owns no module content — no resource shape, no nested HCL blocks, no defaults. Each L1 module ships a real `terraform/` module dir owning its shape, nested blocks, and defaults. The adapter reads the registry and emits `module "x" { source = ... }` blocks. A new module is a new terraform dir, not a code change. *(The v1.12 P67 fix closed a dedup defect for multi-resource L1s — ecs-service, alb; CAP-013 now Verified.)*
|
||||||
- **Pipeline-driven lifecycle testing.** A `modules-lifecycle` pipeline matrix-runs each L1 and L2 module's contracts through apply→modify→destroy against live AWS. **The "test" = the pipeline cell going green.** Defaults to **plan-only** on every PR (fast, no AWS mutation, no cost); `ACDL_LIFECYCLE_MODE=full` overrides to the real apply→destroy for milestone verification (REQ-134, v1.12). The regression gate (D-091) re-runs all 22 capabilities at milestone completion — **22/22 Verified** as of v1.12.
|
- **Pipeline-driven lifecycle testing.** A `modules-lifecycle` pipeline matrix-runs each L1 and L2 module's contracts through apply→modify→destroy against live AWS. **The "test" = the pipeline cell going green.** Defaults to **plan-only** on every PR (fast, no AWS mutation, no cost); `NOVA_LIFECYCLE_MODE=full` overrides to the real apply→destroy for milestone verification (REQ-134, v1.12). The regression gate (D-091) re-runs all 22 capabilities at milestone completion — **22/22 Verified** as of v1.12.
|
||||||
|
|
||||||
The v1.10 lesson is the negative space: a 918-line adapter with type-specific branches decayed silently. The ~80-line stateless adapter + the milestone regression gate are the structural fix.
|
The v1.10 lesson is the negative space: a 918-line adapter with type-specific branches decayed silently. The ~80-line stateless adapter + the milestone regression gate are the structural fix.
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
# The Developer Experience — Talking Points
|
# The Developer Experience — Talking Points
|
||||||
|
|
||||||
> **Companion to:** `the-developer-experience-marp.md` (10 main + Appendix TOC + 7 appendix = 18 slides)
|
> **Companion to:** `the-developer-experience-marp.md` (11 main + Appendix TOC + 7 appendix = 19 slides)
|
||||||
> **Content source:** `the-developer-experience.md` (full source of truth with speaker notes)
|
> **Content source:** `the-developer-experience.md` (full source of truth with speaker notes)
|
||||||
> **Purpose:** Presenter-ready cues — 3-6 talking points per slide + the one key takeaway the audience should remember.
|
> **Purpose:** Presenter-ready cues — 3-6 talking points per slide + the one key takeaway the audience should remember.
|
||||||
> **Audience:** Senior Leadership — CTO, Head of Cloud, Head of Infrastructure, Head of DevOps
|
> **Audience:** Senior Leadership — CTO, Head of Cloud, Head of Infrastructure, Head of DevOps
|
||||||
@@ -21,17 +21,29 @@
|
|||||||
## Slide 2 — Two consumer paths, one safety envelope
|
## Slide 2 — Two consumer paths, one safety envelope
|
||||||
|
|
||||||
**Talking points:**
|
**Talking points:**
|
||||||
- This is the scope-boundary slide — here's who uses the platform, and here's where ACDL's responsibility starts and stops
|
- This is the scope-boundary slide — here's who uses the platform, and here's where Nova's responsibility starts and stops
|
||||||
- Two consumer paths converge on the same contract: **technical** developer writes the contract directly; **citizen** developer declares intent and an AI agent produces a contract that passes the same safety envelope
|
- Two consumer paths converge on the same contract: **technical** developer writes the contract directly; **citizen** developer declares intent and an AI agent produces a contract that passes the same safety envelope
|
||||||
- Upstream is anything — your IDE, an agentic SDLC, or vibe coding on a laptop. ACDL doesn't care how the contract was produced
|
- Upstream is anything — your IDE, an agentic SDLC, or vibe coding on a laptop. Nova doesn't care how the contract was produced
|
||||||
- ACDL is infrastructure only — it provisions and governs AWS resources. Application deployment is upstream of the contract
|
- Nova is infrastructure only — it provisions and governs AWS resources. Application deployment is upstream of the contract
|
||||||
- The two surfaces are *parallel*, not a progression. A citizen developer doesn't "graduate" to the developer surface. There is no "citizen developer mode" with weaker checks
|
- The two surfaces are *parallel*, not a progression. A citizen developer doesn't "graduate" to the developer surface. There is no "citizen developer mode" with weaker checks
|
||||||
|
|
||||||
**Key takeaway:** Two consumer paths, one safety envelope. ACDL is infra only — anything upstream is fair game.
|
**Key takeaway:** Two consumer paths, one safety envelope. Nova is infra only — anything upstream is fair game.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Slide 3 — Three things. The entire consumer surface.
|
## Slide 3 — The platform at a glance
|
||||||
|
|
||||||
|
**Talking points:**
|
||||||
|
- One-slide map — frame it from the left edge: "this is what you touch, this is what the platform owns for you"
|
||||||
|
- The leadership beat: the convergence — two surfaces, one pipeline, one evidence stream — is the design point that lets us expand who can ship safely without lowering the bar
|
||||||
|
- Don't walk every node — point to the contract boundary and say "the rest of this deck zooms into the developer-facing pieces"
|
||||||
|
- The bar rises automatically — the confidence signal and HITL gates scale with the target environment, not with a ticket
|
||||||
|
|
||||||
|
**Key takeaway:** You own the left edge (app + contract). The platform owns everything else, end to end.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Slide 4 — Three things. The entire consumer surface.
|
||||||
|
|
||||||
**Talking points:**
|
**Talking points:**
|
||||||
- Hold this slide — the audience should sit with how small the consumer surface is. Three things: app code, a contract, a one-line CI definition
|
- Hold this slide — the audience should sit with how small the consumer surface is. Three things: app code, a contract, a one-line CI definition
|
||||||
@@ -44,7 +56,7 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Slide 4 — See what the platform does, in real time
|
## Slide 5 — See what the platform does, in real time
|
||||||
|
|
||||||
**Talking points:**
|
**Talking points:**
|
||||||
- This directly answers "but developers hate platforms that hide what they're doing" — the platform is opinionated about *what* runs, not *opaque* about *that* it runs
|
- This directly answers "but developers hate platforms that hide what they're doing" — the platform is opinionated about *what* runs, not *opaque* about *that* it runs
|
||||||
@@ -58,7 +70,7 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Slide 5 — Pick from pre-built, security-reviewed blocks
|
## Slide 6 — Pick from pre-built, security-reviewed blocks
|
||||||
|
|
||||||
**Talking points:**
|
**Talking points:**
|
||||||
- The catalog is what makes "declare intent" practical — you can only declare a module that exists
|
- The catalog is what makes "declare intent" practical — you can only declare a module that exists
|
||||||
@@ -72,7 +84,7 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Slide 6 — The bar rises automatically with sensitivity
|
## Slide 7 — The bar rises automatically with sensitivity
|
||||||
|
|
||||||
**Talking points:**
|
**Talking points:**
|
||||||
- Promotion is a workflow choice, not a contract edit — a promotion can be reviewed as a *diff in the workflow*, not as a rewritten contract
|
- Promotion is a workflow choice, not a contract edit — a promotion can be reviewed as a *diff in the workflow*, not as a rewritten contract
|
||||||
@@ -86,7 +98,7 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Slide 7 — Tearing down is as gated as deploying
|
## Slide 8 — Tearing down is as gated as deploying
|
||||||
|
|
||||||
**Talking points:**
|
**Talking points:**
|
||||||
- The counter-argument to "deletion protection makes cleanup impossible" is this slide. Decommission is a first-class, gated, two-approval flow — not a lock with no key, and not an ungated `terraform destroy`
|
- The counter-argument to "deletion protection makes cleanup impossible" is this slide. Decommission is a first-class, gated, two-approval flow — not a lock with no key, and not an ungated `terraform destroy`
|
||||||
@@ -99,7 +111,7 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Slide 8 — You control when you absorb improvements
|
## Slide 9 — You control when you absorb improvements
|
||||||
|
|
||||||
**Talking points:**
|
**Talking points:**
|
||||||
- This is the "no surprise upgrades" story. Leadership hears two things: (1) consumers aren't forced to chase the platform, (2) the platform isn't forced to support N forks of every workflow
|
- This is the "no surprise upgrades" story. Leadership hears two things: (1) consumers aren't forced to chase the platform, (2) the platform isn't forced to support N forks of every workflow
|
||||||
@@ -113,7 +125,7 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Slide 9 — Fails gracefully, not opaquely
|
## Slide 10 — Fails gracefully, not opaquely
|
||||||
|
|
||||||
**Talking points:**
|
**Talking points:**
|
||||||
- This looks like a small thing; it's actually a cultural one. The platform's posture is "help me get started," not "you should have known"
|
- This looks like a small thing; it's actually a cultural one. The platform's posture is "help me get started," not "you should have known"
|
||||||
@@ -127,7 +139,7 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Slide 10 — The desired outcomes
|
## Slide 11 — The desired outcomes
|
||||||
|
|
||||||
**Talking points:**
|
**Talking points:**
|
||||||
- Close on the strategic frame. The platform is not "a CI/CD tool" — it is the organizational lever for shipping safely at the pace the business demands, with the security and audit posture the regulators require
|
- Close on the strategic frame. The platform is not "a CI/CD tool" — it is the organizational lever for shipping safely at the pace the business demands, with the security and audit posture the regulators require
|
||||||
@@ -220,7 +232,7 @@
|
|||||||
|
|
||||||
**Talking points:**
|
**Talking points:**
|
||||||
- The headline for the Head of Cloud / Finance: less than one cent over 8 days of active development; zero BAU cloud spend
|
- The headline for the Head of Cloud / Finance: less than one cent over 8 days of active development; zero BAU cloud spend
|
||||||
- The lifecycle pipeline defaults to plan-only so the PR-time cost is zero; `ACDL_LIFECYCLE_MODE=full` overrides for milestone verification
|
- The lifecycle pipeline defaults to plan-only so the PR-time cost is zero; `NOVA_LIFECYCLE_MODE=full` overrides for milestone verification
|
||||||
- The pre-mortem is the credibility slide — we already asked "how does this fail?" and the mitigations are structural
|
- The pre-mortem is the credibility slide — we already asked "how does this fail?" and the mitigations are structural
|
||||||
- The v1.10 decay incident is disclosed honestly, not hidden — that disclosure IS the mitigation
|
- The v1.10 decay incident is disclosed honestly, not hidden — that disclosure IS the mitigation
|
||||||
- Cost drivers are spike-scoped: Terraform plan reads (free), S3 state storage (cents), DynamoDB outbox (cents). No running infrastructure between milestones
|
- Cost drivers are spike-scoped: Terraform plan reads (free), S3 state storage (cents), DynamoDB outbox (cents). No running infrastructure between milestones
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
# The Developer Experience
|
# The Developer Experience
|
||||||
|
|
||||||
> **Subtitle:** Agentic Cloud Delivery Platform
|
> **Subtitle:** Nova — The New Dawn of DevSecOps
|
||||||
> **Audience:** Senior Leadership, CTO, Head of Cloud, Head of Infrastructure, Head of DevOps
|
> **Audience:** Senior Leadership, CTO, Head of Cloud, Head of Infrastructure, Head of DevOps
|
||||||
> **Length:** ~15 minutes · 10 main + Appendix TOC + 7 appendix = 18 slides
|
> **Length:** ~16 minutes · 11 main + Appendix TOC + 7 appendix = 19 slides
|
||||||
> **Purpose:** Sell the developer experience and the citizen developer experience to tech leadership — velocity without sacrificing safety, and security/observability/compliance as platform defaults rather than per-team effort.
|
> **Purpose:** Sell the developer experience and the citizen developer experience to tech leadership — velocity without sacrificing safety, and security/observability/compliance as platform defaults rather than per-team effort.
|
||||||
> **Maturity framing:** "Testing" = works internally, dev pilot-ready. "Planned" = on the roadmap. "Agentic" = involves AI agents or autonomous decision-making.
|
> **Maturity framing:** "Testing" = works internally, dev pilot-ready. "Planned" = on the roadmap. "Agentic" = involves AI agents or autonomous decision-making.
|
||||||
> **Re-verification (2026-07-29):** Every "Testing" claim in this deck was re-verified in v1.10 Phase 54 (D-093) and again in v1.11 via the pipeline-driven lifecycle tests (P59–P62). The headline E2E (contract → resolver → adapter → terraform init/validate/plan) passes against the live AWS account; the local emulating tier (Phase 53) runs the full E2E with no cloud credentials. **22/22 auto-verifiable capabilities Verified** (CAP-013 fixed in v1.12 P67 — the adapter's multi-resource L1 dedup defect is closed). The v1.11 lifecycle pipeline ran apply→modify→destroy against live AWS and was then torn down to zero-cost (D-096). See `.ciagent/CAPABILITY_INVENTORY.md` and `.ciagent/PRE_MORTEM.md`.
|
> **Re-verification (2026-07-29):** Every "Testing" claim in this deck was re-verified in v1.10 Phase 54 (D-093) and again in v1.11 via the pipeline-driven lifecycle tests (P59–P62). The headline E2E (contract → resolver → adapter → terraform init/validate/plan) passes against the live AWS account; the local emulating tier (Phase 53) runs the full E2E with no cloud credentials. **22/22 auto-verifiable capabilities Verified** (CAP-013 fixed in v1.12 P67 — the adapter's multi-resource L1 dedup defect is closed). The v1.11 lifecycle pipeline ran apply→modify→destroy against live AWS and was then torn down to zero-cost (D-096). See `.ciagent/CAPABILITY_INVENTORY.md` and `.ciagent/PRE_MORTEM.md`.
|
||||||
@@ -11,6 +11,8 @@
|
|||||||
|
|
||||||
## Slide 1 — Title
|
## Slide 1 — Title
|
||||||
|
|
||||||
|
**Nova — The New Dawn of DevSecOps.** Security as a seamless enabler of fast deployments — not a bottleneck, not a "no" department.
|
||||||
|
|
||||||
The consumer surface is intentionally tiny. The platform's surface is large and opinionated.
|
The consumer surface is intentionally tiny. The platform's surface is large and opinionated.
|
||||||
|
|
||||||
> **Speaker notes:** Brief introduction — this deck covers *who uses the platform and how fast/safe they ship*, not the internal mechanics (that's the companion deck). Set the frame: velocity without sacrificing safety, and security/observability/compliance as platform defaults rather than per-team effort.
|
> **Speaker notes:** Brief introduction — this deck covers *who uses the platform and how fast/safe they ship*, not the internal mechanics (that's the companion deck). Set the frame: velocity without sacrificing safety, and security/observability/compliance as platform defaults rather than per-team effort.
|
||||||
@@ -28,7 +30,7 @@ flowchart LR
|
|||||||
A["Technical dev\n(app code + contract)"]
|
A["Technical dev\n(app code + contract)"]
|
||||||
B["Citizen dev\n(intent → AI agent\n→ contract)"]
|
B["Citizen dev\n(intent → AI agent\n→ contract)"]
|
||||||
end
|
end
|
||||||
subgraph ACDL ["ACDL — infrastructure only"]
|
subgraph ACDL ["Nova — infrastructure only"]
|
||||||
C["Same contract\nSame pipeline\nSame safety"]
|
C["Same contract\nSame pipeline\nSame safety"]
|
||||||
D["Provision\nAWS resources"]
|
D["Provision\nAWS resources"]
|
||||||
E["Evidence\nhash-chained"]
|
E["Evidence\nhash-chained"]
|
||||||
@@ -46,15 +48,70 @@ flowchart LR
|
|||||||
```
|
```
|
||||||
|
|
||||||
- **Technical developer** — owns app code + a contract + a thin CI definition.
|
- **Technical developer** — owns app code + a contract + a thin CI definition.
|
||||||
- **Citizen developer** — declares intent in plain language; an AI agent produces a contract that passes the **same** safety envelope. <span class="badge agentic">Agentic</span>
|
- **Citizen developer** — declares intent in plain language; an AI agent produces a contract that passes the **same** safety envelope.
|
||||||
- **Upstream is anything** — IDE, agentic SDLC, or vibe coding. ACDL doesn't care how the contract was produced.
|
- **Upstream is anything** — IDE, agentic SDLC, or vibe coding. Nova doesn't care how the contract was produced.
|
||||||
- **ACDL is infrastructure only** — it provisions and governs AWS resources. Application deployment is upstream.
|
- **Nova is infrastructure only** — it provisions and governs AWS resources. Application deployment is upstream.
|
||||||
|
|
||||||
> **Speaker notes:** This is the thesis of the deck. The two surfaces are *parallel*, not a progression — a citizen developer doesn't "graduate" to the developer surface. Both produce a contract; both get the same treatment. The scope boundary matters: anything upstream of the contract is out of ACDL's concern. The leadership takeaway: we expand who can ship safely without lowering the bar.
|
> **Speaker notes:** This is the thesis of the deck. The two surfaces are *parallel*, not a progression — a citizen developer doesn't "graduate" to the developer surface. Both produce a contract; both get the same treatment. The scope boundary matters: anything upstream of the contract is out of Nova's concern. The leadership takeaway: we expand who can ship safely without lowering the bar.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Slide 3 — Three things. The entire consumer surface.
|
## Slide 3 — The platform at a glance
|
||||||
|
|
||||||
|
One picture of the whole platform — what you touch, what the platform owns, and where the safety lives. The rest of this deck zooms into the developer-facing pieces.
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
subgraph UP ["Consumer surfaces — upstream"]
|
||||||
|
direction LR
|
||||||
|
U1["Technical dev\napp code + contract"]
|
||||||
|
U2["Citizen dev\nintent → AI agent → contract"]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph ACDL ["Nova — infrastructure only"]
|
||||||
|
direction TB
|
||||||
|
CS["Contract schema\n(validate + fail-fast)"]
|
||||||
|
subgraph PIPE ["Central pipeline — fixed stages, every deployment"]
|
||||||
|
direction LR
|
||||||
|
P1["Validate"] --> P2["Resolve\ntarget stack"] --> P3["Security\nchecks"] --> P4["Infra plan"] --> P5["Policy\nchecks"] --> P6["Confidence\nsignal"] --> P7["Evidence\nevent"] --> P8["Infra apply"]
|
||||||
|
end
|
||||||
|
CAT["Module catalog\nprimitives + modules\n(security-reviewed)"]
|
||||||
|
ADAPT["Engine adapter\n(stateless → Terraform)"]
|
||||||
|
ENV["Platform-managed\nenvironments\naccount · VPC · state · IAM"]
|
||||||
|
HITL["HITL gates\nqa · prod · dr"]
|
||||||
|
EVID["Evidence stream\nhash-chained outbox\n(RPO = 0)"]
|
||||||
|
CS --> PIPE
|
||||||
|
CAT --> P2
|
||||||
|
ADAPT --> P4
|
||||||
|
ADAPT --> P8
|
||||||
|
ENV --> P8
|
||||||
|
P6 --> HITL
|
||||||
|
HITL --> P8
|
||||||
|
P7 --> EVID
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph DOWN ["Downstream"]
|
||||||
|
direction LR
|
||||||
|
D1["AWS resources\nrunning\n(tagged, encrypted)"]
|
||||||
|
D2["Consumer pipeline\ndeploys image"]
|
||||||
|
end
|
||||||
|
|
||||||
|
U1 --> CS
|
||||||
|
U2 --> CS
|
||||||
|
P8 --> D1
|
||||||
|
D1 --> D2
|
||||||
|
```
|
||||||
|
|
||||||
|
- **You own the left edge** — app code and a contract. That is the entire consumer surface.
|
||||||
|
- **The platform owns everything in the middle** — the pipeline, the catalog, the adapter, the environments, the gates, the evidence.
|
||||||
|
- **Two surfaces, one pipeline, one evidence stream** — a senior engineer and a citizen developer converge on the same safety envelope.
|
||||||
|
- **The bar rises automatically** — the confidence signal and HITL gates scale with the target environment, not with a ticket.
|
||||||
|
|
||||||
|
> **Speaker notes:** This is the one-slide map. For a developer-experience audience, frame it from the left edge: "this is what you touch, this is what the platform owns for you." The leadership beat: the convergence — two surfaces, one pipeline, one evidence stream — is the design point that lets us expand who can ship safely without lowering the bar. Don't walk every node; point to the contract boundary and say "the rest of this deck zooms into the developer-facing pieces."
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Slide 4 — Three things. The entire consumer surface.
|
||||||
|
|
||||||
Three things. That is the entire consumer-side surface.
|
Three things. That is the entire consumer-side surface.
|
||||||
|
|
||||||
@@ -82,9 +139,9 @@ The developer does **not**: write infrastructure modules, clone the platform rep
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Slide 4 — See what the platform does, in real time
|
## Slide 5 — See what the platform does, in real time
|
||||||
|
|
||||||
Developers see **what the platform is doing**, in real time. <span class="badge testing">Testing</span>
|
Developers see **what the platform is doing**, in real time.
|
||||||
|
|
||||||
- **Streamed output by default** — the plan, policy-check results, and each check record flow to stdout.
|
- **Streamed output by default** — the plan, policy-check results, and each check record flow to stdout.
|
||||||
- **PR comments after every successful pipeline stage** — a developer always knows where they stand.
|
- **PR comments after every successful pipeline stage** — a developer always knows where they stand.
|
||||||
@@ -96,9 +153,9 @@ Developers see **what the platform is doing**, in real time. <span class="badge
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Slide 5 — Pick from pre-built, security-reviewed blocks
|
## Slide 6 — Pick from pre-built, security-reviewed blocks
|
||||||
|
|
||||||
Developers pick from **pre-built, security-reviewed building blocks.** <span class="badge testing">Testing</span>
|
Developers pick from **pre-built, security-reviewed building blocks.**
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
flowchart LR
|
flowchart LR
|
||||||
@@ -125,14 +182,14 @@ flowchart LR
|
|||||||
- **Primitives** — single-purpose resources (S3, VPC, ECS, IAM, ALB, ECR, CloudFront, WAF, RDS), each with documented inputs/outputs and versioning.
|
- **Primitives** — single-purpose resources (S3, VPC, ECS, IAM, ALB, ECR, CloudFront, WAF, RDS), each with documented inputs/outputs and versioning.
|
||||||
- **Modules** — composed patterns (a static site with CDN + WAF; a microservice with VPC + ECS + ALB + ECR).
|
- **Modules** — composed patterns (a static site with CDN + WAF; a microservice with VPC + ECS + ALB + ECR).
|
||||||
- **Validated examples per module** — `simple.yaml` + `complex.yaml`, validated against the contract schema in CI. Examples cannot drift from the schema silently.
|
- **Validated examples per module** — `simple.yaml` + `complex.yaml`, validated against the contract schema in CI. Examples cannot drift from the schema silently.
|
||||||
- **Auto-promotion of patterns** — auto-promoted to the catalog after 3 observed usages. <span class="badge planned">Planned</span> <span class="badge agentic">Agentic</span>
|
- **Auto-promotion of patterns** — auto-promoted to the catalog after 3 observed usages. <span class="badge planned">Planned</span>
|
||||||
- **Compliance extension points** — each module lists where GDPR, SOX, SOC2, DORA controls will wire in. <span class="badge planned">Planned</span>
|
- **Compliance extension points** — each module lists where GDPR, SOX, SOC2, DORA controls will wire in. <span class="badge planned">Planned</span>
|
||||||
|
|
||||||
> **Speaker notes:** The catalog is what makes "declare intent" practical — you can only declare a module that exists. For leadership: the catalog is the leverage. One well-reviewed module serves every consumer; a fix to the module serves every consumer on the next run. This is the compounding asset.
|
> **Speaker notes:** The catalog is what makes "declare intent" practical — you can only declare a module that exists. For leadership: the catalog is the leverage. One well-reviewed module serves every consumer; a fix to the module serves every consumer on the next run. This is the compounding asset.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Slide 6 — The bar rises automatically with sensitivity
|
## Slide 7 — The bar rises automatically with sensitivity
|
||||||
|
|
||||||
The contract is environment-agnostic. The platform raises the bar automatically.
|
The contract is environment-agnostic. The platform raises the bar automatically.
|
||||||
|
|
||||||
@@ -145,7 +202,7 @@ flowchart LR
|
|||||||
|
|
||||||
| Environment | What the platform adds | Maturity |
|
| Environment | What the platform adds | Maturity |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| dev | Confidence ≥ 0.50, fully autonomous | <span class="badge testing">Testing</span> |
|
| dev | Confidence ≥ 0.50, fully autonomous | — |
|
||||||
| qa | QA human attestation + confidence ≥ 0.75 | <span class="badge planned">Planned</span> |
|
| qa | QA human attestation + confidence ≥ 0.75 | <span class="badge planned">Planned</span> |
|
||||||
| prod | SRE human attestation + confidence ≥ 0.90 | <span class="badge planned">Planned</span> |
|
| prod | SRE human attestation + confidence ≥ 0.90 | <span class="badge planned">Planned</span> |
|
||||||
| dr | SRE human attestation + confidence ≥ 0.95 + DR drill reference | <span class="badge planned">Planned</span> |
|
| dr | SRE human attestation + confidence ≥ 0.95 + DR drill reference | <span class="badge planned">Planned</span> |
|
||||||
@@ -158,9 +215,9 @@ flowchart LR
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Slide 7 — Tearing down is as gated as deploying
|
## Slide 8 — Tearing down is as gated as deploying
|
||||||
|
|
||||||
Tearing down a stack is **as deliberate as deploying one.** <span class="badge testing">Testing</span>
|
Tearing down a stack is **as deliberate as deploying one.**
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
flowchart LR
|
flowchart LR
|
||||||
@@ -176,7 +233,7 @@ flowchart LR
|
|||||||
```yaml
|
```yaml
|
||||||
uses: acdl/.github/workflows/deploy.yml@v1.12
|
uses: acdl/.github/workflows/deploy.yml@v1.12
|
||||||
with:
|
with:
|
||||||
contract: .acdl/contract.yml
|
contract: .nova/contract.yml
|
||||||
mode: decommission
|
mode: decommission
|
||||||
changeRequestId: "CHG0678912"
|
changeRequestId: "CHG0678912"
|
||||||
```
|
```
|
||||||
@@ -192,9 +249,9 @@ The per-stack encryption key enters a **grace window** (default 30 days) so encr
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Slide 8 — You control when you absorb improvements
|
## Slide 9 — You control when you absorb improvements
|
||||||
|
|
||||||
Consumers control **when** they absorb platform improvements. <span class="badge testing">Testing</span>
|
Consumers control **when** they absorb platform improvements.
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
flowchart LR
|
flowchart LR
|
||||||
@@ -231,9 +288,9 @@ flowchart LR
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Slide 9 — Fails gracefully, not opaquely
|
## Slide 10 — Fails gracefully, not opaquely
|
||||||
|
|
||||||
First impressions of a platform are made **when it fails for the first time.** The platform fails gracefully. <span class="badge testing">Testing</span>
|
First impressions of a platform are made **when it fails for the first time.** The platform fails gracefully.
|
||||||
|
|
||||||
When no environment is bound, the platform emits a **user-friendly onboarding prompt** instead of failing opaquely. The prompt tells the consumer:
|
When no environment is bound, the platform emits a **user-friendly onboarding prompt** instead of failing opaquely. The prompt tells the consumer:
|
||||||
|
|
||||||
@@ -250,7 +307,7 @@ The pipeline then **exits without attempting a deployment** — no partial state
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Slide 10 — The desired outcomes
|
## Slide 11 — The desired outcomes
|
||||||
|
|
||||||
- **Velocity without sacrificing safety.** Speed is in the ergonomics; safety is in the gates the consumer cannot bypass.
|
- **Velocity without sacrificing safety.** Speed is in the ergonomics; safety is in the gates the consumer cannot bypass.
|
||||||
- **Security, observability, and compliance as platform defaults** — not per-team effort, not post-hoc remediation.
|
- **Security, observability, and compliance as platform defaults** — not per-team effort, not post-hoc remediation.
|
||||||
@@ -258,7 +315,7 @@ The pipeline then **exits without attempting a deployment** — no partial state
|
|||||||
- **Blast radius contained by design.** Zero-trust OIDC + ABAC means a consumer can only touch its own tagged resources.
|
- **Blast radius contained by design.** Zero-trust OIDC + ABAC means a consumer can only touch its own tagged resources.
|
||||||
- **The bottleneck moves off the platform team's ticket queue.** A merged change progresses through lower environments without a platform engineer joining a thread.
|
- **The bottleneck moves off the platform team's ticket queue.** A merged change progresses through lower environments without a platform engineer joining a thread.
|
||||||
- **Infrastructure as a utility, not a craft.** Teams consume infrastructure, they don't maintain it.
|
- **Infrastructure as a utility, not a craft.** Teams consume infrastructure, they don't maintain it.
|
||||||
- **A path to the citizen developer.** The same safety envelope serves a senior engineer and a non-technical consumer. <span class="badge agentic">Agentic</span>
|
- **A path to the citizen developer.** The same safety envelope serves a senior engineer and a non-technical consumer.
|
||||||
|
|
||||||
> **Speaker notes:** Close on the strategic frame. The platform is not "a CI/CD tool" — it is the organizational lever for shipping safely at the pace the business demands, with the security and audit posture the regulators require. Invite questions; the companion deck ("How the Platform Works") covers the internal mechanics in more depth.
|
> **Speaker notes:** Close on the strategic frame. The platform is not "a CI/CD tool" — it is the organizational lever for shipping safely at the pace the business demands, with the security and audit posture the regulators require. Invite questions; the companion deck ("How the Platform Works") covers the internal mechanics in more depth.
|
||||||
|
|
||||||
@@ -285,7 +342,7 @@ For deep dives — these slides cover details omitted from the main 10.
|
|||||||
A non-technical consumer ships a production deployment **by declaring intent** — without authoring a workflow, a configuration file, or an infrastructure module. Think of this as **vibe coding on a laptop** — the consumer describes what they want; an AI agent turns that into a contract that the platform treats identically to a senior engineer's.
|
A non-technical consumer ships a production deployment **by declaring intent** — without authoring a workflow, a configuration file, or an infrastructure module. Think of this as **vibe coding on a laptop** — the consumer describes what they want; an AI agent turns that into a contract that the platform treats identically to a senior engineer's.
|
||||||
|
|
||||||
- The consumer opens an issue describing what they need (e.g. "a web API for the pricing service").
|
- The consumer opens an issue describing what they need (e.g. "a web API for the pricing service").
|
||||||
- An AI agent maps the intent to a contract referencing a module from the **reviewed skill catalog.** <span class="badge agentic">Agentic</span>
|
- An AI agent maps the intent to a contract referencing a module from the **reviewed skill catalog.**
|
||||||
- The contract enters the **same pipeline** and must clear the **same confidence gate** before promotion.
|
- The contract enters the **same pipeline** and must clear the **same confidence gate** before promotion.
|
||||||
|
|
||||||
**Guardrails that make this safe:**
|
**Guardrails that make this safe:**
|
||||||
@@ -295,7 +352,7 @@ A non-technical consumer ships a production deployment **by declaring intent**
|
|||||||
- The agent's trace and submission confidence are captured in the contract (`profile: agentic`), so a reviewer can see *how* the contract was produced.
|
- The agent's trace and submission confidence are captured in the contract (`profile: agentic`), so a reviewer can see *how* the contract was produced.
|
||||||
- **Initial skill catalog:** web API, worker, scheduled job, static asset, basic observability bootstrap.
|
- **Initial skill catalog:** web API, worker, scheduled job, static asset, basic observability bootstrap.
|
||||||
|
|
||||||
<span class="badge planned">Skill catalog + real agent runtime: planned</span> <span class="badge agentic">Agentic</span>
|
<span class="badge planned">Skill catalog + real agent runtime: planned</span>
|
||||||
|
|
||||||
> **Speaker notes:** Be honest about maturity: the *mechanism* (agent → contract → same pipeline) is designed and the stub was proven in the v1.0 demo; the full skill catalog and real agent runtime are planned. The "vibe coding on a laptop" framing is intentional — it meets the citizen developer where they already are, but every submission still passes the same safety envelope. The design point matters to leadership now: we are building for a world where more of the org can ship safely, not where more of the org has to become a platform engineer.
|
> **Speaker notes:** Be honest about maturity: the *mechanism* (agent → contract → same pipeline) is designed and the stub was proven in the v1.0 demo; the full skill catalog and real agent runtime are planned. The "vibe coding on a laptop" framing is intentional — it meets the citizen developer where they already are, but every submission still passes the same safety envelope. The design point matters to leadership now: we are building for a world where more of the org can ship safely, not where more of the org has to become a platform engineer.
|
||||||
|
|
||||||
@@ -309,7 +366,7 @@ Consumers `uses:` a **versioned** central workflow. The platform fetches itself
|
|||||||
flowchart LR
|
flowchart LR
|
||||||
A["Consumer repo<br/>app + contract + 'uses:'"] -->|triggers on push to main| B["Platform runner"]
|
A["Consumer repo<br/>app + contract + 'uses:'"] -->|triggers on push to main| B["Platform runner"]
|
||||||
B -->|checks out the consumer repo| A
|
B -->|checks out the consumer repo| A
|
||||||
B -->|checks out the ACDL platform repo<br/>into the workspace| C["Platform code<br/>(modules, adapters, schemas)"]
|
B -->|checks out the Nova platform repo<br/>into the workspace| C["Platform code<br/>(modules, adapters, schemas)"]
|
||||||
C --> B
|
C --> B
|
||||||
B -->|runs the pipeline against<br/>the consumer's contract| D["Consumer's resources in AWS"]
|
B -->|runs the pipeline against<br/>the consumer's contract| D["Consumer's resources in AWS"]
|
||||||
```
|
```
|
||||||
@@ -326,7 +383,7 @@ flowchart LR
|
|||||||
|
|
||||||
## A3 — Local Reproducibility
|
## A3 — Local Reproducibility
|
||||||
|
|
||||||
The entire CI pipeline runs **from the shell**, not just in CI. <span class="badge testing">Testing</span>
|
The entire CI pipeline runs **from the shell**, not just in CI.
|
||||||
|
|
||||||
- `scripts/run_ci.sh` mirrors the CI pipeline locally — the same three stages (lint → test → check-only) in sequence.
|
- `scripts/run_ci.sh` mirrors the CI pipeline locally — the same three stages (lint → test → check-only) in sequence.
|
||||||
- `scripts/run_platform.sh --check-only` runs the platform **offline** — no AWS, no policy engine, no outbox required. Validates a contract end-to-end before pushing.
|
- `scripts/run_platform.sh --check-only` runs the platform **offline** — no AWS, no policy engine, no outbox required. Validates a contract end-to-end before pushing.
|
||||||
@@ -371,7 +428,7 @@ flowchart LR
|
|||||||
|
|
||||||
## A6 — Operating Model & Cost
|
## A6 — Operating Model & Cost
|
||||||
|
|
||||||
ACDL runs at **zero cloud cost** for day-to-day development. The v1.0→v1.10 AWS spend was measured directly via Cost Explorer (`COST.md`, 2026-07-28):
|
Nova runs at **zero cloud cost** for day-to-day development. The v1.0→v1.10 AWS spend was measured directly via Cost Explorer (`COST.md`, 2026-07-28):
|
||||||
|
|
||||||
| Metric | Value |
|
| Metric | Value |
|
||||||
|--------|-------|
|
|--------|-------|
|
||||||
@@ -380,8 +437,8 @@ ACDL runs at **zero cloud cost** for day-to-day development. The v1.0→v1.10 AW
|
|||||||
| Projected monthly | ~$0.007 |
|
| Projected monthly | ~$0.007 |
|
||||||
| Peak day | 2026-07-27 ($0.000867 — v1.10 regression + verify run) |
|
| Peak day | 2026-07-27 ($0.000867 — v1.10 regression + verify run) |
|
||||||
|
|
||||||
- **Local emulators are the primary tier** — the full pipeline runs in-process, no AWS credentials, no Checkov, no DynamoDB. <span class="badge testing">Testing</span>
|
- **Local emulators are the primary tier** — the full pipeline runs in-process, no AWS credentials, no Checkov, no DynamoDB.
|
||||||
- **Live-AWS verification is milestone-scoped, then torn down.** The v1.11 lifecycle pipeline ran apply→modify→destroy for every module, then tore down to zero-cost steady state (D-096 — teardown mandatory before milestone COMPLETE). The lifecycle pipeline now **defaults to plan-only** on every PR (fast, no AWS mutation, no cost); a CI variable (`ACDL_LIFECYCLE_MODE=full`) overrides to the real apply→destroy for milestone verification (REQ-134, v1.12).
|
- **Live-AWS verification is milestone-scoped, then torn down.** The v1.11 lifecycle pipeline ran apply→modify→destroy for every module, then tore down to zero-cost steady state (D-096 — teardown mandatory before milestone COMPLETE). The lifecycle pipeline now **defaults to plan-only** on every PR (fast, no AWS mutation, no cost); a CI variable (`NOVA_LIFECYCLE_MODE=full`) overrides to the real apply→destroy for milestone verification (REQ-134, v1.12).
|
||||||
- **Cost drivers** are spike-scoped: Terraform plan reads (free), S3 state storage (cents), DynamoDB outbox (cents). No running infrastructure between milestones.
|
- **Cost drivers** are spike-scoped: Terraform plan reads (free), S3 state storage (cents), DynamoDB outbox (cents). No running infrastructure between milestones.
|
||||||
|
|
||||||
**Pre-mortem (`PRE_MORTEM.md`):** the project's failure modes were pre-mortemed before the leadership pitch. The v1.10 decay incident (diff-scoped VERIFY missed 7 adapter defects — decks advertised capability that wasn't reproducible) is the root pattern: *a claim outruns the verification that backs it.* Four forward failure modes + structural mitigations (regression-tested IAM baseline, mandatory teardown, verified-only deck claims, honest scope).
|
**Pre-mortem (`PRE_MORTEM.md`):** the project's failure modes were pre-mortemed before the leadership pitch. The v1.10 decay incident (diff-scoped VERIFY missed 7 adapter defects — decks advertised capability that wasn't reproducible) is the root pattern: *a claim outruns the verification that backs it.* Four forward failure modes + structural mitigations (regression-tested IAM baseline, mandatory teardown, verified-only deck claims, honest scope).
|
||||||
@@ -395,6 +452,6 @@ ACDL runs at **zero cloud cost** for day-to-day development. The v1.0→v1.10 AW
|
|||||||
v1.11 rebuilt the platform on two architectural pillars that make "Verified" a structural property, not a claim:
|
v1.11 rebuilt the platform on two architectural pillars that make "Verified" a structural property, not a claim:
|
||||||
|
|
||||||
- **The stateless adapter (918 → ~80 lines).** The Terraform adapter was a 918-line monolith with 3 constant tables and 39 type-specific branches. It is now a ~80-line **stateless assembler**: it owns no module content. Each L1 module ships a real `terraform/` module dir owning its resource shape, nested blocks, and defaults. A new module is a new terraform dir, not a code change. *(The v1.12 P67 fix closed a dedup defect for multi-resource L1s — ecs-service, alb; CAP-013 now Verified.)*
|
- **The stateless adapter (918 → ~80 lines).** The Terraform adapter was a 918-line monolith with 3 constant tables and 39 type-specific branches. It is now a ~80-line **stateless assembler**: it owns no module content. Each L1 module ships a real `terraform/` module dir owning its resource shape, nested blocks, and defaults. A new module is a new terraform dir, not a code change. *(The v1.12 P67 fix closed a dedup defect for multi-resource L1s — ecs-service, alb; CAP-013 now Verified.)*
|
||||||
- **Pipeline-driven lifecycle testing.** A `modules-lifecycle` pipeline matrix-runs each L1 and L2 module's contracts through apply→modify→destroy against live AWS. **The "test" = the pipeline cell going green.** Defaults to **plan-only** on every PR (fast, no AWS mutation, no cost); `ACDL_LIFECYCLE_MODE=full` runs the real apply→destroy for milestone verification (REQ-134, v1.12). The regression gate (D-091) re-runs all 22 capabilities at milestone completion — 22/22 Verified as of v1.12.
|
- **Pipeline-driven lifecycle testing.** A `modules-lifecycle` pipeline matrix-runs each L1 and L2 module's contracts through apply→modify→destroy against live AWS. **The "test" = the pipeline cell going green.** Defaults to **plan-only** on every PR (fast, no AWS mutation, no cost); `NOVA_LIFECYCLE_MODE=full` runs the real apply→destroy for milestone verification (REQ-134, v1.12). The regression gate (D-091) re-runs all 22 capabilities at milestone completion — 22/22 Verified as of v1.12.
|
||||||
|
|
||||||
> **Speaker notes:** This is the deep-dive slide for the Head of Engineering / Architecture. The two pillars answer "how do you keep the decks honest?" The adapter is simple enough to reason about (a stateless assembler); the lifecycle pipeline is the automated verification that backs every "Testing" claim. The v1.10 lesson is the negative space: a 918-line adapter with type-specific branches decayed silently. The ~80-line stateless adapter + the milestone regression gate are the structural fix. The plan-only default (v1.12) means verification runs on every PR at zero cost, with the full apply→destroy gated behind a CI variable override.
|
> **Speaker notes:** This is the deep-dive slide for the Head of Engineering / Architecture. The two pillars answer "how do you keep the decks honest?" The adapter is simple enough to reason about (a stateless assembler); the lifecycle pipeline is the automated verification that backs every "Testing" claim. The v1.10 lesson is the negative space: a 918-line adapter with type-specific branches decayed silently. The ~80-line stateless adapter + the milestone regression gate are the structural fix. The plan-only default (v1.12) means verification runs on every PR at zero cost, with the full apply→destroy gated behind a CI variable override.
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
# Agentic Cloud Delivery Vision
|
# Nova Vision
|
||||||
|
|
||||||
|
> **Nova — The New Dawn of DevSecOps.** Security as a seamless enabler of fast deployments — not a bottleneck, not a "no" department.
|
||||||
|
|
||||||
## 1. The Friction
|
## 1. The Friction
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# ACDL Modules
|
# Nova Modules
|
||||||
|
|
||||||
Reusable building blocks for cloud infrastructure. Each module is
|
Reusable building blocks for cloud infrastructure. Each module is
|
||||||
self-documented with a `README.md` following the
|
self-documented with a `README.md` following the
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# ACDL Module Engineering Standards
|
# Nova Module Engineering Standards
|
||||||
|
|
||||||
Standards for authoring and reviewing ACDL modules. These standards
|
Standards for authoring and reviewing Nova modules. These standards
|
||||||
govern the two module tiers — **L1 primitives** (single cloud resource
|
govern the two module tiers — **L1 primitives** (single cloud resource
|
||||||
or small group of related resources) and **L2 modules** (compositions
|
or small group of related resources) and **L2 modules** (compositions
|
||||||
that reference L1 primitives to deploy a complete stack) — and the
|
that reference L1 primitives to deploy a complete stack) — and the
|
||||||
@@ -207,9 +207,12 @@ declares intra-refs from the subnet and route table to the VPC's
|
|||||||
- `aws:wafv2:webacl`
|
- `aws:wafv2:webacl`
|
||||||
- `aws:rds:instance`
|
- `aws:rds:instance`
|
||||||
- `aws:kms:key`, `aws:kms:alias`
|
- `aws:kms:key`, `aws:kms:alias`
|
||||||
- The engine adapter's `TYPE_MAP` is the registry of stack types the
|
- The engine adapter is a **stateless assembler** (v1.11, D-098): it reads
|
||||||
adapter can compile (see §8). A new stack type requires a `TYPE_MAP`
|
the registry, emits a root `main.tf` instantiating each L1 as
|
||||||
entry before the primitive can be deployed.
|
`module "x" { source = "..." }` with resolved inputs and wired refs. There
|
||||||
|
is no `TYPE_MAP` (deleted in the v1.11 stateless rewrite). A new stack
|
||||||
|
type requires a `terraform/` dir in the L1 module + a registry entry with
|
||||||
|
a `terraform_dir` field.
|
||||||
|
|
||||||
## 3. L2 Module Standards
|
## 3. L2 Module Standards
|
||||||
|
|
||||||
@@ -580,8 +583,10 @@ must be checked before the module is registered and published.
|
|||||||
### 9.4 Adapter (stateless assembler)
|
### 9.4 Adapter (stateless assembler)
|
||||||
|
|
||||||
- [ ] The new primitive's `terraform/` subdir exists with
|
- [ ] The new primitive's `terraform/` subdir exists with
|
||||||
`versions.tf`/`variables.tf`/`locals.tf`/`main.tf`/`outputs.tf` and
|
`versions.tf`/`variables.tf`/`main.tf`/`outputs.tf` and
|
||||||
passes `terraform init + validate` standalone.
|
passes `terraform init + validate` standalone. `locals.tf` is required
|
||||||
|
for multi-resource modules; trivial single-resource modules (e.g.
|
||||||
|
`kms-key`, `ecr`, `ecs-cluster`) may inline locals in `main.tf`.
|
||||||
- [ ] `registry.json` has a `terraform_dir` field for the new primitive.
|
- [ ] `registry.json` has a `terraform_dir` field for the new primitive.
|
||||||
- [ ] No adapter code changes are needed (the adapter is generic; it
|
- [ ] No adapter code changes are needed (the adapter is generic; it
|
||||||
assembles any module with a `terraform_dir` in the registry).
|
assembles any module with a `terraform_dir` in the registry).
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
"type": "aws:elbv2:loadbalancer",
|
"type": "aws:elbv2:loadbalancer",
|
||||||
"module": "alb@1.0.0",
|
"module": "alb@1.0.0",
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"name": "acdl-alb",
|
"name": "nova-alb",
|
||||||
"subnets": "subnet-12345",
|
"subnets": "subnet-12345",
|
||||||
"security_group": "sg-12345",
|
"security_group": "sg-12345",
|
||||||
"region": "us-east-1"
|
"region": "us-east-1"
|
||||||
@@ -27,7 +27,7 @@
|
|||||||
"type": "aws:elbv2:targetgroup",
|
"type": "aws:elbv2:targetgroup",
|
||||||
"module": "alb@1.0.0",
|
"module": "alb@1.0.0",
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"name": "acdl-alb",
|
"name": "nova-alb",
|
||||||
"port": 80,
|
"port": 80,
|
||||||
"protocol": "HTTP",
|
"protocol": "HTTP",
|
||||||
"region": "us-east-1"
|
"region": "us-east-1"
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ resource "aws_lb" "this" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
resource "aws_lb_target_group" "this" {
|
resource "aws_lb_target_group" "this" {
|
||||||
name_prefix = "tg-ci-"
|
name_prefix = "${var.name}-"
|
||||||
port = var.port
|
port = var.port
|
||||||
protocol = var.protocol
|
protocol = var.protocol
|
||||||
vpc_id = var.vpc_id
|
vpc_id = var.vpc_id
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
locals {
|
locals {
|
||||||
# OAC defaults (adapter previously hardcoded these).
|
# OAC defaults (adapter previously hardcoded these).
|
||||||
oac_name = "acdl-oac"
|
oac_name = "nova-oac"
|
||||||
oac_origin_type = "s3"
|
oac_origin_type = "s3"
|
||||||
oac_signing_behavior = "always"
|
oac_signing_behavior = "always"
|
||||||
oac_signing_protocol = "sigv4"
|
oac_signing_protocol = "sigv4"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
variable "name" {
|
variable "name" {
|
||||||
type = string
|
type = string
|
||||||
description = "ECS cluster name."
|
description = "ECS cluster name."
|
||||||
default = "acdl-cluster"
|
default = "nova-cluster"
|
||||||
}
|
}
|
||||||
|
|
||||||
variable "region" {
|
variable "region" {
|
||||||
|
|||||||
@@ -8,15 +8,15 @@ resource "aws_ecs_task_definition" "this" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
resource "aws_ecs_service" "this" {
|
resource "aws_ecs_service" "this" {
|
||||||
name = "acdl-microservice"
|
name = "nova-microservice"
|
||||||
cluster = var.cluster_arn
|
cluster = var.cluster_arn
|
||||||
task_definition = aws_ecs_task_definition.this.arn
|
task_definition = aws_ecs_task_definition.this.arn
|
||||||
desired_count = var.desired_count
|
desired_count = var.desired_count
|
||||||
launch_type = var.launch_type
|
launch_type = var.launch_type
|
||||||
|
|
||||||
network_configuration {
|
network_configuration {
|
||||||
subnets = local.subnet_list
|
subnets = local.subnet_list
|
||||||
security_groups = local.security_groups
|
security_groups = local.security_groups
|
||||||
assign_public_ip = var.launch_type == "FARGATE"
|
assign_public_ip = var.launch_type == "FARGATE"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
variable "role_name" {
|
variable "role_name" {
|
||||||
type = string
|
type = string
|
||||||
description = "The IAM role name."
|
description = "The IAM role name."
|
||||||
default = "acdl-microservice-role"
|
default = "nova-microservice-role"
|
||||||
}
|
}
|
||||||
|
|
||||||
variable "assume_role_policy" {
|
variable "assume_role_policy" {
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ rotation enabled. One key per L2 deployment (no shared keys).
|
|||||||
"type": "aws:kms:key",
|
"type": "aws:kms:key",
|
||||||
"module": "kms-key@1.0.0",
|
"module": "kms-key@1.0.0",
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"description": "ACDL per-stack CMK",
|
"description": "Nova per-stack CMK",
|
||||||
"region": "us-east-1"
|
"region": "us-east-1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
locals {
|
locals {
|
||||||
alias_name = "alias/acdl-ci-kms"
|
alias_name = "alias/nova-ci-kms"
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
resource "aws_db_subnet_group" "this" {
|
resource "aws_db_subnet_group" "this" {
|
||||||
count = var.subnet_ids != "" ? 1 : 0
|
count = var.subnet_ids != "" ? 1 : 0
|
||||||
name = "acdl-ci-rds-subnet-group"
|
name = "nova-ci-rds-subnet-group"
|
||||||
subnet_ids = split(",", var.subnet_ids)
|
subnet_ids = split(",", var.subnet_ids)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ locals {
|
|||||||
# Tags: merge caller-supplied tags with the module defaults.
|
# Tags: merge caller-supplied tags with the module defaults.
|
||||||
tags = merge(
|
tags = merge(
|
||||||
{
|
{
|
||||||
"acdl:owner" = "acdl"
|
"nova:owner" = "acdl"
|
||||||
"acdl:environment" = "dev"
|
"nova:environment" = "dev"
|
||||||
},
|
},
|
||||||
var.tags
|
var.tags
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -10,5 +10,5 @@ output "bucket_name" {
|
|||||||
|
|
||||||
output "bucket_regional_domain_name" {
|
output "bucket_regional_domain_name" {
|
||||||
value = aws_s3_bucket.this.bucket_regional_domain_name
|
value = aws_s3_bucket.this.bucket_regional_domain_name
|
||||||
description = "The bucket regional domain name (e.g. acdl-spike-bucket.s3.us-east-1.amazonaws.com)."
|
description = "The bucket regional domain name (e.g. nova-spike-bucket.s3.us-east-1.amazonaws.com)."
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
resource "aws_ecs_task_definition" "uptime" {
|
resource "aws_ecs_task_definition" "uptime" {
|
||||||
family = "acdl-uptime"
|
family = "nova-uptime"
|
||||||
cpu = tostring(var.cpu)
|
cpu = tostring(var.cpu)
|
||||||
memory = tostring(var.memory)
|
memory = tostring(var.memory)
|
||||||
requires_compatibilities = ["FARGATE"]
|
requires_compatibilities = ["FARGATE"]
|
||||||
@@ -8,7 +8,7 @@ resource "aws_ecs_task_definition" "uptime" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
resource "aws_ecs_service" "uptime" {
|
resource "aws_ecs_service" "uptime" {
|
||||||
name = "acdl-uptime"
|
name = "nova-uptime"
|
||||||
cluster = local.cluster_ref
|
cluster = local.cluster_ref
|
||||||
task_definition = aws_ecs_task_definition.uptime.arn
|
task_definition = aws_ecs_task_definition.uptime.arn
|
||||||
desired_count = var.feature_flag_enabled ? 1 : 0
|
desired_count = var.feature_flag_enabled ? 1 : 0
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
locals {
|
locals {
|
||||||
cidr_block = var.cidr != null ? var.cidr : "10.0.0.0/16"
|
cidr_block = var.cidr != null ? var.cidr : "10.0.0.0/16"
|
||||||
az_list = split(",", var.azs)
|
az_list = split(",", var.azs)
|
||||||
name_tag = var.name != null ? var.name : "acdl-vpc"
|
name_tag = var.name != null ? var.name : "nova-vpc"
|
||||||
|
|
||||||
# Derive subnet CIDRs from the VPC CIDR
|
# Derive subnet CIDRs from the VPC CIDR
|
||||||
subnet_cidrs = [
|
subnet_cidrs = [
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ variable "azs" {
|
|||||||
variable "name" {
|
variable "name" {
|
||||||
type = string
|
type = string
|
||||||
description = "Name tag for the VPC and child resources."
|
description = "Name tag for the VPC and child resources."
|
||||||
default = "acdl-vpc"
|
default = "nova-vpc"
|
||||||
}
|
}
|
||||||
|
|
||||||
variable "region" {
|
variable "region" {
|
||||||
|
|||||||