Files
acdl/.ciagent/REQUIREMENTS.md
T
Jon Chery bbfcbcc4d3 docs(P00): grill — v1.28 adversarial review (PROCEED 0.76, 3 critical + 16 tracked conditions applied)
---ci---
project: acdl
phase: 0
milestone: v1.28
status: grill
---/ci---
2026-08-19 22:10:15 +00:00

29 KiB
Raw Blame History

Nova — Requirements

Compressed. The full v1.0v1.25 requirement history (REQ-01..REQ-309) is preserved verbatim at .ciagent/archive/REQUIREMENTS-v1.0-v1.24.md. This file retains only the v1.25 requirement set (the immediate predecessor milestone whose policy-engine substrate is load-bearing for v1.26) + a pointer to the active v1.26 requirements, which live in the consumer subproject at .ciagent/nova-blockchain-exchange/REQUIREMENTS.md (multi-project mode per config.json).

Earlier requirement sets (v1.0v1.24, REQ-01..REQ-290) remain valid for the milestones they governed. They are not re-decided by v1.26. Full text in the archive snapshot + git history.

v1.25 — kyverno-json Unified Policy Engine (immediate predecessor, complete)

Feature milestone — complete. kyverno-json becomes the primary compliance / policy tool, implemented behind a swappable PolicyEngine adapter so OPA (or any other engine) can replace it one day. Tags run on the v1.24.x line (milestone v1.25 → tags v1.24.0..v1.24.5). Tag v1.24.5 = the milestone release.

One problem, one architectural correction:

  1. Fragmented policy posture. Nova's compliance rules were split across Checkov (imperative YAML + a Python custom rule for tagging), Wiz (API findings), the K8s-only Kyverno adapter (inactive for Terraform stacks — D-053), and imperative Python in core/env_transition.py + core/regression_verify.py. There was no single declarative place where "what Nova considers compliant" lived.

The correction: kyverno-json (a Kyverno-ecosystem runtime that applies Kyverno policies to any JSON/YAML payload) becomes the unified orchestrator of compliance checks. Checkov and Wiz remain as raw-finding adapters feeding into kyverno-json meta-policies. The engine is behind a PolicyEngine protocol so it is replaceable. The confidence signal is untouched — it already consumes list[PolicyCheckResult] engine-agnostically.

Decisions (locked in CLARIFY, full autonomy — load-bearing for v1.26)

  • D-115 (C-1): kyverno-json is a runtime dependency installed via go install github.com/kyverno/kyverno-json/cmd/kj@latest (pinned in a scripts/install-kyverno-json.sh helper; the CI image installs it). Not a Python package — kyverno-json is a Go binary. The KyvernoJsonEngine.is_configured() checks which kj and skips gracefully when absent (emits SKIPPED PCR, mirroring the Wiz adapter).
  • D-116 (C-2): kyverno-json PCR records carry engine: "kyverno" (no new enum value). The existing engine enum in schemas/policy_check_result.schema.json already includes "kyverno"; adding "kyverno-json" would force a schema change + checkov_adapter test regression for no semantic gain. The ruleId prefix KJ_ distinguishes kyverno-json rules from the K8s Kyverno adapter's KYVERNO_ prefix where they overlap.
  • D-117 (C-3): Checkov and Wiz adapters keep their current adapt() -> list[PolicyCheckResult] signatures. They emit PCRs as today. The meta-policies in adapters/kyverno-json/policies/meta/ consume the merged PCR list (checkov + wiz + kyverno-json) as their input payload, applying Nova-specific posture rules on top. No adapter signature changes.
  • D-118 (C-4): NOVA_TAG_NAMING (the Checkov custom rule in adapters/terraform/policy/custom_rules/nova_tagging.py) is kept. A kyverno-json mirror policy require-tagging-standard.json is added in adapters/kyverno-json/policies/stack-ir/. The P3 meta-policy tagging-rules-agree.json asserts the two engines agree on every resource; divergence emits an error PCR (defense-in-depth against rule drift). The Checkov rule stays the source of truth for Terraform-static scanning; the kyverno-json policy covers Stack IR.

