diff --git a/.ciagent/CHECKPOINT.json b/.ciagent/CHECKPOINT.json index 0c8f0fb..68dd6c6 100644 --- a/.ciagent/CHECKPOINT.json +++ b/.ciagent/CHECKPOINT.json @@ -1,10 +1,10 @@ { "phase": 0, - "stage": "research", + "stage": "plan", "milestone": "v1.28", "phase_role": "pre_execution", "attempts": 0, - "updated_at": "2026-08-19T20:25:00Z", + "updated_at": "2026-08-19T20:40:00Z", "project": "acdl", "projects": ["acdl", "nova-blockchain-exchange"], "active_milestone": "v1.28", @@ -14,5 +14,15 @@ "previous_milestone": {"milestone": "v1.27", "tag": "v1.26.3", "status": "complete"}, "decisions": ["D-226", "D-227", "D-228", "D-229", "D-230", "D-231"], "personas": ["backend-engineer", "security-engineer", "cli-engineer", "lead-developer"], - "notes": "v1.28 RESEARCH complete. 11 questions answered via ci-researcher subagent. D-228 amended (fail-closed, not pure-Python fallback). kj-binary-in-Lambda-layer is highest risk (P2 spike). KMS = ECC_NIST_P256 / ES256 with DER->raw conversion. nova-pats = 4th DDB table. mode_resolver uses sys.stdin.isatty(). PERSONAS.md + ARCHITECTURE.md §12.10 authored. Next: PLAN." + "phases_planned": 7, + "execution_phases": [ + {"phase": 1, "name": "cli-substrate", "reqs": ["REQ-323..328"], "caps": ["CAP-033", "CAP-034", "CAP-035"], "tag": "v1.27.1"}, + {"phase": 2, "name": "lambda-packaging", "reqs": ["REQ-329..331"], "tag": "v1.27.2"}, + {"phase": 3, "name": "idp-auth", "reqs": ["REQ-333..335"], "caps": ["CAP-036"], "tag": "v1.27.3"}, + {"phase": 4, "name": "token-vend-pat", "reqs": ["REQ-336..344"], "caps": ["CAP-037", "CAP-038"], "tag": "v1.27.4"}, + {"phase": 5, "name": "idp-setup", "reqs": ["REQ-340..341"], "tag": "v1.27.5"}, + {"phase": 6, "name": "docs-integration", "reqs": ["REQ-345..351"], "tag": "v1.27.6"}, + {"phase": 7, "name": "final-review-ship", "reqs": ["REQ-352..353"], "tag": "v1.27.7"} + ], + "notes": "v1.28 PLAN complete. 7 phases (P1..P6 execution + P7 final). 31 REQs mapped to waves+tasks. 6 CAPs mapped to gate rules. MVP/UX sections authored (User-Facing Surface, Happy Path, UX Acceptance Criteria). Highest risk: P4 W1 kj-binary spike. Next: GRILL." } \ No newline at end of file diff --git a/.ciagent/PLAN.md b/.ciagent/PLAN.md index c60ba2f..22dd888 100644 --- a/.ciagent/PLAN.md +++ b/.ciagent/PLAN.md @@ -1,268 +1,400 @@ -# PLAN — v1.27 PO State Catalog & Ciagent Compression +# PLAN — v1.28 CLI Canonicalization + Identity Layer -> **Milestone:** v1.27 (NFR — docs/chore only). Tags on the **v1.26.x** -> line: `v1.26.0` (P0) → `v1.26.1..v1.26.3` (P1..P3). The final phase's -> patch (`v1.26.3`) IS the milestone release. -> **Branch:** `milestone/v1.27-po-state-catalog`. Phase branches: -> `phase/00-pre-execution`, `phase/01-author-archive`, -> `phase/02-fix-stale-wire`, `phase/03-final-review-ship`. +> **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-capability-gate`, `phase/07-final-review-ship`. ## Milestone goal -Author `.ciagent/STATE.md` (PO-facing capability catalog, backfilled -through v1.26) + compress `.ciagent/` by archiving 11 outdated files + -fix 3 stale-but-kept files + wire STATE.md into the P-final ship -discipline. NFR milestone — no code, no schema, no platform behavior -change. +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). ## Requirements -No new REQ-NNN. v1.27 is a docs/chore milestone; the work items are -the user-approved plan from the prior conversation. The traceability -is by-file (the "requirements" are the 15 file operations + 6 doc -edits in the plan summary). +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). ## Phase breakdown -### Phase P1 — author-archive (additive + lossless) +### Phase P1 — cli-substrate (REQ-323..REQ-328) -**Goal:** Author STATE.md (already done in P0 SPECIFY, refined here) -+ archive 11 outdated files. Pure-additive + lossless moves only — -no edits to kept files. +**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. -#### Wave 1 — verify STATE.md backfill -- **Task 1.1** (lead-developer): verify STATE.md 36 capability rows - against the authoritative sources (regression_verify.py CAP-NNN list, - modules/registry.json, REQUIREMENTS.md traceability, CHECKPOINT - tags). Fix any inaccurate citation (shipped tag, file path). +**Capability gate target:** CAP-033 (surface), CAP-034 (delegation). -#### Wave 2 — archive platform-root files (10) -- **Task 2.1** (lead-developer): `git mv` 10 files to - `.ciagent/archive/` with milestone-suffix names: - - `CAPABILITY_INVENTORY.md` → `CAPABILITY_INVENTORY-v1.10.md` - - `CLARIFY.md` → `CLARIFY-v1.26.md` - - `GRILL.md` → `GRILL-v1.26.md` - - `IDEATE.md` → `IDEATE-v1.26.md` - - `RESEARCH.md` → `RESEARCH-v1.26.md` - - `REVIEW-AUDIT-P05.md` → `REVIEW-AUDIT-P05.md` - - `VERIFY-P03.md` → `VERIFY-P03.md` - - `VERIFY-P04.md` → `VERIFY-P04.md` - - `P4-PILOT-RUN-EVIDENCE.md` → `P4-PILOT-RUN-EVIDENCE-v1.26.md` - - `AUTONOMY_THESIS.md` → `AUTONOMY_THESIS-v1.21.md` - - `COST.md` → `COST-v1.14.md` - Use `git mv` to preserve history. NOTE: CLARIFY/GRILL/IDEATE/RESEARCH - were rewritten in P0 with v1.27 content — archive the v1.27 versions - (they document the v1.27 pre-execution; the next P0 writes fresh). - Wait — per D-219, the v1.26 pre-execution artifacts are archived. The - v1.27 versions replace them in active context; they are NOT archived - at P1 (they are the current P0 artifacts, active until v1.27 ships, - then archived at v1.28 P1 if v1.28 happens). **Correction:** archive - only the v1.26-era pre-execution artifacts. But P0 already - overwrote CLARIFY/GRILL/IDEATE/RESEARCH with v1.27 content. The v1.26 - content lives in git history (the pre-P0 commits). So: - - The 4 pre-execution files (CLARIFY/GRILL/IDEATE/RESEARCH) at HEAD - are the v1.27 P0 artifacts — **keep active** through v1.27, archive - at v1.28. - - The v1.26-era content is in git history — reachable. - **Revised archive list (7 files, not 10):** CAPABILITY_INVENTORY, - REVIEW-AUDIT-P05, VERIFY-P03, VERIFY-P04, P4-PILOT-RUN-EVIDENCE, - AUTONOMY_THESIS, COST. +#### 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. - Hold — let me re-check D-219. The user said "Archive all 4 - pre-execution artifacts." That was decided *before* P0 overwrote - them. The intent was to archive the v1.26 pre-execution record. The - v1.27 P0 overwrites are the new pre-execution record. Archiving the - v1.27 versions at v1.27 P1 would lose the v1.27 pre-execution - narrative. **Resolution:** archive the v1.26-era content (preserved - in git history at the pre-P0 commits) by noting it in the archive - README; keep the v1.27 P0 versions active through v1.27. The 4 files - stay active until v1.28 P1. +#### 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`). - **Final archive list (7 files):** CAPABILITY_INVENTORY.md, - REVIEW-AUDIT-P05.md, VERIFY-P03.md, VERIFY-P04.md, - P4-PILOT-RUN-EVIDENCE.md, AUTONOMY_THESIS.md, COST.md. +#### 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. -- **Task 2.2** (lead-developer): grep for dangling references to the - archived filenames across `.ciagent/` + `docs/`; fix any in P2 (the - fix-stale phase). +#### 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. -#### Wave 3 — archive consumer file (1) -- **Task 3.1** (lead-developer): `mkdir - .ciagent/nova-blockchain-exchange/archive/` + `git mv - nova-blockchain-exchange/ROADMAP.md` → - `nova-blockchain-exchange/archive/ROADMAP-v1.26.md` (D-221). +#### 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 4 — commit P1 -- **Task 4.1** (lead-developer): single commit `chore(P01): archive 7 - platform + 1 consumer outdated .ciagent files` with `---ci---` - block. +#### 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. -**Must-haves (verify before ship):** -- STATE.md 36 rows accurate (Wave 1 verification). -- 7 platform files present in `.ciagent/archive/` with milestone - suffixes; originals gone from `.ciagent/` root. -- 1 consumer file present in - `.ciagent/nova-blockchain-exchange/archive/`; original gone. -- 0 dangling references in active files (checked in P2, but flagged - here). +### Phase P2 — lambda-packaging (REQ-329, REQ-330, REQ-331) -### Phase P2 — fix-stale-wire (corrections + wiring) +**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. -**Goal:** Fix 3 stale-but-kept files + wire STATE.md into the P-final -ship discipline + add a pointer in the consumer PROJECT.md. +#### 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`. -#### Wave 1 — fix PROJECT.md phase-status -- **Task 1.1** (lead-developer): `.ciagent/PROJECT.md` lines 424–431 — - the v1.26 phase-status block. Mark P3/P4/P5 complete with shipped - tags (`v1.25.3`, `v1.25.4`, `v1.25.5`); mark v1.26 milestone shipped. -- **Task 1.2** (lead-developer): add a one-line pointer to STATE.md in - the "Capability Status" section header (line 130): "The PO-facing - capability catalog is `.ciagent/STATE.md` (additive; updated at - milestone ship). CAP-NNN IDs cross-reference the regression gate at - `core/regression_verify.py`." +#### Wave 2 — local env synthesizer (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()`. -#### Wave 2 — fix ROADMAP.md phase-status -- **Task 2.1** (lead-developer): `.ciagent/ROADMAP.md` v1.26 section — - mark P3/P4/P5 complete with shipped tags; mark the v1.26 Overview - line (line 181) "(active, ...)" → "(complete, tag `v1.25.5`)". -- **Task 2.2** (lead-developer): add STATE.md to the v1.25 + v1.26 P5 - phase-detail "Updated at ship" list (the convention visibility - point). +#### 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. -#### Wave 3 — wire STATE.md into ship discipline -- **Task 3.1** (lead-developer): `.ciagent/PLAN.md` P5 Wave 3 Task 3.5 - — add STATE.md to the file-update list: "append new capability - entries to `.ciagent/STATE.md`; mark any deprecated capability." -- **Task 3.2** (lead-developer): `.ciagent/NORTH_STAR.md` — add a - one-line note in "Relationship to engineering files" (or the v1.25 - update section): "STATE.md is the *what exists* catalog (PO-owned, - additive, updated at milestone ship); this file is the *why*." +### Phase P3 — idp-auth (REQ-333, REQ-334, REQ-335) -#### Wave 4 — fix archive README + consumer PROJECT pointer -- **Task 4.1** (lead-developer): `.ciagent/archive/README.md` — add - the 8 new archived files (7 platform + 1 consumer) to the contents - tables (Snapshots + Completed-phase artifacts sections). -- **Task 4.2** (lead-developer): - `.ciagent/nova-blockchain-exchange/PROJECT.md` — add a one-line - pointer to the platform ROADMAP for milestone-phase history (since - the consumer ROADMAP is archived): "Phase-by-phase history: - `.ciagent/ROADMAP.md` §v1.26 (the consumer ROADMAP is archived at - `.ciagent/nova-blockchain-exchange/archive/ROADMAP-v1.26.md`)." +**Goal:** `nova-idp-auth` Lambda (sign-up, sign-in, session) with +Argon2id hashing + DynamoDB tables. CAP-036 target. -#### Wave 5 — fix any dangling references from P1 Wave 2 -- **Task 5.1** (lead-developer): apply fixes for any dangling - references found in P1 Wave 2. +#### 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 P5 `nova idp setup`). PITR + enabled on each (REQ-335). -#### Wave 6 — commit P2 -- **Task 6.1** (lead-developer): single commit `docs(P02): fix stale - phase-status + wire STATE.md into ship discipline` with `---ci---` - block. +#### Wave 2 — Argon2id (security-engineer) +- **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). + Lambda memory ≥512 MB. Raw passwords never in logs/traces/env/DDB + (INV-16, REQ-334). -**Must-haves (verify before ship):** -- PROJECT.md v1.26 phase-status matches CHECKPOINT.json (P3/P4/P5 - complete, v1.26 shipped). -- ROADMAP.md v1.26 sections show P3/P4/P5 complete + Overview complete. -- PLAN.md P5 Wave 3 names STATE.md. -- NORTH_STAR.md notes STATE.md. -- archive/README.md lists the 8 new archived files. -- nova-blockchain-exchange/PROJECT.md points to platform ROADMAP. +#### 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). -### Phase P3 — final-review-ship (review + audit + milestone ship) +#### 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. -**Goal:** Final review + audit + milestone ship. +### Phase P4 — token-vend-pat (REQ-336..REQ-344) -#### Wave 1 — review -- **Task 1.1** (lead-developer): review all P1/P2 changes for - correctness (no broken markdown, no inaccurate citations, no - dangling references). -- **Task 1.2** (lead-developer): fix any P0 issues in this phase. +**Goal:** `nova-idp-token-vend` Lambda (KMS-signed OIDC, kyverno-json +ABAC), JWKS endpoint, PAT lifecycle, `nova auth` commands. CAP-037 + +CAP-038 target. **Highest-risk phase** (the `kj` binary in Lambda +layer — RESEARCH §7). -#### Wave 2 — audit -- **Task 2.1** (lead-developer): reconstruction test — git log - `---ci---` blocks ↔ `.ciagent/` files consistent; phase - progression P0→P1→P2→P3. -- **Task 2.2** (lead-developer): `.ciagent/` file discipline — - CHECKPOINT consistent with HEAD; PROJECT/ROADMAP phase-status - consistent with CHECKPOINT; STATE.md present + 36 rows; archive - contents match the moves. -- **Task 2.3** (lead-developer): branch hygiene — only main + - milestone + P3; P1/P2 deleted. -- **Task 2.4** (lead-developer): commit discipline — all v1.27 commits - carry `---ci---` blocks. +#### Wave 1 — kj-binary spike (backend-engineer + security-engineer) +- **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` 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 3 — milestone ship -- **Task 3.1** (lead-developer): merge `phase/03` → - `milestone/v1.27-po-state-catalog` → `main`. -- **Task 3.2** (lead-developer): tag `v1.26.3` (= the v1.27 release per - prev-minor tagging rule; v1.27 is an NFR milestone, tags on v1.26.x). -- **Task 3.3** (lead-developer): create Gitea release with full - milestone summary. -- **Task 3.4** (lead-developer): delete all milestone branches (local - + remote). Tags preserve all history. -- **Task 3.5** (lead-developer): update `.ciagent/REQUIREMENTS.md` - (no REQs to mark — NFR milestone), `.ciagent/ROADMAP.md` (mark - v1.27 complete), `.ciagent/NORTH_STAR.md` (no strategic change), - `.ciagent/STATE.md` (bump "Last milestone ship" to v1.27). -- **Task 3.6** (lead-developer): write checkpoint `stage: complete, - phase: 3, phase_role: final` + clear checkpoint (milestone - complete). +#### Wave 2 — ABAC policy (security-engineer) +- **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}`. 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. -**Must-haves (verify before ship):** -- Review: 0 P0 issues unfixed; P1+ flagged for post-hoc. -- Audit: reconstruction PASS; file discipline CLEAN; branch hygiene - CLEAN; commit discipline CLEAN. -- Ship: `v1.26.3` tag exists; Gitea release created; milestone - branches deleted; main has the milestone merge. +#### Wave 3 — KMS signing (security-engineer) +- **Task 3.1** (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) +- **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). + +#### 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) +- **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. +- **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). + +#### 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. + +### Phase P5 — idp-setup (REQ-340, REQ-341) + +**Goal:** `nova idp setup` command with `--check/--apply/--verify` +modes; CloudFormation template generation + review. + +#### Wave 1 — CloudFormation template (backend-engineer) +- **Task 1.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). + +#### Wave 2 — setup modes (cli-engineer + backend-engineer) +- **Task 2.1** (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 2.2** (backend-engineer): IAM policy delta computation — + compares current `nova-spike-runner` grants to required + `cloudformation:*` + `codeartifact:*` + `kms:*` + `lambda:*` + + `dynamodb:*` + `ssm:*`. + +### Phase P6 — docs-integration (REQ-345..REQ-351) + +**Goal:** Operator guide, developer guide, threat model; E2E +integration test; property tests; KMS round-trip; PAT revocation SLO. + +#### Wave 1 — docs (lead-developer + security-engineer) +- **Task 1.1** (lead-developer): `docs/operator-guide-idp.md` (REQ-345) + — `nova idp setup --check/--apply/--verify`, prerequisite IAM policy, + CloudFormation review flow. +- **Task 1.2** (lead-developer): `docs/developer-guide-auth.md` + (REQ-346) — signup, signin, login, mode resolution, TTY vs piped + stdout behavior. +- **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. + +#### 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 P7 — final-review-ship (Final Phase) + +**Goal:** Multi-persona code review across P1..P6; 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/07` → + `milestone/v1.28-cli-identity` → `main`; tag `v1.27.7` (= 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. --- -## Requirement → Phase Mapping +## User-Facing Surface -No REQ-NNN (NFR milestone). The work items are file operations, -traced by the Wave tasks above. +> 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). + +## Happy Path + +> MVP/UX CHECK §2 (REQ-MVP-UX-001). End-to-end scenario written BEFORE +> execute. + +**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. + +## 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). --- -## Wave Ordering Rationale +## Capability gate (CAP-033..CAP-038) -- **P1 W1 → W2:** verify STATE.md before archiving (the archive removes - the source-of-truth CAPABILITY_INVENTORY; STATE.md must be accurate - first). -- **P1 W2 → W3:** platform archive before consumer archive (the - platform archive pattern is established; the consumer archive - creates a new subdir). -- **P2 W1 → W2 → W3:** PROJECT.md fix before ROADMAP.md fix before - ship-discipline wiring (PROJECT is the source-of-truth narrative; - ROADMAP mirrors it; PLAN/NORTH_STAR wire the convention). -- **P2 W4:** archive README + consumer pointer (cross-cutting; lands - after the active-file fixes). -- **P2 W5:** dangling-reference fixes (lands after all moves + edits - are known). +| 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. --- -## Vertical-slice integrity +## Test evidence required for v1.28 release -Each phase ships a self-contained, verifiable slice: -- P1 ships STATE.md (verified accurate) + 8 archived files (verified - moved). The active `.ciagent/` root drops from 25 to 17 files. -- P2 ships 3 fixed files + 3 wired files + archive README + consumer - pointer. The kept files match CHECKPOINT.json state. -- P3 ships the milestone release + cleared checkpoint. +- [ ] 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). --- -## Durable convention (v1.27 establishes) +## Plan completeness checklist -The P-final (milestone-ship) Wave 3 file-update list for every future -milestone includes `.ciagent/STATE.md`: - -1. Append new capability entries for each shipped REQ (one row per - capability; group by domain). -2. Mark any deprecated capability with a `Deprecated` row citing the - milestone + replacement. -3. Bump the "Last milestone ship" header in STATE.md. - -This is wired into the v1.25 + v1.26 P5 "Updated at ship" lists in -`ROADMAP.md` (see those sections). The next milestone's P0 PLAN.md -inherits this convention by reading ROADMAP.md. \ No newline at end of file +- [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). \ No newline at end of file