From 9ee1cc89258d4c69cdd25e75ab00fd00c59201b4 Mon Sep 17 00:00:00 2001 From: Jon Chery Date: Wed, 19 Aug 2026 21:57:53 +0000 Subject: [PATCH 1/7] =?UTF-8?q?docs(init):=20validate=20specification=20?= =?UTF-8?q?=E2=80=94=20v1.28=20CLI=20Canonicalization=20+=20Identity=20Lay?= =?UTF-8?q?er?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ---ci--- project: acdl phase: 0 milestone: v1.28 status: specify ---/ci--- --- .ciagent/CHECKPOINT.json | 25 ++-- .ciagent/PROJECT.md | 104 +++++++++++++- .ciagent/REQUIREMENTS.md | 301 ++++++++++++++++++++++++++++++++++++++- .ciagent/config.json | 2 +- 4 files changed, 414 insertions(+), 18 deletions(-) diff --git a/.ciagent/CHECKPOINT.json b/.ciagent/CHECKPOINT.json index 7063331..b25335c 100644 --- a/.ciagent/CHECKPOINT.json +++ b/.ciagent/CHECKPOINT.json @@ -1,19 +1,16 @@ { - "phase": 3, - "stage": "complete", - "milestone": "v1.27", - "phase_role": "final", + "phase": 0, + "stage": "specify", + "milestone": "v1.28", + "phase_role": "pre_execution", "attempts": 0, - "updated_at": "2026-08-19T05:30:00Z", + "updated_at": "2026-08-19T20:00:00Z", "project": "acdl", "projects": ["acdl", "nova-blockchain-exchange"], - "active_milestone": "v1.27", - "milestone_branch": "milestone/v1.27-po-state-catalog", - "phase_branch": "phase/03-final-review-ship", - "tag_line": "v1.26.x", - "current_phase": {"phase": 3, "tag": "v1.26.3", "status": "complete"}, - "previous_phase": {"phase": 2, "tag": "v1.26.2", "status": "complete"}, - "milestone_complete": true, - "milestone_release": {"tag": "v1.26.3", "type": "nfr"}, - "notes": "v1.27 COMPLETE. NFR milestone (PO State Catalog & Ciagent Compression). 3 phases. STATE.md authored (32 CAPs, 11 invariants, 10 domains). 8 files archived (7 platform + 1 consumer). PROJECT.md + ROADMAP.md v1.26 phase-status fixed. STATE.md wired into P-final ship discipline. Review: 0 P0. Audit: reconstruction PASS, file discipline CLEAN, branch hygiene CLEAN, commit discipline CLEAN (13/13 ---ci--- blocks). Next run starts a new milestone." + "active_milestone": "v1.28", + "milestone_branch": "milestone/v1.28-cli-identity", + "phase_branch": "phase/00-pre-execution", + "tag_line": "v1.27.x", + "previous_milestone": {"milestone": "v1.27", "tag": "v1.26.3", "status": "complete"}, + "notes": "v1.28 SPECIFY. Re-mapped from source spec (v1.18 framing) to v1.28. ID allocations: REQ-323..353, CAP-033..038, INV-12..17, D-226..231. kj engine mapped to kyverno-json (D-227). Requirements validated in REQUIREMENTS.md. Next: CLARIFY." } \ No newline at end of file diff --git a/.ciagent/PROJECT.md b/.ciagent/PROJECT.md index 7151a04..9a54ef3 100644 --- a/.ciagent/PROJECT.md +++ b/.ciagent/PROJECT.md @@ -344,7 +344,7 @@ plan-JSON policies + pipeline wiring (REQ-300,301,302), meta-policies (REQ-303), regression-gate policies (REQ-304,305), docs + adapter README (REQ-306,307), tests (REQ-308,309). -## v1.26 — Live Pilot Estate Activation (active) +## v1.26 — Live Pilot Estate Activation (complete, tag `v1.25.5`, merged to main 2026-08-19) > **Active milestone.** Feature milestone — the first real consumer estate > (a stock exchange on a homegrown PoA blockchain, equities only) is @@ -438,4 +438,104 @@ already exist). > live in `.ciagent/PLAN.md` (the active phase plan, retained in full). > v1.26 pre-execution artifacts (CLARIFY/GRILL/IDEATE/RESEARCH) are in > git history (pre-v1.27-P0 commits); the v1.26 phase verifications + -> review are archived at `.ciagent/archive/{VERIFY-P03,VERIFY-P04,REVIEW-AUDIT-P05}.md`. \ No newline at end of file +> review are archived at `.ciagent/archive/{VERIFY-P03,VERIFY-P04,REVIEW-AUDIT-P05}.md`. + +## v1.27 — PO State Catalog & Ciagent Compression (complete, tag `v1.26.3`, merged to main 2026-08-19) + +> **NFR milestone — complete.** STATE.md authored (32 CAPs, 11 invariants, +> 10 domains). 8 outdated `.ciagent/` files archived (7 platform + 1 +> consumer). PROJECT.md + ROADMAP.md v1.26 phase-status corrected. +> STATE.md wired into P-final ship discipline. Tags: `v1.26.0` (P0) → +> `v1.26.1..v1.26.2` (P1..P2) → `v1.26.3` (P3 final = milestone release). +> Review: 0 P0. Audit: reconstruction PASS, file/branch/commit discipline CLEAN. +> Full phase detail: `.ciagent/archive/` (v1.27 artifacts) + git history. + +## v1.28 — CLI Canonicalization + Identity Layer (active) + +> **Feature milestone — active.** The Nova CLI becomes 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 +> (sign-up through token vending) with no AWS-managed identity services +> in the path. Nova-idp is introduced: two Lambda functions (`nova-idp-auth`, +> `nova-idp-token-vend`), KMS-signed OIDC tokens, ABAC-gated token vending +> via the existing kyverno-json engine (INV-4 swappable), and PAT +> lifecycle (issuance, revocation, status). +> +> Tags run on the **v1.27.x** line: `v1.27.0` (P0) → `v1.27.1..v1.27.N` +> (execution phases) → `v1.27.(N+1)` (final phase = milestone release). +> Milestone branch: `milestone/v1.28-cli-identity`. + +### v1.28 ID allocations (re-mapped — no collisions with shipped history) + +- **Decisions:** `D-226..D-231` (authored in CLARIFY). Repo decision + namespace is `D-NNN` (max D-225); no `D-NEW-*` namespace exists. +- **Requirements:** `REQ-323..REQ-353` (31 REQs, mapping the spec's + REQ-001..REQ-031 1:1). Max existing REQ = REQ-322. +- **Capabilities:** `CAP-033..CAP-038` (mapping the spec's CAP-025..CAP-030). + Existing CAP-025..032 are blockchain/pilot — collision avoided. +- **Invariants:** `INV-12..INV-17` (mapping the spec's INV-63,64,65,18..21,34). + Max existing INV = INV-11. +- **`kj` engine → kyverno-json.** The spec references a `kj` engine; the + repo's actual policy engine is `kyverno-json` (INV-4 swappable). v1.28 + uses kyverno-json as the ABAC evaluator for token-vend; no new `kj` + engine is built. This is a CLARIFY-grounded re-mapping, not a silent + assumption (D-229). + +### v1.28 Requirements + +New requirements REQ-323..REQ-353 — full text in +`.ciagent/REQUIREMENTS.md` §v1.28. Summary by priority: + +- **P1 — CLI Substrate (REQ-323..REQ-328):** CodeArtifact wheel + Lambda + layer pipeline; CLI subcommand per `core/` module; `nova init` + scaffolding; `nova cli-action` published to GitHub + Gitea; + `mode_resolver.py` (flag → env → credential type → TTY); audit + emission with `mode` + `selection_reason`. +- **P2 — Lambda Packaging + Identity Layer (REQ-329..REQ-344):** dual-use + `core/lambda/contract_ingestor.py`; local env synthesizer; JWS signing + key from PAT; `nova-idp-auth` Lambda (Argon2id, DynamoDB); DynamoDB + tables (`nova-users`, `nova-sessions`, `nova-password-resets`); + `nova-idp-token-vend` Lambda (KMS-signed OIDC, JWKS endpoint); kyverno-json + ABAC policy at `platform/abac/token-vend.policy`; `nova idp setup` + (`--check/--apply/--verify`); CloudFormation review; PAT issuance + + hashes in DynamoDB; `nova auth login/revoke/status`. +- **P3 — Documentation (REQ-345..REQ-347):** operator guide for + `nova idp setup`; developer guide for `nova auth login`; identity-layer + threat model. +- **P4 — Integration Testing (REQ-348..REQ-351):** E2E sign-up → sign-in → + token-vend → apply → audit; property tests for `mode_resolver`; KMS + round-trip test; PAT revocation SLO test (≤60s P95). +- **P5 — Capability Gate (REQ-352..REQ-353):** CAP-033..038 verification + gates wired into CI. + +### v1.28 Hard constraints + +- DO NOT depend on Cognito, IAM Identity Center, or any AWS-managed + identity service for sign-up/sign-in/token-vending (NFR-5). Nova-idp + signs OIDC tokens directly via KMS. (Note: no Cognito exists in the + repo today — this is a greenfield build, not a "Cognito drop".) +- DO NOT build a new `kj` engine — use kyverno-json (INV-4). +- DO NOT enforce MFA/TOTP for prod/dr this milestone — ship the code path, + enforce in v1.21+ (deferred, INV scope). +- DO NOT add WebAuthn/FIDO2, upstream IdP federation, or password breach + detection — deferred to v1.23+. +- DO NOT add Lambda layer auto-update on `core/` changes — v1.18 ships + manual `nova layer update`; v1.19 adds CI-triggered auto-update. +- The token-vend Lambda MUST evaluate the kyverno-json ABAC policy before + signing; allow/deny decisions MUST be emitted to the audit stream + (NFR-9, D-227). +- `nova idp setup --apply` MUST present the CloudFormation template for + review before any resource is created (NFR-10). + +### v1.28 phase status (active — phase 0 in progress) + +- **P0** pre-execution (SPECIFY→CLARIFY→RESEARCH→PLAN→GRILL→MVP/UX) — in + progress, target tag `v1.27.0`. +- **P1..PN** execution phases — planned in PLAN.md. +- **P(N+1)** final review + audit + milestone ship — target tag + `v1.27.(N+1)` = the v1.28 release. + +> Phase-by-phase task breakdown, wave ordering, and persona assignments +> will live in `.ciagent/PLAN.md`. Authoritative resume state: +> `.ciagent/CHECKPOINT.json`. \ No newline at end of file diff --git a/.ciagent/REQUIREMENTS.md b/.ciagent/REQUIREMENTS.md index cba79fd..843170d 100644 --- a/.ciagent/REQUIREMENTS.md +++ b/.ciagent/REQUIREMENTS.md @@ -299,4 +299,303 @@ Full v1.26 requirement text: `.ciagent/nova-blockchain-exchange/REQUIREMENTS.md`. Active phase plan: -`.ciagent/PLAN.md`. \ No newline at end of file +`.ciagent/PLAN.md`. + +## v1.28 — CLI Canonicalization + Identity Layer (active) + +> **Feature milestone — active.** 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. +> +> Tags run on the **v1.27.x** line: `v1.27.0` (P0) → +> `v1.27.1..v1.27.N` → `v1.27.(N+1)` (final = milestone release). +> Milestone branch: `milestone/v1.28-cli-identity`. +> +> **ID re-mapping (no collisions):** the source spec used `REQ-001..031`, +> `CAP-025..030`, `INV-63/64/65/18..21/34`, `D-NEW-26/37..41`, and a `kj` +> engine — none of which exist in this repo (CAP-025..032 and +> INV-1..11 are already allocated to blockchain/pilot work; the policy +> engine is kyverno-json, not `kj`). This file uses the re-mapped IDs: +> `REQ-323..353`, `CAP-033..038`, `INV-12..17`, `D-226..231`. The 1:1 +> mapping is recorded in CLARIFY.md. Decisions D-226..D-231 are authored +> in CLARIFY (full autonomy) — they are not pre-existing "locked inputs". + +### Decisions (locked in CLARIFY — full autonomy, load-bearing for v1.28) + +- **D-226 (Mode resolution priority):** flag → env (`NOVA_CLIENT_MODE`) → + credential type → TTY heuristic. Invalid env values are ignored + warned, + falling through to credential type. No silent fallbacks (NFR-1). +- **D-227 (ABAC engine = kyverno-json):** the token-vend Lambda uses the + existing kyverno-json engine (INV-4 swappable) as the ABAC evaluator, + not a new `kj` engine. Policy at `platform/abac/token-vend.policy`. +- **D-228 (Argon2id in Lambda):** `argon2-cffi` with bundled wheels; if + the C extension fails to load, fall back to the pure-Python + implementation; if both fail, document the Fargate migration path. +- **D-229 (PAT revocation SLO):** strongly-consistent DynamoDB read on + every token-vend request; revocation takes effect within 60s P95 (NFR-4). +- **D-230 (JWKS endpoint):** Lambda function URL behind a custom domain; + rate limiting at the DNS/CDN layer. API Gateway migration deferred to + v1.19+ if throttling requirements grow. +- **D-231 (ABAC policy ownership + versioning):** Platform Security owns + `platform/abac/token-vend.policy`; changes require PR review; the + policy version (git SHA) is recorded in every token-vend audit event. + +### P1 — CLI Substrate + +#### REQ-323 — CodeArtifact wheel + Lambda layer pipeline +**Journeys:** J3. **Priority:** High. +**AC:** Given a merge to `main` affecting `core/`, when CI runs, then both +the wheel and the Lambda layer are published to CodeArtifact with +identical version strings; if either fails, the merge is rejected. + +#### REQ-324 — CLI subcommand per `core/` module +**Journeys:** J3. **Priority:** High. +**AC:** (1) Every module in `core/` has a corresponding `nova/.py` +subcommand. (2) Subcommand files are ≤ 50 lines and contain no business +logic — they delegate to `core/`. (3) CAP-034 verifies delegation by AST +scan. + +#### REQ-325 — `nova init` scaffolds project +**Journeys:** J2. **Priority:** High. +**AC:** Given a directory with no `.nova/`, when Dev runs `nova init`, +then `.nova/`, `.nova/contract.yml.attestations/`, and `.gitignore` +(excluding secrets) are created. + +#### REQ-326 — `nova cli-action` published +**Journeys:** J3. **Priority:** High. +**AC:** (1) Action is available on both GitHub and Gitea marketplaces. +(2) Integration test verifies byte-identical behavior on both platforms. +(3) Python 3.12 is pinned. + +#### REQ-327 — `mode_resolver.py` priority +**Journeys:** J2, J3. **Priority:** High. +**AC:** (1) Explicit `--mode=agent|interactive` flag always wins. +(2) Otherwise `NOVA_CLIENT_MODE` env var. (3) Otherwise credential type +default. (4) Otherwise TTY heuristic. (5) Property tests cover all four +levels. (6) INV-13 (mode determinism) enforced at PR time. + +#### REQ-328 — Audit emission with mode + selection_reason +**Journeys:** J3. **Priority:** High. +**AC:** Given any CLI invocation, when the CLI runs, then the emitted +`cli.invocation` audit event contains `mode`, `selection_reason`, +`credential_type`, `command`, and `args`. INV-12 (mode observability) +enforced. + +### P2 — Lambda Packaging + Identity Layer + +#### REQ-329 — Dual-use Lambda/CLI import +**Journeys:** J2. **Priority:** High. +**AC:** Given `core/lambda/contract_ingestor.py`, when imported from the +Lambda handler, then it executes the Lambda path; when imported from the +CLI, then it executes the local path; and the two paths share ≥ 80% of +their code. + +#### REQ-330 — Local env synthesizer +**Journeys:** J2. **Priority:** High. +**AC:** Given a contract and a `--local` flag, when `nova apply --local` +runs, then a local env is synthesized via `core/env.py:get_env()` without +provisioning cloud resources. + +#### REQ-331 — Attestations directory scaffolded +**Journeys:** J2. **Priority:** High. +**AC:** Given `nova init` ran, when Dev lists +`.nova/contract.yml.attestations/`, then the directory exists and is empty. + +#### REQ-332 — JWS signing key from PAT +**Journeys:** J2. **Priority:** High. +**AC:** Given a PAT, when Dev runs `nova apply --local --sign-local-review`, +then a JWS attestation is produced; the public key is derivable from the +PAT and the JWS verifies. INV-14..17 (attestation invariants) enforced. + +#### REQ-333 — `nova-idp-auth` Lambda +**Journeys:** J1, J2. **Priority:** High. +**AC:** (1) Lambda exposes sign-up, sign-in, and session creation +endpoints. (2) Passwords are hashed with Argon2id. (3) Sessions are +stored in DynamoDB. (4) CAP-036 verifies end-to-end auth flow. + +#### REQ-334 — Argon2id password hashing +**Journeys:** J1, J2. **Priority:** High. +**AC:** Given a sign-up request, when the user record is persisted, then +the password is stored as an Argon2id hash; raw passwords never appear in +logs, traces, environment variables, or DynamoDB records. + +#### REQ-335 — DynamoDB tables for identity +**Journeys:** J1. **Priority:** High. +**AC:** (1) Tables exist: `nova-users`, `nova-sessions`, +`nova-password-resets`. (2) Tables are provisioned by `nova idp setup`. +(3) Point-in-time recovery is enabled on each. + +#### REQ-336 — `nova-idp-token-vend` Lambda +**Journeys:** J1, J2, J4. **Priority:** High. +**AC:** (1) Lambda accepts a PAT (or session token) and returns a +KMS-signed OIDC token. (2) Token claims include `sub`, `aud`, `iss`, +`exp`, and role claims. (3) ABAC policy is evaluated before signing. + +#### REQ-337 — KMS-signed OIDC tokens +**Journeys:** J1, J4. **Priority:** High. +**AC:** (1) Signing key is a KMS asymmetric key (RSA or ECDSA). +(2) Token signature is verifiable via the JWKS endpoint. (3) KMS +round-trip test passes. CAP-037 verifies. + +#### REQ-338 — JWKS endpoint as Lambda function URL +**Journeys:** J1, J4. **Priority:** High. +**AC:** Given the identity stack is deployed, when a client GETs the JWKS +URL, then the public key(s) for token verification are returned with +`Content-Type: application/json`. + +#### REQ-339 — kyverno-json ABAC policy file +**Journeys:** J1, J4. **Priority:** High. +**AC:** (1) Policy at `platform/abac/token-vend.policy`. (2) Policy inputs +include subject, requested claims, target resource, and environment. +(3) kyverno-json `evaluate` returns allow/deny; the decision is emitted to +the audit stream. + +#### REQ-340 — `nova idp setup` walks admin +**Journeys:** J1. **Priority:** High. +**AC:** (1) Command supports `--check`, `--apply`, and `--verify` modes. +(2) `--check` reports missing prerequisites and the required IAM policy. +(3) `--apply` generates a CloudFormation template and requires explicit +approval. (4) `--verify` runs the KMS round-trip test. + +#### REQ-341 — CloudFormation template for review +**Journeys:** J1. **Priority:** High. +**AC:** Given `nova idp setup --apply`, when the template is generated, +then the template is presented for review; resources are not created until +the operator approves; `--dry-run` shows the resource list without writing. + +#### REQ-342 — PAT issuance via portal +**Journeys:** J4. **Priority:** High. +**AC:** (1) PAT is a signed JWT. (2) PAT hash is stored in DynamoDB. +(3) PAT includes a unique `jti` and an expiry claim. (4) Revocation marks +the `jti` as revoked. + +#### REQ-343 — PAT hashes in DynamoDB +**Journeys:** J4. **Priority:** High. +**AC:** (1) Only the hash (not the raw PAT) is stored. (2) Table supports +lookup-by-hash and lookup-by-`jti`. (3) Revoked PATs are retained for +audit, not deleted. + +#### REQ-344 — `nova auth` commands +**Journeys:** J2, J4. **Priority:** High. +**AC:** (1) `nova auth login` exchanges session → OIDC token, stores +locally. (2) `nova auth revoke --pat ` marks a PAT revoked. +(3) `nova auth status` shows current credential, mode, and +selection_reason. (4) All commands emit audit events. + +### P3 — Documentation + +#### REQ-345 — Operator guide for `nova idp setup` +**Priority:** High. +**AC:** Guide published covering `--check`, `--apply`, `--verify`, +prerequisite IAM policy, and the CloudFormation review flow. + +#### REQ-346 — Developer guide for `nova auth login` +**Priority:** High. +**AC:** Guide published covering signup, signin, login, mode resolution, +and credential-type behavior at a TTY vs. piped stdout. + +#### REQ-347 — Identity-layer threat model +**Priority:** High. +**AC:** Threat model published covering Argon2id storage, KMS signing, +JWKS exposure, PAT revocation SLO, ABAC token vending, and the no-AWS- +managed-identity constraint (NFR-5). + +### P4 — Integration Testing + +#### REQ-348 — E2E integration test +**Priority:** High. +**AC:** Given a deployed Nova-idp, when the test runs, then sign-up → +sign-in → token-vend → apply → audit completes successfully; the audit +event chain is verifiable. + +#### REQ-349 — Property tests for `mode_resolver` +**Priority:** High. +**AC:** (1) Property tests cover all four priority levels. (2) Edge cases: +TTY but piped stdout, missing credential, conflicting flag/env, invalid +env value. (3) INV-13 enforced via test. + +#### REQ-350 — KMS round-trip test +**Priority:** High. +**AC:** Given a token signed by the token-vend Lambda, when the test +fetches the JWKS and verifies the signature, then verification succeeds. + +#### REQ-351 — PAT revocation SLO test +**Priority:** High. +**AC:** Issue PAT → use to vend token → revoke → assert denial within 60s +P95. Test passes in CI. + +### P5 — Capability Gate + +#### REQ-352 — CAP-033..038 gate rules wired into CI +**Priority:** High. +**AC:** (1) CAP-033 (CLI subcommand surface exists): `nova --help` lists a +subcommand for every `core/` module. (2) CAP-034 (subcommand delegates to +`core/`): every `nova/.py` ≤ 50 lines, no business logic, AST +scan. (3) CAP-035 (layer matches wheel): Lambda layer ARN version matches +the `nova-cli` wheel version. (4) CAP-036 (Nova-idp auth flow works): E2E +test (REQ-348) passes. (5) CAP-037 (token-vend signs via KMS): KMS +round-trip (REQ-350) passes. (6) CAP-038 (PAT issuance + revocation): +REQ-351 passes. Failure of any → merge blocked. + +#### REQ-353 — Capability gate GREEN for v1.28 release +**Priority:** High. +**AC:** CAP-001..CAP-032 remain Verified; CAP-033..CAP-038 are Verified. +All v1.28 release-gate criteria in PLAN.md §6 met. + +### v1.28 Invariants (new — INV-12..INV-17) + +- **INV-12 (Mode observability):** Every CLI invocation emits a + `cli.invocation` audit event containing `mode`, `selection_reason`, + `credential_type`, `command`, and `args`. +- **INV-13 (Mode resolution determinism):** Resolution priority is + flag → env (`NOVA_CLIENT_MODE`) → credential type → TTY. No silent + fallbacks. Deviations rejected at PR time. +- **INV-14 (Credential type encodes role):** `developer_pat` / + `nova_oidc_token` + TTY present → `interactive`; TTY absent → `agent`. +- **INV-15 (No AWS-managed identity in path):** Nova-idp MUST NOT depend + on Cognito, IAM Identity Center, or any AWS-managed identity service. +- **INV-16 (Password storage):** Passwords hashed with Argon2id; raw + passwords never in logs/traces/env/DynamoDB. +- **INV-17 (ABAC discipline):** The token-vend Lambda evaluates the + kyverno-json ABAC policy before signing; allow/deny + policy inputs + emitted to the audit stream. + +### v1.28 Traceability (live — see CHECKPOINT.json for authoritative state) + +| REQ | Phase | Status | +|-----|-------|--------| +| REQ-323 | P1 | planned | +| REQ-324 | P1 | planned | +| REQ-325 | P1 | planned | +| REQ-326 | P1 | planned | +| REQ-327 | P1 | planned | +| REQ-328 | P1 | planned | +| REQ-329 | P2 | planned | +| REQ-330 | P2 | planned | +| REQ-331 | P2 | planned | +| REQ-332 | P2 | planned | +| REQ-333 | P2 | planned | +| REQ-334 | P2 | planned | +| REQ-335 | P2 | planned | +| REQ-336 | P2 | planned | +| REQ-337 | P2 | planned | +| REQ-338 | P2 | planned | +| REQ-339 | P2 | planned | +| REQ-340 | P2 | planned | +| REQ-341 | P2 | planned | +| REQ-342 | P2 | planned | +| REQ-343 | P2 | planned | +| REQ-344 | P2 | planned | +| REQ-345 | P3 | planned | +| REQ-346 | P3 | planned | +| REQ-347 | P3 | planned | +| REQ-348 | P4 | planned | +| REQ-349 | P4 | planned | +| REQ-350 | P4 | planned | +| REQ-351 | P4 | planned | +| REQ-352 | P5 | planned | +| REQ-353 | P5 | planned | \ No newline at end of file diff --git a/.ciagent/config.json b/.ciagent/config.json index 9e62f9b..c884cec 100644 --- a/.ciagent/config.json +++ b/.ciagent/config.json @@ -13,7 +13,7 @@ ], "active_project": "acdl", "active_projects": ["acdl", "nova-blockchain-exchange"], - "active_milestone": "v1.27", + "active_milestone": "v1.28", "autonomy": { "level": "full", "escalation_hooks": ["deploy", "delete_data", "merge_to_main"], From 05efb014d61b6155100a099bb14bef494fe116ab Mon Sep 17 00:00:00 2001 From: Jon Chery Date: Wed, 19 Aug 2026 21:58:41 +0000 Subject: [PATCH 2/7] =?UTF-8?q?docs(P00):=20clarify=20=E2=80=94=20v1.28=20?= =?UTF-8?q?ambiguities=20resolved=20(6=20Qs=20+=205=20grounding=20gaps,=20?= =?UTF-8?q?D-226..D-231)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ---ci--- project: acdl phase: 0 milestone: v1.28 status: clarify ---/ci--- --- .ciagent/CHECKPOINT.json | 7 +- .ciagent/CLARIFY.md | 371 ++++++++++++++++++++++++--------------- 2 files changed, 236 insertions(+), 142 deletions(-) diff --git a/.ciagent/CHECKPOINT.json b/.ciagent/CHECKPOINT.json index b25335c..5973b92 100644 --- a/.ciagent/CHECKPOINT.json +++ b/.ciagent/CHECKPOINT.json @@ -1,10 +1,10 @@ { "phase": 0, - "stage": "specify", + "stage": "clarify", "milestone": "v1.28", "phase_role": "pre_execution", "attempts": 0, - "updated_at": "2026-08-19T20:00:00Z", + "updated_at": "2026-08-19T20:10:00Z", "project": "acdl", "projects": ["acdl", "nova-blockchain-exchange"], "active_milestone": "v1.28", @@ -12,5 +12,6 @@ "phase_branch": "phase/00-pre-execution", "tag_line": "v1.27.x", "previous_milestone": {"milestone": "v1.27", "tag": "v1.26.3", "status": "complete"}, - "notes": "v1.28 SPECIFY. Re-mapped from source spec (v1.18 framing) to v1.28. ID allocations: REQ-323..353, CAP-033..038, INV-12..17, D-226..231. kj engine mapped to kyverno-json (D-227). Requirements validated in REQUIREMENTS.md. Next: CLARIFY." + "decisions": ["D-226", "D-227", "D-228", "D-229", "D-230", "D-231"], + "notes": "v1.28 CLARIFY. 6 open Qs + 5 grounding gaps resolved at full autonomy. D-226..D-231 authored (confidence >= 0.80). kj mapped to kyverno-json (D-227). Cognito-drop reframed as greenfield (G3). CAP-033..038, INV-12..17, REQ-323..353 allocated. Next: RESEARCH." } \ No newline at end of file diff --git a/.ciagent/CLARIFY.md b/.ciagent/CLARIFY.md index 3609e65..3609a1f 100644 --- a/.ciagent/CLARIFY.md +++ b/.ciagent/CLARIFY.md @@ -1,183 +1,276 @@ -# CLARIFY — v1.27 PO State Catalog & Ciagent Compression +# CLARIFY — v1.28 CLI Canonicalization + Identity Layer > **Autonomy:** full. Auto-resolution with assumption logging per -> `config.autonomy.level: "full"`. No human escalation unless -> confidence < 0.60. The prior conversation resolved all material -> ambiguities (4 user-answered questions). This file records the -> assumptions for the v1.27 record. +> `config.autonomy.level: "full"`. No human escalation unless confidence +> < 0.60. The user-approved re-mapping plan (v1.18 spec → v1.28) resolved +> the headline discrepancy. This file records the remaining ambiguities +> and the grounding gaps surfaced in pre-flight. --- ## Method -The clarify stage identifies ambiguities in the v1.27 specification -and resolves them at full autonomy. The v1.27 spec is the user-approved -plan from the prior conversation + the STATE.md design locked by 4 -question answers. Each ambiguity gets a decision ID (D-214+; continuing -from the v1.26 decisions D-200..D-213), a resolution, a confidence -score, and a rationale. +The clarify stage identifies ambiguities in the v1.28 specification and +resolves them at full autonomy. The v1.28 spec is the user-provided +"Universal Feature Specification — v1.18 CLI Canonicalization + Identity +Layer," re-mapped to v1.28 (milestone number, tag line, and all +ID namespaces) per the user-approved plan. Each ambiguity gets a +decision ID (D-226+, continuing from v1.27's D-214..D-225), a resolution, +a confidence score, and a rationale. --- ## Prior-conversation resolutions (already locked, restated for the record) -These were resolved by user-answered questions in the conversation that -spawned v1.27. They are load-bearing for v1.27 execution and cited -here so the v1.27 record is self-contained. +These were resolved by the user-approved re-mapping plan in the +conversation that spawned v1.28. They are load-bearing for v1.28 +execution. -### Q-P1 — What should the new PO-reference file catalog? +### Q-P1 — The source spec is titled "v1.18" but v1.18 already shipped. What milestone is this? -**Resolution:** Capability catalog (what the system can do today). -**Confidence:** 1.0 (user-confirmed). **Decision:** D-214. +**Resolution:** Re-map the spec's *content* (CLI Canonicalization + +Identity Layer) to **v1.28**, the next milestone after v1.27 (complete). +Tags run on the **v1.27.x** line (P0 = `v1.27.0`). Milestone branch: +`milestone/v1.28-cli-identity`. +**Confidence:** 1.0 (user-confirmed — "Re-map to v1.28"). **Decision:** n/a (milestone identity, not a D-ID). -### Q-P2 — How should the new file relate to CAPABILITY_INVENTORY.md? +### Q-P2 — The spec's "locked inputs" (D-NEW-26, kj engine, Nova-idp, INV-63/64/65, CAP-025..030, REQ-001..031) don't exist in the repo. How to handle? -**Resolution:** Call it `STATE.md`. PO-owned, ciagent-updated after -milestone implementation. CAPABILITY_INVENTORY.md is archived. -**Confidence:** 1.0 (user-confirmed). **Decision:** D-215. +**Resolution:** Author them fresh in this milestone's CLARIFY/RESEARCH as +**D-226..D-231, INV-12..17, CAP-033..038, REQ-323..353**. The `kj` engine +is mapped to the existing **kyverno-json** engine (INV-4 swappable) — no +new engine is built. CAP/INV/REQ IDs are re-allocated to avoid collisions +with shipped history (CAP-025..032 and INV-1..11 are blockchain/pilot). +**Confidence:** 1.0 (user-confirmed — "Re-map to v1.28"). **Decision:** D-227 (kj→kyverno-json), plus the ID-allocation block in REQUIREMENTS.md. -### Q-P3 — Where should the file live, and who owns it? +### Q-P3 — The spec claims a "Cognito drop." No Cognito exists in the repo. What does NFR-5 mean? -**Resolution:** Owned by the PO, updated by ciagent after the milestone -is implemented with additives. -**Confidence:** 1.0 (user-confirmed). **Decision:** D-216. - -### Q-P4 — How should "additive when new features are implemented" be enforced? - -**Resolution:** On the last phase / milestone ship (the P-final Wave 3 -"milestone ship" step). No regression-gate check in this pass. -**Confidence:** 1.0 (user-confirmed). **Decision:** D-217. - -### Q-P5 — Should the initial STATE.md backfill all shipped capabilities through v1.26? - -**Resolution:** Backfill all shipped capabilities through v1.26 -(compressed one-liners for v1.1–v1.24; full entries for v1.25 + v1.26). -**Confidence:** 1.0 (user-confirmed). **Decision:** D-218. - -### Q-P6 — Should the v1.26 pre-execution artifacts (CLARIFY, GRILL, IDEATE, RESEARCH) be archived? - -**Resolution:** Archive all 4 to `.ciagent/archive/` with `-v1.26` -suffixes. The next milestone's P0 writes fresh versions. Decisions are -already folded into PROJECT.md load-bearing decisions + PLAN.md -binding revisions. -**Confidence:** 1.0 (user-confirmed). **Decision:** D-219. +**Resolution:** NFR-5 (no AWS-managed identity in the path) is a +**greenfield constraint**, not a migration. Nova-idp is built fresh; no +Cognito/IAM Identity Center is *introduced*. The "drop" framing is +aspirational language from the source spec, not a literal removal. +**Confidence:** 1.0. **Decision:** D-226 (recorded below; NFR-5 restated +as a greenfield constraint in INV-15). --- -## Ambiguities + Resolutions (this CLARIFY pass) +## Open questions from the spec's §7 (auto-resolved at full autonomy) -### Q1 — Is v1.27 a feature milestone or an NFR milestone? +### Q1 — Argon2 native dependency in Lambda runtime -**Ambiguity:** v1.27 authors `STATE.md` (a new file/capability for the -PO) and archives 11 files. Does the new-file authoring count as `feat:` -(making this a feature milestone, tags on v1.26.x with progressive -patches) or `docs:`/`chore:` (NFR milestone, same tag behavior but -subject to the NFR purity gate)? +`argon2-cffi` has a C extension that may not build cleanly in the Lambda +Python 3.12 runtime. -**Resolution:** NFR milestone. `STATE.md` is documentation (a catalog of -existing capabilities), not a new platform capability. The archive moves -are `chore:` (file relocation, lossless). No code, no schema, no -platform behavior change. Tags run on the v1.26.x patch 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. +**Resolution (D-228):** Use `argon2-cffi` with bundled wheels; if the +extension fails to load, fall back to the pure-Python implementation. If +both fail, document the Fargate migration path for the auth Lambda. +CAP-036 covers end-to-end verification. +**Confidence:** 0.85. **Rationale:** Bundled wheels are the standard +workaround for Lambda native deps; the pure-Python fallback is a safe +degradation. Fargate is the escape hatch if Lambda's runtime is +fundamentally incompatible. RESEARCH will validate wheel availability for +Python 3.12 + the Lambda execution environment. +**Impact if wrong:** Auth Lambda migrates to Fargate, adding ~1 week to P2. -**Confidence:** 0.95. **Decision:** D-220. +### Q2 — PAT revocation propagation latency -### Q2 — Where does the consumer-side archive (nova-blockchain-exchange/ROADMAP.md) land? +The 60-second SLO (NFR-4) depends on whether the token-vend Lambda reads +PAT revocation state from DynamoDB on every request (eventually +consistent reads) or via a cached/denylist mechanism. -**Ambiguity:** The platform archive convention is -`.ciagent/archive/-.md`. The consumer subproject -(`nova-blockchain-exchange/`) has no `archive/` subdirectory. Does the -consumer ROADMAP archive at `.ciagent/archive/` (platform-side, mixed) -or `.ciagent/nova-blockchain-exchange/archive/` (consumer-side, new -subdir)? +**Resolution (D-229):** Read-on-every-request with strongly consistent +reads on the PAT hash table. Cost is acceptable given expected request +volume (token vending is not a hot path — it precedes a deploy, not every +request). REV-351 verifies the SLO in CI. +**Confidence:** 0.90. **Rationale:** Strongly consistent DynamoDB reads +have single-digit-ms latency at expected volume; the 60s SLO has >10x +headroom. A cache layer adds invalidation complexity that the SLO does +not require. +**Impact if wrong:** If read latency exceeds 60s under load, introduce a +DynamoDB TTL + cache layer; SLO must be re-verified. -**Resolution:** Consumer-side. Create -`.ciagent/nova-blockchain-exchange/archive/` and relocate to -`ROADMAP-v1.26.md`. This preserves the per-project path convention -(multi-project mode: `.ciagent//` paths). The platform archive -directory is not mixed with consumer archives. +### Q3 — JWKS endpoint: Lambda function URL vs. API Gateway -**Confidence:** 0.92. **Decision:** D-221. +A function URL is simpler and cheaper but lacks throttling, WAF, and +custom domains out of the box. -### Q3 — Does archiving CLARIFY/GRILL/IDEATE/RESEARCH lose the "how v1.26 was specified" traceability? +**Resolution (D-230):** Start with a Lambda function URL behind a custom +domain; rate limiting configured at the DNS/CDN layer. API Gateway +migration deferred to v1.19+ if throttling requirements grow. +**Confidence:** 0.80. **Rationale:** The JWKS endpoint is public-key +only (no secrets); the threat surface is low. Function URL + CDN rate- +limiting covers the v1.28 volume. API Gateway is over-engineering until +traffic patterns are known. +**Impact if wrong:** If throttling becomes a requirement, API Gateway +migration adds ~3-5 days. -**Ambiguity:** The pre-execution artifacts document the v1.26 decision -path. Archiving them moves them out of active context. Is the -traceability preserved? +### Q4 — Mode resolver precedence with invalid `NOVA_CLIENT_MODE` value -**Resolution:** Yes. Three layers preserve it: (1) the archive files -are byte-identical relocations inside `.ciagent/archive/` (reachable by -agents + git history); (2) the decisions D-200..D-213 are folded into -`PROJECT.md` load-bearing decisions (the durable record); (3) git -history at the v1.26 commits preserves the authoritative state. The -active-context reduction is the point — v1.26 is shipped; the next P0 -writes fresh CLARIFY/GRILL/IDEATE/RESEARCH. +What happens if the env var is set to something other than `agent` or +`interactive` (e.g., `NOVA_CLIENT_MODE=auto`)? -**Confidence:** 0.95. **Decision:** D-222. +**Resolution (D-226):** Invalid env var values are ignored, falling +through to credential type. A warning is logged. Behavior is documented +in the `nova-cli` README. This is a sub-clause of the mode-resolution +priority decision. +**Confidence:** 0.90. **Rationale:** Ignoring + warning is the least +surprising behavior for an operator debugging mode issues. Failing hard +would block legitimate workflows that set a stale/typo'd env var. +**Impact if wrong:** Operators debugging mode issues may be confused; +non-blocking. -### Q4 — Should IAM_POLICY.md be archived (it predates v1.26 and is dated v1.11)? +### Q5 — Service-account PAT vs. developer PAT in the same session -**Ambiguity:** `IAM_POLICY.md` is dated v1.11 (2026-07-28). It predates -v1.26 by 5 milestones. The D-207 future key-split (P1+ R-3 in -REVIEW-AUDIT-P05) is pending. Archive or keep? +What if both credential types are available (e.g., a developer explicitly +exports a service-account PAT)? -**Resolution:** Keep active. `IAM_POLICY.md` is a live baseline — -referenced by the regression gate -(`tests/test_iam_policy_baseline.py`), enforced by a managed policy on -account `581513795199`, and the D-207 key-split is a pending future- -hardening item. It is not stale; it is a baseline that grows when -grants change. The v1.11 date reflects the last grant addition, not -staleness. +**Resolution (D-226):** The most recently acquired credential wins. +Documented in `nova auth login` output. The credential type is what +drives mode resolution (INV-14), so the operator sees which mode was +selected and why. +**Confidence:** 0.85. **Rationale:** "Most recent wins" is the simplest +deterministic rule that matches operator mental models of "I just logged +in as X." The audit event records the winning credential type, so the +selection is traceable. +**Impact if wrong:** Mode selection may surprise the operator; non- +blocking, but `nova auth status` must make the active credential explicit. -**Confidence:** 0.90. **Decision:** D-223. +### Q6 — ABAC policy ownership and versioning -### Q5 — Should REGRESSION_REPORT.{json,md} be refreshed as part of v1.27? +`platform/abac/token-vend.policy` is referenced, but who owns changes? +How are policy versions tracked in audit? -**Ambiguity:** Both files are dated 2026-08-01 (v1.10 Phase 52), show -CAP-025 absent, and mark live-aws CAPs "Skipped" (state bucket absent -pre-v1.26 re-bootstrap). They are stale. Should v1.27 refresh them? - -**Resolution:** No. Both files are machine-managed — written by -`core/regression_verify.py:704-705` on every `run_regression.sh` run. -They regenerate on the next regression run. v1.27 is docs/chore only -(no code); touching machine-managed files by hand creates a drift -source. The stale state is honest (the last gate run was v1.10; the -next run regenerates). The STATE.md Domain 7 row "Regression gate" -notes the current CAP range (CAP-001..025). - -**Confidence:** 0.88. **Decision:** D-224. - -### Q6 — Does PROJECT.md get the v1.26 phase-status fix in v1.27 P1 or P2? - -**Ambiguity:** The plan splits work into P1 (author + archive) and P2 -(fix stale + wire). The PROJECT.md phase-status fix (P3/P4/P5 pending -→ complete) is a "fix stale" item. P1 or P2? - -**Resolution:** P2. P1 is the additive authoring + lossless archive -moves. P2 is the corrections to kept files + the ship-discipline wiring. -This keeps P1 a pure-additive, no-edit phase (easier review + audit) and -P2 the correction phase. The PROJECT.md fix is a correction; P2. - -**Confidence:** 0.85. **Decision:** D-225. +**Resolution (D-231):** Policy changes require PR review; the policy +version (git SHA) is recorded in every token-vend audit event. Owner: +Platform Security. The policy file lives in the platform repo at +`platform/abac/token-vend.policy` and is reviewed like any other +production config. +**Confidence:** 0.90. **Rationale:** Git SHA is the natural version +identifier for a repo-resident policy; recording it in the audit event +makes every allow/deny decision reconstructable to the exact policy text. +**Impact if wrong:** Untracked policy changes could lead to unexpected +allow/deny decisions in production, undermining audit defensibility. --- -## Summary +## Grounding gaps surfaced in pre-flight (auto-resolved) -6 prior-conversation resolutions (D-214..D-219, all user-confirmed) -+ 6 new ambiguities (D-220..D-225, all auto-resolved at full autonomy, -confidence ≥ 0.60). 0 escalations. +### G1 — The `kj` engine does not exist; the spec treats it as locked. -**Key decisions:** -- D-220: v1.27 is an NFR milestone (tags on v1.26.x; final patch is the - milestone release). -- D-221: Consumer archives land in `.ciagent/nova-blockchain-exchange/archive/`. -- D-222: Archiving pre-execution artifacts preserves traceability - (archive files + PROJECT.md load-bearing decisions + git history). -- D-223: IAM_POLICY.md stays active (live baseline, test-enforced, - D-207 pending). -- D-224: REGRESSION_REPORT.{json,md} regenerate on next - `run_regression.sh` (machine-managed; v1.27 is docs/chore only). -- D-225: PROJECT.md phase-status fix is P2 (correction phase), not P1 - (additive phase). \ No newline at end of file +**Resolution (D-227):** The token-vend Lambda uses the existing +**kyverno-json** engine (INV-4 swappable) as the ABAC evaluator. The +policy at `platform/abac/token-vend.policy` is a kyverno-json policy. +No new `kj` engine is built in v1.28. If a distinct `kj` engine is +desired later, it is a separate research spike (not this milestone). +**Confidence:** 0.95. **Rationale:** The repo already has a swappable +policy engine (INV-4) implemented as kyverno-json. Building a second +engine to do the same job violates the swappable-engine invariant's +spirit. kyverno-json's `evaluate` semantics cover the spec's ABAC needs +(subject, claims, resource, environment → allow/deny). +**Impact if wrong:** If the user actually wants a new `kj` engine, v1.28 +scope expands significantly (engine design + implementation + migration). +This was flagged as caveat #3 in the approved plan; the recommended path +(kyverno-json) is locked here. + +### G2 — The spec's INV-18..21, INV-34, INV-63/64/65 don't exist. + +**Resolution:** Re-allocated as **INV-12..INV-17** (see REQUIREMENTS.md +§v1.28 Invariants). The 1:1 mapping: +- INV-63 (mode observability) → INV-12 +- INV-64 (mode determinism) → INV-13 +- INV-65 (credential type encodes role) → INV-14 +- INV-18..21 (attestation invariants) → INV-15 (no AWS-managed identity), + INV-16 (password storage), INV-17 (ABAC discipline). The spec's + attestation invariants INV-18..21 are partially covered by existing + invariants (INV-6 immutable audit) + INV-17; the JWS-from-PAT behavior + (REQ-332) is a requirement, not a separate invariant, in this mapping. +- INV-34 (MFA enforcement) → deferred to v1.21+ (out of scope per §2.2); + no INV allocated in v1.28. +**Confidence:** 0.85. **Rationale:** The mapping preserves the spec's +intent without colliding with the repo's INV-1..11. INV-34 (MFA) is +explicitly deferred per the spec's own §2.2 out-of-scope table. +**Impact if wrong:** If the user wants the exact INV-18..21 semantics as +separate invariants, INV-12..17 can be re-numbered; non-blocking. + +### G3 — The spec's CAP-025..030 collide with blockchain/pilot CAPs. + +**Resolution:** Re-allocated as **CAP-033..CAP-038** (see REQUIREMENTS.md +§v1.28 + REQ-352). The 1:1 mapping: +- CAP-025 (CLI subcommand surface) → CAP-033 +- CAP-026 (subcommand delegates to core/) → CAP-034 +- CAP-027 (layer matches wheel) → CAP-035 +- CAP-028 (Nova-idp auth flow) → CAP-036 +- CAP-029 (token-vend signs via KMS) → CAP-037 +- CAP-030 (PAT issuance + revocation) → CAP-038 +**Confidence:** 1.0. **Rationale:** Existing CAP-025..032 are +blockchain/pilot capabilities (STATE.md); re-use would corrupt the +capability registry. The re-allocated IDs are the next available. +**Impact if wrong:** None — this is a numbering decision, not a semantic +one. + +### G4 — The spec's REQ-001..031 collide / don't exist. + +**Resolution:** Re-allocated as **REQ-323..REQ-353** (1:1 with the spec's +REQ-001..031). Full text in REQUIREMENTS.md §v1.28. Max existing REQ = +REQ-322. +**Confidence:** 1.0. **Rationale:** Same as G3 — avoid collision, use +next available range. + +### G5 — `platform/abac/`, `nova/` subcommand dir, `nova-idp-*` Lambdas don't exist. + +**Resolution:** These are **greenfield deliverables** of v1.28 execution +phases, not pre-existing "locked architectures." RESEARCH will design +them; PLAN will sequence them; EXECUTE will build them. The spec's +"Operating Principle 1" (incremental delivery) is honored — v1.28 is +net-new work. +**Confidence:** 1.0. **Rationale:** The spec itself describes these as +new ("introducing Nova-idp"). The mis-framing was in calling them +"locked" — they are locked in *scope*, not in *prior existence*. +**Impact if wrong:** None — this is a framing correction. + +--- + +## Decision ledger (v1.28 — D-226..D-231) + +| ID | Title | Confidence | Load-bearing for | +|----|-------|------------|------------------| +| D-226 | Mode resolution priority + invalid-env + dual-credential | 0.90 | REQ-327, INV-12, INV-13, INV-14 | +| D-227 | ABAC engine = kyverno-json (no `kj` engine built) | 0.95 | REQ-336, REQ-339, INV-17, NFR-9 | +| D-228 | Argon2id in Lambda: bundled wheels + pure-Python fallback + Fargate path | 0.85 | REQ-333, REQ-334, INV-16, NFR-8 | +| D-229 | PAT revocation: strongly-consistent DDB read-on-every-request, 60s SLO | 0.90 | REQ-342, REQ-343, REQ-351, NFR-4 | +| D-230 | JWKS endpoint: Lambda function URL + custom domain + CDN rate-limit | 0.80 | REQ-338, NFR-5 | +| D-231 | ABAC policy ownership: Platform Security, git SHA in audit | 0.90 | REQ-339, NFR-9 | + +--- + +## Assumptions logged (full autonomy, no human escalation) + +1. **CodeArtifact is provisionable** in AWS account `581513795199` (the + pilot account). RESEARCH will confirm IAM permissions + repository + creation. If not, v1.28 falls back to a private PyPI server or a + Gitea-hosted wheel index; the CLI subcommand surface (REQ-324) and + identity layer (REQ-333+) are unaffected. +2. **Python 3.12** is the target runtime for both the CLI wheel and the + Lambda functions (spec §4 REQ-004.3). The repo's current Python + version will be confirmed in RESEARCH; if it differs, the CLI pins + 3.12 and Lambda uses the 3.12 runtime regardless. +3. **KMS asymmetric signing** (RSA-2048 or ECDSA P-256) is available in + the target account. RESEARCH will confirm. If only symmetric KMS is + available, the token-vend Lambda uses symmetric signing + a public-key + publication step (less ideal, but functional); INV-15 is unaffected. +4. **The Forge action** (REQ-326) is the existing `nova cli-action` + pattern, extended to both GitHub and Gitea marketplaces. The repo's + current Forge/Gitea workflow conventions (`.gitea/workflows/`, + `deploy.yml@v1.25`) are the baseline. +5. **MFA/TOTP** code path ships in v1.28 (per spec §2.2) but enforcement + for prod/dr is deferred to v1.21+. This is a doc/test-only path in + v1.28 — no enforcement gate. + +--- + +## CLARIFY complete + +All material ambiguities resolved at full autonomy (6 open questions + +5 grounding gaps → D-226..D-231, confidence ≥ 0.80). No human escalation +triggered (all confidences ≥ 0.60 threshold). REQUIREMENTS.md updated +with the decision ledger + invariants. Next: RESEARCH. \ No newline at end of file From c629809d75068f4208eda858cbdcdd48b8a4504d Mon Sep 17 00:00:00 2001 From: Jon Chery Date: Wed, 19 Aug 2026 22:05:24 +0000 Subject: [PATCH 3/7] =?UTF-8?q?docs(P00):=20research=20findings=20?= =?UTF-8?q?=E2=80=94=20v1.28=20CLI=20+=20identity=20layer=20(11=20Qs,=20D-?= =?UTF-8?q?228=20amended)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ---ci--- project: acdl phase: 0 milestone: v1.28 status: research ---/ci--- --- .ciagent/ARCHITECTURE.md | 82 ++++++- .ciagent/CHECKPOINT.json | 7 +- .ciagent/PERSONAS.md | 158 +++++++++----- .ciagent/RESEARCH.md | 448 ++++++++++++++++++++++++++++----------- 4 files changed, 505 insertions(+), 190 deletions(-) diff --git a/.ciagent/ARCHITECTURE.md b/.ciagent/ARCHITECTURE.md index 88facf4..a7236e2 100644 --- a/.ciagent/ARCHITECTURE.md +++ b/.ciagent/ARCHITECTURE.md @@ -576,4 +576,84 @@ key only after the new one propagates to the consumer's Actions secret store, verified by a post-PUT GET; on upload/verify failure the old key is left Active and the run exits non-zero. The synced workflow file is forge-agnostic (REQ-230): forge base URL / owner / consumer repo come from -repository secrets (`NOVA_FORGE_*`, `NOVA_CONSUMER_REPO`), not literals. \ No newline at end of file +repository secrets (`NOVA_FORGE_*`, `NOVA_CONSUMER_REPO`), not literals. + +### §12.10 — Nova-idp Identity Layer (v1.28, current) + +Nova owns its identity layer end-to-end. Two (optionally three) Lambda +functions + four DynamoDB tables + one KMS asymmetric signing key + one +kyverno-json ABAC policy. **No Cognito, no IAM Identity Center (INV-15).** +The `nova-cli` Lambda layer carries the Nova wheel + `argon2-cffi` + +`cryptography` + `pyjwt` + the `kj` Go binary, making the same code +importable in both the CLI and the Lambda (REQ-329 dual-use, NFR-7). + +**Components:** + +- `nova-idp-auth` Lambda — sign-up, sign-in, session creation. Argon2id + password hashing (D-228: bundled abi3 wheel; fail-closed on + `ImportError`, no pure-Python fallback). DynamoDB: `nova-users` + (PK `user_id`, Argon2id `password_hash`), `nova-sessions` (PK + `session_id`, TTL `expires_at`), `nova-password-resets` (PK + `reset_token`, TTL 15m). Function URL with IAM auth. +- `nova-idp-token-vend` Lambda — accepts a PAT (or session token), + validates revocation (`nova-pats.GetItem(jti, ConsistentRead=True)` — + D-229, 60s SLO), evaluates the kyverno-json ABAC policy at + `platform/abac/token-vend.policy` (D-227, INV-17), KMS-signs an + ECDSA P-256 JWT (`ES256`), converts DER→raw ECDSA signature (RFC 7515 + §3.1.3), returns the OIDC token. The `policy_version` (git SHA, + D-231) is recorded in every `token.vend.allowed/denied` audit event. +- `nova-idp-jwks` Lambda (optional, separation of concerns) — function + URL with `AuthType: NONE` (public key only), `Cache-Control: max-age=3600`. + `kms.get_public_key` → DER SPKI → JWK via `cryptography`. Custom + domain + WAF via CloudFront is OPTIONAL (`--public-jwks-domain` flag + on `nova idp setup`, D-230). +- `nova-pats` DynamoDB table — PK `jti`, GSI1 `sub` (list PATs for + user), GSI2 `pat_hash` (lookup by hash). Only the hash stored (not + raw PAT, REQ-343). Revoked PATs retained for audit. + +**CLI surface (`nova` package, greenfield):** + +- Entry point: `[project.scripts] nova = "nova.cli:main"` (argparse-only, + no click/typer — repo convention). `nova/cli.py` auto-discovers + `nova/.py` subcommands via `pkgutil.iter_modules`, dispatches, + emits the `cli.invocation` audit event (INV-12) with `mode`, + `selection_reason`, `credential_type`, `command`, `args`. +- Each `nova/.py` is ≤50 lines, delegates to `core/` (CAP-034 + AST scan). Subgroups: `nova auth login/revoke/status`, `nova idp + setup --check/--apply/--verify`, `nova init`, `nova apply --local`. +- `core/mode_resolver.py` — flag → env (`NOVA_CLIENT_MODE`) → credential + type → `sys.stdin.isatty()` (D-226). CLI-only; Lambdas don't resolve + modes. Property-tested with `hypothesis` (REQ-349). +- `core/env.py:+synthesize_local_env()` — synthesizes a local env dict + from a contract + `--local` flag (REQ-330). No cloud provisioning. + +**Packaging (NFR-6, CAP-035):** + +- CI publishes a wheel to CodeArtifact AND a Lambda layer with identical + version strings on every merge affecting `core/`/`adapters/`/`nova/`. + Version mapping recorded in SSM `/nova/layer/nova-cli/version`. + If either publish fails, the merge is blocked (REQ-323). +- `nova cli-action` composite action at + `.github/actions/nova-cli/action.yml`, referenced by both GitHub + + Gitea (`uses: continuous-intelligence/acdl/.github/actions/nova-cli@v1.28`). + Python 3.12 pinned. Byte-identical behavior verified by CI matrix + (REQ-326, NFR-11). + +**Data flows:** + +1. Sign-up → `nova-idp-auth` → Argon2id → `nova-users` PutItem → session + → `nova-sessions` PutItem → return session token. +2. Token vend (hot path) → `nova-idp-token-vend` → `nova-pats` strong + read (revocation) → kyverno-json ABAC eval → if allow → KMS sign → + DER→raw → return OIDC JWT. Audit at every step. +3. JWKS fetch → `nova-idp-jwks` → `kms.get_public_key` → DER→JWK → + `{"keys":[...]}`. Cached 1h at CloudFront (if custom domain) / client. +4. PAT revoke → `nova auth revoke --pat ` → `nova-pats.UpdateItem( + status=revoked)` → audit. Strong read on next vend → 403 (within 60s). + +**`nova idp setup` (REQ-340, NFR-10):** generates a CloudFormation +template (raw dict → JSON, no troposphere dep), presents for review +(`$PAGER` + resource summary), requires explicit `y/N` approval before +`cloudformation deploy --capabilities CAPABILITY_IAM`. `--check` reports +prerequisites + IAM policy delta; `--verify` runs the KMS round-trip +test. New IAM grants required: `cloudformation:*`, `codeartifact:*`. \ No newline at end of file diff --git a/.ciagent/CHECKPOINT.json b/.ciagent/CHECKPOINT.json index 5973b92..0c8f0fb 100644 --- a/.ciagent/CHECKPOINT.json +++ b/.ciagent/CHECKPOINT.json @@ -1,10 +1,10 @@ { "phase": 0, - "stage": "clarify", + "stage": "research", "milestone": "v1.28", "phase_role": "pre_execution", "attempts": 0, - "updated_at": "2026-08-19T20:10:00Z", + "updated_at": "2026-08-19T20:25:00Z", "project": "acdl", "projects": ["acdl", "nova-blockchain-exchange"], "active_milestone": "v1.28", @@ -13,5 +13,6 @@ "tag_line": "v1.27.x", "previous_milestone": {"milestone": "v1.27", "tag": "v1.26.3", "status": "complete"}, "decisions": ["D-226", "D-227", "D-228", "D-229", "D-230", "D-231"], - "notes": "v1.28 CLARIFY. 6 open Qs + 5 grounding gaps resolved at full autonomy. D-226..D-231 authored (confidence >= 0.80). kj mapped to kyverno-json (D-227). Cognito-drop reframed as greenfield (G3). CAP-033..038, INV-12..17, REQ-323..353 allocated. Next: RESEARCH." + "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." } \ No newline at end of file diff --git a/.ciagent/PERSONAS.md b/.ciagent/PERSONAS.md index c75a899..eb61501 100644 --- a/.ciagent/PERSONAS.md +++ b/.ciagent/PERSONAS.md @@ -1,77 +1,119 @@ --- project: acdl -milestone: v1.27 +milestone: v1.28 generated_at: 2026-08-19 generator: lead-developer verification_toolchain: - typecheck: "python3 -m py_compile core/confidence_signal.py 2>&1 | head -5 || true" - test: "bash scripts/run_regression.sh 2>&1 | tail -10 || true" - lint: "ruff check .ciagent/STATE.md 2>/dev/null || true" + typecheck: "python3 -m py_compile core/mode_resolver.py nova/cli.py 2>&1 | head -5 || true" + test: "pytest tests/test_mode_resolver.py tests/test_cli_subcommands.py -q 2>&1 | tail -15 || true" + lint: "ruff check nova/ core/lambda/nova_idp_*.py 2>/dev/null || true" note: | - v1.27 is an NFR milestone (PO State Catalog & Ciagent Compression) — - a docs/chore milestone. Single active persona: lead-developer owns - the milestone narrative (STATE.md authoring, PROJECT/ROADMAP fixes, - archive moves, PLAN/NORTH_STAR wiring, final review + audit). No - code, no schema, no policy authoring. The pre-existing - core/confidence_signal.py LSP diagnostic is out of scope (not - touched by v1.27). Territory enforcement: warn. + v1.28 is a feature milestone (CLI Canonicalization + Identity Layer). + Four active personas: backend-engineer (Lambda/DynamoDB/KMS/CodeArtifact), + security-engineer (Argon2id/KMS/ABAC/threat model), cli-engineer + (subcommand surface/mode_resolver/argparse/CAP-034), lead-developer + (plan/review/ship/capability gate). frontend-engineer + data-engineer + deactivated (no UI, no data pipelines). The kj-binary-in-Lambda-layer + risk (D-227, RESEARCH §7) is the highest-risk item; P2 spike confirms. --- -# PERSONAS — v1.27 PO State Catalog & Ciagent Compression +# Personas — v1.28 CLI Canonicalization + Identity Layer -> Generated by the lead-developer at the end of RESEARCH. Assesses the -> project domains, activates/deactivates personas, aligns frameworks + -> territory + constraints to the actual project structure. +## Roster -## Active Roster (1) +### backend-engineer +```yaml +active: true +domain: "Lambda functions, DynamoDB, KMS integration, dual-use packaging, CodeArtifact publish, CloudFormation generation" +frameworks: ["Python 3.12", "boto3", "argparse", "pytest", "moto[dynamodb]", "CloudFormation"] +constraints: ["INV-15", "INV-16", "INV-17", "D-228", "D-229", "D-230", "NFR-5", "NFR-6", "NFR-7", "NFR-8"] +territory: + - "core/lambda/**" + - "core/metrics/**" + - "core/env.py" + - "core/outbox_writer.py" + - "terraform/bootstrap/**" + - ".gitea/workflows/publish.yml" + - ".github/workflows/publish.yml" + - ".github/actions/nova-cli/**" +``` -### 1. lead-developer (active) -- **active:** true -- **phase_specific:** false -- **reason:** Owns the full v1.27 milestone narrative: STATE.md - authoring (PO-facing capability catalog, 36 entries across 10 - domains + 11 invariants), archive moves (11 files to - `.ciagent/archive/` + 1 to consumer archive), PROJECT.md + ROADMAP.md - phase-status corrections, archive/README.md contents update, - PLAN.md + ROADMAP.md + NORTH_STAR.md ship-discipline wiring, final - review + audit. -- **domain:** `.ciagent/` docs (STATE.md, PROJECT.md, ROADMAP.md, - PLAN.md, NORTH_STAR.md, archive/README.md), consumer - `.ciagent/nova-blockchain-exchange/` (PROJECT.md pointer, - archive/ROADMAP-v1.26.md). -- **frameworks:** markdown, JSON (CHECKPOINT.json, config.json). -- **territory:** `.ciagent/`, `docs/`. -- **constraints:** no code changes (NFR milestone, D-220); no schema - changes; archive moves are lossless (byte-identical relocation, git - history preserves authoritative state); STATE.md is additive only. +### security-engineer +```yaml +active: true +domain: "Argon2id hashing, KMS asymmetric signing (ECDSA P-256 / ES256), ABAC policy, JWKS exposure, PAT lifecycle, threat model, DER→raw ECDSA conversion" +frameworks: ["argon2-cffi", "cryptography", "pyjwt", "kyverno-json", "JMESPath", "KMS Sign/Verify/GetPublicKey"] +constraints: ["INV-15", "INV-16", "INV-17", "NFR-5", "NFR-8", "NFR-9", "D-227", "D-231"] +territory: + - "platform/abac/**" + - "core/policy_engine.py" + - "adapters/kyverno-json/**" + - "core/lambda/nova_idp_auth.py" + - "core/lambda/nova_idp_token_vend.py" + - "core/lambda/nova_idp_jwks.py" + - "docs/threat-model.md" +``` -## Deactivated (5) +### cli-engineer +```yaml +active: true +domain: "CLI subcommand surface, mode_resolver, argparse, [project.scripts] entry-point, CAP-034 AST scan, nova auth/idp subgroups, property tests" +frameworks: ["Python 3.12", "argparse", "setuptools [project.scripts]", "hypothesis", "pkgutil"] +constraints: ["INV-12", "INV-13", "INV-14", "D-226", "NFR-1", "NFR-2", "NFR-3"] +territory: + - "nova/**" + - "core/mode_resolver.py" + - "pyproject.toml" + - "tests/test_mode_resolver.py" + - "tests/test_cli_subcommands.py" +``` -### backend-engineer (inactive) -- **active:** false -- **reason:** No code changes in v1.27. The pre-existing - `core/confidence_signal.py` LSP diagnostic is out of scope (not - touched by v1.27). +### lead-developer +```yaml +active: true +domain: "Phase plan, persona roster, review gates, milestone ship, capability gate (CAP-033..038), ROADMAP/STATE/PROJECT wiring" +frameworks: ["git", "Gitea Actions", "semver tagging", ".ciagent/ discipline"] +constraints: ["INV-1..17 (cross-cutting)", "v1.28 hard constraints", "NFR-6", "NFR-11"] +territory: + - ".ciagent/**" + - "PLAN.md" + - "CHECKPOINT.json" + - "STATE.md" + - "REQUIREMENTS.md" + - "ROADMAP.md" +``` -### data-engineer (inactive) -- **active:** false -- **reason:** No schema, migration, or ORM changes. +### frontend-engineer +```yaml +active: false +phase_specific: false +reason: "No UI in v1.28 (CLI + JSON endpoints only). JWKS serves application/json; no HTML/CSS/JS surface." +``` -### policy-engineer (inactive) -- **active:** false -- **reason:** No policy authoring. STATE.md Domain 3 catalogues - existing v1.25 + v1.26 policies (descriptive, not authoring). +### data-engineer +```yaml +active: false +phase_specific: false +reason: "No data pipelines / metrics / PowerBI work in v1.28. The metrics layer is v1.17-complete; v1.28 adds audit events but no new fact/dim tables." +``` -### frontend-engineer (inactive) -- **active:** false -- **reason:** No UI. Deactivated since v1.26 (PERSONAS.md:141). +## Territory overlap notes -### blockchain-engineer (inactive) -- **active:** false -- **reason:** No chain code. The v1.26 pilot is shipped; v1.27 is - platform-side docs/chore only. +- `core/lambda/contract_ingestor.py` (dual-use refactor, REQ-329) = + backend-engineer territory. `core/lambda/nova_idp_auth.py` + + `nova_idp_token_vend.py` are **co-owned** by backend-engineer (Lambda + plumbing, DynamoDB, function URLs) + security-engineer (crypto, ABAC, + Argon2id logic inside). +- `core/mode_resolver.py` = cli-engineer. `core/policy_engine.py` = + security-engineer (the ABAC evaluation path). +- `nova/idp/setup.py` = cli-engineer (the subcommand + arg parsing) + + backend-engineer (the CloudFormation generation + deploy). +- `nova/auth/*` = cli-engineer (subcommands) + security-engineer (the + token exchange + credential storage logic). -## Territory Enforcement +## Phase-specific personas -- **Mode:** `warn` (the milestone is `.ciagent/`-only; the lead- - developer owns all writes; no cross-territory collisions expected). \ No newline at end of file +None. All four active personas span the full milestone. The +security-engineer is heaviest in P2 (identity layer) + P3 (threat model); +the cli-engineer is heaviest in P1 (CLI substrate); the backend-engineer +spans P1 (CodeArtifact/layer) + P2 (Lambdas/DynamoDB). \ No newline at end of file diff --git a/.ciagent/RESEARCH.md b/.ciagent/RESEARCH.md index 8257a56..98b86e6 100644 --- a/.ciagent/RESEARCH.md +++ b/.ciagent/RESEARCH.md @@ -1,144 +1,336 @@ -# Nova — v1.27 Research Findings +# Nova — v1.28 Research Findings -> Phase: research (pre-execution). Milestone: v1.27 (PO State Catalog & -> Ciagent Compression). Status: research. Researcher: ci-researcher. +> Phase: research (pre-execution). Milestone: v1.28 (CLI Canonicalization +> + Identity Layer). Status: research. Researcher: ci-researcher. > Autonomy: full. - -v1.27 is an NFR milestone (docs/chore only, no code, no schema). There -is no new domain to research. The research is a codebase-grounded -inventory of (a) the files to archive + their staleness evidence, and -(b) the sources backing the STATE.md capability backfill. This file -records the inventory for the v1.27 record; the active authoring used -these sources directly. +> +> Research delegated to the ci-researcher subagent (full domain/ecosystem +> research with web citations). This file is the curated summary; the +> full 868-line research document is preserved in git history (the +> subagent's task output). Key findings + recommendations are below. --- -## 1. Files to archive (staleness inventory) +## §1 — Codebase Inventory (grounding) -### 1.1 Pre-execution artifacts (v1.26 — shipped, decisions folded) +### 1.1 `core/` modules (the REQ-324 subcommand surface) -| File | Lines | Staleness evidence | Decisions folded into | -|---|---|---|---| -| `CLARIFY.md` | 225 | v1.26 milestone shipped (`v1.25.5`); decisions D-200..D-213 | `PROJECT.md` load-bearing decisions | -| `GRILL.md` | 225 | v1.26 grill verdict PROCEED 0.84; binding revisions applied | `PLAN.md` revisions (G-Q4 REQ-322→P2 W0; G-Q6 enforcement deferred; G-Q9 key-split future) | -| `IDEATE.md` | 193 | all 7 accepted ideas → REQ-315..322 (shipped) | `REQUIREMENTS.md` v1.26 traceability | -| `RESEARCH.md` | 250 | v1.26 domain research (blockchain, deploy, modules, metrics) | `ARCHITECTURE.md` §12.8; shipped REQs | +19 Python files under `core/` (plus `core/lambda/`, `core/metrics/`). +Two already have `_cli.py` companions (`contract_resolver_cli.py` 40 +lines, `regression_verify_cli.py` 32 lines) — the thin-delegate +precedent for `nova/.py`. **No `nova/` dir, no `bin/`, no +`[project.scripts]` entry exists today.** The CLI is greenfield. -All four are pre-execution artifacts for a shipped milestone. The next -P0 writes fresh versions. Per D-219 (user-confirmed) + D-222: archive -all four with `-v1.26` suffixes. +### 1.2 Existing Lambda pattern (`core/lambda/contract_ingestor.py`) -### 1.2 Phase verifications + review (v1.26 — shipped, PASS) +521 lines. Function URL + IAM auth (D-051). DynamoDB via lazy +module-global `boto3.resource`. Secrets Manager for tokens. Schema +validation in-Lambda. **`__main__` block already does CLI dispatch** +(`--check-readiness` → `core.submission_readiness.cli_main`) — this is +the dual-use precedent for REQ-329. Local testing via +`core/local_emulators.py:LocalLambdaStub`. -| File | Lines | Staleness evidence | +### 1.3 `core/env.py` — getter, not synthesizer + +31 lines. `get_env(name, default)` reads `NOVA_` from `os.environ`. +**REQ-330 needs a NEW `synthesize_local_env()` function** added here. +The closest existing pattern is `core/onboarding.py:generate_env_file()`. + +### 1.4 `PolicyEngine` Protocol + `KyvernoJsonEngine` (the ABAC substrate) + +`core/policy_engine.py`: `PolicyEngine` Protocol with `evaluate(payload, +policy_dir, contract_id) -> list[dict]`. `KyvernoJsonEngine` shells to +`kj scan --policy --payload --output json`. Policy shape = +`ValidatingPolicy` (`apiVersion: json.kyverno.io/v1alpha1`) with +`spec.rules[].assert.all[].check` using JMESPath. Severity from +`metadata.annotations["nova.cloudinit.dev/severity"]`. **The payload +can be ANY JSON** — not just contracts (the v1.25 design point). This +is what makes kyverno-json usable for ABAC token vending (D-227). + +### 1.5 `pyproject.toml` state + +name `nova`, version `1.14.0`, requires-python `>=3.10` (spec wants +3.12 — bump needed for REQ-326). setuptools build backend. No +`[project.scripts]`, no `[tool.setuptools.packages.find]` — both needed. +Deps: `boto3`, `jsonschema`, `pyyaml`. No `argon2-cffi`, `cryptography`, +`pyjwt`, `click`/`typer` — **argparse-only** is the repo convention. + +### 1.6 Forge conventions + +`.github/workflows/` + `.gitea/workflows/` kept byte-identical. Python +3.12 already pinned via `actions/setup-python@v5`. No composite action +exists yet — `nova cli-action` (REQ-326) is greenfield. + +### 1.7 IAM baseline (load-bearing for REQ-340) + +`.ciagent/IAM_POLICY.md` + `terraform/bootstrap/spike_runner_policy.json`. +The `nova-spike-runner` principal already has KMS (incl. `CreateKey`, +`Sign`, `GetPublicKey`), Lambda (incl. `PublishLayerVersion`), DynamoDB +grants. **New grants needed:** `cloudformation:*` (for `nova idp setup +--apply`) + `codeartifact:*` (for the wheel publish pipeline). Flagged +for P1/P2. + +--- + +## §2 — CodeArtifact + Lambda Layer Pipeline (REQ-323) + +**Recommendation:** single CI job on merge to `main` affecting +`core/**`/`adapters/**`/`nova/**`/`pyproject.toml`. Build wheel +(`python -m build --wheel`) → `twine upload` to CodeArtifact → build +layer (`pip install --target layer/python/ dist/nova-*.whl argon2-cffi +cryptography pyjwt`) → `aws lambda publish-layer-version` → record +version mapping in SSM `/nova/layer/nova-cli/version` (CAP-035). If +either publish fails, the job fails (merge blocked, REQ-323 AC). + +**Atomicity:** wheel publish is idempotent (pin version to +`+`); layer publish retries on failure. CAP-035 reads the +SSM parameter to verify layer-version ↔ wheel-version match. + +**Risks:** CodeArtifact not yet provisioned in `581513795199` (CLARIFY +assumption #1); `codeartifact:*` grant missing. Fallback: Gitea-hosted +wheel index. Layer `--compatible-architectures`: build x86_64 only for +v1.28 (aarch64 only if Graviton Lambda needed). + +--- + +## §3 — CLI Subcommand Architecture (REQ-324) + +**Recommendation:** three-layer. `nova/__init__.py` (marker) → +`nova/cli.py` (~80 lines, auto-discovers `nova/.py` via +`pkgutil.iter_modules`, dispatches, emits `cli.invocation` audit event) +→ `nova/.py` (≤50 lines each, exports `add_parser(subparsers)` ++ `run(args) -> int`, delegates to `core/`). Entry point: +`[project.scripts] nova = "nova.cli:main"`. **argparse-only** (no +click/typer — repo convention). + +**CAP-034 AST scan:** ≤50 lines; ≤3 function defs; every `ast.Call` +resolves to a `core.` import; no conditionals beyond `if __name__`. + +**Subcommand groups:** `nova auth`, `nova idp`, `nova metrics` = +nested subparsers (same pattern, one level deeper). + +**setuptools:** add `[tool.setuptools.packages.find]` including `nova`, +`nova.*`, `core`, `core.*`, `adapters.*`. + +--- + +## §4 — Argon2id in Lambda Python 3.12 (REQ-334, D-228) + +**Findings:** `argon2-cffi-bindings` v25.1.0 ships `cp39-abi3` +manylinux x86_64 + aarch64 wheels — **ABI-stable, compatible with +Python 3.9..3.13**. Lambda Python 3.12 runs Amazon Linux 2023 (glibc +2.34 ≥ 2.28 required). **The abi3 manylinux wheel loads cleanly.** +Confidence: 0.92. + +**D-228 AMENDMENT:** the "pure-Python fallback" clause is **weaker than +stated** — there is no maintained pure-Python Argon2 implementation. A +pure-Python crypto fallback is a **liability** (weaker hashing, +violates INV-16's spirit). Revised recommendation: +1. **Primary:** bundled manylinux abi3 wheel in the `nova-cli` Lambda + layer. Works. Confidence 0.92. +2. **Fallback:** detect `ImportError` at Lambda cold-start → **fail + closed** (503, refuse sign-ups). The Lambda health check reports + C-extension status. **Do NOT ship a pure-Python fallback.** +3. **Escape hatch:** Fargate (~1 week, per CLARIFY Q1). + +Lambda memory ≥ 512 MB (Argon2id memory_cost ~20 MB + overhead). + +--- + +## §5 — KMS Asymmetric Signing for OIDC Tokens (REQ-337) + +**Recommendation: key spec = `ECC_NIST_P256`, alg = `ECDSA_SHA_256` +(JWS `ES256`).** RSA-2048 is larger + slower; P-256 is RFC 7518's +recommended JWT alg. Signature size 64 bytes (vs RSA 256). JWKS +compactness matters (fetched often). + +**The #1 gotcha:** KMS returns DER-encoded ECDSA signatures; **JWS +requires raw r‖s concatenation** (RFC 7515 §3.1.3). The token-vend +Lambda converts via `cryptography.hazmat.primitives.asymmetric.utils. +decode_dss_signature` → `r.to_bytes(32) + s.to_bytes(32)`. ~5 lines. +Flagged for the threat model (REQ-347) + KMS round-trip test (REQ-350). + +**Flow:** validate PAT → ABAC eval → build JWT header/payload → +`kms.sign(Message=signing_input, MessageType="RAW", SigningAlgorithm= +"ECDSA_SHA_256")` → DER→raw → JWT. `kid` = KMS key alias. + +**Verification:** use `pyjwt` (`jwt.decode` handles JWK→key natively); +`cryptography` only for SPKI→JWK in the JWKS Lambda. + +**Rotation:** manual, 90 days (matches D-069 CMK cadence). New key + +re-point alias + JWKS serves both `kid`s during overlap. + +--- + +## §6 — JWKS Endpoint (REQ-338, D-230) + +**D-230 confirmed.** Lambda function URL (`AuthType: NONE` — JWKS is +public-key only) + reserved concurrency 10 (max 100 RPS, JWKS is +cached client-side). `Cache-Control: max-age=3600`. Separate tiny +`nova-idp-jwks` Lambda (separation of concerns). + +**Custom domain + WAF = OPTIONAL** via `--public-jwks-domain ` +flag on `nova idp setup`. Without it, raw function URL (acceptable for +v1.28 pilot). With it: CloudFront + ACM + WAF rate-based rule (>100 +req/5min per IP) + Route53 ALIAS. Adds ~8 CloudFormation resources. + +**Defer API Gateway** (D-230) — $3.50/M + complexity for no benefit at +v1.28 volume. + +--- + +## §7 — kyverno-json ABAC Policy (REQ-339, D-227) + +**D-227 confirmed.** Policy at `platform/abac/token-vend.policy` = +`ValidatingPolicy` with JMESPath checks against a payload of +`{subject, requested_claims, target_resource, environment, pat_jti, +policy_version}`. Decision logic: any `fail` PCR with severity +`critical` → deny (403 + audit); all pass → allow → KMS sign. + +**`policy_version` (D-231):** git SHA of the policy file, baked into +the Lambda layer, recorded in every `token.vend.allowed/denied` audit +event. + +**BIGGEST PACKAGING RISK:** the token-vend Lambda needs the `kj` Go +binary (~40 MB) on PATH. Bundle it in the `nova-cli` Lambda layer +(`wget` the Linux amd64 release into `layer/bin/kj`). `KyvernoJsonEngine +.is_configured()` checks `which kj` → `/opt/bin/kj` (layer mount). P2 +spike confirms it runs in AL2023 Lambda. Fallback: Fargate. Confidence +0.75 — needs the spike. + +--- + +## §8 — PAT Lifecycle (REQ-342, REQ-343, REQ-344) + +**PAT = signed JWT** (KMS-signed, `typ: "developer_pat"` distinguishes +from `nova_oidc_token` per INV-14). Claims: `iss, sub, typ, jti, iat, +exp, roles, owner`. + +**`nova-pats` DynamoDB table** (4th table): PK=`jti`, GSI1=`sub` (list +PATs for user), GSI2=`pat_hash` (lookup by hash). Only the hash stored +(not raw PAT). Revoked PATs retained for audit. + +**Revocation (D-229 CLARIFIED):** GSIs don't support strongly-consistent +reads. The token-vend Lambda extracts `jti` from the PAT JWT (decode +without verifying — signature verified separately) → +`GetItem(PK=jti, ConsistentRead=True)` on the main table. Satisfies the +60s SLO. Confidence 0.90. + +**CLI:** `nova auth login` (session→OIDC token, store locally), +`nova auth revoke --pat `, `nova auth status` (active credential, +mode, selection_reason). Local file `~/.nova/credentials.json` (0600, +never to stdout, in `.gitignore`). "Most recent wins" (D-226 Q5) = +`active_credential_jti` field. + +--- + +## §9 — `nova idp setup` CloudFormation (REQ-340, REQ-341) + +**Template (raw dict → JSON, no troposphere dep):** 2-3 Lambdas, 4 +DynamoDB tables (`nova-users`, `nova-sessions`, `nova-password-resets`, +`nova-pats`), KMS key `alias/nova-oidc-signing` (ECC_NIST_P256), +function URLs, IAM roles, optional CloudFront/WAF/ACM. + +**`--check`:** validates prerequisites (AWS creds, CFN perms, KMS perms, +layer exists via CAP-035). Prints required IAM policy delta. +**`--apply`:** generate → print to temp file + resource summary → +`$PAGER` → `Apply? [y/N]` → `cloudformation deploy --capabilities +CAPABILITY_IAM`. NFR-10 satisfied by the explicit prompt. +**`--dry-run`:** resource list only, no write. +**`--verify`:** runs the KMS round-trip test (REQ-350). + +**New IAM grants needed:** `cloudformation:*`, `iam:CreateRole`/`PassRole`, +`lambda:CreateFunction`/`CreateFunctionUrlConfig`, +`dynamodb:CreateTable`, `kms:CreateKey`/`CreateAlias`, `ssm:PutParameter`. + +--- + +## §10 — GitHub + Gitea Marketplace Composite Action (REQ-326) + +**Single `action.yml`** at `.github/actions/nova-cli/action.yml`, +referenced by both GitHub + Gitea via `uses: continuous-intelligence/ +acdl/.github/actions/nova-cli@v1.28`. Composite action: `setup-python@v5` +(python 3.12) → CodeArtifact login + `pip install nova` → `nova +${{ inputs.command }}`. `NOVA_CLIENT_MODE` env from input. + +**Byte-identical test (REQ-326 AC2):** CI matrix runs the action on +GitHub `ubuntu-latest` + Gitea `act_runner` with same inputs; assert +same stdout/exit code. + +**Risk:** Gitea `actions/checkout`/`setup-python` may need Gitea +mirrors (`https://gitea.com/actions/...`). P1 test on the actual Gitea +instance. Confidence 0.70. + +--- + +## §11 — `mode_resolver` Priority (REQ-327, D-226) + +**TTY detection: check `sys.stdin.isatty()`** (NOT stdout). Edge 3 +(`nova apply | tee log.txt`): stdout piped, stdin is TTY → user is +present → `interactive` (correct). `sys.stdout.isatty()` would +misresolve to `agent`. **`stdin` answers "is a human at a terminal?"** + +**Credential type detection:** read `~/.nova/credentials.json` → +`active_credential_jti`'s `type` (`developer_pat`/`nova_oidc_token`). +Both + TTY → `interactive`; + no TTY → `agent` (INV-14). + +**Property tests (REQ-349):** `hypothesis` with strategies for +flag/env/cred/tty. Properties: deterministic (INV-13), flag-wins, +invalid-env-ignored, no-silent-fallback (every resolution has a +non-empty `selection_reason`). + +**`mode_resolver.py` lives in `core/`** (not `nova/`) so Lambdas could +import it, but **it's CLI-only** — the token-vend Lambda doesn't resolve +modes. + +--- + +## §12 — Persona Assessment + +See `.ciagent/PERSONAS.md` for the full YAML roster. Summary: +- **Deactivate** frontend-engineer (no UI) + data-engineer (no data + pipelines in v1.28). +- **Activate** backend-engineer (Lambda/DynamoDB/KMS/CodeArtifact) + + lead-developer (plan/review/ship). +- **Add** security-engineer (Argon2id/KMS/ABAC/threat model) + + cli-engineer (subcommand surface/mode_resolver/argparse/CAP-034). + +--- + +## §13 — Architecture Sketch (ARCHITECTURE.md §12.10) + +See `.ciagent/ARCHITECTURE.md` §12.10 (appended this stage). New +greenfield files: `nova/` CLI package, `platform/abac/token-vend.policy`, +`core/mode_resolver.py`, `core/env.py:+synthesize_local_env()`, +`core/lambda/nova_idp_{auth,token_vend,jwks}.py`, `tests/test_*`, +`docs/{operator-guide-idp,developer-guide-auth,threat-model}.md`. + +--- + +## Decisions re-validated / amended + +| Decision | Status | Change | |---|---|---| -| `VERIFY-P03.md` | 39 | v1.26 P3 verification — PASS; shipped `v1.25.3` | -| `VERIFY-P04.md` | 31 | v1.26 P4 verification — PASS; shipped `v1.25.4` | -| `REVIEW-AUDIT-P05.md` | 218 | v1.26 P5 final review + audit — PROCEED; shipped `v1.25.5`; 0 P0 remain; audit CLEAN | -| `P4-PILOT-RUN-EVIDENCE.md` | 46 | v1.26 live apply evidence (`blkex-pilot-apply-v0.2`); summarized in `nova-blockchain-exchange/README.md` §5 + REVIEW-AUDIT-P05 §2.2 | +| D-226 | re-validated + refined | `sys.stdin.isatty()` is the TTY check (not stdout) | +| D-227 | re-validated | `kj` Go binary bundled in Lambda layer — packaging risk flagged | +| D-228 | **amended** | Pure-Python fallback → fail-closed + Fargate (pure-Python crypto is a liability) | +| D-229 | re-validated + clarified | Strong read on main table PK (`jti`), not GSI (GSIs don't support strong reads) | +| D-230 | re-validated | CloudFront/WAF/ACM made optional via `--public-jwks-domain` flag | +| D-231 | re-validated | `policy_version` (git SHA) in the ABAC payload | -### 1.3 Durable references (superseded or stale) - -| File | Lines | Staleness evidence | Superseded by | -|---|---|---|---| -| `CAPABILITY_INVENTORY.md` | 120 | dated 2026-07-27; framed as "v1.1→v1.8 re-verification sweep"; predates v1.26 pilot (CAP-025 absent; blockchain capabilities absent) | `STATE.md` (this milestone) | -| `AUTONOMY_THESIS.md` | 65 | "Last refined: v1.21"; thesis fully folded into `NORTH_STAR.md` Vision (lines 17–22) + Anti-Goals #2 | `NORTH_STAR.md` | -| `COST.md` | 106 | dated 2026-07-29; framed "v1.0 → v1.14"; predates v1.26 live pilot (ECS + ALB + DynamoDB + S3 costs not reflected) | A future cost milestone writes a fresh report; `STATE.md` Domain 7 notes cost tracking as a capability | - -### 1.4 Consumer-side (nova-blockchain-exchange) - -| File | Lines | Staleness evidence | -|---|---|---| -| `nova-blockchain-exchange/ROADMAP.md` | 57 | marks P3/P4/P5 as "planned" but v1.26 shipped (`v1.25.5`); phase narrative preserved in platform `ROADMAP.md` v1.26 section | - -Per D-221: consumer archives land in -`.ciagent/nova-blockchain-exchange/archive/ROADMAP-v1.26.md`. +**New recommendations for PLAN/GRILL to formalize (no D-ID yet):** +- KMS key spec = `ECC_NIST_P256`, alg `ES256`; DER→raw ECDSA conversion required. +- `nova-cli` Lambda layer bundles the `kj` Go binary (~40 MB). +- `nova-pats` = 4th DynamoDB table; PK=`jti`, GSI1=`sub`, GSI2=`pat_hash`. +- `sys.stdin.isatty()` is the TTY heuristic. +- `[project.scripts] nova = "nova.cli:main"`; argparse-only. +- `cloudformation:*` + `codeartifact:*` = new IAM baseline grants (P1/P2). --- -## 2. Files to keep active (no-edit or fix-only) +## RESEARCH complete -### 2.1 No-edit (live code paths or durable) - -| File | Why keep active | -|---|---| -| `CHECKPOINT.json` | Authoritative resume state — never archive | -| `config.json` | Operational config — never archive | -| `REGRESSION_REPORT.json` | Written by `core/regression_verify.py:705`; read by `core/metrics/collector.py:27` + `trust_snapshot.py:21` + metrics views (D-224: regenerates on next `run_regression.sh`) | -| `REGRESSION_REPORT.md` | Written by `core/regression_verify.py:704`; read by `scripts/run_regression.sh` (D-224) | -| `PERSONAS.md` | Regenerated at each milestone P0 by the lead-developer; not stale until then | -| `IAM_POLICY.md` | Live baseline, test-enforced (`tests/test_iam_policy_baseline.py`); D-207 future key-split pending (D-223) | -| `PLAN.md` | Active phase plan; reset to next milestone at next P0 | -| `ARCHITECTURE.md` | Durable target architecture (§1–§12 + §12.7 + §12.8 + §12.9) | -| `NORTH_STAR.md` | PO strategy; loaded every ci-run via `config.strategic_direction_file` | -| `nova-blockchain-exchange/PROJECT.md` | Consumer project charter; D-200..D-205 load-bearing | -| `nova-blockchain-exchange/REQUIREMENTS.md` | REQ-310..322 spec intent (shipped but spec stays for reference) | -| `nova-blockchain-exchange/README.md` | Consumer onboarding guide; still accurate (deploy workflow, secrets, contract shape, verification) | - -### 2.2 Fix-only (corrections to stale-but-kept files) - -| File | Fix | -|---|---| -| `PROJECT.md` | v1.26 phase-status block (lines 424–431): P3/P4/P5 "pending" → "complete" with shipped tags `v1.25.3/4/5`; add STATE.md pointer (D-225: P2 phase) | -| `ROADMAP.md` | v1.26 P3/P4/P5 sections (lines 238, 261, 272) "planned" → "complete" with shipped tags; v1.26 Overview line "active" → "complete"; add STATE.md to P5 ship-update list (D-225: P2 phase) | -| `archive/README.md` | Add the 11 new archived files to the contents tables (P2 phase) | - ---- - -## 3. STATE.md capability backfill sources - -The STATE.md backfill (36 capabilities across 10 domains) was sourced -from: - -| Source | Used for | -|---|---| -| `core/regression_verify.py` (lines 129–768) | CAP-001..CAP-025 IDs, names, tiers, evidence pointers | -| `.ciagent/CAPABILITY_INVENTORY.md` (pre-archive) | CAP-001..022 descriptions, defect notes, evidence | -| `modules/registry.json` | L1/L2 module catalog (13 L1 + 2 L2 entries) | -| `.ciagent/REQUIREMENTS.md` v1.25 traceability | REQ-291..309 → policy-engine capabilities | -| `.ciagent/nova-blockchain-exchange/REQUIREMENTS.md` + `.ciagent/REQUIREMENTS.md` v1.26 traceability | REQ-310..322 → pilot capabilities | -| `.ciagent/CHECKPOINT.json` | shipped tags `v1.25.0..v1.25.5` | -| `git log --all --oneline` | file paths for v1.26 shipped features | -| `.ciagent/PROJECT.md` load-bearing decisions | INV-1..INV-11 invariants | -| `docs/submission-readiness.md` + `schemas/contract.schema.json` | INV-1 contract surface | - ---- - -## 4. Persona assessment - -v1.27 is a docs/chore milestone. The active roster: - -- **lead-developer** (active): owns the milestone narrative (STATE.md - authoring, PROJECT/ROADMAP fixes, archive README, PLAN/NORTH_STAR - wiring, this RESEARCH, CLARIFY, PLAN, final review + audit). Territory: - `.ciagent/`, `docs/`. -- **backend-engineer** (active, limited): no code changes in v1.27. - Consulted on the `core/confidence_signal.py` LSP diagnostic (pre- - existing, not touched by v1.27). No territory writes. -- **data-engineer** (inactive): no schema/migration/ORM changes. -- **policy-engineer** (inactive): no policy authoring. -- **frontend-engineer** (inactive): no UI. -- **blockchain-engineer** (inactive): no chain code. - -Territory enforcement: warn. The milestone is `.ciagent/`-only; the -lead-developer owns all writes. - ---- - -## 5. Risk analysis - -| Risk | Likelihood | Impact | Mitigation | -|---|---|---|---| -| Archive move breaks a relative path reference in an active file | Low | Medium | `grep` for the archived filenames across `.ciagent/` + `docs/` before commit; fix any dangling references in P2 | -| STATE.md capability row is inaccurate (wrong shipped tag / wrong file path) | Medium | Low | The backfill sources are the authoritative registries (regression_verify.py, registry.json, CHECKPOINT.json, git log); citations are direct | -| PROJECT.md phase-status fix conflicts with a future v1.26-era commit | Low | Low | v1.26 is shipped (main has the milestone merge); no v1.26-era commits will arrive | -| REGRESSION_REPORT stale state is mistaken for v1.27 scope | Low | Low | D-224 records the decision; STATE.md Domain 7 notes the current CAP range | - ---- - -## 6. Verdict - -v1.27 is feasible, scoped, and the sources are grounded. No new domain, -no new code, no schema breaks. The archive moves are lossless (git -history + archive directory both preserve bytes). The STATE.md -backfill is sourced from authoritative registries. Proceed to PLAN. \ No newline at end of file +All 11 research questions answered with cited findings + concrete +recommendations + risks. D-228 amended (fail-closed, not pure-Python +fallback). The `kj` binary packaging is the highest-risk item (P2 +spike). Next: PLAN. \ No newline at end of file From e1dc59ba7951e09093a4a75e257d3021624dcdc9 Mon Sep 17 00:00:00 2001 From: Jon Chery Date: Wed, 19 Aug 2026 22:06:38 +0000 Subject: [PATCH 4/7] =?UTF-8?q?docs(P00):=20create=20phase=20plans=20?= =?UTF-8?q?=E2=80=94=20v1.28=20(7=20phases,=2031=20REQs,=206=20CAPs,=20MVP?= =?UTF-8?q?/UX=20sections)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ---ci--- project: acdl phase: 0 milestone: v1.28 status: plan ---/ci--- --- .ciagent/CHECKPOINT.json | 16 +- .ciagent/PLAN.md | 568 ++++++++++++++++++++++++--------------- 2 files changed, 363 insertions(+), 221 deletions(-) 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 From bbfcbcc4d3d4d4e66576fbdc286f70e8f68aba42 Mon Sep 17 00:00:00 2001 From: Jon Chery Date: Wed, 19 Aug 2026 22:10:15 +0000 Subject: [PATCH 5/7] =?UTF-8?q?docs(P00):=20grill=20=E2=80=94=20v1.28=20ad?= =?UTF-8?q?versarial=20review=20(PROCEED=200.76,=203=20critical=20+=2016?= =?UTF-8?q?=20tracked=20conditions=20applied)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ---ci--- project: acdl phase: 0 milestone: v1.28 status: grill ---/ci--- --- .ciagent/CHECKPOINT.json | 18 ++-- .ciagent/GRILL.md | 227 +++++++++++++++++---------------------- .ciagent/PLAN.md | 183 +++++++++++++++++++++++-------- .ciagent/REQUIREMENTS.md | 49 +++++---- 4 files changed, 269 insertions(+), 208 deletions(-) diff --git a/.ciagent/CHECKPOINT.json b/.ciagent/CHECKPOINT.json index 68dd6c6..e30a353 100644 --- a/.ciagent/CHECKPOINT.json +++ b/.ciagent/CHECKPOINT.json @@ -1,10 +1,10 @@ { "phase": 0, - "stage": "plan", + "stage": "grill", "milestone": "v1.28", "phase_role": "pre_execution", "attempts": 0, - "updated_at": "2026-08-19T20:40:00Z", + "updated_at": "2026-08-19T20:55:00Z", "project": "acdl", "projects": ["acdl", "nova-blockchain-exchange"], "active_milestone": "v1.28", @@ -14,15 +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"], - "phases_planned": 7, + "phases_planned": 6, "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": 2, "name": "lambda-packaging", "reqs": ["REQ-329..332"], "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"} + {"phase": 4, "name": "token-vend-pat", "reqs": ["REQ-336..344", "REQ-340..341"], "caps": ["CAP-037", "CAP-038"], "tag": "v1.27.4"}, + {"phase": 5, "name": "docs-integration", "reqs": ["REQ-345..351"], "tag": "v1.27.5"}, + {"phase": 6, "name": "final-review-ship", "reqs": ["REQ-352..353"], "tag": "v1.27.6"} ], - "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." + "grill": {"verdict": "PROCEED-WITH-CONDITIONS", "confidence": 0.76, "critical_conditions": 3, "tracked_conditions": 16, "escalations": 0}, + "notes": "v1.28 GRILL complete. 9 axes reviewed; all PROCEED-WITH-CONDITIONS (>=0.70). 3 critical fixes applied (ABAC fail-closed C-6.1, JWS KDF C-5.2, traceability drift C-9.1) + 16 tracked conditions. P5 folded into P4 Wave 8 (C-2.1) -> 6 execution phases. Cost envelope ~$9/mo. Next: MVP/UX CHECK -> SHIP." } \ No newline at end of file diff --git a/.ciagent/GRILL.md b/.ciagent/GRILL.md index cf099d4..e92669d 100644 --- a/.ciagent/GRILL.md +++ b/.ciagent/GRILL.md @@ -1,141 +1,110 @@ -# GRILL — v1.27 PO State Catalog & Ciagent Compression +# GRILL — v1.28 CLI Canonicalization + Identity Layer -> Adversarial review of the v1.27 SPECIFY + CLARIFY + RESEARCH + PLAN. -> The grill red-teams the proposal across feasibility, scope, and the -> compression-loss claims. Each challenge gets a binding verdict -> (PROCEED / REVISE / ESCALATE). Autonomy: full. - -## Verdict: PROCEED (0.88) — 0 escalations, 1 revision - -The milestone is feasible, scoped, and the compression is lossless. One -binding revision (G-Q2) refines the archive list; already captured in -PLAN. No work is blocked. +> Adversarial review of the v1.28 SPECIFY + CLARIFY + RESEARCH + PLAN. +> Griller: ci-griller subagent. Autonomy: full. All 9 axes reviewed; +> every claim verified against the live codebase. --- -## Challenges +## Overall verdict: **PROCEED-WITH-CONDITIONS** · Confidence 0.76 -### G-Q1 — Is archiving AUTONOMY_THESIS.md + COST.md a context loss? +The plan is fundamentally sound — architecture correct, re-mapping +clean (no ID collisions), technical depth accurate (DER→raw, strong- +read revocation, stdin TTY), highest-risk item (kj binary) has a +Fargate fallback. Not unfeasible, not over-scoped beyond an agent-driven +repo's capacity, not security-broken by design. -**Challenge:** `AUTONOMY_THESIS.md` is the "autonomy in operations; -human at stage gates" thesis — the defensibility brief. `COST.md` is -the only AWS cost record. Archiving both moves them out of active -context. Does this lose load-bearing content? - -**Verdict:** PROCEED (confidence 0.90). -- `AUTONOMY_THESIS.md` (65 lines, "Last refined: v1.21") is fully - folded into `NORTH_STAR.md` Vision (lines 17–22: "infrastructure - operations become visible... human attestation remains required at - stage gates") + Anti-Goals #2 ("Not a system that removes humans from - accountability"). The thesis is the source; NORTH_STAR is the - authoritative durable copy. Archive preserves the v1.21 refinement; - active context reads NORTH_STAR. -- `COST.md` (106 lines, dated 2026-07-29, "v1.0 → v1.14") predates the - v1.26 live pilot. The v1.26 live apply (ECS + ALB + DynamoDB + S3) - incurred real costs this snapshot doesn't reflect. Archiving it is - honest — a stale cost record misleads. STATE.md Domain 7 notes cost - tracking as a capability (pre-apply Infracost grounded; actual-spend - CUR deferred D-096). A future cost milestone writes a fresh report. - No revision needed. - -### G-Q2 — Does the archive list include the v1.27 P0 pre-execution files by mistake? - -**Challenge:** D-219 (user-confirmed) says "archive all 4 pre-execution -artifacts" (CLARIFY/GRILL/IDEATE/RESEARCH). But P0 already overwrote -them with v1.27 content. Archiving the v1.27 versions at v1.27 P1 would -lose the v1.27 pre-execution narrative (the decisions D-214..D-225, the -research inventory, this grill). Is the archive list wrong? - -**Verdict:** REVISE (confidence 0.92). This is a real ambiguity in the -plan. The user's D-219 decision was made *before* P0 overwrote the -files; the intent was to archive the *v1.26* pre-execution record. The -v1.26-era content is preserved in git history (the pre-P0 commits) — -the archive directory is not the only preservation layer. PLAN Task 2.1 -already self-corrected: the final archive list is **7 platform files + -1 consumer file = 8 files**, excluding the 4 pre-execution files. The 4 -v1.27 P0 versions stay active through v1.27; they archive at v1.28 P1 -if v1.28 happens. The archive README notes the v1.26 pre-execution -record is in git history. No further revision needed — the plan self- -corrected. - -### G-Q3 — Is the STATE.md backfill accurate enough to be the PO's source of truth? - -**Challenge:** STATE.md has 36 capability rows across 10 domains, -backfilled from 8 sources. The PO will read this before writing new -REQs. If a row is inaccurate (wrong shipped tag, wrong file path, -wrong controlling REQ), the PO could re-spec an existing capability or -cite a stale invariant. Is the backfill accurate? - -**Verdict:** PROCEED (confidence 0.85). The backfill sources are -authoritative: `core/regression_verify.py` (the machine CAP-NNN -registry), `modules/registry.json` (the live module catalog), -`REQUIREMENTS.md` traceability (the REQ→phase→status record), -`CHECKPOINT.json` (shipped tags), `git log` (file paths). The -citations are direct (each row cites the controlling REQ + decision -ID). The 11 invariants are distilled from PROJECT.md load-bearing -decisions D-034..D-072 + W1..BA + Q1.3. The accuracy risk is -mitigated by P1 Wave 1 (verify STATE.md against sources before -archive). No revision needed — the verification step is in the plan. - -### G-Q4 — Does the NFR purity gate (zero `feat:` commits) hold for v1.27? - -**Challenge:** v1.27 authors STATE.md (a new file). Is authoring a new -catalog file a `feat:` (feature) that breaks the NFR purity gate? - -**Verdict:** PROCEED (confidence 0.92). D-220 (CLARIFY) resolved this: -STATE.md is documentation (a catalog of *existing* capabilities), not a -new platform capability. The archive moves are `chore:` (file -relocation, lossless). No code, no schema, no platform behavior -change. The NFR purity gate (zero `feat:` commits) holds. All v1.27 -commits use `docs(P0N):` or `chore(P01):` prefixes. No revision -needed. - -### G-Q5 — Does fixing PROJECT.md phase-status in P2 create a P0/P1 audit inconsistency? - -**Challenge:** The PROJECT.md phase-status block shows P3/P4/P5 as -"pending" (the bug flagged in the prior conversation). P0 + P1 ship -with the bug still present (the fix is P2). Does the P0/P1 audit see -the inconsistency? - -**Verdict:** PROCEED (confidence 0.86). The bug is pre-existing -(it predates v1.27; it was the trigger for the prior conversation). -P0/P1 audits check the *v1.27* commits against the `.ciagent/` state, -not the pre-existing PROJECT.md drift. The P2 fix is the correction; -the P3 audit verifies the fix landed. The intermediate state (P0/P1 -with the bug present) is honest — the bug is documented in the v1.27 -PLAN + the prior conversation, and the fix is scheduled. No revision -needed — the phasing is intentional (D-225: P1 additive, P2 -correction). - -### G-Q6 — Is the milestone scoped too small (3 phases, 8 archive moves)? - -**Challenge:** v1.27 is a small milestone (3 phases, ~15 file -operations, no code). Is it worth a milestone, or should it be a -patch on v1.26? - -**Verdict:** PROCEED (confidence 0.88). v1.27 is not a patch on v1.26 -— v1.26 is shipped (`v1.25.5`, merged to main, milestone complete). -The work is a new milestone by definition. The size is appropriate: -STATE.md is a durable PO-facing artifact (loaded every ci-run going -forward); the compression reduces active context by ~26%; the -ship-discipline wiring affects every future milestone ship. Small but -high-leverage. No revision needed. +**3 critical conditions (must-fix before P1) + 16 tracked conditions.** +No escalations (all axes ≥ 0.70 confidence). --- -## Summary +## Axis verdicts -6 challenges; 0 escalations; 1 binding revision (G-Q2, already -captured in PLAN Task 2.1). Overall verdict: PROCEED (confidence -0.88). +| Axis | Verdict | Confidence | Critical condition | +|------|---------|-----------|-------------------| +| §1 Feasibility | PROCEED-WITH-CONDITIONS | 0.82 | C-1.1 KMS asym verify; C-1.2 Argon2 fail-closed test | +| §2 Scope | PROCEED-WITH-CONDITIONS | 0.74 | C-2.1 fold P5 into P4; C-2.2 P4 overload | +| §3 Cost | PROCEED-WITH-CONDITIONS | 0.70 | C-3.1 cost estimate; C-3.2 CodeArtifact P1 task | +| §4 Schedule | PROCEED-WITH-CONDITIONS | 0.76 | C-4.1 P4 critical path; C-4.2 per-phase exit | +| §5 Technical Depth | PROCEED-WITH-CONDITIONS | 0.80 | C-5.1 ABAC shape; **C-5.2 JWS KDF** | +| §6 Operational Readiness | PROCEED-WITH-CONDITIONS | 0.72 | **C-6.1 ABAC fail-closed**; C-6.2 threat model; C-6.3 ops guide | +| §7 Security Posture | PROCEED-WITH-CONDITIONS | 0.73 | **C-7.1 ABAC fail-closed**; C-7.2 Argon2 params; C-7.3 cred file | +| §8 Dependency Risk | PROCEED-WITH-CONDITIONS | 0.83 | C-8.1 CodeArtifact P1; C-8.2 pin kj version | +| §9 Re-mapping Integrity | PROCEED-WITH-CONDITIONS | 0.84 | **C-9.1 traceability fix**; C-9.2 INV audit | -**Binding revisions:** -- **G-Q2:** Archive list refined to 7 platform + 1 consumer = 8 files. - The 4 pre-execution files (CLARIFY/GRILL/IDEATE/RESEARCH) stay active - through v1.27 (they hold the v1.27 P0 content); the v1.26-era content - is in git history. Already in PLAN. +--- -**No work is blocked.** The milestone is feasible, scoped, the -compression is lossless (archive + git history), the STATE.md backfill -is source-grounded with a verification step, the NFR purity holds, and -the phasing (P1 additive, P2 correction, P3 ship) is sound. \ No newline at end of file +## Critical conditions (the 3 must-fix-before-P1) + +### 🔴 C-6.1 / C-7.1 — ABAC fail-closed +The token-vend Lambda's behavior on `kj` absence/error is unspecified. +Without fail-closed, INV-17 is documentation, not a runtime guarantee — +a `kj` load failure would bypass the ABAC gate (every PAT gets a token). +**Fix applied to PLAN.md P4 Wave 4 Task 4.1:** "If +`KyvernoJsonEngine.is_configured()` returns false or `evaluate()` +raises, return 403 + audit `token.vend.denied` (reason: +`abac_eval_failed`). Never fail open. Test: `tests/test_abac_fail_closed.py`." + +### 🔴 C-5.2 — JWS-from-PAT key derivation +REQ-332's AC ("public key derivable from the PAT") is unimplementable +without a specified KDF. A PAT is a JWT, not a keypair. +**Fix applied to PLAN.md P2 Wave 2 Task 2.3 + REQ-332 AC:** the JWS +uses HMAC-SHA256 with a key derived via +`HKDF-SHA256(PAT_bytes, salt='nova-local-attestation', info='jws-signing-key')` +→ 32-byte symmetric key. The "public key derivable" AC is re-interpreted: +the *verification key* is derived from the PAT via the same KDF (the +PAT is the shared secret). This is a symmetric scheme, not asymmetric. + +### 🔴 C-9.1 — Traceability drift +REQUIREMENTS.md §v1.28 traceability table mapped 16 REQs to P2; +PLAN.md splits them across P2/P3/P4/P5/P6. **Fix applied to +REQUIREMENTS.md** — traceability table updated to match PLAN.md phase +structure. + +--- + +## Tracked conditions (16 — applied to PLAN.md as amendments) + +- **C-1.1** KMS asymmetric key verification before P4 Wave 3 (one + `aws kms create-key --key-spec ECC_NIST_P256` call). +- **C-1.2** Argon2 fail-closed test in P3 Wave 2 (Lambda returns 503 + on `ImportError`, not a crash or pure-Python hash). +- **C-2.1** Fold P5 (idp-setup) into P4 as P4 Wave 8 → **reduces to 6 + execution phases** (P1..P6, P7 = final). Applied. +- **C-2.2** P4 is a double-length phase; acknowledged in P4 header. +- **C-3.1** Cost envelope subsection added to PLAN.md. +- **C-3.2 / C-8.1** CodeArtifact provisioning = P1 Wave 0 task with + binary go/no-go gate; Gitea wheel index fallback documented. +- **C-4.1** P4 flagged as critical-path phase (kj spike = highest- + probability schedule slip; Fargate = +1 week). +- **C-4.2** Per-phase exit criteria added to PLAN.md. +- **C-5.1** `requested_claims` = list of claim names (the policy + asserts the subject is *allowed* to request those claims). +- **C-6.2** Threat model (REQ-347) adds: JWKS DDoS surface, PAT theft + + max TTL (≤24h dev, ≤1h service-account), ABAC fail-closed, + INV-18..21 compression audit. +- **C-6.3** Operator guide (REQ-345) adds: KMS rotation, layer update, + PITR restore, emergency PAT revocation. +- **C-7.2** Argon2id parameters: t=3, m=65536 KiB, p=1 (OWASP min). +- **C-7.3** `~/.nova/credentials.json` stores OIDC token + PAT metadata + (jti, exp, type), NOT the raw PAT. +- **C-8.2** `kj` pinned to a specific release + SHA256 recorded. +- **C-9.2** Threat model includes INV-18..21 compression audit + (verify spec's attestation invariant semantics are captured by + INV-15/16/17 + REQ-332). + +--- + +## Escalations + +None. All 9 axes resolved at confidence ≥ 0.70. No human escalation +required (full autonomy). + +--- + +## Grill complete + +The plan proceeds with the 3 critical fixes and 16 tracked conditions +applied to PLAN.md + REQUIREMENTS.md. The binding decisions above are +the authoritative grill record. Next: MVP/UX CHECK → SHIP phase 0. \ No newline at end of file diff --git a/.ciagent/PLAN.md b/.ciagent/PLAN.md index 22dd888..a513095 100644 --- a/.ciagent/PLAN.md +++ b/.ciagent/PLAN.md @@ -8,7 +8,11 @@ > `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`. +> `phase/06-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). ## Milestone goal @@ -37,7 +41,15 @@ cli-action` composite action; `core/mode_resolver.py`; audit emission with `mode` + `selection_reason`. The CLI is installable and every `core/` module is reachable. -**Capability gate target:** CAP-033 (surface), CAP-034 (delegation). +**Exit criterion:** CAP-033 + CAP-034 + CAP-035 Verified + all REQ-323..328 +tests pass. CodeArtifact provisioned (Wave 0 gate). + +#### 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. #### Wave 1 — pyproject + entry point (cli-engineer) - **Task 1.1** (cli-engineer): `pyproject.toml` — add @@ -103,7 +115,11 @@ with `mode` + `selection_reason`. The CLI is installable and every **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. +`nova apply --local`; `.nova/contract.yml.attestations/` scaffolded; +JWS-from-PAT key derivation (C-5.2). + +**Exit criterion:** all REQ-329..331 tests pass + JWS-from-PAT KDF +specified. #### Wave 1 — dual-use refactor (backend-engineer) - **Task 1.1** (backend-engineer): refactor @@ -113,7 +129,7 @@ paths share ≥80% code); `core/env.py:+synthesize_local_env()` for precedent per RESEARCH §1.2). Verify ≥80% code share (CAP-034 / code review). Local path via `core/local_emulators.py:LocalLambdaStub`. -#### Wave 2 — local env synthesizer (backend-engineer) +#### 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 @@ -121,6 +137,14 @@ paths share ≥80% code); `core/env.py:+synthesize_local_env()` for - **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. #### Wave 3 — attestations dir (cli-engineer) - **Task 3.1** (cli-engineer): verify `nova init` (P1 Wave 2 Task 2.3) @@ -131,18 +155,25 @@ paths share ≥80% code); `core/env.py:+synthesize_local_env()` for **Goal:** `nova-idp-auth` Lambda (sign-up, sign-in, session) with Argon2id hashing + DynamoDB tables. CAP-036 target. +**Exit criterion:** CAP-036 Verified (E2E sign-up → sign-in → session +passes in CI). + #### 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). + in a CloudFormation snippet (reused by P4 Wave 8 `nova idp setup`). + PITR enabled on each (REQ-335). -#### Wave 2 — Argon2id (security-engineer) +#### 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). - Lambda memory ≥512 MB. Raw passwords never in logs/traces/env/DDB - (INV-16, REQ-334). + 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. #### Wave 3 — auth Lambda (backend-engineer + security-engineer) - **Task 3.1** (backend-engineer): `nova-idp-auth` Lambda handler — @@ -157,46 +188,68 @@ Argon2id hashing + DynamoDB tables. CAP-036 target. → sign-in → session round-trip (moto[dynamodb] for local; deployed for CI). CAP-036 verification. -### Phase P4 — token-vend-pat (REQ-336..REQ-344) +### Phase P4 — token-vend-pat (REQ-336..REQ-344, REQ-340, REQ-341) [was P4+P5] **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). +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). -#### Wave 1 — kj-binary spike (backend-engineer + security-engineer) +**Exit criterion:** CAP-037 + CAP-038 Verified + `nova idp setup +--check/--apply/--verify` works against a fresh AWS account. + +#### 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` 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). + 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 2 — ABAC policy (security-engineer) +#### 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}`. JMESPath checks for role/scope/env/owner. Severity - `critical` = deny on fail. + 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. -#### Wave 3 — KMS signing (security-engineer) -- **Task 3.1** (security-engineer): KMS key `alias/nova-oidc-signing` +#### 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) +#### 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` — @@ -204,15 +257,20 @@ layer — RESEARCH §7). `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) +#### 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). + 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` @@ -220,43 +278,50 @@ layer — RESEARCH §7). 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) +#### 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. +modes; CloudFormation template generation + review (REQ-340, REQ-341). -#### Wave 1 — CloudFormation template (backend-engineer) -- **Task 1.1** (backend-engineer): `nova/idp/setup.py` (+ backend +- **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). - -#### Wave 2 — setup modes (cli-engineer + backend-engineer) -- **Task 2.1** (cli-engineer): `--check` (prerequisites + IAM policy +- **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 2.2** (backend-engineer): IAM policy delta computation — +- **Task 8.3** (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) +### 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. -#### Wave 1 — docs (lead-developer + security-engineer) +**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. + 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. + 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. + 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) — @@ -267,9 +332,9 @@ integration test; property tests; KMS round-trip; PAT revocation SLO. 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) +### Phase P6 — final-review-ship (Final Phase) -**Goal:** Multi-persona code review across P1..P6; project-health +**Goal:** Multi-persona code review across P1..P5; project-health audit; milestone ship to main; CAP-033..038 Verified. #### Wave 1 — review (lead-developer) @@ -282,8 +347,8 @@ audit; milestone ship to main; CAP-033..038 Verified. 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 +- **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 @@ -356,7 +421,6 @@ the audit event chain in CI against a deployed Nova-idp. | 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. @@ -397,4 +461,29 @@ CAP-033..CAP-038 are Verified; all v1.28 release-gate criteria met. - [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 +- [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. \ No newline at end of file diff --git a/.ciagent/REQUIREMENTS.md b/.ciagent/REQUIREMENTS.md index 843170d..ab11100 100644 --- a/.ciagent/REQUIREMENTS.md +++ b/.ciagent/REQUIREMENTS.md @@ -408,8 +408,11 @@ provisioning cloud resources. #### REQ-332 — JWS signing key from PAT **Journeys:** J2. **Priority:** High. **AC:** Given a PAT, when Dev runs `nova apply --local --sign-local-review`, -then a JWS attestation is produced; the public key is derivable from the -PAT and the JWS verifies. INV-14..17 (attestation invariants) enforced. +then a JWS attestation is produced; the JWS is HMAC-SHA256 with a key +derived from the PAT via `HKDF-SHA256(PAT_bytes, salt='nova-local-attestation', +info='jws-signing-key')` → 32-byte symmetric key (C-5.2 grill fix). The +verification key is derived from the PAT via the same KDF (the PAT is +the shared secret). INV-14..17 (attestation invariants) enforced. #### REQ-333 — `nova-idp-auth` Lambda **Journeys:** J1, J2. **Priority:** High. @@ -578,24 +581,24 @@ All v1.28 release-gate criteria in PLAN.md §6 met. | REQ-330 | P2 | planned | | REQ-331 | P2 | planned | | REQ-332 | P2 | planned | -| REQ-333 | P2 | planned | -| REQ-334 | P2 | planned | -| REQ-335 | P2 | planned | -| REQ-336 | P2 | planned | -| REQ-337 | P2 | planned | -| REQ-338 | P2 | planned | -| REQ-339 | P2 | planned | -| REQ-340 | P2 | planned | -| REQ-341 | P2 | planned | -| REQ-342 | P2 | planned | -| REQ-343 | P2 | planned | -| REQ-344 | P2 | planned | -| REQ-345 | P3 | planned | -| REQ-346 | P3 | planned | -| REQ-347 | P3 | planned | -| REQ-348 | P4 | planned | -| REQ-349 | P4 | planned | -| REQ-350 | P4 | planned | -| REQ-351 | P4 | planned | -| REQ-352 | P5 | planned | -| REQ-353 | P5 | planned | \ No newline at end of file +| REQ-333 | P3 | planned | +| REQ-334 | P3 | planned | +| REQ-335 | P3 | planned | +| REQ-336 | P4 | planned | +| REQ-337 | P4 | planned | +| REQ-338 | P4 | planned | +| REQ-339 | P4 | planned | +| REQ-340 | P4 | planned | +| REQ-341 | P4 | planned | +| REQ-342 | P4 | planned | +| REQ-343 | P4 | planned | +| REQ-344 | P4 | planned | +| REQ-345 | P5 | planned | +| REQ-346 | P5 | planned | +| REQ-347 | P5 | planned | +| REQ-348 | P5 | planned | +| REQ-349 | P5 | planned | +| REQ-350 | P5 | planned | +| REQ-351 | P5 | planned | +| REQ-352 | P6 | planned | +| REQ-353 | P6 | planned | \ No newline at end of file From a0c363c06367c70ba1731f4f6bc8c8b83bf94cde Mon Sep 17 00:00:00 2001 From: Jon Chery Date: Wed, 19 Aug 2026 22:10:24 +0000 Subject: [PATCH 6/7] =?UTF-8?q?decision(P00):=20mvp/ux=20gate=20=E2=80=94?= =?UTF-8?q?=20auto-generated=20(3=20sections=20verified,=20PASS)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ---ci--- project: acdl phase: 0 milestone: v1.28 status: mvp_ux_check ---/ci--- --- .ciagent/CHECKPOINT.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.ciagent/CHECKPOINT.json b/.ciagent/CHECKPOINT.json index e30a353..fdee9ad 100644 --- a/.ciagent/CHECKPOINT.json +++ b/.ciagent/CHECKPOINT.json @@ -1,6 +1,6 @@ { "phase": 0, - "stage": "grill", + "stage": "mvp_ux_check", "milestone": "v1.28", "phase_role": "pre_execution", "attempts": 0, @@ -24,5 +24,5 @@ {"phase": 6, "name": "final-review-ship", "reqs": ["REQ-352..353"], "tag": "v1.27.6"} ], "grill": {"verdict": "PROCEED-WITH-CONDITIONS", "confidence": 0.76, "critical_conditions": 3, "tracked_conditions": 16, "escalations": 0}, - "notes": "v1.28 GRILL complete. 9 axes reviewed; all PROCEED-WITH-CONDITIONS (>=0.70). 3 critical fixes applied (ABAC fail-closed C-6.1, JWS KDF C-5.2, traceability drift C-9.1) + 16 tracked conditions. P5 folded into P4 Wave 8 (C-2.1) -> 6 execution phases. Cost envelope ~$9/mo. Next: MVP/UX CHECK -> SHIP." + "notes": "v1.28 MVP/UX CHECK passed (auto, full autonomy). 3 PLAN.md sections verified: User-Facing Surface (nova --help/init/auth login/apply --local/idp setup + developer-guide + test_e2e_idp), Happy Path (J2 Dev authenticates + deploys locally, written BEFORE execute), UX Acceptance Criteria (7 criteria). Gate PASS. Next: SHIP phase 0." } \ No newline at end of file From 88e2389a952bf2a0cdaa00866090052a74aefa15 Mon Sep 17 00:00:00 2001 From: Jon Chery Date: Wed, 19 Aug 2026 22:11:01 +0000 Subject: [PATCH 7/7] =?UTF-8?q?docs(ship):=20P0=20complete=20=E2=86=92=20v?= =?UTF-8?q?1.27.0=20(v1.28=20pre-execution)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ---ci--- project: acdl phase: 0 milestone: v1.28 status: complete ---/ci--- --- .ciagent/CHECKPOINT.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.ciagent/CHECKPOINT.json b/.ciagent/CHECKPOINT.json index fdee9ad..537bf4d 100644 --- a/.ciagent/CHECKPOINT.json +++ b/.ciagent/CHECKPOINT.json @@ -1,6 +1,6 @@ { "phase": 0, - "stage": "mvp_ux_check", + "stage": "complete", "milestone": "v1.28", "phase_role": "pre_execution", "attempts": 0, @@ -24,5 +24,5 @@ {"phase": 6, "name": "final-review-ship", "reqs": ["REQ-352..353"], "tag": "v1.27.6"} ], "grill": {"verdict": "PROCEED-WITH-CONDITIONS", "confidence": 0.76, "critical_conditions": 3, "tracked_conditions": 16, "escalations": 0}, - "notes": "v1.28 MVP/UX CHECK passed (auto, full autonomy). 3 PLAN.md sections verified: User-Facing Surface (nova --help/init/auth login/apply --local/idp setup + developer-guide + test_e2e_idp), Happy Path (J2 Dev authenticates + deploys locally, written BEFORE execute), UX Acceptance Criteria (7 criteria). Gate PASS. Next: SHIP phase 0." + "notes": "v1.28 P0 SHIP. Pre-execution complete (SPECIFY->CLARIFY->RESEARCH->PLAN->GRILL->MVP/UX). Tag v1.27.0. Merged phase/00 -> milestone/v1.28-cli-identity. 6 execution phases planned (P1..P6). Next: P1 cli-substrate." } \ No newline at end of file