docs(milestone): complete v1.15 — Nova Rebrand (tag v1.15.4)

P5 final-review-ship complete: dual-read fallback removed (REQ-164) —
core/env.py NOVA-only, .env.secrets load paths NOVA-only (G-106 retired),
nova_tagging.py hard-fails any acdl:* tag, legacy ACDL_* Gitea secrets
deleted, ACDL_LIFECYCLE_MODE/ACDL_LOCAL_TIER/ACDL_HITL_* exports removed
from scripts, SNS subject → Nova SoD halt (P1-2), bootstrap scripts
NOVA-only. Review: 2 P0 auto-fixed (duplicate delenv), P1-1/P1-2 resolved,
doc-drift fixed. Audit: tags v1.15.0-4 exist; traceability REQ-155..164
all complete; ARCHITECTURE naming table matches codebase. 615 pytest PASS;
run_ci.sh 3-stage PASS. NOVA_MIGRATION.md marked COMPLETE.

---ci---
project: acdl
phase: 5
milestone: v1.15
status: complete
phase_role: final
requirements:
  covered: [REQ-155, REQ-156, REQ-157, REQ-158, REQ-159, REQ-160, REQ-161, REQ-162, REQ-163, REQ-164]
  partial: []
---/ci---
This commit is contained in:
Jon Chery
2026-07-30 02:23:55 +00:00
parent eb7634da28
commit e15eea067b
20 changed files with 106 additions and 137 deletions
+14 -31
View File
@@ -1,19 +1,14 @@
"""Dual-read environment helper (D-108, REQ-159, G-106).
"""Environment helper (D-108, REQ-159, REQ-164).
During the Nova rebrand transition window (P2P4), every `NOVA_*`
environment variable is the preferred source, with the legacy `ACDL_*`
name as the fallback. This keeps deployments from breaking while the
keys are rotated across `.env`, `.env.secrets`, Gitea repo secrets, and
operator-managed process environments.
During the Nova rebrand transition window (P2P4), `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>` first, then falls
back to `ACDL_<name>`, then returns `default` if neither is set.
This helper is removed (NOVA-only) in P5 (REQ-164). 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`) mirror this contract inline per the G-106
binding — see those sites for the dual-read shell/Python forms.
`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
@@ -25,24 +20,12 @@ __all__ = ["get_env"]
def get_env(name: str, default: Optional[str] = None) -> Optional[str]:
"""Resolve a config value with a NOVA-preferred / ACDL-fallback read.
"""Resolve a config value from the `NOVA_*` environment.
`name` is the bare key WITHOUT the prefix (e.g. ``"AWS_ACCOUNT_ID"``).
The lookup order is:
1. ``NOVA_<name>`` (preferred)
2. ``ACDL_<name>`` (legacy fallback, removed in P5)
3. ``default``
Returns the first value that is present and non-empty, or ``default``
if neither env var is set. An explicitly-set empty string is treated
as "unset" so an operator cannot accidentally shadow the fallback
with a blank NOVA key.
Returns ``NOVA_<name>`` if set and non-empty, else ``default``.
"""
nova_val = os.environ.get(f"NOVA_{name}")
if nova_val:
return nova_val
acdl_val = os.environ.get(f"ACDL_{name}")
if acdl_val:
return acdl_val
val = os.environ.get(f"NOVA_{name}")
if val:
return val
return default
+1 -1
View File
@@ -502,6 +502,6 @@ if __name__ == "__main__":
# 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"
os.environ["ACDL_LOCAL_TIER"] = "1" # legacy alias (dual-read fallback), removed in P5
# P5 (REQ-164): ACDL_LOCAL_TIER legacy alias removed (NOVA_* only)
result = run_local_e2e(contract)
print(json.dumps(result, indent=2))
+3 -6
View File
@@ -315,13 +315,10 @@ def _load_aws_env() -> Dict[str, str]:
continue
if "=" in line:
k, v = line.split("=", 1)
# G-106 binding: dual-read NOVA_* first, ACDL_* fallback.
# The .env.secrets keys are renamed to NOVA_* in P2; the
# ACDL_* fallback covers operators who haven't rotated
# their local .env.secrets yet. Removed in P5.
if k == "NOVA_AWS_ACCESS_KEY_ID" or 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
elif k == "NOVA_AWS_SECRET_ACCESS_KEY" or k == "ACDL_AWS_SECRET_ACCESS_KEY":
elif k == "NOVA_AWS_SECRET_ACCESS_KEY":
env["AWS_SECRET_ACCESS_KEY"] = v
elif k == "AWS_DEFAULT_REGION":
env["AWS_DEFAULT_REGION"] = v
+1 -1
View File
@@ -74,7 +74,7 @@ def route_halt_artifact(contract_id: str, violation_reason: str,
sns.publish(
TopicArn=topic_arn,
Message=json.dumps(halt_payload),
Subject="ACDL SoD halt",
Subject="Nova SoD halt",
)
print(f"[halt-artifact] SNS published contract={contract_id} "
f"topic={topic_arn}", flush=True)