From e560adacb60c628d59943637f3a69e0358ae517d Mon Sep 17 00:00:00 2001 From: CIAgent Date: Thu, 20 Aug 2026 13:17:16 +0000 Subject: [PATCH] =?UTF-8?q?docs(P00):=20create=20phase=20plans=20=E2=80=94?= =?UTF-8?q?=20P1=20leadership-deck=20(4=20waves)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ---ci--- project: acdl phase: 0 milestone: v1.30 status: plan ---/ci--- --- .ciagent/CHECKPOINT.json | 6 +- .ciagent/PLAN.md | 1236 +++++++------------------------------- 2 files changed, 229 insertions(+), 1013 deletions(-) diff --git a/.ciagent/CHECKPOINT.json b/.ciagent/CHECKPOINT.json index 29faaba..2682cc6 100644 --- a/.ciagent/CHECKPOINT.json +++ b/.ciagent/CHECKPOINT.json @@ -1,10 +1,10 @@ { "phase": 0, - "stage": "research", + "stage": "plan", "milestone": "v1.30", "phase_role": "pre_execution", "attempts": 0, - "updated_at": "2026-08-20T13:35:00Z", + "updated_at": "2026-08-20T13:45:00Z", "project": "acdl", "projects": ["acdl", "nova-blockchain-exchange"], "active_milestone": "v1.30", @@ -23,5 +23,5 @@ "branches_deleted": true, "releases_created": true }, - "notes": "v1.30 Phase 0 RESEARCH complete. R1-R8: render pipeline behavior+limits, smoke-test conventions, Marp frontmatter/footer/notes, theme enforcement, python-pptx install (resolved), vision grounding, CAP-024 non-collision, slides.yml non-interference. PERSONAS.md: lead-developer + backend-engineer + ci-doc-writer (phase-specific) + ci-cli-engineer. frontend/data/security deactivated. Next: PLAN." + "notes": "v1.30 Phase 0 PLAN complete. 1 execution phase (P1) — single-shot artifact. 4 waves: W1 render_pptx.py extension, W2 deck source, W3 smoke test, W4 render+records. MVP/UX checklist: User-Facing Surface (PPTX+source+smoke test), Happy Path (J1), UX Acceptance Criteria (8 items). Next: GRILL." } \ No newline at end of file diff --git a/.ciagent/PLAN.md b/.ciagent/PLAN.md index 19eb629..7e007af 100644 --- a/.ciagent/PLAN.md +++ b/.ciagent/PLAN.md @@ -1,1059 +1,275 @@ -# PLAN — v1.28 CLI Canonicalization + Identity Layer +# PLAN — v1.30 Single-shot Leadership Deck -> **Milestone:** v1.28 (feature — CLI substrate + Nova-idp identity -> layer). Tags on the **v1.27.x** line: `v1.27.0` (P0) → -> `v1.27.1..v1.27.6` (P1..P6) → `v1.27.7` (P7 final = milestone -> release). The final phase's patch IS the milestone release. -> **Branch:** `milestone/v1.28-cli-identity`. Phase branches: -> `phase/00-pre-execution`, `phase/01-cli-substrate`, -> `phase/02-lambda-packaging`, `phase/03-idp-auth`, -> `phase/04-token-vend-pat`, `phase/05-docs-integration`, -> `phase/06-final-review-ship`. +> **Milestone:** v1.30 (feature — single-shot PPTX leadership deck). +> Tags on the **v1.29.x** line: `v1.29.1` (P0) → `v1.29.2` (P1 +> execution) → `v1.29.3` (P2 final = milestone release). The final +> phase's patch IS the milestone release. +> **Branch:** `milestone/v1.30-leadership-deck`. Phase branches: +> `phase/00-pre-execution`, `phase/01-leadership-deck`, +> `phase/02-final-review-ship`. > -> **Tags:** `v1.27.0` (P0) → `v1.27.1..v1.27.5` (P1..P5) → -> `v1.27.6` (P6 final = milestone release). 6 execution phases -> (P5 idp-setup folded into P4 Wave 8 per grill C-2.1). +> **Tags:** `v1.29.1` (P0) → `v1.29.2` (P1) → `v1.29.3` (P2 final = +> milestone release). **1 execution phase** (P1) — this is a +> single-shot artifact, not a multi-phase build. ## Milestone goal -The Nova CLI is installable from internal PyPI (CodeArtifact); every -`core/` module is reachable as a `nova `; the CLI and -Lambda functions share a single `core/` source tree; and Nova owns its -identity layer end-to-end (Nova-idp: `nova-idp-auth` + -`nova-idp-token-vend` Lambdas, KMS-signed OIDC tokens, kyverno-json -ABAC token vending, PAT lifecycle). No AWS-managed identity services -in the path (INV-15). +A single-shot, 7-slide PPTX leadership deck for Infrastructure & +Operations leadership (CTO + VP Technology + Product Management), +presented live in August 2026, securing architecture endorsement and +a November 2026 runway. Authored as Marp markdown, rendered via the +existing `scripts/render_pptx.py` (narrowly extended per D-242), +verified by `scripts/check_leadership_deck.sh`. The deck is discrete +from the existing citizen-developer pitch (D-241: NOT a compression). ## Requirements -31 requirements: REQ-323..REQ-353 (full text in -`.ciagent/REQUIREMENTS.md` §v1.28). 6 capabilities: CAP-033..CAP-038. -6 invariants: INV-12..INV-17. 6 decisions: D-226..D-231 (CLARIFY) + -RESEARCH amendments (D-228 fail-closed, D-229 strong-read-on-PK). +12 requirements: REQ-372.1..REQ-372.12 (full text in +`.ciagent/REQUIREMENTS.md` §v1.30). 1 capability: CAP-042. 3 +decisions: D-241..D-243 (CLARIFY). Vision grounding: `[1]` → +`docs/vision.md`. ## Phase breakdown -### Phase P1 — cli-substrate (REQ-323..REQ-328) +### Phase P1 — leadership-deck (REQ-372.1..REQ-372.12) -**Goal:** CodeArtifact wheel + Lambda layer pipeline; `nova/` CLI -package with a subcommand per `core/` module; `nova init`; `nova -cli-action` composite action; `core/mode_resolver.py`; audit emission -with `mode` + `selection_reason`. The CLI is installable and every -`core/` module is reachable. +**Goal:** Author the Marp markdown deck source, extend the render +pipeline, render the PPTX, author the smoke test, and append the +ship-wave records (CAP-042, D-241). The deck is a single-shot +artifact; all 12 REQs ship in this one phase. -**Exit criterion:** CAP-033 + CAP-034 + CAP-035 Verified + all REQ-323..328 -tests pass. CodeArtifact provisioned (Wave 0 gate). +**Personas:** lead-developer, backend-engineer, ci-doc-writer +(phase-specific), ci-cli-engineer. -#### Wave 0 — CodeArtifact provisioning (backend-engineer) [C-3.2/C-8.1] -- **Task 0.1** (backend-engineer): provision CodeArtifact domain - (`nova`) + repository (`nova-pypi`) in `581513795199`. Verify - `codeartifact:*` IAM grant on `nova-spike-runner`. **Binary go/no-go - gate for Wave 4.** If fail: activate Gitea wheel index fallback - (CLARIFY assumption #1) and document in PLAN.md. +**Territory:** `docs/presentations/nova-leadership-deck-marp.md` +(ci-doc-writer), `scripts/render_pptx.py` + +`docs/presentations/nova-leadership-deck.pptx` (backend-engineer), +`scripts/check_leadership_deck.sh` (ci-cli-engineer), +`.ciagent/STATE.md` + `.ciagent/PROJECT.md` (lead-developer). -#### Wave 1 — pyproject + entry point (cli-engineer) -- **Task 1.1** (cli-engineer): `pyproject.toml` — add - `[project.scripts] nova = "nova.cli:main"`; add - `[tool.setuptools.packages.find]` including `nova`, `nova.*`, `core`, - `core.*`, `adapters.*`; bump `requires-python` to `>=3.12`; add - `argon2-cffi`, `cryptography`, `pyjwt`, `hypothesis` to deps/test-deps. - Verify `pip install -e .` produces a `nova` executable. +#### Wave 1 — render pipeline prerequisite (backend-engineer) -#### Wave 2 — CLI dispatch + subcommands (cli-engineer) -- **Task 2.1** (cli-engineer): `nova/__init__.py` + `nova/cli.py` - (~80 lines, auto-discovers `nova/.py` via `pkgutil.iter_modules`, - dispatches, emits `cli.invocation` audit event stub with INV-12 fields). -- **Task 2.2** (cli-engineer): `nova/.py` for each `core/` - module (≤50 lines, `add_parser` + `run` delegates to `core/`). Cover: - `resolve`, `decommission`, `env-transition`, `env-check`, `hitl`, - `onboard`, `outbox`, `publish-outputs`, `policy`, `regression`, `sod`, - `readiness`, `attestation-matrix`, `confidence`. Skip internal-only - (`env`, `local_emulators`, `output_publisher` if not user-facing). -- **Task 2.3** (cli-engineer): `nova/init.py` (REQ-325) — scaffolds - `.nova/`, `.nova/contract.yml.attestations/`, `.gitignore` (excludes - secrets, `~/.nova/credentials.json`). +**Task P1.W1.T1:** Extend `scripts/render_pptx.py` per D-242: +- Accept an explicit source `.md` path as argv[1] (if it ends in + `.md` and contains a `/`, treat as a path; else treat as a deck + name per the existing convention — backward compatible). +- Accept `--output ` for the custom output filename. Default: + derive from the source name (strip `-marp.md` → add `.pptx`) for + backward compatibility. +- Add a `_add_footer(slide, text)` helper that adds a right-aligned + textbox at the bottom of every slide with the exact string + `Nova Platform - Infrastructure & Operations` (grey, small). Call + it in both `render_title_slide` and `render_content_slide`. +- The footer text is read from the Marp frontmatter `footer:` + directive if present; else default to the existing deck's footer + (backward compatible). Parse the frontmatter to extract the + `footer:` value (the existing code strips frontmatter without + reading it — add a frontmatter parser). +- **No other renderer change.** Speaker notes remain skipped + (acceptable per RESEARCH R1). -#### Wave 3 — mode_resolver + audit (cli-engineer) -- **Task 3.1** (cli-engineer): `core/mode_resolver.py` — - `resolve_mode(flag, env_var, credential_type, stdin_isatty)` per D-226. - `sys.stdin.isatty()` is the TTY check (RESEARCH §11). Invalid env → - warn + fall through. Returns `(mode, selection_reason)`. -- **Task 3.2** (cli-engineer): wire `mode_resolver` into `nova/cli.py` - — resolve mode before dispatch, emit `cli.invocation` with `mode`, - `selection_reason`, `credential_type`, `command`, `args` (INV-12, - REQ-328). -- **Task 3.3** (cli-engineer): `tests/test_mode_resolver.py` — - `hypothesis` property tests (REQ-349): deterministic, flag-wins, - invalid-env-ignored, no-silent-fallback. Edge cases: TTY + piped - stdout, missing credential, conflicting flag/env, invalid env value. +**Must-haves:** +- `python3 scripts/render_pptx.py docs/presentations/nova-leadership-deck-marp.md --output docs/presentations/nova-leadership-deck.pptx` works. +- `python3 scripts/render_pptx.py nova-autonomous-cloud-delivery` still works (backward compatible — renders `{deck}-marp.md` → `{deck}-python.pptx`). +- Every rendered slide has a right-aligned footer textbox. +- `python3 -m py_compile scripts/render_pptx.py` exits 0. -#### Wave 4 — CodeArtifact + layer pipeline (backend-engineer) -- **Task 4.1** (backend-engineer): `.gitea/workflows/publish.yml` + - `.github/workflows/publish.yml` (byte-identical) — build wheel → - CodeArtifact `twine upload` → build layer (`pip install --target - layer/python/` + `argon2-cffi` + `cryptography` + `pyjwt`) → - `lambda publish-layer-version` → SSM `/nova/layer/nova-cli/version` - mapping (CAP-035). Fail either → job fails (merge blocked, REQ-323). - Pin version to `+` for idempotent re-runs. +**REQs covered:** (prerequisite, not REQ-372 directly — per spec §3.3 +Edge 2 + D-242). -#### Wave 5 — composite action (cli-engineer + backend-engineer) -- **Task 5.1** (cli-engineer): `.github/actions/nova-cli/action.yml` — - composite action, `setup-python@v5` (3.12), CodeArtifact login + - `pip install nova`, `nova ${{ inputs.command }}`. `NOVA_CLIENT_MODE` - from input. -- **Task 5.2** (backend-engineer): byte-identical integration test — - CI matrix runs the action on GitHub `ubuntu-latest` + Gitea - `act_runner`; assert same stdout/exit code (REQ-326 AC2, NFR-11). +#### Wave 2 — deck source (ci-doc-writer) -#### Wave 6 — CAP-033/034 gate (cli-engineer) -- **Task 6.1** (cli-engineer): `tests/test_cli_subcommands.py` — - CAP-033 (`nova --help` lists a subcommand for every `core/` module) - + CAP-034 (AST scan: ≤50 lines, ≤3 defs, all calls resolve to `core.`, - no conditionals beyond `if __name__`). Wire into CI merge gate. +**Task P1.W2.T1:** Author +`docs/presentations/nova-leadership-deck-marp.md`: +- **Header comment (REQ-372.9):** an HTML comment at the top (before + frontmatter) naming this deck as the leadership artifact for + Infrastructure & Operations, August 2026 presentation date, and + naming `nova-autonomous-cloud-delivery-marp.md` as a related-but- + distinct artifact that this deck does not compress or modify. +- **Frontmatter (cover note):** `marp: true; theme: default; footer: + "Nova Platform - Infrastructure & Operations"; paginate: false; + size: 16:9` + a `style:` block using ONLY the 4 S&P tokens + (`#D6002A`, `#1B1B1B`, `#FFFFFF`, `#F0F0F0`). **Replace the + existing deck's `#2E2E2E` blockquote color with `#1B1B1B`** to + satisfy REQ-372.6. +- **7 slides** delimited by `---` on its own line (REQ-372.3). All + slides use `##` H2 titles (content slides, white bg, red title bar + — per RESEARCH R1 final call for visual consistency). +- **On-slide body** per the Slide Content Map in PROJECT.md §v1.30 + (REQ-372.7). Use `>` blockquotes for the italic callouts, `**...**` + bold lead for the slide titles' sub-headings, `-` bullets with `→` + prefix for the arrow lines (so they render as proper bullets). +- **Speaker notes** per slide as HTML comments `` within + the slide body before the next `---` (REQ-372.4). Word counts: + slides 1/2/4/6 in 150–300; slides 3/5 in 250–400; slide 7 in + 200–300. +- **`[1]` citations** (REQ-372.12): at least one `[1]` in speaker + notes of slides 3, 5, 7 (the architecture-load slides), grounding + to `docs/vision.md` tenets/anti-goals/boundaries. +- **No hex colors** outside the 4 S&P tokens anywhere in the source + (REQ-372.6). -### Phase P2 — lambda-packaging (REQ-329, REQ-330, REQ-331) +**Must-haves:** +- File exists, parses as valid Marp, exactly 7 `---`-delimited slides. +- Header comment present with all 3 elements (REQ-372.9). +- Frontmatter has the exact footer string + `paginate: false`. +- Per-slide speaker-note word counts in band. +- `[1]` present in slides 3, 5, 7 speaker notes. +- `grep -oiE '#[0-9A-Fa-f]{6}'` returns only the 4 S&P tokens. -**Goal:** Dual-use `core/lambda/contract_ingestor.py` (Lambda + CLI -paths share ≥80% code); `core/env.py:+synthesize_local_env()` for -`nova apply --local`; `.nova/contract.yml.attestations/` scaffolded; -JWS-from-PAT key derivation (C-5.2). +**REQs covered:** REQ-372.1, REQ-372.3, REQ-372.4, REQ-372.6, +REQ-372.7, REQ-372.9, REQ-372.12. -**Exit criterion:** all REQ-329..331 tests pass + JWS-from-PAT KDF -specified. +#### Wave 3 — smoke test (ci-cli-engineer) -#### Wave 1 — dual-use refactor (backend-engineer) -- **Task 1.1** (backend-engineer): refactor - `core/lambda/contract_ingestor.py` — extract the shared logic into - importable functions; the Lambda handler + the CLI `__main__` block - both call them. The `__main__` block already exists (the dual-use - precedent per RESEARCH §1.2). Verify ≥80% code share (CAP-034 / code - review). Local path via `core/local_emulators.py:LocalLambdaStub`. +**Task P1.W3.T1:** Author `scripts/check_leadership_deck.sh`: +- Shebang `#!/usr/bin/env bash`, `set -euo pipefail`, header comment + with Usage + Returns. +- Assertions (REQ-372.8 a–f): + - (a) `docs/presentations/nova-leadership-deck-marp.md` exists. + - (b) slide count = 7 (count `^---\s*$` lines after frontmatter + end, +1; or count `---` separators — careful with frontmatter). + - (c) per-slide speaker-note word counts in band (extract `` content per slide, `wc -w`; slides 1/2/4/6: 150–300; 3/5: + 250–400; 7: 200–300). Exit non-zero on violation. + - (d) footer string `Nova Platform - Infrastructure & Operations` + present in source (frontmatter `footer:` directive). + - (e) only S&P hex colors in source (`grep -oiE '#[0-9A-Fa-f]{6}'`, + `sort -u`, compare to 4-token allow-list). + - (f) `docs/presentations/nova-leadership-deck.pptx` exists (hard + fail per Q-M4). +- Exit 0 on pass, non-zero (1) on fail. Runnable from repo root. +- NOT wired as a CI gate (no `.github/workflows/` or + `workflows-src/` integration). -#### Wave 2 — local env synthesizer + JWS KDF (backend-engineer) -- **Task 2.1** (backend-engineer): `core/env.py:+synthesize_local_env() - ` — produces a local env dict (account_id placeholder, region local, - no real AWS) from a contract + `--local` flag. Mirrors - `core/onboarding.py:generate_env_file()`. -- **Task 2.2** (cli-engineer): `nova/apply.py` (≤50 lines) — `nova - apply --local` delegates to `core.env.synthesize_local_env()` + - `core.contract_resolver.resolve()`. -- **Task 2.3** (security-engineer): JWS-from-PAT key derivation - (C-5.2). HKDF-SHA256(PAT_bytes, salt='nova-local-attestation', - info='jws-signing-key') → 32-byte symmetric key. The JWS is - HMAC-SHA256 (symmetric, not asymmetric). The "public key derivable - from the PAT" AC (REQ-332) is re-interpreted: the *verification key* - is derived from the PAT via the same KDF (the PAT is the shared - secret). Document in `docs/developer-guide-auth.md`. Update REQ-332 - AC accordingly. +**Must-haves:** +- `bash scripts/check_leadership_deck.sh` exits 0 after the deck + + PPTX are authored/rendered. +- Exits non-zero if any assertion fails (test by temporary + mutation). -#### Wave 3 — attestations dir (cli-engineer) -- **Task 3.1** (cli-engineer): verify `nova init` (P1 Wave 2 Task 2.3) - creates `.nova/contract.yml.attestations/` (empty). REQ-331 test. +**REQs covered:** REQ-372.8. -### Phase P3 — idp-auth (REQ-333, REQ-334, REQ-335) +#### Wave 4 — render + ship-wave records (backend-engineer + lead-developer) -**Goal:** `nova-idp-auth` Lambda (sign-up, sign-in, session) with -Argon2id hashing + DynamoDB tables. CAP-036 target. +**Task P1.W4.T1 (backend-engineer):** Render the PPTX: +- `python3 scripts/render_pptx.py docs/presentations/nova-leadership-deck-marp.md --output docs/presentations/nova-leadership-deck.pptx` +- Verify: PPTX written with 7 slides, python-pptx raised no + exceptions, footer textbox present on every slide. +- Run `bash scripts/check_leadership_deck.sh` → exits 0. -**Exit criterion:** CAP-036 Verified (E2E sign-up → sign-in → session -passes in CI). +**Task P1.W4.T2 (lead-developer):** Append CAP-042 to STATE.md: +- CAP-042 row in the capability table: artifact paths + (`nova-leadership-deck-marp.md`, `nova-leadership-deck.pptx`), + audience (Infrastructure & Operations leadership), single-shot + intent, presentation month (August 2026), milestone v1.30 / tag + `v1.29.3`. -#### Wave 1 — DynamoDB schema (backend-engineer) -- **Task 1.1** (backend-engineer): define the 4 DynamoDB table schemas - (`nova-users`, `nova-sessions`, `nova-password-resets`, `nova-pats`) - in a CloudFormation snippet (reused by P4 Wave 8 `nova idp setup`). - PITR enabled on each (REQ-335). +**Task P1.W4.T3 (lead-developer):** Record D-241 in PROJECT.md: +- D-241 entry in the decisions section: single-shot nature, audience, + August 2026 anchor + November 2026 runway, explicit decision not + to compress the existing citizen-developer deck. -#### Wave 2 — Argon2id (security-engineer) [C-1.2/C-7.2] -- **Task 2.1** (security-engineer): `core/lambda/nova_idp_auth.py` — - Argon2id password hashing via `argon2-cffi` (D-228: bundled abi3 - wheel; **fail-closed on `ImportError` → 503, no pure-Python - fallback**). **Parameters: t=3, m=65536 KiB, p=1** (OWASP-recommended - minimum). Lambda memory ≥512 MB (m=64 MiB + Python overhead fits). - Raw passwords never in logs/traces/env/DDB (INV-16, REQ-334). -- **Task 2.2** (security-engineer): `tests/test_argon2_fail_closed.py` - — mock `argon2.low_level` import failure → assert auth Lambda - returns 503 (not a crash, not a weak hash). C-1.2. +**Must-haves:** +- PPTX exists with 7 slides. +- Smoke test exits 0. +- CAP-042 row in STATE.md. +- D-241 record in PROJECT.md. -#### Wave 3 — auth Lambda (backend-engineer + security-engineer) -- **Task 3.1** (backend-engineer): `nova-idp-auth` Lambda handler — - sign-up, sign-in, session creation endpoints. Function URL + IAM - auth. DynamoDB via lazy `boto3.resource` (the existing pattern). -- **Task 3.2** (security-engineer): session token issuance + session - storage in `nova-sessions` (TTL `expires_at`). Password reset flow - in `nova-password-resets` (TTL 15m). +**REQs covered:** REQ-372.2, REQ-372.5, REQ-372.8, REQ-372.10, +REQ-372.11. -#### Wave 4 — CAP-036 E2E (backend-engineer) -- **Task 4.1** (backend-engineer): `tests/test_idp_auth.py` — sign-up - → sign-in → session round-trip (moto[dynamodb] for local; deployed - for CI). CAP-036 verification. +### Phase P2 — final-review-ship (review + audit + milestone ship) -### Phase P4 — token-vend-pat (REQ-336..REQ-344, REQ-340, REQ-341) [was P4+P5] +**Goal:** Multi-persona review of the milestone changes, project- +health audit, and milestone ship (merge to main, tag `v1.29.3` = +milestone release, delete milestone branches). -**Goal:** `nova-idp-token-vend` Lambda (KMS-signed OIDC, kyverno-json -ABAC), JWKS endpoint, PAT lifecycle, `nova auth` commands, **and** -`nova idp setup` (folded from P5 per C-2.1). CAP-037 + CAP-038 target. -**Highest-risk phase — critical-path.** The kj-binary spike (Wave 1) -is the single highest-probability schedule slip; Fargate fallback adds -~1 week (D-227). This is a **double-length phase** (8 waves). +**Personas:** lead-developer (review + audit + ship coordination). -**Exit criterion:** CAP-037 + CAP-038 Verified + `nova idp setup ---check/--apply/--verify` works against a fresh AWS account. +**Tasks:** +- Review all v1.30 changes (deck source, render extension, smoke + test, STATE.md/PROJECT.md records). Auto-apply P0 fixes; flag P1+ + for post-hoc review. +- Audit: reconstruction test (git log matches `.ciagent/` files), + file discipline, branch hygiene, commit discipline. +- Ship: merge `phase/02` → `milestone/v1.30-leadership-deck` → + `main`, tag `v1.29.3`, create release, delete milestone branches. +- Complete: mark REQ-372.1..12 complete in REQUIREMENTS.md, mark + v1.30 complete in ROADMAP.md. -#### Wave 1 — kj-binary spike (backend-engineer + security-engineer) [C-8.2] -- **Task 1.1** (backend-engineer): confirm the `kj` Go binary - (~40 MB Linux amd64) runs in the Lambda Python 3.12 runtime on - AL2023. Bundle it in the `nova-cli` layer (`wget` a **pinned - release** (e.g. `kj@v1.x.y`) + record SHA256 into `layer/kj.sha256` - — C-8.2, supply-chain safety) into `layer/bin/kj`, `chmod +x`. - Verify `KyvernoJsonEngine.is_configured()` finds `/opt/bin/kj`. - **If this fails:** fall back to Fargate for the token-vend Lambda - (D-227 risk, RESEARCH §7). Escalate to user only if both fail (full - autonomy: log assumption + proceed with Fargate). +## Wave dependency graph -#### Wave 2 — ABAC policy (security-engineer) [C-5.1] -- **Task 2.1** (security-engineer): `platform/abac/token-vend.policy` - — kyverno-json `ValidatingPolicy` (D-227). Payload: - `{subject, requested_claims, target_resource, environment, pat_jti, - policy_version}`. **`requested_claims` = list of claim names** (the - policy asserts the subject is *allowed* to request those claims; the - values are assigned by the Lambda, not the requestor — C-5.1). - JMESPath checks for role/scope/env/owner. Severity `critical` = deny - on fail. -- **Task 2.2** (security-engineer): `policy_version` = git SHA of the - policy file, baked into the Lambda layer (D-231). Recorded in every - `token.vend.allowed/denied` audit event. +``` +W1 (render_pptx.py extension) ─┐ + ├─→ W4.T1 (render PPTX) ─→ W4.T2/T3 (records) +W2 (deck source) ──────────────┤ │ + │ ↓ +W3 (smoke test) ───────────────┴────────────────────────→ P1 VERIFY + │ + ↓ + P1 SHIP (v1.29.2) + │ + ↓ + P2 (v1.29.3) +``` -#### Wave 3 — KMS signing (security-engineer) [C-1.1] -- **Task 3.1** (security-engineer): **verify KMS asymmetric key - support** before implementation: `aws kms create-key --key-spec - ECC_NIST_P256 --key-usage SIGN_VERIFY` in the target account - (C-1.1). If fail: fall back to RSA-2048 (also supported, larger - tokens) or escalate. Do not discover this mid-Wave. -- **Task 3.2** (security-engineer): KMS key `alias/nova-oidc-signing` - (ECC_NIST_P256, SIGN_VERIFY). Token-vend Lambda signs via - `kms.sign(SigningAlgorithm="ECDSA_SHA_256")` → DER→raw ECDSA - conversion (`decode_dss_signature` → `r.to_bytes(32) + s.to_bytes(32)`, - RESEARCH §5). JWT header `{"alg":"ES256","typ":"JWT","kid":"..."}`. - -#### Wave 4 — token-vend Lambda (backend-engineer + security-engineer) [C-6.1/C-7.1 ABAC FAIL-CLOSED] -- **Task 4.1** (backend-engineer): `core/lambda/nova_idp_token_vend.py` - — accepts PAT/session, validates revocation (`nova-pats.GetItem(jti, - ConsistentRead=True)` — D-229), evaluates ABAC (Wave 2), signs (Wave - 3), returns OIDC JWT. Audit at every step. -- **Task 4.2** (security-engineer): token claims `sub, aud, iss, exp, - iat, jti, roles` (REQ-336). -- **Task 4.3** (security-engineer) 🔴: **ABAC fail-closed.** If - `KyvernoJsonEngine.is_configured()` returns false or `evaluate()` - raises, return 403 + audit `token.vend.denied` (reason: - `abac_eval_failed`). **Never fail open.** This is INV-17's runtime - enforcement (C-6.1/C-7.1 — the grill's #1 finding). Test: - `tests/test_abac_fail_closed.py` — mock `kj` absent → assert 403 + - audit event. - -#### Wave 5 — JWKS endpoint (backend-engineer) -- **Task 5.1** (backend-engineer): `core/lambda/nova_idp_jwks.py` — - function URL `AuthType: NONE`, `Cache-Control: max-age=3600`. - `kms.get_public_key` → DER SPKI → JWK via `cryptography`. Returns - `{"keys":[...]}`. Custom domain + WAF = optional (D-230). - -#### Wave 6 — PAT lifecycle (security-engineer + cli-engineer) [C-7.3] -- **Task 6.1** (security-engineer): PAT issuance — signed JWT - (`typ: "developer_pat"`, INV-14), `nova-pats` PutItem (jti, pat_hash, - status=active). Only hash stored (REQ-343). Revoked PATs retained. - **Max TTL: ≤24h for developer PATs, ≤1h for service-account PATs** - (C-6.2 threat model). -- **Task 6.2** (cli-engineer): `nova/auth/{login,revoke,status}.py` — - `nova auth login` (session→OIDC token, store in - `~/.nova/credentials.json` 0600), `nova auth revoke --pat `, - `nova auth status` (active credential, mode, selection_reason). - All emit audit events (REQ-344). **C-7.3: `~/.nova/credentials.json` - stores the OIDC token + PAT metadata (jti, exp, type) ONLY — NOT the - raw PAT.** The raw PAT is entered once at `nova auth login` and not - persisted (reduces filesystem-compromise blast radius). - -#### Wave 7 — CAP-037/038 (security-engineer) -- **Task 7.1** (security-engineer): `tests/test_kms_roundtrip.py` - (REQ-350, CAP-037) — sign test JWT via token-vend, fetch JWKS, - verify with `pyjwt`. `tests/test_pat_revocation.py` (REQ-351, - CAP-038) — issue → vend → revoke → assert 403 within 60s P95. - -#### Wave 8 — nova idp setup (cli-engineer + backend-engineer) [folded from P5 per C-2.1] - -**Goal:** `nova idp setup` command with `--check/--apply/--verify` -modes; CloudFormation template generation + review (REQ-340, REQ-341). - -- **Task 8.1** (backend-engineer): `nova/idp/setup.py` (+ backend - helper) — generates the Nova-idp CloudFormation template (raw dict → - JSON): 2-3 Lambdas, 4 DDB tables, KMS key, function URLs, IAM roles, - optional CloudFront/WAF/ACM (`--public-jwks-domain` flag). -- **Task 8.2** (cli-engineer): `--check` (prerequisites + IAM policy - delta), `--apply` (generate → `$PAGER` → `y/N` → `cloudformation - deploy --capabilities CAPABILITY_IAM`, NFR-10), `--dry-run` (resource - list only), `--verify` (KMS round-trip, delegates to REQ-350 test). -- **Task 8.3** (backend-engineer): IAM policy delta computation — - compares current `nova-spike-runner` grants to required - `cloudformation:*` + `codeartifact:*` + `kms:*` + `lambda:*` + - `dynamodb:*` + `ssm:*`. - -### Phase P5 — docs-integration (REQ-345..REQ-351) - -**Goal:** Operator guide, developer guide, threat model; E2E -integration test; property tests; KMS round-trip; PAT revocation SLO. - -**Exit criterion:** all REQ-345..351 tests pass + docs published + -threat model reviewed. - -#### Wave 1 — docs (lead-developer + security-engineer) [C-6.2/C-6.3/C-9.2] -- **Task 1.1** (lead-developer): `docs/operator-guide-idp.md` (REQ-345) - — `nova idp setup --check/--apply/--verify`, prerequisite IAM policy, - CloudFormation review flow. **C-6.3 additions:** KMS key rotation - procedure (90 days), Lambda layer update procedure, DDB PITR restore - procedure, emergency PAT revocation (DDB-level, not CLI). -- **Task 1.2** (lead-developer): `docs/developer-guide-auth.md` - (REQ-346) — signup, signin, login, mode resolution, TTY vs piped - stdout behavior, JWS-from-PAT KDF (P2 Wave 2 Task 2.3). -- **Task 1.3** (security-engineer): `docs/threat-model.md` (REQ-347) — - Argon2id storage, KMS signing, JWKS exposure, PAT revocation SLO, - ABAC token vending, no-AWS-managed-identity (INV-15), DER→raw ECDSA - gotcha. **C-6.2 additions:** (a) JWKS unauthenticated endpoint DDoS - surface + reserved-concurrency mitigation; (b) PAT theft + max TTL - (≤24h dev, ≤1h service-account); (c) ABAC fail-closed guarantee - (C-6.1). **C-9.2 addition:** INV-18..21 compression audit — verify - the spec's attestation invariant semantics are fully captured by - INV-15/16/17 + REQ-332. - -#### Wave 2 — integration tests (backend-engineer + security-engineer) -- **Task 2.1** (backend-engineer): `tests/test_e2e_idp.py` (REQ-348) — - sign-up → sign-in → token-vend → apply → audit. Verifiable audit - chain. Runs in CI against deployed Nova-idp. -- **Task 2.2** (security-engineer): verify REQ-349 (mode_resolver - property tests, P1 Wave 3 Task 3.3) + REQ-350 (KMS round-trip, P4 - Wave 7 Task 7.1) + REQ-351 (PAT revocation SLO, P4 Wave 7 Task 7.1) - pass in CI. - -### Phase P6 — final-review-ship (Final Phase) - -**Goal:** Multi-persona code review across P1..P5; project-health -audit; milestone ship to main; CAP-033..038 Verified. - -#### Wave 1 — review (lead-developer) -- **Task 1.1** (lead-developer): `ciagent-review` across all phases. - Auto-fix P0; flag P1+ for post-hoc. If P1+ found, fix in this phase. - -#### Wave 2 — audit (lead-developer) -- **Task 2.1** (lead-developer): `ciagent-audit` — reconstruction test - (git log ↔ `.ciagent/`), file/branch/commit discipline. Fix critical - issues in this phase. - -#### Wave 3 — milestone ship (lead-developer) -- **Task 3.1** (lead-developer): `ciagent-ship` — merge `phase/06` → - `milestone/v1.28-cli-identity` → `main`; tag `v1.27.6` (= the v1.28 - release); Gitea release with full milestone summary; delete all - milestone branches. Update REQUIREMENTS.md (mark REQ-323..353 - complete), ROADMAP.md (mark v1.28 complete), STATE.md (append - CAP-033..038 + INV-12..17), NORTH_STAR.md. - ---- +W1, W2, W3 are independent (no cross-dependencies at author time). +W4 depends on W1 + W2 (render needs the extension + the source) + +W3 (smoke test validates the render). W4.T2/T3 (records) depend on +W4.T1 (render confirms ship readiness). ## User-Facing Surface -> MVP/UX CHECK §1 (REQ-MVP-UX-001). - -1. **CLI flag:** `nova --help` lists every subcommand; `nova init` - scaffolds a project; `nova auth login` authenticates; `nova apply - --local` runs locally; `nova idp setup` deploys the identity stack. -2. **README quickstart:** `docs/developer-guide-auth.md` (REQ-346) - documents signup → signin → login → `nova apply` in a quickstart. -3. **`.feature` Scenario:** `tests/test_e2e_idp.py` (REQ-348) is the - E2E happy path (sign-up → sign-in → token-vend → apply → audit). +- **The PPTX deck** (`docs/presentations/nova-leadership-deck.pptx`) + — the primary leadership-facing artifact, presented live in August + 2026. +- **The Marp markdown source** + (`docs/presentations/nova-leadership-deck-marp.md`) — the source- + of-truth, reproducible via `scripts/render_pptx.py`. +- **The smoke test** (`scripts/check_leadership_deck.sh`) — runnable + on demand by the PO/presenter to verify deck integrity before + presentation. ## Happy Path -> MVP/UX CHECK §2 (REQ-MVP-UX-001). End-to-end scenario written BEFORE -> execute. +**J1 — PO presents the deck live** (from spec §3.2): -**Journey 2 — Dev authenticates and deploys locally:** -1. `nova auth signup` → `nova-idp-auth` Lambda → Argon2id hash → - `nova-users` PutItem → session token. -2. `nova auth signin` → `nova-idp-auth` → Argon2id verify → session. -3. `nova auth login` → `nova-idp-token-vend` (exchanges session for - Nova OIDC token; stores in `~/.nova/credentials.json` 0600). -4. `nova init` in a project dir → `.nova/`, `.gitignore`, - `.nova/contract.yml.attestations/`. -5. `nova apply --local --sign-local-review` → - `core.env.synthesize_local_env()` → `core.contract_resolver.resolve()` - → JWS attestation signed with a key derived from the PAT → local - ledger entry. - -The E2E test (`tests/test_e2e_idp.py`, REQ-348) verifies this chain + -the audit event chain in CI against a deployed Nova-idp. +1. PO authors `nova-leadership-deck-marp.md` against the Slide + Content Map; `bash scripts/check_leadership_deck.sh` exits 0 + (verifies content). _(REQ-372.1, .4, .6, .7, .8, .12.)_ +2. PO renders the markdown to PPTX via + `python3 scripts/render_pptx.py docs/presentations/nova-leadership-deck-marp.md --output docs/presentations/nova-leadership-deck.pptx`; + visual inspection confirms 7 slides + footer. _(REQ-372.2, .5.)_ +3. PO presents live to Infrastructure & Operations leadership; + speaker notes carry architecture depth. _(REQ-372.4.)_ +4. PO updates STATE.md with CAP-042 and PROJECT.md with D-241 at the + v1.30 ship wave. _(REQ-372.10, .11.)_ ## UX Acceptance Criteria -> MVP/UX CHECK §3 (REQ-MVP-UX-001). - -1. `nova --help` exits 0 and lists a subcommand for every `core/` - module (CAP-033). -2. `nova init` in an empty dir creates `.nova/`, - `.nova/contract.yml.attestations/`, `.gitignore` (secrets excluded). -3. `nova auth login` at a TTY resolves `mode=interactive, - selection_reason=credential:developer_pat` (INV-12, INV-14). -4. `nova apply --local` produces a JWS attestation verifiable with the - public key derived from the PAT (REQ-332). -5. `nova idp setup --check` reports prerequisites + IAM policy delta; - `--apply` presents the CloudFormation template for review before any - resource is created (NFR-10); `--verify` confirms the KMS round-trip. -6. The Forge action (`nova cli-action`) runs `nova apply` in - `mode=agent, selection_reason=credential:service_account_pat` with - no TTY dependency (Journey 3, INV-12). -7. PAT revocation takes effect within 60s P95 (NFR-4, CAP-038). - ---- - -## Capability gate (CAP-033..CAP-038) - -| CAP | Name | Phase | Gate rule | -|-----|------|-------|-----------| -| CAP-033 | CLI subcommand surface exists | P1 | `nova --help` lists a subcommand for every `core/` module | -| CAP-034 | Subcommand delegates to `core/` | P1 | Every `nova/.py` ≤50 lines, no business logic, AST scan | -| CAP-035 | Layer matches wheel | P1 | Lambda layer ARN version matches `nova-cli` wheel version (SSM mapping) | -| CAP-036 | Nova-idp auth flow works | P3 | E2E test (sign-up → sign-in → session) passes in CI | -| CAP-037 | Token-vend signs via KMS | P4 | KMS round-trip test (REQ-350) passes in CI | -| CAP-038 | PAT issuance + revocation | P4 | Issue → vend → revoke → 403 within 60s P95 (REQ-351) passes in CI | -**Release gate (§6 of the spec):** CAP-001..CAP-032 remain Verified; -CAP-033..CAP-038 are Verified; all v1.28 release-gate criteria met. - ---- - -## Test evidence required for v1.28 release - -- [ ] Code coverage ≥ 80% on new modules (`mode_resolver.py`, - `nova-idp-auth`, `nova-idp-token-vend`, PAT lifecycle). -- [ ] CI/CD pipeline GREEN: wheel + Lambda layer publish on every merge - (REQ-323, CAP-035). -- [ ] QA sign-off: all four happy-path journeys (J1–J4) pass integration - tests in CI. -- [ ] Security/compliance review: threat model published, Argon2id - verified, ABAC policy reviewed. -- [ ] Capability gate GREEN: CAP-001..032 remain Verified; CAP-033..038 - Verified. -- [ ] Mode resolver property tests pass (all four priority levels + edge - cases; REQ-349). -- [ ] KMS round-trip test passes against deployed JWKS (REQ-350). -- [ ] PAT revocation SLO verified: ≤60s P95 in CI (REQ-351, NFR-4). -- [ ] Operator + developer guides published. -- [ ] `nova idp setup` succeeds in a fresh AWS account. -- [ ] Byte-identical Forge action on GitHub + Gitea (REQ-326, NFR-11). - ---- - -## Plan completeness checklist - -- [x] Every REQ-323..353 mapped to a phase + wave + task. -- [x] Every CAP-033..038 mapped to a phase + gate rule. -- [x] Every INV-12..17 referenced in persona constraints. -- [x] Every D-226..231 referenced in task rationale. -- [x] Vertical slices: each phase ships independently (P1 CLI substrate - is useful before P2 packaging; P2 before P3 auth; etc.). -- [x] Wave ordering within phases (no wave N+1 depends on wave N work - in the same phase). -- [x] Persona assignments per task (4 active personas). -- [x] MVP/UX CHECK: 3 sections present (User-Facing Surface, Happy Path, - UX Acceptance Criteria). -- [x] Highest-risk item flagged (P4 Wave 1 kj-binary spike). -- [x] Grill conditions applied (3 critical + 16 tracked; see GRILL.md). - ---- - -## Cost envelope (C-3.1) - -Monthly estimate for the default (no CloudFront) Nova-idp deployment in -account `581513795199`: - -| Resource | Quantity | Pricing | Est. monthly | -|----------|----------|---------|-------------| -| DynamoDB (on-demand) | 4 tables | $1.25/1M write, $0.25/1M read | ~$1 (pilot volume) | -| DynamoDB PITR | 4 tables | $0.20/GB-month | ~$1 (small tables) | -| KMS asymmetric key | 1 key | $1/key-month + $0.03/10k signs | ~$1 | -| Lambda invocations | 3 Lambdas | $0.20/1M req + $0.000016/GB-s | ~$2 (low volume) | -| Lambda layer storage | ~50 MB | $0.02/GB-month | <$1 | -| CodeArtifact | 1 domain + 1 repo | $1/domain + $1/repo | $2 | -| SSM Parameter | 1 | $0.05/param (advanced) | <$1 | -| **Total (default)** | | | **~$9/month** | - -Optional CloudFront + WAF + ACM (if `--public-jwks-domain`): +~$3/month -at pilot volume. ACM is free for CloudFront-attached certs. - -This is a pilot-scale cost envelope. Production scale (100x volume) -would still be <$50/month. No hidden costs identified. - ---- - -# PLAN — v1.29 Reposplit + Identity Layer Bring-Live - -> **Milestone:** v1.29 (feature — reposplit + identity layer bring-live). -> Tags on the **v1.28.x** line: `v1.28.0` (P0) → `v1.28.1..v1.28.5` -> (P1..P5) → `v1.28.6` (P6 final = milestone release). The final phase's -> patch IS the milestone release. -> **Branch:** `milestone/v1.29-reposplit-identity`. Phase branches: -> `phase/00-pre-execution` (complete), `phase/01-publish-pipeline`, -> `phase/02-gitea-scrub-decisions`, `phase/03-cfn-archive-tf-delegation`, -> `phase/04-operator-guide-reference-tracking`, -> `nova-blockchain-exchange/phase/05-consumer-deploy-bump` (cross-project), -> `phase/06-final-review-ship`. -> -> **Scope split (CLARIFY-grounded):** Terraform modules authored -> out-of-band in `nova-platform-ops`. CIAgent in `acdl` authors only the -> acdl-side REQs (354, 367, 368, 369, REQ-OPS-GUIDE, REQ-CONSUMER-BUMP). -> Covered-reference REQs (355-366, 371) verified via cutover gates -> documented in the operator guide (P4). - -## Milestone goal - -v1.29 makes platform operations a Terraform-controlled discipline that -lives outside the engineering repo, with a narrow-IAM `kj` substrate -shared by the primary runtime and its defensive fallback. `acdl/acdl` -standardizes on GitHub (Gitea scrub); Nova-idp is brought live in -account `581513795199` (code complete since v1.28, unverified in-account -at Phase 0); `kj` has exactly one identity (one ECR image digest) shared -by both substrates (KJ-LOCKSTEP, REQ-371). - -## Requirements - -17 requirements: REQ-354..REQ-369 + REQ-371 + REQ-363b + REQ-OPS-GUIDE -+ REQ-CONSUMER-BUMP (full text in `.ciagent/REQUIREMENTS.md` §v1.29). -1 invariant: INV-18 (JWKS-EDGE-ONLY). 10 NFR constraints: KJ-STATIC, -KJ-LOCKSTEP, KJ-WARMUP-HEALTH, OPER-PRIV, IAM-NARROW, DRIFT-DETECT, -IMPORT-IDEMPOTENT, TFM-HITL, JWKS-SLO, JWKS-ROTATION. 9 decisions: -D-232..D-238 (CLARIFY) + D-239/D-240 (RESEARCH spec corrections). - -## Phase breakdown - -### Phase P1 — publish-pipeline (REQ-354) - -**Goal:** `publish.yml` attaches Lambda zip + layer wheel + Python wheel -+ ECR container image (static `kj`, `CGO_ENABLED=0`, tag -`v1.29.x-kj-`) to GitHub Release for each tag, with matching -SHA-256 in the body. The M1.5 verification gate tests -(`test_idp_auth`, `test_kms_roundtrip`, ABAC E2E) are authored. - -**Exit criterion:** REQ-354 criteria 1-4 pass; KJ-STATIC audit (file(1) -asserts `statically linked`) runs in CI; ECR image pushed with tag -`v1.29.x-kj-` (D-239); GitHub Release body lists image URI + digest -alongside wheel + layer + Lambda zip; M1.5 gate tests exist + pass in -moto-DDB (live KMS round-trip is covered-reference, runs in -nova-platform-ops CI). - -**Branch:** `phase/01-publish-pipeline`. **Tag:** `v1.28.1`. - -#### Wave 0 — publish.yml trigger model (backend-engineer) -- **Task 0.1** (backend-engineer): change `.github/workflows/publish.yml` - trigger from `push: branches: [main]` to `push: tags: ['v1.29.*']`. - Preserve the existing wheel + Lambda layer publish steps (REQ-323/ - CAP-035). Add the Lambda zip packaging step - (`nova-lambda-token-vend-v1.29.x.zip`). Verify the trigger fires on - `git tag v1.29.0 && git push --tags`. - -#### Wave 1 — kj source confirmation + static build + ECR image (backend-engineer, security-engineer) -- **Task 1.0** (backend-engineer): **kj source-fetch confirmation - (grill CF-4/G-4 — binary go/no-go gate before Wave 1).** Confirm the - `kj` Go source repo URL + commit at SHA `4ebb9a19...` (read from - `platform/abac/kj-version.txt`). Record the repo URL as a 3rd line - in `platform/abac/kj-version.txt`. If unfetchable → P1 fails closed - → escalate (this is a spec dependency, not a CIAgent ambiguity). The - source repo is the `kyverno-json/kj` Go binary project (distinct - from the kyverno-json Python engine adapter in `adapters/kyverno- - json/`). -- **Task 1.1** (backend-engineer): add a `build-kj-image` job to - `publish.yml` that: - (a) reads `platform/abac/kj-version.txt` (v0.0.3 + SHA - `4ebb9a19...`); - (b) fetches the `kj` Go source at the pinned SHA (RESEARCH §7 — - source repo confirmed in P1 RESEARCH); - (c) builds with `CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build - -ldflags="-s -w" -o kj ./…`; - (d) runs `file kj` and asserts output contains `statically linked` - AND does NOT contain `shared library` (KJ-STATIC — fail build - otherwise); - (e) builds the container image from - `public.ecr.aws/lambda/python:3.12-al2023`, copying `kj` to - `/opt/kj/kj` with `chmod 0555` owned by `sbx_user:1051`; - (f) pushes the image to ECR with tag `v1.29.x-kj-` - (D-239 — assert tag matches `^[a-zA-Z0-9._-]+$` before push); - (g) records the image URI + digest for the GitHub Release body. -- **Task 1.2** (security-engineer): add a KJ-STATIC audit step that - runs `file(1)` + `readelf -d kj` (assert no `NEEDED` entries) as a - CI gate. If either fails, the publish job fails closed. This is the - mechanical enforcement of KJ-STATIC (not just a human review). - -#### Wave 2 — GitHub Release body + SHA-256 (backend-engineer) -- **Task 2.1** (backend-engineer): extend the `publish.yml` release step - to attach: (a) `nova-lambda-token-vend-v1.29.x.zip`; (b) - `nova-cli-layer-v1.29.x.zip`; (c) `nova-1.29.x-py3-none-any.whl`; (d) - the ECR image URI + digest. Compute SHA-256 for each artifact + list - in the release body. Verify REQ-354 criteria 1, 2, 4 (artifacts - appear, independent per tag, image URI + digest listed). - -#### Wave 3 — M1.5 verification gate tests (security-engineer) -- **Task 3.1** (security-engineer): author `tests/test_idp_auth.py` — - sign-up → sign-in → session flow against moto-DDB (covers Edge 5 - item 5). Skip live-KMS assertions (covered-reference — runs in - nova-platform-ops CI). -- **Task 3.2** (security-engineer): author - `tests/test_kms_roundtrip.py` — sign/verify round-trip against - `alias/nova-oidc-signing`. Mark as `@pytest.mark.live_aws` (skipped in - acdl CI; runs in nova-platform-ops CI against the live key, REQ-362). -- **Task 3.3** (security-engineer): author - `tests/test_abac_e2e.py` — known PAT → ABAC-allowed action → signed - OIDC token → `jose` verification → green; known PAT + ABAC-denied - action → 403 with deny reason logged (INV-17 fail-closed, Edge 5 - item 7). Uses moto-DDB + mock KMS. - -#### Wave 4 — regression + ship (lead-developer) -- **Task 4.1** (lead-developer): run full test suite; verify 1000+ - tests still pass (no regressions from publish.yml changes). Verify - CAP-001..038 regression gate green. **Push a `v1.29.0` intermediate - tag at P1 ship** (grill CF-3/G-3) to trigger `publish.yml` + produce - the v1.29 artifacts (Lambda zip + layer wheel + Python wheel + ECR - image). The milestone release tag remains `v1.28.6`; the `v1.29.0` - artifact tag is a P1 intermediate to produce publish artifacts for - P5's smoke test. Ship P1 → `v1.28.1`. - -### Phase P2 — gitea-scrub-decisions (REQ-367, REQ-368) - -**Goal:** Hard scrub of all Gitea references in `acdl/acdl`; `.gitea/` -removed; `forge_parity_disabled` CI assertion; pyproject → 1.29.0; -decisions D-232..238 recorded in PROJECT.md + CLARIFY (already done in -P0; this phase adds the CI assertion + the actual file scrub). - -**Exit criterion:** `grep -rni gitea .github/ docs/ pyproject.toml -README.md .ciagent/` returns zero matches outside the spec archive -section; `find .gitea` returns nothing; CI `forge_parity_disabled` -assertion passes; pyproject.toml version = 1.29.0. - -**Branch:** `phase/02-gitea-scrub-decisions`. **Tag:** `v1.28.2`. - -#### Wave 0 — pyproject bump (lead-developer) -- **Task 0.1** (lead-developer): bump `pyproject.toml` version → - `1.29.0` (spec §7.2). Verify `nova --version` reports `1.29.0`. - -#### Wave 1 — .gitea/ removal (lead-developer) -- **Task 1.1** (lead-developer): `rm -rf .gitea/` (7 workflow files + - README.md, RESEARCH §9d). Remove `scripts/sync_workflows.py` (the - byte-identical-forges generator — central removal target, D-232). - Remove Gitea references from `scripts/sync_to_nova.sh` (line 201: - `--exclude=/.gitea`) + `scripts/rotate_spike_key.sh` (Gitea API - secret upload). Scrub `terraform/bootstrap/` Gitea OIDC references - (the OIDC role for act_runner moves to nova-platform-ops; the - bootstrap here becomes archived reference). - -#### Wave 2 — Gitea reference scrub (lead-developer) -- **Task 2.1** (lead-developer): `grep -rni gitea .github/ docs/ - pyproject.toml README.md .ciagent/` — scrub all matches outside the - spec archive section (`.ciagent/REQUIREMENTS.md` §v1.29 + CLARIFY §v1.29 - + RESEARCH §v1.29 retain "Gitea" as historical/reference text; these - are the "spec archive section" exemption per REQ-367 AC 1). Update - `.github/workflows/ci.yml` to remove any Gitea-specific steps. - -#### Wave 3 — forge_parity_disabled CI assertion (lead-developer) -- **Task 3.1** (lead-developer): add a CI step to `.github/workflows/ci.yml` - that asserts `forge_parity_disabled` — the step runs - `test ! -d .gitea/` and `! grep -rqi gitea .github/workflows/` and - exits 0 on success, non-zero with `forge_parity_disabled` message on - failure (REQ-367 AC 3, D-232). This is the deliberate CI failure that - documents the abandoned parity. - -#### Wave 4 — decisions verification + ship (lead-developer) -- **Task 4.1** (lead-developer): verify D-232..238 + D-239/240 are - present in PROJECT.md + CLARIFY.md + REQUIREMENTS.md (REQ-368 AC 1-2, - already authored in P0; this task is a verification, not re-authoring). - Run full test suite; ship P2 → `v1.28.2`. - -### Phase P3 — cfn-archive-tf-delegation (REQ-369) - -**Goal:** Archive the CFN template in `nova/idp/setup.py` + -`core/lambda/nova_idp_setup.py` to `docs/archive/nova-idp-cfn-v1.28.md` -(read-only reference); `nova idp setup --apply` delegates to `terraform -apply` (the CLI detects terraform via `which terraform`; if absent, -falls back to the CFN path with a deprecation warning). - -**Exit criterion:** `docs/archive/nova-idp-cfn-v1.28.md` exists + -contains the CFN template as read-only reference; `nova idp setup ---apply` invokes `terraform apply` when terraform is on PATH (tested -with a mock terraform binary); the CFN path emits a deprecation warning -when terraform is absent. - -**Branch:** `phase/03-cfn-archive-tf-delegation`. **Tag:** `v1.28.3`. - -#### Wave 0 — CFN archive (cli-engineer, backend-engineer) -- **Task 0.1** (backend-engineer): extract the CFN template from - `core/lambda/nova_idp_setup.py` + write it to - `docs/archive/nova-idp-cfn-v1.28.md` as a fenced code block with a - read-only header ("Archived at v1.29.0 — the active path is - `terraform apply` in `nova-platform-ops`. Deletion is a follow-up - after Terraform parity is verified."). -- **Task 0.2** (cli-engineer): mark the CFN generation code path in - `core/lambda/nova_idp_setup.py` as deprecated (add a - `DeprecationWarning` when the CFN path is invoked + a docstring - pointing to the archive + the terraform delegation path). - -#### Wave 1 — terraform delegation (cli-engineer) -- **Task 1.1** (cli-engineer): modify `nova/idp/setup.py` `--apply` to - detect terraform via `shutil.which("terraform")`. If terraform is on - PATH: delegate to `subprocess.run(["terraform", "apply", - "-auto-approve"])` in the `nova-platform-ops` checkout (the operator - runs this from the ops repo root). If terraform is absent: fall back - to the CFN path with a `DeprecationWarning` ("CFN path is archived; - install terraform or use nova-platform-ops. See - docs/archive/nova-idp-cfn-v1.28.md."). -- **Task 1.2** (cli-engineer): add `nova idp setup --verify` delegation - to `terraform plan` (same `which terraform` detection). The verify - path runs `terraform plan` + reports the diff. - -#### Wave 2 — tests (cli-engineer) -- **Task 2.1** (cli-engineer): author - `tests/test_idp_setup_tf_delegation.py` — test the `--apply` path - with a mock terraform binary on PATH (assert `subprocess.run` called - with `["terraform", "apply", "-auto-approve"]`); test the fallback - path with terraform absent (assert `DeprecationWarning` raised + CFN - path invoked); test `--verify` delegates to `terraform plan`. - -#### Wave 3 — ship (lead-developer) -- **Task 3.1** (lead-developer): run full test suite; ship P3 → - `v1.28.3`. - -### Phase P4 — operator-guide-reference-tracking (REQ-OPS-GUIDE) - -**Goal:** `docs/operator-guide-platform-ops.md` covering KMS rotation, -JWKS reachability via CloudFront edge, PITR restore, PAT revocation, -edge configuration, Fargate standby health, cost section, artifact- -mirror fallback, and the M1/M1.5/M2 cutover gates as release-gate -entries for the covered-reference REQs. ARCHITECTURE.md §12.9. STATE.md -v1.29 CAPs + invariants. REQUIREMENTS.md covered-reference markers. - -**Exit criterion:** operator guide exists + covers all sections per -REQ-OPS-GUIDE AC; ARCHITECTURE.md §12.9 added; STATE.md updated with -v1.29 rows; covered-reference REQs in REQUIREMENTS.md marked with their -cutover gate. - -**Branch:** `phase/04-operator-guide-reference-tracking`. **Tag:** -`v1.28.4`. - -#### Wave 0 — operator guide (lead-developer, data-engineer, security-engineer) -- **Task 0.1** (lead-developer): author - `docs/operator-guide-platform-ops.md` sections: (a) Overview + the - reposplit rationale (Vision §4); (b) Day-0 cutover procedure (M1 - steps from spec §3.2 Journey 2); (c) M1.5 verification gate (8-item - spike, 3 consecutive rebuilds); (d) M2 operational handoff loop - (tag-pin bump → plan → HITL approval → apply); (e) M2a Fargate - activation (conditional on M1.5 failure); (f) Rollback procedure - (D-236 — revert `nova_platform_version` pin); (g) cost section (WAF - ~$5-10/month + Fargate ~$15-20/month, REQ-363b AC 4); (h) artifact- - mirror fallback (operator-local mirror by SHA-256 when Gitea - act_runner cannot reach GitHub Releases, Edge 6). -- **Task 0.2** (data-engineer): author the operator guide data - sections: (a) DynamoDB PITR restore procedure (per-table); (b) - DynamoDB import addresses (nova-contracts, nova-change-requests, - nova-outbox, nova-users, nova-sessions, nova-pats — the - `importable-resources.tf` map, REQ-361 covered-reference); (c) audit - outbox bootstrap; (d) JWKS-ROTATION (24-hour overlap window on key - rotation). -- **Task 0.3** (security-engineer): author the operator guide security - sections: (a) KMS rotation (90-day cadence, `alias/nova-oidc- - signing`, `ECC_NIST_P256`, D-234); (b) JWKS reachability via - CloudFront edge (OAC pinning, `AuthType: AWS_IAM`, direct Function - URL → 403, INV-18); (c) PAT revocation (60s SLO, D-229); (d) edge - configuration (CloudFront + WAF + ACM + Route53 — REQ-364/365/366 - covered-reference); (e) Fargate standby health checks (`GET /health` - every 10s, `KJ-WARMUP-HEALTH`, 3 consecutive probe failures → alert + - token-vend fails closed, REQ-363b AC 2); (f) Fargate sunset - discipline (D-237 — ≥30 consecutive days green before deletion + - architecture review); (g) IAM scope (IAM-NARROW, REQ-360 covered- - reference — no `Action: "*"` or `Resource: "*"`); (h) the - `route53_record_not_resolvable` debugging path (ACM cert status - check). - -#### Wave 1 — covered-reference cutover gates (lead-developer) -- **Task 1.1** (lead-developer): add a "Cutover Gates" section to the - operator guide listing each covered-reference REQ (355, 356, 357, - 358, 359, 360, 361, 362, 363, 363b, 364, 365, 366, 371) with its - gate entry (M1/M1.5/M2) + the verification command + a **"Result" - column** (grill CF-2/G-5). P6 audit verifies every covered-reference - REQ has a non-empty, green Result. Empty/red → P6 blocks. The Result - column is populated by the operator attestation (the operator runs - the verification command in `nova-platform-ops` CI + records the - outcome). This is the acdl-side evidence surface for covered- - reference REQs. -- **Task 1.2** (lead-developer): update REQUIREMENTS.md §v1.29 traceability - table — mark each covered-reference REQ with its cutover gate in the - Status column (e.g., `planned (M1 gate: nova-platform-ops)`). - -#### Wave 2 — ARCHITECTURE.md + STATE.md (lead-developer) -- **Task 2.1** (lead-developer): add ARCHITECTURE.md §12.9 (Platform - Ops Reposplit) — the domain boundary (engineering ends at the - compiled artifact; operations begins at the live platform under - guardrails), the `kj` substrate (one ECR image digest, KJ-LOCKSTEP), - the covered-reference REQ tracking pattern, the operator guide - pointer. -- **Task 2.2** (lead-developer): update STATE.md — append v1.29 - capability rows (CAP-039: platform-ops-reposplit, CAP-040: - kj-substrate-lockstep, CAP-041: jwks-edge-only) + bump invariants - (INV-18 JWKS-EDGE-ONLY + the 10 NFR constraints). Bump "Last - milestone ship" to v1.29 (pending). - -#### Wave 3 — ship (lead-developer) -- **Task 3.1** (lead-developer): run full test suite; ship P4 → - `v1.28.4`. - -### Phase P5 — consumer-deploy-bump (REQ-CONSUMER-BUMP, cross-project) - -**Goal:** Bump `nova-blockchain-exchange` deploy.yml `@v1.25` → `@v1.29` -in both `.github/workflows/deploy.yml` + `.gitea/workflows/deploy.yml` -+ smoke test (sign-up → sign-in → token-vend → apply → audit against -v1.29 publish artifacts). - -**Exit criterion:** both deploy.yml files reference `@v1.29`; smoke -test passes (the chain completes against v1.29 publish artifacts). - -**Branch:** `nova-blockchain-exchange/phase/05-consumer-deploy-bump` -(cross-project, multi-project branch naming per branch-strategy.md). -**Tag:** `v1.28.5`. - -#### Wave 0 — deploy.yml bump (lead-developer) -- **Task 0.1** (lead-developer): in the `nova-blockchain-exchange` - project, update `.github/workflows/deploy.yml` + `.gitea/workflows/ - deploy.yml` `uses:` ref from `acdl/.github/workflows/deploy.yml@v1.25` - → `@v1.29` (RESEARCH §9e — the consumer's `.gitea/` is out of scope - for the acdl REQ-367 scrub; the consumer may keep its Gitea mirror or - follow suit — this is a consumer-repo decision, not an acdl one). - -#### Wave 1 — smoke test (lead-developer, security-engineer) -- **Task 1.1** (security-engineer): author - `nova-blockchain-exchange/tests/test_v1.29_smoke.py` — sign-up → - sign-in → token-vend → apply → audit chain against the v1.29 publish - artifacts (the consumer's contract → `deploy.yml@v1.29` mode=full → - apply → attest → record against `581513795199`). Uses the existing - CAP-025 round-trip assertion (v1.26). -- **Task 1.2** (lead-developer): run the smoke test; verify the chain - completes against the real v1.29.0 publish artifacts (produced by - P1's intermediate tag, grill CF-3/G-3). **No hedge** — the smoke - test MUST run against the published v1.29.x artifacts or P5 fails - closed. If the artifacts are not available (P1 did not push the - intermediate tag), P5 blocks until P1 re-ships. - -#### Wave 2 — ship (lead-developer) -- **Task 2.1** (lead-developer): ship P5 → `v1.28.5`. The consumer - project ships independently (merge to the consumer's main, not - acdl's milestone branch). - -### Phase P6 — final-review-ship (Final Phase) - -**Goal:** Multi-persona code review across P1..P5; audit (reconstruction -test, branch hygiene, commit discipline, file discipline); milestone -ship (merge `phase/06` → `milestone/v1.29-reposplit-identity` → `main`; -tag `v1.28.6` = the v1.29 release; Gitea release; delete all milestone -branches); mark all v1.29 REQs complete in REQUIREMENTS.md + ROADMAP.md. - -**Exit criterion:** review P0 issues auto-fixed, P1+ flagged; audit -PASS; milestone merged to main; tag `v1.28.6` created; Gitea release -published; milestone branches deleted; REQUIREMENTS.md + ROADMAP.md -marked complete. - -**Branch:** `phase/06-final-review-ship`. **Tag:** `v1.28.6` = -milestone release. - -#### Wave 0 — review (lead-developer) -- **Task 0.1** (lead-developer): delegate to `ciagent-review` — - multi-persona review (lead-developer, backend-engineer, security- - engineer, data-engineer, cli-engineer) across P1..P5. Auto-apply P0 - fixes; flag P1+ for post-hoc review. If P1+ issues found: fix them - in this phase. - -#### Wave 1 — audit (lead-developer) -- **Task 1.1** (lead-developer): delegate to `ciagent-audit` — - reconstruction test (git log ↔ `.ciagent/`), branch hygiene, commit - discipline, file discipline. If critical issues found: fix them in - this phase. - -#### Wave 2 — milestone ship (lead-developer) -- **Task 2.1** (lead-developer): delegate to `ciagent-ship` — merge - `phase/06` → `milestone/v1.29-reposplit-identity` → `main`; tag - `v1.28.6`; Gitea release with full milestone summary; delete all - milestone branches (phase/00..06 + milestone/v1.29-reposplit- - identity). - -#### Wave 3 — milestone completion (lead-developer) -- **Task 3.1** (lead-developer): update REQUIREMENTS.md (all v1.29 REQs - → complete), ROADMAP.md (v1.29 → complete), NORTH_STAR.md (note - Strategic Objective — platform operations as a Terraform-controlled - discipline), STATE.md (bump "Last milestone ship" to v1.29, tag - `v1.28.6`). Commit `docs(milestone): complete v1.29-reposplit- - identity`. - ---- - -## User-Facing Surface - -1. **CLI flag:** `nova idp setup --apply` now delegates to `terraform - apply` (REQ-369 AC 2) — the operator runs this from the - `nova-platform-ops` checkout. `nova idp setup --verify` delegates to - `terraform plan`. -2. **GitHub Release artifacts page:** each `v1.29.x` tag's GitHub - Release page lists the Lambda zip + layer wheel + Python wheel + ECR - image URI/digest with SHA-256 (REQ-354) — this is the engineering- - to-ops handoff surface (D-235 tag-pin handoff). -3. **Operator guide:** `docs/operator-guide-platform-ops.md` — the - operator-facing runbook covering KMS rotation, JWKS reachability, - PITR restore, PAT revocation, edge config, Fargate standby, cost, - artifact-mirror fallback, and the M1/M1.5/M2 cutover gates. -4. **CI assertion:** `forge_parity_disabled` — the deliberate CI - failure documenting the abandoned byte-identical-forges parity - (D-232, REQ-367 AC 3). - -## Happy Path - -**M1.5 verification gate (spec §3.3 Edge 5, 12-item spike — written -BEFORE execute, extended per grill CF-1):** - -1. `kj` v0.0.3 (pinned SHA in `platform/abac/kj-version.txt`) compiles - with `CGO_ENABLED=0 GOOS=linux GOARCH=amd64`. -2. Resulting binary reports `file kj → ELF 64-bit LSB executable, - x86-64, statically linked, no shared library` (KJ-STATIC). -3. Container image built from - `public.ecr.aws/lambda/python:3.12-al2023` with the binary copied - to `/opt/kj/kj`, `chmod 0555`, owned by `sbx_user:1051`. -4. Lambda runtime `python3.12` executes - `nova_idp_token_vend.handler`; the handler invokes - `subprocess.run(['/opt/kj/kj', 'apply', ...])` and parses stdout - JSON. -5. `tests/test_idp_auth.py` passes against the live image in moto-DDB. -6. `tests/test_kms_roundtrip.py` passes against the live KMS key - (REQ-362 path — covered-reference, runs in nova-platform-ops CI). -7. End-to-end: known PAT → known ABAC-allowed action → signed OIDC - token → `jose` verification → green. Known PAT + ABAC-denied action - → 403 with deny reason logged (INV-17). -8. Image URI is recorded in Terraform state and in the operator guide. -9. **(grill CF-1)** Direct JWKS Function URL → 403 / via-CloudFront → - 200 (INV-18, JWKS-EDGE-ONLY — `AuthType: AWS_IAM` verified, not - prose). -10. **(grill CF-1)** IAM-NARROW: no `Action: "*"` or `Resource: "*"` - in the Gitea OIDC role effective permissions (REQ-360). -11. **(grill CF-1)** TFM-HITL: self-approval rejected — - `gitea.triggering_actor == pull_request.user.login` → apply fails - closed (REQ-357, INV-3). -12. **(grill CF-1)** Rollback drill — revert `nova_platform_version` - pin → prior digest runs (D-236 cutover shape + rollback procedure). - -If items 1-7 fail three consecutive rebuilds, M2a activates REQ-363b -(Fargate toggle) with the same image — no warmup hit because the -standby is always running the same digest. - -**HARD P6 SHIP GATE (grill CF-1/G-2.1):** P6 must not ship `v1.28.6` -until the operator guide contains an operator-attested "M1.5 -Verification Gate Result" row (3 consecutive green rebuilds, run -IDs/SHAs, attestor identity). P6 audit verifies the row exists. The -M1.5 gate is verified in `nova-platform-ops` CI (out-of-band); the -operator attestation in the guide is the acdl-side evidence surface. - -## UX Acceptance Criteria - -1. **M1 acceptance gate (spec §2.3):** `terraform apply` from `main` - brings the live AWS account to a state where Nova-idp identity - tables exist, JWT-issuing paths are wired but not yet consuming - container images, JWKS infrastructure is in place, WAF + OAC pinning - the CloudFront edge; `acdl/acdl v1.29.0` ships with zero `.gitea/` - references and zero platform-infra files; D-232..238 recorded in - PROJECT.md/CLARIFY. -2. **M1.5 acceptance gate:** items 1-12 of the Edge 5 spike all green - on three consecutive rebuilds; image digest resolvable via - `data.aws_ecr_image.kj_image`; sign/verify round-trip passes; ABAC - fail-closed path verified against live policy; JWKS-EDGE-ONLY - verified (item 9); IAM-NARROW verified (item 10); TFM-HITL - self-approval rejected (item 11); rollback drill passes (item 12). - **HARD P6 ship gate** — operator-attested "M1.5 Verification Gate - Result" row in the operator guide (grill CF-1/G-2.1). -3. **M2 acceptance gate:** Bumping `local.nova_platform_version` in a - PR and merging it results in `terraform apply` updating both - `aws_lambda_function.nova_idp_token_vend.image_uri` and - `aws_ecs_task_definition.kj.container_definitions[0].image` to the - same digest (KJ-LOCKSTEP, REQ-371), with zero diff on KMS, DDB, - IAM, edge. - -## Test evidence required for v1.29 release - -- [ ] Code coverage ≥ 80% on new modules (the acdl-side files: - `publish.yml` changes, `nova/idp/setup.py` terraform delegation, - `docs/operator-guide-platform-ops.md` is docs — no coverage - requirement; the M1.5 gate tests). -- [ ] CI/CD pipeline GREEN for `acdl/acdl` (the `nova-platform-ops` - pipeline is out-of-band). -- [ ] M1.5 verification gate green: items 1-12 of §3.3 Edge 5 spike - pass on three consecutive rebuilds (covered-reference — verified - in nova-platform-ops CI; acdl authors the tests in P1; operator - attests in the guide, P4; P6 audit verifies the attestation row, - grill CF-1/G-2.1). -- [ ] Covered-reference REQs (355-366, 371) have non-empty, green - Result in the operator guide "Cutover Gates" section (grill - CF-2/G-5 — P6 audit verifies). -- [ ] `lifecycle.precondition` enforced on both image-bearing resources - (REQ-371 mechanical proof — covered-reference in - nova-platform-ops). -- [ ] Live KMS sign/verify round-trip verified in account - `581513795199` (covered-reference). -- [ ] Live ABAC sign/verify round-trip verified against the production - policy (covered-reference). -- [ ] Pilot consumer (`nova-blockchain-exchange`) smoke test green: - sign-up → sign-in → token-vend → apply → audit chain (P5). -- [ ] All existing capabilities (CAP-001..038) still pass the - regression gate. -- [ ] Drift-detection baseline: `terraform plan` exit 0 against live - AWS state, captured at cutover (covered-reference). -- [ ] `kj` standby Fargate task health `READY` before M1 cutover - (covered-reference, KJ-WARMUP-HEALTH). -- [ ] Fargate standby sunset discipline documented in operator-guide - (D-237, P4). -- [ ] `forge_parity_disabled` CI assertion passes (P2, REQ-367 AC 3). -- [ ] `grep -rni gitea .github/ docs/ pyproject.toml README.md - .ciagent/` returns zero matches outside the spec archive section - (P2, REQ-367 AC 1). - -## Plan completeness checklist - -- [x] Every REQ mapped to a phase + wave + task. -- [x] Covered-reference REQs identified + their verification surface - documented (operator guide P4, cutover gates). -- [x] Decisions D-232..240 referenced in the plan. -- [x] Invariants + NFR constraints referenced (KJ-STATIC, KJ-LOCKSTEP, - INV-18, etc.). -- [x] Personas assigned to every task (lead-developer, backend-engineer, - security-engineer, cli-engineer, data-engineer). -- [x] User-Facing Surface section (3 surfaces named). -- [x] Happy Path section (M1.5 8-item spike, written before execute). -- [x] UX Acceptance Criteria section (M1, M1.5, M2 gates). -- [x] Test evidence checklist. -- [x] Phase boundaries + tags (v1.28.0 → v1.28.6). -- [x] Cross-project phase (P5, nova-blockchain-exchange) identified. - -## Cost envelope (v1.29) - -Monthly estimate for the `nova-platform-ops` live platform (documented -in the operator guide, P4): - -| Resource | Quantity | Est. monthly | -|----------|----------|-------------| -| WAF WebACL (CloudFront-scoped) | 1 | ~$5-10/month (+ per-request) | -| Fargate standby (0.25 vCPU, 512 MB) | 1 task | ~$15-20/month (REQ-363b AC 4) | -| KMS asymmetric key | 1 | ~$1/month | -| DynamoDB (on-demand, 7 tables) | 7 | ~$2/month (pilot volume) | -| DynamoDB PITR | 7 tables | ~$2/month | -| Lambda invocations (3 Lambdas) | 3 | ~$2/month | -| ECR image storage | ~100 MB | <$1/month | -| S3 state bucket + access logs | 1 | <$1/month | -| CloudFront + ACM + Route53 | 1 distribution | ~$1/month (ACM free) | -| **Total** | | **~$30-40/month** | - -This is the pilot-scale ops cost envelope. The Fargate standby -(~$15-20/month) is the largest line item + is explicitly documented in -the operator guide (REQ-363b AC 4) with the D-237 sunset discipline -(≥30 consecutive days green before deletion + architecture review). \ No newline at end of file +1. `bash scripts/check_leadership_deck.sh` exits 0 (all 6 assertions + a–f pass). +2. `docs/presentations/nova-leadership-deck.pptx` exists, has 7 + slides, and python-pptx raised no exceptions during render. +3. Visual review: each slide N (1–7) matches the Slide Content Map + in PROJECT.md §v1.30 (on-slide body + speaker notes fingerprint). +4. Footer `Nova Platform - Infrastructure & Operations` is visible + (right-aligned) on every rendered slide. +5. Only S&P theme colors appear in the source markdown. +6. `[1]` citations present in slides 3, 5, 7 speaker notes. +7. CAP-042 row in STATE.md; D-241 record in PROJECT.md. +8. `nova-autonomous-cloud-delivery-marp.md` is unmodified (D-241 + discrete-artifact constraint). + +## Risks (from RESEARCH + GRILL) + +| Risk | Mitigation | +|---|---| +| python-pptx render fails on the new frontmatter/style block | The python-pptx path strips frontmatter without reading it; the `style:` block is source-only (Marp CLI). No render risk. | +| Footer textbox overlaps content | Place footer at `SLIDE_H - 0.3"` (bottom margin); content area tops out at ~6.5". No overlap. | +| Speaker notes word-count band violation | ci-doc-writer counts words per slide during authoring; smoke test (8c) is the gate. | +| `→` lines render as plain text (not bullets) | Use `- → ...` bullets so the renderer treats them as bullet blocks with the arrow in the text. | +| `*italic*` in source matches unordered-list regex | Verified in RESEARCH R1: `*italic*` (no space after `*`) does NOT match `[-*+]\s+`. Safe. | +| CAP-024 regression policy collides | Verified in RESEARCH R7: CAP-024 validates fixtures, not deck files. No collision. | +| `slides.yml` CI interferes | Verified in RESEARCH R8: CI only renders the citizen-developer deck (hardcoded DECK). No interference. | \ No newline at end of file