Compare commits

..

2 Commits

Author SHA1 Message Date
Jon Chery 9421442afd verify(P2): user-facing ACDL→Nova sweep — 4-layer verify PASS + ship
VERIFY: structural — all user-facing strings Nova; behavioral — 79 tests
+ CI PASS; security — no creds; quality — new onboarding Nova-header test.
REQ-166 complete. Internal ship_phase.sh helper added.

---ci---
project: acdl
phase: 2
milestone: v1.16
status: complete
phase_role: execution
requirements:
  covered: [REQ-166]
  partial: []
---/ci---
2026-08-01 12:12:45 +00:00
Jon Chery bb43d94563 verify(P1): state-bucket + Kyverno rebrand — 4-layer verify PASS + ship
VERIFY: structural — adapter.py:117 nova-tfstate-*; kyverno policy nova:*
labels; behavioral — 80 tests PASS + run_ci.sh 3-stage PASS; security —
emitted backend no longer points at a non-existent bucket; quality —
new test_adapt_emits_nova_state_bucket regression guard. REQ-165 complete.

---ci---
project: acdl
phase: 1
milestone: v1.16
status: complete
phase_role: execution
requirements:
  covered: [REQ-165]
  partial: []
---/ci---
2026-07-30 15:17:43 +00:00
16 changed files with 91 additions and 30 deletions
+4 -4
View File
@@ -1,9 +1,9 @@
{ {
"phase": 0, "phase": 1,
"stage": "plan", "stage": "execute",
"milestone": "v1.16", "milestone": "v1.16",
"phase_role": "pre_execution", "phase_role": "execution",
"attempts": 0, "attempts": 0,
"updated_at": "2026-07-30T15:15:00Z", "updated_at": "2026-07-30T15:30:00Z",
"milestone_complete": false "milestone_complete": false
} }
+1 -1
View File
@@ -1,4 +1,4 @@
# ACDL Adapters # Nova Adapters
## Overview ## Overview
+4 -4
View File
@@ -1,7 +1,7 @@
# Kyverno Adapter # Kyverno Adapter
The Kyverno adapter translates Kyverno `PolicyReport` results to the The Kyverno adapter translates Kyverno `PolicyReport` results to the
normalized ACDL normalized Nova
[`PolicyCheckResult`](../../schemas/policy_check_result.schema.json) schema [`PolicyCheckResult`](../../schemas/policy_check_result.schema.json) schema
(engine: `"kyverno"`), mirroring the Checkov/Wiz adapter pattern. (engine: `"kyverno"`), mirroring the Checkov/Wiz adapter pattern.
@@ -15,7 +15,7 @@ publishes results to `PolicyReport` resources.
## When to use it ## When to use it
Kyverno is the right engine **when the platform emits Kubernetes Kyverno is the right engine **when the platform emits Kubernetes
manifests** (a K8s-native stack). The ACDL platform today emits Terraform manifests** (a K8s-native stack). The Nova platform today emits Terraform
only (D-053), so this adapter is **ready but inactive**: it ships now so only (D-053), so this adapter is **ready but inactive**: it ships now so
the schema path, severity/result mapping and sample policies are in place the schema path, severity/result mapping and sample policies are in place
ahead of the GitOps reconciler that will emit K8s manifests (roadmap). ahead of the GitOps reconciler that will emit K8s manifests (roadmap).
@@ -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.
+1 -1
View File
@@ -1,4 +1,4 @@
"""Kyverno adapter — translate Kyverno PolicyReport results to ACDL PolicyCheckResult records. """Kyverno adapter — translate Kyverno PolicyReport results to Nova PolicyCheckResult records.
Kyverno is a Kubernetes-native policy engine. It evaluates K8s manifests Kyverno is a Kubernetes-native policy engine. It evaluates K8s manifests
and produces PolicyReport resources. This adapter translates those results and produces PolicyReport resources. This adapter translates those results
@@ -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: "?*"
+2 -2
View File
@@ -1,4 +1,4 @@
"""ACDL Terraform adapter — stateless assembler (v1.11 RESTART, P56a). """Nova Terraform adapter — stateless assembler (v1.11 RESTART, P56a).
A STATELESS ASSEMBLER. It owns no module content — no resource shape, no A STATELESS ASSEMBLER. It owns no module content — no resource shape, no
nested HCL blocks, no defaults, no type-specific logic. It reads the nested HCL blocks, no defaults, no type-specific logic. It reads the
@@ -114,7 +114,7 @@ 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") account_id = env.get_env("AWS_ACCOUNT_ID", "581513795199")
state_bucket = f"acdl-tfstate-{account_id}-us-east-1" 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'
+1 -1
View File
@@ -1,4 +1,4 @@
"""Wiz adapter — translate Wiz API results to ACDL PolicyCheckResult records. """Wiz adapter — translate Wiz API results to Nova PolicyCheckResult records.
Wiz is a SaaS security platform with a GraphQL API. This adapter Wiz is a SaaS security platform with a GraphQL API. This adapter
translates Wiz issue records to the normalized PolicyCheckResult schema translates Wiz issue records to the normalized PolicyCheckResult schema
+1 -1
View File
@@ -1,4 +1,4 @@
"""ACDL Confidence Signal (REQ-19). """Nova Confidence Signal (REQ-19).
The platform's certified answer to "is this safe to proceed?" (vision The platform's certified answer to "is this safe to proceed?" (vision
tenet: "Safety is Computed, Not Assumed"). Every delivery action produces tenet: "Safety is Computed, Not Assumed"). Every delivery action produces
+2 -2
View File
@@ -1,4 +1,4 @@
"""ACDL Contract Resolver — resolve a consumer contract to a Target Stack instance. """Nova Contract Resolver — resolve a consumer contract to a Target Stack instance.
The contract resolver is the bridge between the consumer's declared intent The contract resolver is the bridge between the consumer's declared intent
(a contract YAML) and the platform's executable representation (a Target (a contract YAML) and the platform's executable representation (a Target
@@ -471,7 +471,7 @@ def resolve(contract_path, repo_root=None, environment_override=None):
Args: Args:
contract_path: Path to the contract YAML file. contract_path: Path to the contract YAML file.
repo_root: Root of the ACDL repo (defaults to two levels up from this file). repo_root: Root of the Nova repo (defaults to two levels up from this file).
environment_override: When set (dev/qa/prod/dr), overrides the environment_override: When set (dev/qa/prod/dr), overrides the
contract's 'environment' field BEFORE schema validation, so contract's 'environment' field BEFORE schema validation, so
interpolation context is consistent (D-088). Used by interpolation context is consistent (D-088). Used by
+2 -2
View File
@@ -56,9 +56,9 @@ def load(env_name, root=None):
def _onboarding_message(env_name): def _onboarding_message(env_name):
return ( return (
"=== ACDL Environment Onboarding ===\n" "=== Nova Environment Onboarding ===\n"
f"No environment named '{env_name}' is bound to this repository.\n\n" f"No environment named '{env_name}' is bound to this repository.\n\n"
"ACDL environments are platform-managed. The platform provisions on\n" "Nova environments are platform-managed. The platform provisions on\n"
"your behalf:\n" "your behalf:\n"
" - an AWS account (or a scoped partition of one)\n" " - an AWS account (or a scoped partition of one)\n"
" - a network (VPC + subnets)\n" " - a network (VPC + subnets)\n"
+2 -2
View File
@@ -142,7 +142,7 @@ def _report_error(payload):
raise RuntimeError(f"failed to read GitHub token from Secrets Manager: {e}") raise RuntimeError(f"failed to read GitHub token from Secrets Manager: {e}")
owner, repo = PLATFORM_REPO.split("/") owner, repo = PLATFORM_REPO.split("/")
title = f"[ACDL-ALERT] Deploy failure: {consumer_repo} / {contract_id}" title = f"[NOVA-ALERT] Deploy failure: {consumer_repo} / {contract_id}"
# Check for an existing open issue with the same title (idempotency) # Check for an existing open issue with the same title (idempotency)
# URL-encode the contract_id to prevent search-query injection (P1-1). # URL-encode the contract_id to prevent search-query injection (P1-1).
@@ -188,7 +188,7 @@ def _report_error(payload):
{stack_trace} {stack_trace}
``` ```
_This issue was auto-created by the ACDL platform Lambda (D-055). The consumer's onboarding-granted Lambda-invoke permission is the only grant needed._ _This issue was auto-created by the Nova platform Lambda (D-055). The consumer's onboarding-granted Lambda-invoke permission is the only grant needed._
""" """
if existing: if existing:
+2 -2
View File
@@ -36,14 +36,14 @@ import json, sys
stage = '''$STAGE''' stage = '''$STAGE'''
status = '''$STATUS''' status = '''$STATUS'''
details = json.loads('''$DETAILS''') details = json.loads('''$DETAILS''')
lines = [f'### ACDL Stage: {stage} — {status}', ''] lines = [f'### Nova Stage: {stage} — {status}', '']
if details: if details:
lines.append('| Metric | Value |') lines.append('| Metric | Value |')
lines.append('|--------|-------|') lines.append('|--------|-------|')
for k, v in details.items(): for k, v in details.items():
lines.append(f'| {k} | {v} |') lines.append(f'| {k} | {v} |')
lines.append('') lines.append('')
lines.append('> _Auto-posted by the ACDL deploy pipeline (D-055)._') lines.append('> _Auto-posted by the Nova deploy pipeline (D-055)._')
print('\n'.join(lines)) print('\n'.join(lines))
") ")
+1 -1
View File
@@ -36,7 +36,7 @@ banner() {
fail() { echo "FAIL: $*" >&2; exit 1; } fail() { echo "FAIL: $*" >&2; exit 1; }
echo "=== ACDL CI Pipeline (local reproduction) ===" echo "=== Nova CI Pipeline (local reproduction) ==="
echo "contract: pipelines/ci.yml (3 stages)" echo "contract: pipelines/ci.yml (3 stages)"
echo "" echo ""
+46
View File
@@ -0,0 +1,46 @@
#!/usr/bin/env bash
# scripts/ship_phase.sh — internal CIAgent per-phase ship helper (v1.16)
# Usage: bash scripts/ship_phase.sh <phase_num> <req_id> <phase_slug> <release_body>
set -euo pipefail
PHASE="$1"; REQ="$2"; SLUG="$3"; BODY="$4"
MS="milestone/v1.16-nova-simplification"
BR="phase/$(printf '%02d' "$PHASE")-${SLUG}"
cd "$(git rev-parse --show-toplevel)"
git checkout "$MS" 2>/dev/null
git merge --squash "$BR" 2>&1 | tail -2
MSG="verify(P${PHASE}): ${SLUG} — 4-layer verify PASS + ship
${BODY}
---ci---
project: acdl
phase: ${PHASE}
milestone: v1.16
status: complete
phase_role: execution
requirements:
covered: [${REQ}]
partial: []
---/ci---"
git commit -q -m "$MSG"
PREV=$(git tag -l "v1.15.*" --sort=-version:refname | head -1)
PATCH=$(($(echo "$PREV" | sed 's/v1.15.//')))
NEWPATCH=$((PATCH + 1))
TAG="v1.15.${NEWPATCH}"
git tag -a "$TAG" -m "${TAG}: v1.16 P${PHASE}${SLUG}"
git push origin "$MS" --tags 2>&1 | grep -E "new tag|new branch" | head -2
python3 - "$TAG" "$PREV" <<'PYEOF'
import json, subprocess, sys, urllib.request, urllib.error
tag, prev = sys.argv[1], sys.argv[2]
tok = [l.split("=",1)[1].strip() for l in open(".env.secrets") if l.startswith("NOVA_GITEA_TOKEN=")][0]
body = subprocess.check_output(["git","log",f"{prev}..{tag}","--oneline"]).decode()
payload = {"tag_name":tag,"name":f"Nova {tag} — v1.16 P{tag.split('.')[-1]}","body":body}
req = urllib.request.Request("https://git.cloudinit.dev/api/v1/repos/continuous-intelligence/acdl/releases", data=json.dumps(payload).encode(), headers={"Authorization":f"token {tok}","Content-Type":"application/json"}, method="POST")
try:
r = urllib.request.urlopen(req, timeout=30); d = json.loads(r.read()); print(f"release_id: {d.get('id')} tag: {tag}")
except urllib.error.HTTPError as e:
if e.code == 409: print(f"release exists for {tag}")
else: print(f"HTTP {e.code}: {e.read().decode()[:120]}")
except Exception as e: print(f"ERROR: {e}")
PYEOF
echo "SHIPPED ${TAG}"
+9
View File
@@ -90,6 +90,15 @@ class TestModuleAssembly:
assert 'backend "s3"' in terraform_tf assert 'backend "s3"' in terraform_tf
assert 'spike/s3/dev/terraform.tfstate' in terraform_tf assert 'spike/s3/dev/terraform.tfstate' in terraform_tf
def test_adapt_emits_nova_state_bucket(self, tmp_path):
"""P1 (REQ-165): the emitted backend references nova-tfstate-*
(not acdl-tfstate-*); the live bucket was renamed in v1.15 P4."""
instance = json.load(open(ROOT / "modules/l1/s3/instance.json"))
adapt(instance, str(tmp_path))
terraform_tf = (tmp_path / "terraform.tf").read_text()
assert "nova-tfstate-" in terraform_tf
assert "acdl-tfstate-" not in terraform_tf
def test_adapt_emits_root_outputs(self, tmp_path): def test_adapt_emits_root_outputs(self, tmp_path):
instance = json.load(open(ROOT / "modules/l1/s3/instance.json")) instance = json.load(open(ROOT / "modules/l1/s3/instance.json"))
instance["outputs"] = { instance["outputs"] = {
+6
View File
@@ -31,6 +31,12 @@ class TestEnvironmentCheck:
assert "state backend" in msg.lower() assert "state backend" in msg.lower()
assert "IAM role" in msg assert "IAM role" in msg
def test_onboarding_message_says_nova_not_acdl(self):
"""P2 (REQ-166): the onboarding message is rebranded Nova."""
msg = _onboarding_message("qa")
assert "Nova Environment Onboarding" in msg
assert "ACDL" not in msg
def test_contract_with_dev_environment_passes(self): def test_contract_with_dev_environment_passes(self):
ok, msg = check(contract_path=str(ROOT / "contracts/static-assets.yml"), root=ROOT) ok, msg = check(contract_path=str(ROOT / "contracts/static-assets.yml"), root=ROOT)
assert ok is True assert ok is True