Category: Policy Engine Core (feat)

  • REQ-291: core/policy_engine.py defines a PolicyEngine Python Protocol (PEP 544) with three members: name -> str, is_configured() -> bool, and evaluate(payload: dict | str, policy_dir: Path, contract_id: str) -> list[dict] (where each dict conforms to schemas/policy_check_result.schema.json). A PolicyEngineRegistry singleton selects the active engine from config.json's new policy.engine key (default "kyverno-json"); raises KeyError on an unknown engine name. The registry exposes get_engine() and register(name, factory). Pure stdlib, no engine imports at the protocol layer.
  • REQ-292: .ciagent/config.json gains a new top-level policy object: {"engine": "kyverno-json", "policy_root": "adapters/kyverno-json/policies"}. The registry reads policy.engine to select the active engine and policy.policy_root as the default policy directory. Backward-compatible: if the policy key is absent, the registry returns a NullEngine that emits only SKIPPED records (so existing tests that don't set the key still pass).

Category: kyverno-json Engine Adapter (feat)

  • REQ-293: adapters/kyverno-json/kyverno_json_engine.py implements KyvernoJsonEngine satisfying the PolicyEngine protocol. is_configured() returns True when which kj succeeds. evaluate() writes the payload to a temp JSON file, invokes kj scan --policy <policy_dir> --payload <payload.json> -o json, parses the native result list, and translates each entry to a PCR dict (engine: "kyverno", ruleId prefixed KJ_<policy_name>, severity mapped, result mapped pass/fail/skip → pass/fail/skipped). When is_configured() is false, evaluate() returns a single SKIPPED PCR with ruleId: "KJ_ENGINE_NOT_CONFIGURED". Native output parsing is defensive: any kyverno-json output that doesn't match the expected shape produces an error PCR, never an exception.
  • REQ-294: adapters/kyverno-json/__init__.py exports KyvernoJsonEngine. adapters/kyverno-json/policies/_smoke.json is a single trivial policy (require-contract-id) used to validate the engine round-trip end-to-end in tests. scripts/install-kyverno-json.sh runs go install github.com/kyverno/kyverno-json/cmd/kj@latest and prints kj version; documented in adapters/kyverno-json/README.md. The CI image installs Go + kj when policy.engine == "kyverno-json"; the install is cached.

Category: Contract Policies (feat)

  • REQ-295: adapters/kyverno-json/policies/contract/ holds kyverno-json policies over consumer contract JSON. Four policies mirroring schemas/contract.schema.json constraints: require-id-pattern.json, require-env-in-enum.json, require-infrastructure-min-1.json, forbid-unknown-fields.json. Each policy is a single Kyverno Policy resource with one validate.assert rule using JMESPath against the payload root.
  • REQ-296: core/contract_resolver.py invokes the PolicyEngineRegistry.get_engine().evaluate() with the contract dict and policies/contract/ before resolving (early-fail on contract violations) and emits a nova.policy.evaluated metrics event. Failures feed the confidence signal's policy input as fail PCRs; the resolver does not exit — the confidence signal decides the gate (consistent with the existing --soft-fail Checkov pattern).

Category: Stack-IR Policies (feat)

  • REQ-297: adapters/kyverno-json/policies/stack-ir/ holds policies over the resolved Target Stack IR dict. require-tagging-standard.json (every resource carries nova:owner + nova:environment tags — ports nova_tagging.py into a declarative Kyverno policy). forbid-public-ingress.json (no resource has public_ingress: true). require-encryption-by-default.json (every S3 bucket + EBS volume + KMS-aliased resource carries encryption config — ports the v1.8 D-encryption-default rule).
  • REQ-298: core/contract_resolver.py invokes the engine with the resolved Stack IR and policies/stack-ir/ after resolving. The resulting PCRs are appended to the contract-policy PCRs and fed to the confidence signal. The resolver's existing tests/test_contract_resolver.py continues to pass (the policy call is additive — it does not change resolver return values or exceptions).
  • REQ-299: tests/test_stack_ir_policies.py + fixture tests/fixtures/stack_ir/ — a passing IR + a failing IR. Tests run the KyvernoJsonEngine against real kj when which kj succeeds, and pytest.skip("kj not installed") when absent.

Category: Plan-JSON Policies + Pipeline Wiring (feat)

  • REQ-300: adapters/kyverno-json/policies/plan-json/ holds policies over terraform show -json output. forbid-plaintext-secrets.json (ports CKV_AWS_41/45/46). forbid-iam-wildcard.json (ports CKV_AWS_1/40). require-kms-reference.json (ports CKV_AWS_7/33). The Checkov RULE_MAP in checkov_adapter.py is unchanged — these are declarative mirrors, not replacements.
  • REQ-301: run_platform.sh Step 5 ("runtime policy scan") gains a parallel kyverno-json pass: after Checkov/Wiz produce raw PCRs, the script runs kj scan and pipes through adapters/kyverno-json/kyverno_json_engine.py to produce a second PCR list. Both lists are concatenated and fed to the confidence signal's policy input. When which kj is false, the script logs and proceeds with the Checkov/Wiz list only (no hard failure).
  • REQ-302: tests/test_plan_json_policies.py + fixture tests/fixtures/plan_json/ — a passing + failing plan JSON. tests/test_run_platform_plan_json_policies.py asserts run_platform.sh has the kyverno-json Step 5 block and that it concatenates PCR lists.

Category: Meta-Policies (feat)

  • REQ-303: adapters/kyverno-json/policies/meta/ holds policies whose payload is the merged list[PolicyCheckResult] itself. block-on-any-critical.json — asserts no PCR in the list has severity: "critical" + result: "fail"; if any does, the meta-policy emits a fail PCR with ruleId: "KJ_META_BLOCK_CRITICAL" and severity critical. This is the declarative source of truth for "critical = block"; the confidence_signal.py PENALTY["critical"]: None hard-override stays as defense-in-depth. tagging-rules-agree.json — for every resource in the Stack IR, asserts the Checkov NOVA_TAG_NAMING result and the kyverno-json KJ_REQUIRE_TAGGING_STANDARD result agree; divergence emits an error PCR. tests/test_meta_policies.py covers both.

Category: Regression-Gate Policies (feat, quality improvement from IDEATE)

  • REQ-304: adapters/kyverno-json/policies/regression/ holds policies over the capability-inventory JSON frontmatter. Three policies port the imperative checks in core/regression_verify.py: cap-013-adapter-dedup.json, cap-023-metrics-collector.json, cap-024-deck-structure.json. The existing core/regression_verify.py is kept (it drives the CI gate); the policies are the declarative mirror that makes capability regression auditable as a policy artifact, not imperative Python. Future milestones may switch the gate to the policy version.
  • REQ-305: tests/test_regression_policies.py + fixture tests/fixtures/capability_inventory.json — a clean inventory (all caps pass) + a drifted inventory. The regression gate (pytest suite) continues to pass; the new policy tests are additive.

Category: Documentation (docs)

  • REQ-306: adapters/README.md gains a new row for the kyverno-json adapter + a new section "Policy Engine Protocol" documenting the PolicyEngine Protocol, the registry, and the swap boundary (how to add an OpaEngine). adapters/kyverno-json/README.md documents the engine, the install path, the policy directory layout, and the four policy categories.
  • REQ-307: .ciagent/ARCHITECTURE.md gains §12.7 "Policy Engine Registry" with the registry diagram. schemas/README.md notes the engine: "kyverno" value is shared by the K8s Kyverno adapter and the kyverno-json engine (distinguished by ruleId prefix). modules/STANDARDS.md gains a "Policy authoring standard" section. docs/METRICS.md notes the policy engine is now swappable (Strategic Objective #2 — provable trust via a replaceable substrate, not a vendor lock-in).

Category: Tests (test)

  • REQ-308: tests/test_policy_engine.py — protocol conformance, unknown-engine KeyError, NullEngine fallback when the policy key is absent, KyvernoJsonEngine.is_configured() returns false when which kj fails (mocked). tests/test_kyverno_json_engine.pyevaluate() returns valid PCR dicts validated against schemas/policy_check_result.schema.json; native-output parsing is defensive (malformed → error PCR, not exception); is_configured()==falseSKIPPED PCR with KJ_ENGINE_NOT_CONFIGURED.
  • REQ-309: All new tests use pytest.skip("kj not installed") when which kj is absent, so the suite passes in environments without the binary (CI matrix: with-kj and without-kj). pyproject.toml + requirements-test.txt unchanged (kyverno-json is a Go binary, not a Python dep).

Out of Scope (v1.25)

  • Removing Checkov or Wiz. Both stay as raw-finding adapters.
  • OpaEngine implementation. The protocol is the swap boundary; the OPA implementation is a future milestone.
  • Per-module policies. modules/<name>/policies/ is documented as the future pattern in modules/STANDARDS.md but not populated this milestone.
  • kyverno-json as a long-running service. v1.25 uses the CLI (kj scan); the kj serve web-app mode is future.
  • Replacing the K8s Kyverno adapter. The K8s adapter (adapters/kyverno/) remains documentation-only (D-053).

v1.25 Traceability

REQ Phase Status
REQ-291 P1 complete
REQ-292 P1 complete
REQ-293 P1 complete
REQ-294 P1 complete
REQ-295 P2 complete
REQ-296 P2 complete
REQ-297 P2 complete
REQ-298 P2 complete
REQ-299 P2 complete
REQ-300 P3 complete
REQ-301 P3 complete
REQ-302 P3 complete
REQ-303 P3 complete
REQ-304 P4 complete
REQ-305 P4 complete
REQ-306 P4 complete
REQ-307 P4 complete
REQ-308 P1 complete
REQ-309 P1 complete

v1.26 — Live Pilot Estate Activation (active)

Feature milestone. The first real consumer estate (a stock exchange on a homegrown PoA blockchain, equities only) is activated against live AWS account 581513795199, lifting D-096. Tags run on the v1.25.x line: v1.25.0 (P0) → v1.25.1..v1.25.4 (P1P4) → v1.25.5 (P5 final = milestone release).

Multi-project mode: the v1.26 requirements live in .ciagent/nova-blockchain-exchange/REQUIREMENTS.md (the consumer subproject). The platform-side requirement REQ-322 (DynamoDB L1 primitive) landed in P2 of the platform repo. The 13 requirements (REQ-310..322) cover: blockchain core (REQ-310), order engine (REQ-311), settlement (REQ-312), consumer contract (REQ-313), deploy invocation (REQ-314), settlement-finality policy (REQ-315), pilot regression CAP (REQ-316), outcome backfill (REQ-317), escalation reason (REQ-318), env-JSON wiring (REQ-319), pilot-readiness policy (REQ-320), docs (REQ-321), DynamoDB L1 primitive (REQ-322).

v1.26 Traceability (live — see CHECKPOINT.json for authoritative state)

REQ Phase Status
REQ-310 P1 complete (v1.25.1)
REQ-311 P1 complete (v1.25.1)
REQ-312 P1 complete (v1.25.1)
REQ-322 P2 complete (v1.25.2)
REQ-313 P2 complete (v1.25.2)
REQ-314 P2 complete (v1.25.2)
REQ-315 P3 complete (v1.25.3)
REQ-316 P3 + P4 complete (v1.25.3 — CAP-025; v1.25.4 — live-verify complete)
REQ-317 P3 complete (v1.25.3)
REQ-318 P3 complete (v1.25.3)
REQ-319 P3 complete (v1.25.3)
REQ-320 P3 complete (v1.25.3)
REQ-321 P4 complete (v1.25.4)

Full v1.26 requirement text: .ciagent/nova-blockchain-exchange/REQUIREMENTS.md. Active phase plan: .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 <subcommand>; 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.Nv1.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/<module>.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 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. 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 <id> 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/<module>.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 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