v1.15-Nova rebrand doc verification (phase/05-final-review-ship) found stale ACDL references where the code/terraform already uses Nova names. Critical drift fixed (doc said old name; code uses new): - README.md: `.acdl/contract.yml` -> `.nova/contract.yml`; `acdl-spike-runner` -> `nova-spike-runner`; ABAC tag keys `acdl:owner|contract` -> `nova:owner|contract`. - docs/environments/index.md: `acdl-contract-ingestor` -> `nova-contract-ingestor`; `aws:PrincipalTag/acdl:owner` -> `nova:owner`; `acdl-contracts` -> `nova-contracts`. - docs/consumer-guide.md: `acdl-change-requests` -> `nova-change-requests`; state bucket example `acdl-qa-state` -> `nova-qa-state`. - docs/presentations/* (4 decks + 2 HTML + talking-points): `acdl:owner|contract| environment|cost-center` -> `nova:*`; `ACDL_LIFECYCLE_MODE` -> `NOVA_LIFECYCLE_MODE`. - pipelines/modules-lifecycle.yml comments: `ACDL_LIFECYCLE_MODE` -> `NOVA_LIFECYCLE_MODE` (workflows already use NOVA_; the contract comments were stale). - docs/NOVA_MIGRATION.md: status banner -> COMPLETE (P5 cutoff passed). - .ciagent/ARCHITECTURE.md: header `ACDL` -> `Nova`; NOVA_LIFECYCLE_MODE rename noted in the live lifecycle-mode section (v1.15 addendum already correct). - .ciagent/ROADMAP.md: v1.15 phase statuses P1-P4 pending -> complete (v1.15.1..v1.15.4); P5 -> in progress (phase/05-final-review-ship). Verification: - grep for `acdl:*` tag keys / `.acdl/contract` / `acdl-contract-ingestor` / `acdl-contracts` / `ACDL_LIFECYCLE_MODE` in README/docs/pipelines -> 0 hits (excluding explicitly-unchanged repo path `acdl/.github/...`, `continuous-intelligence/acdl`, and historical narrative). - core/output_publisher.py uses `/nova`; schemas/tagging-standard.json uses `nova:*`; terraform uses `nova-*` (0 `acdl-` in *.tf) — docs now match. - git tag v1.15.0..v1.15.4 exist. - pytest tests/test_lifecycle_mode_flag.py tests/test_pipeline_contract.py -> 111 passed. ---ci--- project: acdl
38 KiB
Nova — Architecture (v1.1 target)
Target architecture for the real Agentic Cloud Delivery Platform (rebranded Nova in v1.15). Source of truth for how:
docs/architecture.md(v0.2) is the upstream draft; this file is the Nova-repo operating copy, refined at phase boundaries. Where this file anddocs/vision.mdconflict, the vision wins.
Status
Architecture is at v0.2 upstream (docs/architecture.md). Milestone v1.1
finalizes it to v1.0 in Phase 07 by resolving the 11 open decisions
(see PROJECT.md open-decision resolutions table). This file records the
locked commitments and the v1.1 spike scope.
Overview
The platform is four layers + six cross-cutting concerns. The sixth concern — the engine abstraction (§12) — is first-class, not an implementation detail. The vision's "Two Consumer Surfaces, One Platform" tenet binds everything: L3A and L3B converge on the same contract schema, the same policy envelope, and the same evidence stream.
┌──────────── acdl-contracts ────────────┐
Developer ───▶ │ commit contract.yaml │ (L3A)
Citizen dev ──▶ │ Issue → agent → contract.yaml │ (L3B)
└────────────────┬───────────────────────┘
│ (push)
▼
┌──────────────────────┐
│ central pipeline │
│ (acdl repo, Gitea │
│ Actions / act_runner) │
└────────┬─────────────┘
│
┌─────────────────────────┼─────────────────────────┐
▼ ▼ ▼
contract→IR resolution policy (Checkov/Kyverno) confidence signal
│ │ │
▼ ▼ ▼
Terraform adapter ──▶ terraform plan ──▶ PolicyCheckResult ──▶ {score,band}
│ │
▼ ▼
dev (autonomous, ≥0.50) qa (HITL, ≥0.75) prod (HITL, ≥0.90) dr (HITL, ≥0.95)
│
▼
DynamoDB outbox ──▶ S3 Object Lock (7-yr, source of truth) ──▶ GitHub audit repo (hot index)
│
▼
acdl-evidence (timeline UI)
Layers
Layer 1 — Foundational Primitives
Single-purpose, engine-agnostic primitive modules. L1 modules do not compose with other L1s; L1 takes its environment as input. The L1 interface is defined against the Target Stack IR, not against Terraform directly (the IR is shaped to round-trip to Terraform in v1, per §12.1).
- No inter-L1 references. L1 may call Terraform data sources.
- Semver: interface → MAJOR, behavior → MINOR, lifecycle → PATCH (W3.D).
- Immutability on publication. 12-month deprecation window.
- AI refinement is a flag; the trigger is the W1.A joint condition.
Layer 2 — Composed Stacks
Combine L1 primitives into deployable shapes. Each codebase maps to one
canonical L2 stack (multiStack: true only per W1.B). Shape X
(parameterized module) or Shape Y (thin-composition layer). Hierarchical
composition, max depth 5, only registered L1s. The thin-composition tree's
wires field is defined against the IR's relationship type, not a Terraform
module block.
Pipeline quality checks: secrets-in-plaintext, public ingress, IAM wildcard, KMS key reference, tag compliance, naming convention. Restricted from thin-composition: IAM principal creation, network boundary creation, key/secret creation, external data transfer. Auto-promote after 3 observed usages.
Layer 3A — Developer Consumer Surface
Tag-based reference to the central pipeline template. Developer-owned workflow file, no platform auto-sync. L3A and L3B are parallel paths, not a progression. W2.A (Path B): tag for dev/qa, SHA for prod; platform CLI resolves tag→SHA for prod-bound workflows.
Layer 3B — Agentic Consumer Surface
Hybrid runtime, skill as markdown, agent as executor. Trust model: trust
and always verify on the platform side. Skill envelope (4 dimensions).
Stateless agents, all state in the platform. profile: agentic marker
unlocks naturalLanguageIntent, confidenceAtSubmission, agentTrace.
Initial skill catalog (BA.A): web API, worker, scheduled job, static asset,
basic observability bootstrap.
Environment progression:
| Environment | Autonomy | Attester | Gate |
|---|---|---|---|
| dev | Full autonomy (no HITL) | — | Confidence ≥ 0.50, all six inputs present |
| qa | Held for attestation | QA | GitHub Deployment approval + full QA matrix (§10) |
| prod | Held for attestation | SRE | GitHub Deployment approval + full SRE matrix (§10) |
| dr | Held for attestation | SRE | GitHub Deployment approval + dr-drill evidence |
Staging is removed. Dev is the only autonomous environment.
Cross-cutting concerns
Central pipeline template (§6)
JSON Schema (draft 2020-12) with a thin domain wrapper. Central repo +
generated client libraries. Multi-stage validation: schema → policy → NFR →
confidence. Distributed enrichment. GitOps reconciler (K8s API; cdlc-gitops
state → CRDs) + Terraform execution layer (§12.5). The pipeline emits one
PolicyCheckResult per policy rule; the confidence signal consumes them as
one normalized input.
Contract schema (§7)
Central repo + generated client libraries. Strict fail-fast at schema stage, multi-stage validation with reason codes from a published vocabulary. W3.E: per-env mandatory inputs —
- dev:
stack,environment - qa adds:
validation.e2eSuite,validation.loadTest - prod adds:
runbook,dashboard,oncall - dr adds:
drDrillRef inputsalways optional;profile: agenticfields optional everywhere.
Confidence signal (§8)
Six canonical inputs, weighted sum with per-input breakdown. Per-env
thresholds: dev ≥ 0.50, qa ≥ 0.75, prod ≥ 0.90, dr ≥ 0.95. Structured output
{ score, band, perInput, reasonCodes }. 1-year storage, no retraining in
v1. Halt with explicit reason on missing input.
Policy input = list of PolicyCheckResult records (engine-agnostic).
Severity → penalty: critical → hard override to mandatory block; high →
-0.2; medium → -0.05; low → -0.01; info → 0.0. One critical finding
hard-overrides the score regardless of all other inputs.
BA.B: thresholds frozen for v1; tuning begins v1.2 (quarterly FP/FN tracking; override = Infra & Ops + SRE joint sign-off, itself a confidence-event).
Audit and evidence stream (§9)
Tiered ledger: S3 with Object Lock in compliance mode (cold, source of
truth, 7-year retention) + GitHub audit repo (acdl-evidence, hot
query index, not part of the chain). Daily checkpoints. Event schema: JWS
detached signature, prev_event_hash chain, controlled-vocabulary
event_type. Outbox pattern: local durable outbox + async worker.
Outbox database = DynamoDB. RPO = 0 (synchronous write to local outbox before contract submission ack); RTO = async worker's dead-letter recovery. Single-region in v1. The outbox also stores per-contract QA and prod approver identities (the only durable record outside GitHub's audit log).
Human-in-the-Loop mechanics (§10)
Pre-execution gates. qa, prod, dr are PR-based attestation gates backed by GitHub Environments with required reviewers. No partial deployment to roll back on rejection (qa, prod); dr is a separate GitHub Deployment against a separate cluster/region.
Reviewer routing: GitHub CODEOWNERS + Environment required reviewers (qa → QA; prod → SRE; dr → SRE). CODEOWNERS routes, does not enforce identity distinctness.
Separation of duties (platform-internal, not GitHub-native, not Kyverno
in v1): on dev→qa promotion the platform writes the QA approver's GitHub
identity to the DynamoDB outbox keyed by contractId; on qa→prod it reads
the stored QA approver and the new SRE approver; if equal, it blocks, emits
SEPARATION_OF_DUTIES_VIOLATION, and routes a halt artifact to SRE on-call.
Full 8-concern attestation matrix (functional, performance, security
posture, contract NFRs, operational readiness, incident response,
capacity/cost, resilience) — see docs/architecture.md §10.4.
Timeout: 1 business day = warn + escalate; 2 business days = auto-freeze +
re-submit (linked via supersedes). Rejection returns the contract to HELD;
the audit chain is extended, not torn up.
Agentic stack (§11)
Hybrid runtime: platform-managed control plane + consumer-owned agent. Versioned, signed skill catalog over MCP. Skill envelope enforced on invocation and result submission. Consumer-owned skill execution; the platform does not run the skill. Stateless agents, all state in the platform. Skills are reviewed for sensitive data before release (Infra & Ops owns the review; it is the mandatory release gate).
Angine execution (§12) — the binding constraint
Target Stack IR (locked): a engine-neutral description of resources (typed inputs/outputs/NFRs), relationships (single parent per child), composition (tree, max depth 5), and policy hooks. The L1 registry, L2 thin-composition tree, contract YML, and PolicyCheckResult schema are all defined against the IR — none against any specific engine.
Angine adapters are the only engine-specific code. An adapter compiles the IR into a engine execution plan. v1 ships exactly one adapter: the Terraform adapter. v2+ may add OpenTofu, Pulumi, K8s CRDs without architectural change.
v1 reality: the IR is shaped to round-trip cleanly to Terraform (nearly isomorphic). As more adapters appear, the IR gets more expressive and the adapters gain translation logic; the L1 content, the YML standard, and the thin-composition tree do not change.
Terraform adapter (v1): translates IR-typed L1 interface → Terraform
variable/output blocks; IR-typed L2 thin-composition tree → Terraform
root module; IR-typed relationships → module references; emits a
terraform plan from the IR. The adapter is a thin layer; it does not own
L1/L2 content.
State storage: S3 (state) + DynamoDB (locking), cloud-managed, single-region in v1.
Policy toolchain: Checkov for Terraform plan policy (the L2 checks + tag/naming); Kyverno for K8s-native/platform-internal policy; OPA reserved for cross-resource cases, explicitly last resort.
Policy result normalization (§12.6): the confidence signal consumes a
normalized PolicyCheckResult schema, not raw engine output.
{
"contractId": "uuid",
"evaluatedAt": "ISO-8601",
"engine": "checkov | kyverno | opa",
"ruleId": "CKV_AWS_24 | KYVERNO_NO_PRIVILEGED | ...",
"severity": "critical | high | medium | low | info",
"result": "pass | fail | skipped | error",
"message": "human-readable",
"evidence": { "...engine-specific, opaque to the signal..." },
"resourceRef": "IR-typed resource identifier"
}
Execution layer: GitHub/Gitea Actions in the central pipeline repo. State
locking via DynamoDB. AWS credentials via OIDC federation — long-lived
credentials are forbidden (§12.5). The platform does not run
terraform apply against a developer's workstation; all execution is in
the central pipeline.
Registry maintenance: L1 publication updates the L1 registry in the same PR. The registry is the IR-typed contract, not a Terraform-specific variable schema.
Contract→IR resolution: the contract declares intent in IR-typed terms; the pipeline resolves it to a target stack (list of L1 instances + inputs + relationships); the Terraform adapter compiles the target stack to a plan.
v1.1 spike scope
The spike (Phases 08–10) materializes the minimum that proves the IR commitments hold (no polyglot mess):
- One L1:
l1-s3(IR-typed interface; the only AWS resource in the spike). - One L2 thin-composition:
l2-static-assets(referencesl1-s3only). - Terraform adapter: IR →
terraform planagainst AWS via OIDC. - One contract submission → contract→IR →
terraform plan→ CheckovPolicyCheckResult→ confidence signal → evidence event to the DynamoDB outbox. - State: S3 + DynamoDB (real AWS, single-region).
Out of spike scope: full HITL matrix wiring, Kyverno, OPA, MCP skill catalog, GitOps reconciler, multi-region, prod/dr environments, the 5-skill L3B catalog. Those are post-spike (v1.2+) platform build-out.
Gitea API surface (carried from v1.0, refined)
| Capability | Gitea support | ACDL approach (v1.1) |
|---|---|---|
| Org-scoped repo create | POST /api/v1/orgs/{org}/repos |
Used for any new repos |
| Native Pages | None | Serve acdl-evidence via raw file URLs (unchanged from v1.0) |
| Environments API | None; act_runner ignores environment: |
Model HITL gates via workflow_dispatch approval inputs (v1.0 D-013 pattern) — refined in Phase 07 for the real pre-execution gate model |
repository_dispatch |
Not supported | Cross-repo trigger via workflow_dispatch API (unchanged) |
| Reusable workflows | Supported | acdl/.gitea/workflows/pipeline.yml via uses: ...@<ref> |
id-token: write / OIDC |
Not supported (RESEARCH TARGET 1, conf 0.95). Gitea docs list id-token as an unsupported GitHub-only scope; open proposal go-gitea/gitea#33681; draft PR go-gitea/gitea#36988 unmerged. Even Gitea's own CI uses long-lived AWS keys (issue #37980). |
Spike waiver D-039: per-run-rotated long-lived key (rotated after each run by scripts/rotate_spike_key.sh). Real OIDC deferred to v1.2, blocked on PR #36988. |
actions/configure-aws-credentials |
Unusable without OIDC | Spike uses static AWS creds from a (rotated) Gitea Actions secret via the aws-actions/configure-aws-credentials@v4 access-key-id/secret-access-key inputs, or plain AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY env vars. v1.2 switches to role-to-assume when OIDC lands. |
Branch pinning rule (refined for W2.A)
- Dev/qa contracts reference the reusable workflow by tag
(
@v1.1-spike). - Prod-bound workflows reference by SHA; the platform CLI
(
platform/cli/resolve-tag.ts, Phase 07) resolves the current tag to its SHA. (Spike scope: the CLI is a stub; the real CLI lands in v1.2.)
Verification toolchain
ACDL has no package.json. The verification gate substitutes:
- typecheck:
terraform validate,python3 -m py_compile, JSON Schema validation (ajvorpython -m jsonschema) againstschemas/. - test: per-phase
scripts/verify_phaseNN.sh(Phase 06: archive integrity; Phase 07: schema validation + decision-resolution completeness; Phase 08: OIDC assume-role + state backend; Phase 09: IR + L1 + adapterterraform plan; Phase 10: end-to-end contract submission). - build:
terraform init(real build for the spike). - See
PERSONAS.mdverification_toolchain.
Build order (v1.1)
- Phase 06 — archive demo, reorient repo.
- Phase 07 — finalize architecture v1.0; author schemas + designs.
- Phase 08 — AWS OIDC bootstrap (use temp key once, rotate).
- Phase 09 — IR +
l1-s3+ Terraform adapter →terraform plan. - Phase 10 —
l2-static-assets+ contract→IR → end-to-end spike. - COMPLETE gate — review → ship
v1.2.0→ audit. DONE.
v1.2 build-out scope
v1.2 takes the v1.1 spike (dev-only, plan-only, single S3 L1) to a real,
simpler, better-documented platform that delivers a microservice to AWS ECS
Fargate end-to-end. The locked architecture (§1–§12) is unchanged — v1.2
extends the implementation, not the design.
In scope (five axes, user-directed 2026-07-21)
- Re-evaluate the current state. go-gitea/gitea#36988 (OIDC for Gitea
Actions) re-checked 2026-07-21: still open (last updated 2026-05-27,
not merged). Real OIDC remains deferred to v1.3+; v1.2 extends the D-039
per-run-rotated-key waiver as D-047. The waiver continues to satisfy
§12.5's intent (no persistently long-lived key): the spike key is
rotated after each run by
scripts/rotate_spike_key.sh, and Phase 12 tightens the IAM scoping + rotation hygiene. - NFR improvements on the existing spike. Least-privilege IAM audit of
spike_runner_policy.json; idempotentcreate_state_backend.py/create_iam_user.py; proper exit codes / error handling; P1-1 redaction (two AWS access key IDs in.ciagent/VERIFY.mdPhase 09 narrative). - Streamline / simplify the current setup. Consolidate
run_spike_plan.sh+run_spike_e2e.shinto onescripts/run_platform.sh; remove dead code and staleplatform/paths. - README.md fully up to date on how the platform works. Reflect v1.1
complete; document the actual spike flow,
scripts/run_platform.sh, the real repo layout, and the v1.2 objective. - Bootstrap a consumer repo with a basic microservice deployed to ECS
end-to-end. New Gitea repo
acdl-consumer-microservice(orgcontinuous-intelligence); new IR-typed L1s (l1-vpc,l1-ecs-cluster,l1-ecs-service,l1-iam-role,l1-alb,l1-ecr); newl2-microservicethin-composition; one contract submission →terraform apply(dev, autonomous per §10, confidence ≥ 0.50) → a live ECS Fargate service serving HTTP 200 → evidence event to the DynamoDB outbox → acdl-evidence timeline.
Angine extension (ECS Fargate)
The Terraform adapter (§12) remains the only engine-specific code. v1.2
expands the adapter TYPE_MAP to cover the six new ECS-shaped IR resource
types. The L1 interface shape (IR-typed inputs/outputs/NFRs, registered in
modules-ir/registry.json) is unchanged — only the set of registered L1s
grows. The IR commitments (REQ-28) continue to hold: modules-ir/,
schemas/, contracts/, core/confidence_signal.py,
core/contract_resolver.py, core/outbox_writer.py
remain engine-agnostic.
terraform apply (dev only)
v1.2 lifts the engine execution from plan to apply for the dev
environment only. Dev is autonomous per §10 (confidence ≥ 0.50, no HITL).
apply for qa/prod/dr remains HITL-gated and out of scope for v1.2. The
apply result (resources created, plan diff) is captured in the evidence
stream as a terraform.apply event.
Out of scope for v1.2 (deferred to v1.3+)
| Feature | Reason |
|---|---|
| Real OIDC federation | go-gitea/gitea#36988 still open. v1.2 extends D-039 waiver (D-047); real OIDC is v1.3+. |
| Full HITL matrix wiring (qa/prod/dr) | v1.2 is dev-only autonomous apply; HITL wiring is v1.3. |
| Kyverno + OPA policy engines | v1.2 keeps Checkov only; Kyverno/OPA are v1.3. |
| MCP skill catalog + real L3B agent | v1.2 keeps the L3B stub; the 5-skill catalog is v1.3. |
| Audit ledger build-out (S3 Object Lock + JWS + async worker + DLQ + daily checkpoints) | v1.2 keeps the v1.1 outbox; the regulatory ledger is v1.3. |
| Multi-region state / outbox | Single-region in v1 (§9, §12.3); multi-region is v1.3+. |
| Prod/dr environments | v1.2 is dev-only; prod/dr are v1.3. |
| GitOps reconciler (ArgoCD/Flux) | v1.3+. |
Build order (v1.2)
- Phase 11 — re-eval #36988 + NFR audit + simplification findings + README rewrite.
- Phase 12 — NFR harden + simplify (idempotent bootstrap, one
run_platform.sh, IAM audit, redactions). - Phase 13 — six ECS L1s + adapter
TYPE_MAPexpansion. - Phase 14 —
l2-microservice+ contract schema extension. - Phase 15 — consumer repo +
terraform apply(dev) → live ECS service. - Phase 16 — capstone e2e: consumer commit → live HTTP 200 → evidence → timeline.
- COMPLETE gate — review → ship
v1.3.0→ audit.
v1.8 Architecture Addendum
Milestone v1.8 (complete, tag
v1.8.0). Adds encryption-by-default, deletion-protection-by-default, uptime monitoring, decommission alias, engineering standards, and path documentation.
New Primitives
kms-key(aws:kms:key) — Per-stack customer-managed KMS key withenable_key_rotation = true. One key per L2 deployment (no shared keys). Wired into both L2 compositions as a child, with itskms_key_arnoutput connected to all children'skms_key_arninput. Adapter emitsaws_kms_key+enable_key_rotation.uptime(aws:ecs:uptime-service) — Uptime-kuma on ECS Fargate with a feature flag (feature_flag_enabled), monitored endpoints (HTTP/DNS/TCP), alert channels (Teams/email/SMS/GitHub issues). Deployed by default after any L2 module with a separate terraform state. When the feature flag is false, the adapter emits no resources.
Encryption by Default
All 12 L1 primitives have encryption_enabled NFR (default true). Primitives
with at-rest data (s3, rds, ecr, ecs-service, ecs-cluster) have an optional
kms_key_arn input. The adapter emits encryption blocks (SSE-KMS for S3,
storage_encrypted for RDS, encryption_configuration for ECR) referencing the
per-stack CMK when provided. Managed KMS fallback with stderr warning for
standalone L1 deployments.
Deletion Protection by Default
All 12 L1 primitives have deletion_protection NFR (default true). The
adapter emits lifecycle { prevent_destroy = true } when true. L2 modules
expose a features.deletion_protection flag (default true) propagated to
all children via the resolver. Setting inputs.deletion_protection: false
in the contract disables it for the whole stack.
Decommission Alias
A mode: decommission on the deploy pipeline implements a 2-step destroy:
- Disable deletion protection (resolve with
deletion_protection: false, terraform plan/apply, HITL SRE gate via GitHub environment). - Zero counts + destroy (
decommission_transformzeroes all scalable counts, terraform plan/apply, second HITL SRE gate).
CMDB validation via DynamoDB acdl-change-requests table. The Lambda
validate_change_request action queries the table and asserts
status == "approved" + consumerRepo match.
Adapter Expansion
TYPE_MAP grew from 16 to 19 entries (+ aws:kms:key, aws:kms:alias,
aws:ecs:uptime-service). Specialized emission branches added for KMS key
rotation, S3 SSE-KMS configuration, uptime ECS Fargate task, and
prevent_destroy lifecycle on all resources.
Pipeline Stages
The deploy pipeline grew from 8 to 9 stages (+ deploy-uptime after
publish-outputs). The deploy-uptime stage constructs a synthetic uptime
contract from the L2 stack outputs, resolves + adapts it to a separate
terraform state directory, and publishes the uptime URL via PR comment.
Forge-Agnostic API URLs
The platform Lambda (contract_ingestor.py) reads GITHUB_API_BASE env
for forge-agnostic API URLs. GitHub uses /search/issues; Gitea uses
/repos/{owner}/{repo}/issues. Detection via /api/v1 in the base URL.
v1.9 Addendum (2026-07-23)
New Components
core/contract_resolver.pyinterpolation (D-081): the resolver now expands${env.<field>}+${contract.<field>}tokens post-schema-validation, pre-IR-resolution. The env context is the loaded environment onboarding JSON (core/environments/<name>.json, schemaschemas/environment.schema.json). The resolver'schild_input_maproutes L2 wires to the sub-resource that declares the input (P1-1 —desired_count→aws:ecs:service,family→aws:ecs:task_definition).core/environment_check.pyload()(REQ-104): loads + returns the parsed environment JSON; emits a stderr warning for placeholderaccount_idwhen env != dev.core/hitl_gates.py(REQ-108, D-084): the HITL pre-execution attestation gate. Records the approver identity to the DynamoDB outbox (approver_qa/approver_prod/approver_dr), runs the separation-of- duties check on prod, invokes the attestation matrix, returns(ok, reason). Dev skips (autonomous).run_platform.shcallsattestbefore apply for qa/prod/dr.core/attestation_matrix.py(REQ-109, D-084): the 8-concern attestation matrix fromhitl_matrix_design.md§10.4. Offline-testable concerns (contract NFRs, schema validity, policy pass) run for real; operator-supplied concerns accept signed evidence artifacts validated for freshness + schema. Signature verification skips whenACDL_ATTESTATION_SIGNING_KEY_IDis unset (D-089).core/separation_of_duties.pyroute_halt_artifact(REQ-107): real SNS publish (acdl-sod-halttopic, ARN fromACDL_SOD_HALT_TOPIC_ARN) + outbox fallback (SEPARATION_OF_DUTIES_VIOLATIONevent). The SNS topic is defined interraform/platform/main.tf.adapters/wiz/wiz_adapter.pyWizClient(REQ-110): real GraphQL API client (<WIZ_API_URL>/graphql, Bearer auth, pagination viapageInfo.hasNextPage).fetch_and_adapttranslates issues →PolicyCheckResult. Graceful degrade when unconfigured.adapters/kyverno/kyverno_adapter.py(REQ-111): fleshed-outPolicyReport→PolicyCheckResultmapping (pass/fail/skip/warn + severity + skip-with-reason + resource construction). Inactive-for-TF guard preserved.
Per-Environment Promotion (D-082)
The deploy workflow (.github/workflows/deploy.yml +
.gitea/workflows/deploy.yml, byte-identical) declares an environment
workflow_call input. When non-empty, run_platform.sh --environment <name> overrides the contract's environment field before schema
validation (D-088). One CI job per environment; promotion = running the
matching job, no environment: field editing. Per-env contract files
(contracts/<module>.<env>.yaml) use interpolation for env-specific
values.
Adapter Parameterization (P1-1, D-085)
The adapter (adapters/terraform/adapter.py) reads ECS/ALB/VPC defaults
from L1 interface.json inputs (desired_count, launch_type,
family, target_type, load_balancer_type, name). The adapter is a
thin translator; the child_input_map routes wires to the declaring
sub-resource.
Deferred (D-083)
S3 Object Lock + JWS detached signatures + async worker + DLQ + daily checkpoints (audit ledger build-out) — deferred to a future milestone. The hash-chain + DynamoDB-outbox path remains the v1.9 production audit record.
v1.10 Addendum — Regression VERIFY + Local Emulators + Capability Re-Verification
Regression-Class VERIFY (D-091, core/regression_verify.py)
The standard VERIFY stage was diff-scoped (it checked the phase diff
only, never re-ran underlying capability). This let 8 NFR-patch phases
(v1.9.1–v1.9.8) pass while the platform decayed. The regression-class
VERIFY (core/regression_verify.py) re-runs capability checks against
the current codebase and tags each Verified/Decayed/Broken. It fails
closed on any non-Verified capability, blocking milestone completion.
The registry (CAPABILITY_REGISTRY) holds 16 capability checks
(CAP-001..CAP-016): 12 local-tier + 4 live-AWS. Adding a capability is
a single function + one registry entry. The gate runs via
scripts/run_regression.sh and writes .ciagent/REGRESSION_REPORT.md
.json.
Local Emulating Adapters (D-092, core/local_emulators.py)
Four local adapters let the platform run the full headline E2E without cloud credentials:
FlatFileOutbox— flat-file DynamoDB outbox emulator (hash-chained JSONL; resumable across instances; chain verification).LocalEcsEmulator— local ECS Fargate HTTP 200 emulator (binds port 0 on 127.0.0.1; daemon thread; clean destroy).LocalS3StateBackend— rewrites the terraform S3 backend to a local backend (per-stack tfstate in a temp folder).LocalLambdaStub— invokes the contract_ingestor handler in-process (patches_get_dynamodb/_get_secrets_client/urllib.urlopen; DynamoDB writes redirected to the FlatFileOutbox).
run_local_e2e() runs the full pipeline: contract → resolver → adapter
→ local S3 backend → local ECS (HTTP 200) → flat-file outbox (chain
verified) → local Lambda (200). Gated on ACDL_LOCAL_TIER=1.
Capability Re-Verification Sweep (D-093)
.ciagent/CAPABILITY_INVENTORY.md enumerates 16 auto-verified
capabilities + 6 IAM-gated escalated resources. The sweep found and
fixed 7 adapter defects in adapters/terraform/adapter.py (duplicate
outputs, duplicate args, missing required args, deprecated AWS provider
v5 arg names). The headline E2E now passes at both tiers: local
emulator + live-AWS terraform init/validate/plan.
Adapter Defect Fixes (P54)
7 defects fixed in adapters/terraform/adapter.py:
- Duplicate output definitions (per-resource + stack-level both emitted).
- Duplicate
desired_count/launch_typeon ECS service. - Duplicate
target_type/family/load_balancer_type. - Missing
assume_role_policy/role_nameon IAM role (L2 composition gap). - Missing
cidr_block/vpc_id/namedefaults on VPC/subnet/route_table/ ECS cluster/ECR repository. - ECR
kms_key_arnunsupported arg →encryption_configurationblock. - CloudFront OAC + WAF deprecated arg names (AWS provider v5):
signing_behavior,signing_protocol,origin_access_control_id,s3_origin_config.origin_access_identity,origin_id,rule(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 |
| 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 tonovafor 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)
- P1 docs/decks/prose — no runtime impact; ships consumer migration guide announcing the 5 breaking changes.
- P2 code + env vars (dual-read) + consumer path — deployments don't break during the transition window (dual-read fallback).
- P3 SSM path (copy → read → delete) + tag keys (parallel-tag → policy swap → remove old).
- 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). - 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.