From 707d8a1e39480efa268fc60d94ec690d8bbbb3d2 Mon Sep 17 00:00:00 2001 From: Jon Chery Date: Wed, 12 Aug 2026 21:16:42 +0000 Subject: [PATCH] =?UTF-8?q?docs(P00):=20research=20findings=20=E2=80=94=20?= =?UTF-8?q?v1.26=20(PoA=20blockchain,=20deploy=20model,=20DynamoDB=20gap,?= =?UTF-8?q?=20personas)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ---ci--- project: acdl phase: 0 milestone: v1.26 status: research requirements: [REQ-310..REQ-322] personas: [lead-developer, backend-engineer, data-engineer, policy-engineer, blockchain-engineer] ---/ci--- --- .ciagent/CHECKPOINT.json | 6 +- .ciagent/PERSONAS.md | 265 ++++---- .ciagent/PROJECT.md | 6 +- .ciagent/RESEARCH.md | 572 ++++++------------ .ciagent/ROADMAP.md | 8 +- .../nova-blockchain-exchange/REQUIREMENTS.md | 38 +- 6 files changed, 392 insertions(+), 503 deletions(-) diff --git a/.ciagent/CHECKPOINT.json b/.ciagent/CHECKPOINT.json index d650b8d..48c352f 100644 --- a/.ciagent/CHECKPOINT.json +++ b/.ciagent/CHECKPOINT.json @@ -1,17 +1,17 @@ { "phase": 0, - "stage": "clarify", + "stage": "research", "milestone": "v1.26", "phase_role": "pre_execution", "attempts": 0, - "updated_at": "2026-08-12T21:12:00Z", + "updated_at": "2026-08-12T21:13:00Z", "project": "acdl", "projects": ["acdl", "nova-blockchain-exchange"], "active_milestone": "v1.26", "milestone_branch": "milestone/v1.26-pilot-activation", "phase_branch": "phase/00-specify-clarify-research-plan", "tag_line": "v1.25.x", - "requirements": ["REQ-310", "REQ-311", "REQ-312", "REQ-313", "REQ-314", "REQ-315", "REQ-316", "REQ-317", "REQ-318", "REQ-319", "REQ-320", "REQ-321"], + "requirements": ["REQ-310", "REQ-311", "REQ-312", "REQ-313", "REQ-314", "REQ-315", "REQ-316", "REQ-317", "REQ-318", "REQ-319", "REQ-320", "REQ-321", "REQ-322"], "pre_run": { "flaky_test_fixed": "8c68d68 test(metrics): fix attestation-event test freshness time-bomb", "acdl_to_nova_migration": "f844fea chore(bootstrap): migrate ACDL_* env vars to NOVA_*", diff --git a/.ciagent/PERSONAS.md b/.ciagent/PERSONAS.md index 3a064ff..f65f3a4 100644 --- a/.ciagent/PERSONAS.md +++ b/.ciagent/PERSONAS.md @@ -1,132 +1,169 @@ --- project: acdl -milestone: v1.25 +milestone: v1.26 generated_at: 2026-08-12 generator: lead-developer verification_toolchain: - typecheck: "python3 -m py_compile core/policy_engine.py adapters/kyverno-json/kyverno_json_engine.py tests/test_policy_engine.py tests/test_kyverno_json_engine.py" - test: "pytest tests/test_policy_engine.py tests/test_kyverno_json_engine.py tests/test_adapter.py tests/test_contract_resolver.py tests/test_confidence_signal.py tests/test_checkov_adapter.py tests/test_kyverno_adapter.py tests/test_pipeline.py -v" - lint: "ruff check core/policy_engine.py adapters/kyverno-json/ 2>/dev/null || python3 -m py_compile core/policy_engine.py" + typecheck: "python3 -m py_compile core/confidence_signal.py core/metrics/outcome_backfill.py adapters/terraform/adapter.py modules/l1/dynamodb/terraform/main.tf" + test: "pytest tests/test_adapter.py tests/test_contract_resolver.py tests/test_confidence_signal.py tests/test_outcome_backfill.py tests/test_settlement_finality_policy.py tests/test_pilot_readiness_policy.py tests/test_block.py tests/test_order_book.py tests/test_settlement.py -v" + lint: "ruff check core/metrics/outcome_backfill.py adapters/kyverno-json/policies/pilot-readiness/ adapters/kyverno-json/policies/settlement-finality/ 2>/dev/null || python3 -m py_compile core/metrics/outcome_backfill.py" note: | - v1.25 is the kyverno-json Unified Policy Engine milestone — a feat + v1.26 is the Live Pilot Estate Activation milestone — a feat milestone. Four active personas: lead-developer (coordination + - docs + ARCHITECTURE.md §12.7), backend-engineer (core/policy_engine.py - protocol + registry + contract_resolver.py wiring + run_platform.sh - Step 5 + pipeline tests), policy-engineer (adapters/kyverno-json/ - engine + policies across all 4 target dirs + meta-policies + policy - tests + adapter README + STANDARDS.md policy-authoring section), - data-engineer (config.json policy object + schemas/README.md note + - capability-inventory JSON fixture for regression policies). - frontend-engineer stays deactivated (no UI). The policy-engineer is a - new custom persona created for this milestone's policy domain (see - RESEARCH.md §4 — kyverno-json + JMESPath is a distinct framework from - backend-engineer's fastify/hono). + docs + ARCHITECTURE.md §12.8), backend-engineer (confidence_signal.py + escalation reason + outcome_backfill.py + run_platform.sh wiring + + env-JSON state_backend reconciliation), data-engineer (DynamoDB L1 + primitive + metrics cold store outcome backfill), policy-engineer + (kyverno-json pilot-readiness + settlement-finality policies), + + blockchain-engineer (custom, phase-specific — chain core + order + engine + settlement). frontend-engineer is deactivated (no UI). + Territory enforcement: warn (the pilot is cross-territory by + nature — the consumer repo + the platform repo share the milestone). --- -# ACDL — Persona Roster (v1.25 kyverno-json Unified Policy Engine) +# PERSONAS — v1.26 Live Pilot Estate Activation -> v1.25 roster. Four active personas + one deactivated. This is a feat -> milestone: the work is a swappable policy-engine protocol + a new -> adapter + policies across 4 Nova artifacts + pipeline wiring + docs. -> The policy-engineer is a new custom persona — kyverno-json + JMESPath -> is a specialized domain that doesn't fit backend-engineer's -> fastify/hono frameworks or data-engineer's drizzle/postgresql. +> Generated by the lead-developer at the end of RESEARCH. Assesses the +> project domains, activates/deactivates personas, creates custom +> personas for domains beyond the default four, aligns frameworks + +> territory + constraints to the actual project structure. -## Active personas +## Active Roster (5) -### lead-developer -- **Domain:** coordination + docs -- **Frameworks:** [] -- **Constraints:** ["pragmatic", "battle-tested defaults", "docs match code", "swap boundary is the moat"] -- **Territory:** - - `.ciagent/ARCHITECTURE.md` (§12.7 Policy Engine Registry — NEW) - - `.ciagent/PROJECT.md` (v1.25 section) - - `.ciagent/REQUIREMENTS.md` (v1.25 section) - - `.ciagent/ROADMAP.md` (v1.25 section) - - `.ciagent/PLAN.md`, `.ciagent/RESEARCH.md`, `.ciagent/CLARIFY.md`, - `.ciagent/GRILL.md`, `.ciagent/PERSONAS.md` - - `docs/METRICS.md` (swappable engine narrative — REQ-307) -- **Reason:** Owns the milestone coordination + the architecture - narrative. The swap boundary (PolicyEngine protocol) is the moat per - Strategic Objective #2 — the lead-developer owns the boundary - description in ARCHITECTURE.md §12.7 and the docs/METRICS.md note. - No Python policy code (backend-engineer + policy-engineer territory). - No UI (frontend-engineer deactivated). +### 1. lead-developer (active) +- **active:** true +- **phase_specific:** false +- **reason:** Coordinates task decomposition + resolves conflicts between + engineering personas. Owns the milestone narrative (PROJECT.md, + ROADMAP.md, ARCHITECTURE.md §12.8). Final architectural decisions when + personas disagree (e.g. where the outcome-backfill emitter lives). +- **domain:** project coordination, milestone narrative, cross-persona + conflict resolution. +- **frameworks:** none (coordination role). +- **territory:** `.ciagent/`, `docs/METRICS.md`, `adapters/README.md`, + `modules/README.md`, `modules/STANDARDS.md`. +- **constraints:** does not write Python/Terraform (delegates to + backend/data-engineer); does not author policies (delegates to + policy-engineer); does not author chain code (delegates to + blockchain-engineer). -### backend-engineer -- **Domain:** backend (Python + bash + pipeline wiring) -- **Frameworks:** ["boto3", "terraform"] -- **Constraints:** ["api-first", "strict-typing", "engine-agnostic confidence signal", "fail-soft when kj absent"] -- **Territory:** - - `core/policy_engine.py` (NEW — PolicyEngine Protocol + PolicyEngineRegistry + NullEngine) - - `core/contract_resolver.py` (MODIFIED — invoke registry pre/post resolve) - - `scripts/run_platform.sh` (MODIFIED — Step 5 kyverno-json parallel pass) - - `scripts/install-kyverno-json.sh` (NEW) - - `tests/test_policy_engine.py` (NEW — protocol conformance, registry, NullEngine) - - `tests/test_run_platform_plan_json_policies.py` (NEW — script-substring assertion) - - `.github/workflows/ci.yml` + `.gitea/workflows/ci.yml` (MODIFIED — Go + kj install) -- **Reason:** Owns the Python protocol layer + the pipeline wiring. The - `PolicyEngine` Protocol + `PolicyEngineRegistry` are Python structural- - typing constructs (PEP 544) — backend-engineer's strict-typing - constraint. The `contract_resolver.py` wiring + `run_platform.sh` - Step 5 are backend territory. Does NOT write kyverno-json policy - files (policy-engineer territory) — only the Python that *invokes* the - engine. Does NOT modify the confidence signal (it already consumes - `list[PolicyCheckResult]` engine-agnostically — PROJECT.md hard- - constraint). +### 2. backend-engineer (active) +- **active:** true +- **phase_specific:** false +- **reason:** Owns the platform-side Python changes: confidence signal + escalation reason (REQ-318), outcome-backfill emitter (REQ-317), + env-JSON state_backend wiring (REQ-319), adapter test updates for + DynamoDB (REQ-322), regression CAP-025 (REQ-316). +- **domain:** core Python (confidence_signal.py, metrics/, adapter.py, + regression_verify.py, contract_resolver.py), run_platform.sh wiring. +- **frameworks:** Python 3.12, pytest, boto3, SQLite, DynamoDB. +- **territory:** `core/confidence_signal.py`, `core/metrics/`, + `adapters/terraform/adapter.py`, `core/regression_verify.py`, + `core/environments/`, `scripts/run_platform.sh`, `tests/test_adapter.py`, + `tests/test_confidence_signal.py`, `tests/test_outcome_backfill.py`, + `tests/test_regression_pilot.py`. +- **constraints:** does not change `schemas/policy_check_result.schema.json` + (v1.25 moat, D-211); does not change `schemas/contract.schema.json` + (no schema breaks, D-213); does not author Terraform modules + (delegates to data-engineer for DynamoDB); does not author policies + (delegates to policy-engineer); does not author chain code (delegates + to blockchain-engineer). -### policy-engineer -- **Domain:** policy (declarative compliance rules) -- **Frameworks:** ["kyverno-json", "jmespath", "kyverno ValidatingPolicy"] -- **Constraints:** ["declarative-policies", "no-imperative-rules", "schema-validated", "severity-via-annotation", "assertion-trees-not-foreach"] -- **Territory:** - - `adapters/kyverno-json/` (NEW — engine impl + __init__.py + README) - - `adapters/kyverno-json/kyverno_json_engine.py` (NEW — KyvernoJsonEngine) - - `adapters/kyverno-json/policies/` (NEW — all 4 target dirs: contract/, stack-ir/, plan-json/, meta/, regression/) - - `adapters/kyverno-json/policies/_smoke.json` (NEW) - - `adapters/README.md` (MODIFIED — new adapter row + PolicyEngine Protocol section) - - `tests/test_kyverno_json_engine.py` (NEW — PCR schema validity, defensive parsing) - - `tests/test_stack_ir_policies.py` (NEW) - - `tests/test_plan_json_policies.py` (NEW) - - `tests/test_meta_policies.py` (NEW) - - `tests/test_regression_policies.py` (NEW) - - `tests/fixtures/stack_ir/`, `tests/fixtures/plan_json/`, `tests/fixtures/capability_inventory.json` (NEW) - - `modules/STANDARDS.md` (MODIFIED — Policy authoring standard section — REQ-307) -- **Reason:** The policy-engineer owns the declarative policy artifacts. - kyverno-json's `ValidatingPolicy` + assertion trees + JMESPath is a - distinct framework from backend-engineer's fastify/hono and requires - its own constraints: no imperative rules (everything is an assertion - tree), severity via the `nova.cloudinit.dev/severity` annotation (not - in the engine adapter), no `forEach` (use the `~` modifier). The - adapter pattern (engine ↔ protocol ↔ registry) is backend-engineer - territory, but the policy *content* and the engine *translation* - (`_to_pcr()`) are policy-engineer territory because they require - kyverno-json output-shape knowledge. Created per RESEARCH.md §4 — this - is a phase-spanning persona (active for P1..P4), not phase-specific. +### 3. data-engineer (active) +- **active:** true +- **phase_specific:** false +- **reason:** Owns the DynamoDB L1 primitive (REQ-322) — the single + platform-side module build-out. Owns the metrics cold store + outcome-backfill integration (REQ-317, the `fact_decision.outcome` + column + `backfilled_at` timestamp). Owns the env-JSON data updates + (REQ-319, `core/environments/*.json` account_id + state_backend.bucket). +- **domain:** Terraform modules (`modules/l1/`), schema definitions + (`interface.json`), registry (`modules/registry.json`), metrics cold + store (`metrics/nova_metrics.db`, `core/metrics/collector.py`). +- **frameworks:** Terraform, JSON, SQLite, DynamoDB, boto3. +- **territory:** `modules/l1/dynamodb/`, `modules/registry.json`, + `modules/README.md`, `core/environments/*.json`, + `core/metrics/collector.py`, `tests/test_adapter.py` (DynamoDB + emission test). +- **constraints:** does not change the adapter (stateless, v1.11); + follows the v1.8 NFR defaults (encryption + deletion protection + + PITR); follows the module standards (`modules/STANDARDS.md`). -### data-engineer -- **Domain:** data (config schema + structured fixtures) -- **Frameworks:** ["jsonschema", "yaml"] -- **Constraints:** ["schema-first", "type-safe config", "backward-compatible additions"] -- **Territory:** - - `.ciagent/config.json` (MODIFIED — new `policy` object: engine + policy_root) - - `schemas/policy_check_result.schema.json` (READ-ONLY — no change per D-116) - - `schemas/README.md` (MODIFIED — note engine: "kyverno" shared by K8s adapter + kj) - - `tests/fixtures/capability_inventory.json` (NEW — clean + drifted inventory fixtures for regression policies) -- **Reason:** The `config.json.policy` object is a schema-first addition - (new top-level key with `engine` + `policy_root` fields). The - capability-inventory JSON fixtures for the regression-gate policies - (REQ-304) are structured data — the data-engineer owns the fixture - shape. The `policy_check_result.schema.json` is read-only (D-116 — no - enum change); the data-engineer documents the `engine: "kyverno"` - sharing in `schemas/README.md`. No migrations (no database). No Python - (backend-engineer + policy-engineer territory). +### 4. policy-engineer (active, custom — added in v1.25) +- **active:** true +- **phase_specific:** false +- **reason:** Owns the kyverno-json policy authoring for the pilot: + settlement-finality (REQ-315), pilot-readiness (REQ-320). Extends + v1.25's policy engine to the securities domain. +- **domain:** declarative policies (kyverno-json ValidatingPolicy YAML), + JMESPath assertions, policy tests. +- **frameworks:** kyverno-json, JMESPath, JSON, pytest. +- **territory:** `adapters/kyverno-json/policies/pilot-readiness/`, + `adapters/kyverno-json/policies/settlement-finality/`, + `tests/test_settlement_finality_policy.py`, + `tests/test_pilot_readiness_policy.py`. +- **constraints:** policies are declarative (no imperative Python); + `is_configured()` guard skips gracefully when `kj` absent; follows + the v1.25 policy-authoring standard (`modules/STANDARDS.md` policy + section + `adapters/kyverno-json/README.md`). -## Deactivated personas +### 5. blockchain-engineer (active, custom, phase-specific — added in v1.26) +- **active:** true +- **phase_specific:** true (created for v1.26 P1; removed after P1 + unless the chain has ongoing work in P2..P4) +- **reason:** The pilot introduces a homegrown blockchain — a domain + beyond the default four personas. Owns the chain core (block, ledger, + validator, REQ-310), the order-matching engine (REQ-311), the + settlement service (REQ-312), and the consumer `contract.yaml` + (REQ-313) + deploy invocation (REQ-314). +- **domain:** blockchain consensus (PoA, single validator), order + matching (limit order book, price-time priority), settlement + (T+1, finality = block commit), consumer-repo deploy model. +- **frameworks:** Python 3.12 (the chain is Python, not Solidity/Go — + it's a homegrown ledger, not a smart-contract platform), pytest, + YAML (contract.yaml), GitHub Actions / Gitea Actions (deploy.yml + invocation). +- **territory:** `/root/nova-blockchain-exchange/` (the consumer repo: + `chain/`, `engine/`, `settlement/`, `contract.yaml`, + `contracts/*.yml`, `.github/workflows/deploy.yml`, + `.gitea/workflows/deploy.yml`, `tests/`). +- **constraints:** the chain is deterministic (same inputs → same block) + — it is automation, not AI (NORTH_STAR Objective #2 tenet); equities + only (D-200); single validator PoA (D-201); the consumer deploy MUST + go through `deploy.yml@v1.25` (no direct terraform apply); the + contract MUST validate against `schemas/contract.schema.json`. -### frontend-engineer +## Deactivated (1) + +### frontend-engineer (inactive) - **active:** false -- **Reason:** ACDL has no frontend (no package.json — confirmed in - config.json personas.personas[frontend-engineer].reason). v1.25 adds - no UI work — the policy engine is backend + policy artifacts only. - Deactivated per the v1.15+ convention. \ No newline at end of file +- **phase_specific:** false +- **reason:** The pilot has no UI — the blockchain exchange is a + backend service (matching engine + settlement). The consumer repo + has no web/frontend. Reactivated if a future milestone adds a trading + dashboard. + +## Phase-Specific Notes + +- **blockchain-engineer** is created for v1.26 P1 (blockchain core + + order engine + settlement). If P2..P4 have no chain changes, the + persona is removed after P1 (the chain is a stable substrate for the + pilot run). If P2 (consumer-contract-and-deploy) requires chain + adjustments, the persona stays through P2. +- **policy-engineer** is active for P3 (pilot-metrics-and-policies) + + may consult on P4 (pilot run policy verification). +- **data-engineer** is active for P3 (DynamoDB primitive + outcome + backfill + env-JSON) + P4 (regression CAP-025 may touch the registry). + +## Territory Enforcement + +- **Mode:** `warn` (the pilot is cross-territory by nature — the + consumer repo + the platform repo share the milestone; the + blockchain-engineer works in the consumer repo, backend/data/policy + engineers work in the platform repo). +- **Cross-territory collisions:** REQ-322 (DynamoDB primitive) is + data-engineer territory, but the adapter test update + (`tests/test_adapter.py` `EXPECTED_L1_KEYS`) is backend-engineer + territory. The lead-developer resolves: data-engineer authors the + module + registry; backend-engineer updates the test assertion + (the test is backend territory, the module is data territory). \ No newline at end of file diff --git a/.ciagent/PROJECT.md b/.ciagent/PROJECT.md index eb56064..3f065ed 100644 --- a/.ciagent/PROJECT.md +++ b/.ciagent/PROJECT.md @@ -1759,13 +1759,15 @@ the pilot end-to-end. ### Requirements -New requirements REQ-310..REQ-321 — see +New requirements REQ-310..REQ-322 — see `.ciagent/nova-blockchain-exchange/REQUIREMENTS.md` §v1.26. Summary: 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). +wiring (REQ-319), pilot-readiness policy (REQ-320), docs (REQ-321), +DynamoDB L1 primitive (REQ-322 — the single platform-side module +build-out; ECS + S3 already exist). ### Hard constraints diff --git a/.ciagent/RESEARCH.md b/.ciagent/RESEARCH.md index fcaf2b9..bfee4ee 100644 --- a/.ciagent/RESEARCH.md +++ b/.ciagent/RESEARCH.md @@ -1,438 +1,250 @@ -# Nova — v1.25 Research Findings +# Nova — v1.26 Research Findings -> Phase: research (pre-execution). Milestone: v1.25 (kyverno-json Unified -> Policy Engine). Status: research. Researcher: ci-researcher. +> Phase: research (pre-execution). Milestone: v1.26 (Live Pilot Estate +> Activation). Status: research. Researcher: ci-researcher. > Autonomy: full. -## 1. Problem domain +--- -Nova's compliance/policy posture is fragmented across three engines with -three rule languages and three adapter shapes (see PROJECT.md v1.25 -"Why" for the full diagnosis). The `PolicyCheckResult` schema -(`schemas/policy_check_result.schema.json`) is already the engine-agnostic -contract that `core/confidence_signal.py` consumes — the *contract* is -right; the *orchestration* is fragmented. There is no single declarative -place where "what Nova considers compliant" lives. The K8s-only Kyverno -adapter (`adapters/kyverno/`) can't help because it only speaks to K8s -manifests and the platform emits Terraform (D-053). +## 1. Domain — Homegrown PoA Blockchain for Securities Settlement -`kyverno-json` is the correction: a Kyverno-ecosystem runtime that applies -Kyverno policies to **any** JSON/YAML payload. It becomes the **unified -orchestrator** of compliance checks, behind a swappable `PolicyEngine` -protocol so OPA can replace it one day. Checkov and Wiz remain as -raw-finding adapters feeding *into* kyverno-json meta-policies. +### 1.1 Why a homegrown chain (not Ethereum/Solana/Hyperledger) -## 2. kyverno-json — the engine surface +The pilot's purpose is to exercise the Nova platform's deploy/policy/ +attestation gates over a real consumer estate — not to build a +production blockchain. A homegrown PoA ledger is the minimal viable +chain: append-only blocks, single validator (pilot), SHA-256 hash chain, +deterministic block production. It records every order, match, and +settlement as transactions; settlement finality = block commit. This +is sufficient to demonstrate that Nova's policy engine (kyverno-json) +can assert settlement finality declaratively (REQ-315) and that the +Decision Ledger captures the apply decision. -### 2.1 What it is +A production chain (Ethereum/Solana/Hyperledger) would be the *consumer +app's* choice, not the platform's. The platform is chain-agnostic — it +deploys whatever the consumer's `contract.yaml` declares. For the pilot, +the homegrown chain is the simplest way to produce a real consumer +estate without a heavyweight external dependency. -[kyverno-json](https://github.com/kyverno/kyverno-json) is a standalone Go -binary from the Kyverno project. It is a **separate runtime** from the -Kyverno K8s admission controller — same policy lineage, different -application target. Where Kyverno (K8s) evaluates `ClusterPolicy` -resources against Kubernetes manifests at admission time, kyverno-json -evaluates `ValidatingPolicy` resources against **any** JSON or YAML -payload file via the CLI (`kj scan`) or a Go library. It is **not** a -Python package (no PyPI release); it is installed via -`go install github.com/kyverno/kyverno-json/cmd/kj@latest` (D-115) or by -downloading a pinned binary from GitHub releases. +### 1.2 PoA consensus — single validator (pilot) -### 2.2 CLI surface (the v1.25 invocation path) +Proof-of-Authority with a single validator is the minimal consensus +model: the validator proposes + commits blocks. No Byzantine fault +tolerance (single validator = no forks). Deterministic block +production: same ordered transactions → same block (same hash). This +makes the chain auditable (the hash chain is verifiable) and +reproducible (a replay produces the same chain). Multi-validator BFT +is a future milestone (D-201). -The v1.25 engine uses the `kj scan` subcommand: +### 1.3 T+1 settlement finality -``` -kyverno-json scan [flags] +Equities settle T+1 (trade date + 1 business day). The pilot's +settlement service records matches as transactions on the chain; a +settlement is final when its block is committed. The settlement-finality +kyverno-json policy (REQ-315) asserts `all_committed: true` before any +promotion (qa→prod) — the declarative gate that turns settlement +finality into a policy artifact. This is the securities-specific +extension of v1.25's policy engine: the same `KyvernoJsonEngine` +evaluates a policy over a new payload shape (settlement-service status +JSON). -Flags: - --labels strings Labels selectors for policies - --output string Output format (text or json) (default "text") - --payload string Path to payload (json or yaml file) - --policy strings Path to kyverno-json policies - --pre-process strings JMESPath expression used to pre process payload -``` +### 1.4 Equities-only scope (D-200) -The `KyvernoJsonEngine.evaluate()` implementation (REQ-293) invokes: -``` -kj scan --policy --payload --output json -``` -and parses the JSON `results[]` array. The `--pre-process` flag is -available for JMESPath pre-projection (noted for the meta-policy use case -where the payload is the merged PCR list and a pre-process expression -can index by `ruleId` — recorded as a future optimization, not used in -v1.25's initial implementation). +Bonds (T+2), derivatives (varying), and options (exercise models) have +different settlement models. A pilot should demonstrate the Nova +platform's gates over the simplest case (equities T+1) before +expanding. "All types of securities" is the product vision; v1.26 is +the pilot (equities first). Future milestones add other security types +with their settlement models. -Other subcommands (`kj jp`, `kj serve`, `kj playground`, `kj docs`) are -out of scope for v1.25. `kj serve` is the long-running web-app mode -(noted as a future consideration for lower-latency evaluation in the -Out of Scope section of REQUIREMENTS.md). `kj jp` is the JMESPath REPL — -useful for policy authoring/debugging, not invoked by the engine. +--- -### 2.3 Policy structure (the `ValidatingPolicy` resource) +## 2. Nova Consumer Deploy Model -kyverno-json policies are Kubernetes-style resources (cluster-scoped) -belonging to the `json.kyverno.io` API group, kind `ValidatingPolicy`, -version `v1alpha1`: +### 2.1 The reusable `deploy.yml@v1.25` workflow -```yaml -apiVersion: json.kyverno.io/v1alpha1 -kind: ValidatingPolicy -metadata: - name: # becomes the KJ_ ruleId prefix -spec: - rules: - - name: - identifier: # optional — path to the unique entry id - match: # assertion tree — which payload entries - any: # the rule applies to - - - exclude: # optional — exclude matching entries - any: - - - context: # optional — named bindings available to - - name: # the rule's assertions ($) - variable: - validate: - message: "" # optional per-rule message - assert: - all: # all assertions must hold - - check: - message: "" - # OR - any: # at least one assertion must hold - - check: -``` +The platform's `.github/workflows/deploy.yml` is a `workflow_call` — +a reusable workflow that a consumer repo invokes via +`uses: acdl/.github/workflows/deploy.yml@v1.25`. Inputs: `contract` +(default `.nova/contract.yml`), `mode` (default `full`; enum +`full|plan-only|check-only|decommission`), `environment` (override). +The workflow checks out the consumer repo + the platform repo, runs +`scripts/run_platform.sh`, and records the apply decision + +attestation in the Decision Ledger. Secrets: `NOVA_AWS_*` +(account + access key + secret) + `NOVA_LAMBDA_URL` (error reporting). -Key differences from K8s Kyverno policies: -- **Always cluster-scoped** — no `namespace` field. -- **No `forEach`, pattern operators, anchors, or wildcards.** Iteration - is done via the `~` projection modifier in assertion trees (see §2.4). -- **Assertion trees** with JMESPath expressions replace Kyverno's - pattern-matching syntax (see §2.4). +The pilot consumer (`nova-blockchain-exchange`) invokes this workflow +with `mode: full` for `dev` (D-209). The `.gitea/workflows/deploy.yml` +mirror is byte-identical (the platform's deploy workflow is +forge-agnostic — Gitea + GitHub). -### 2.4 Assertion trees (the rule language) +### 2.2 `run_platform.sh --apply` path (confirmed) -An `assert` declaration contains an `all` or `any` list. Each entry has a -`check` (the assertion tree — a nested JMESPath projection) and an -optional `message`. **All comparisons happen in the leaves of the tree.** +`scripts/run_platform.sh:431-455` — the `--apply` (or `mode: full`) +path runs `terraform apply -auto-approve` after the HITL gate +(`:438`). For `dev` (autonomous, no HITL gate), the apply proceeds +directly. The apply records the env via `core/env_transition.py record` +(`:450`). The full pipeline (no `--apply` flag) continues to Step 7 +(confidence signal) + Step 8 (outbox write). -A simple example (assert a pod doesn't use the default service account): -```yaml -validate: - assert: - all: - - message: "serviceAccountName 'default' is not allowed" - check: - spec: - (serviceAccountName == 'default'): false -``` +**Gap (noted in RESEARCH §4):** the `--apply` path exits before the +outbox write (Step 8). The pilot runs the full pipeline (not `--apply` +alone), so the outbox write happens. The `run.completed` event lands in +the JSONL Decision Ledger (not the DynamoDB outbox) — this is by design +(the outbox is the platform-run evidence stream; the Decision Ledger is +the cold store for metrics). -The `(expression)` syntax evaluates a JMESPath expression; the result -becomes the current object for descendants; the leaf value is compared -to the expected value. +### 2.3 Contract schema — multi-module manifest -**Iteration via the `~` modifier.** The `~` prefix on a key applies -descendant assertions to **each element** of an array/map individually -(rather than comparing the whole array). Given `foo.bar: [1,2,3]`: -```yaml -check: - foo: - ~.bar: # iterate each element - (@ < `5`): true # assert each element < 5 -``` -The `~index_name.bar` form binds the index (array) or key (map) to -`$index_name` for use in descendants. This is how v1.25 iterates -`resources[]` in the Stack IR policies (REQ-297) and -`planned_values.root_module.resources[]` in the plan-JSON policies -(REQ-300). +`schemas/contract.schema.json:7,24-48` — required fields: `id`, +`name`, `environment`, `infrastructure`. The `infrastructure` block is +`minProperties: 1` with `patternProperties` accepting any module name +key. Multi-module manifest is supported: one contract can declare +`infrastructure: { microservice: {...}, dynamodb: {...}, s3: {...} }`. +The constraint is the `modules/registry.json` (the module must be +registered), not the schema. -**Explicit bindings** via `->binding_name` allow descendants to refer -to a parent node via `$binding_name`. Built-in bindings: `$payload` -(the whole input), `$policy`, `$rule`. +--- -**Escaping** via `\key\` prevents projection when a payload key collides -with the projection syntax. Not needed for Nova payloads (no `(key)` -fields), noted for completeness. +## 3. Platform Module Readiness (the critical finding) -### 2.5 Output shape (what `kj scan --output json` produces) +### 3.1 The adapter is stateless (v1.11 rewrite) -The JSON output is a `results[]` array. Each result entry has (at -minimum): -- `policy`: the policy metadata.name -- `rule`: the rule name -- `result`: `"pass"` | `"fail"` | `"error"` | `"skip"` (lowercase) -- `message`: the assertion message (or engine error message) -- `resource`: the matched payload entry (the `identifier` value, or the - whole payload when no identifier/match) -- `namespace`/`kind`/`name`: K8s-style fields (present but empty for - non-K8s payloads — the K8s Kyverno adapter's evidence uses these; the - kyverno-json engine's evidence uses `assertion`/`jmespath` instead) -- `severity`: not present by default (kyverno-json does not assign - severities — the Nova policy author assigns severity via a Nova- - specific annotation; see §2.6) +`adapters/terraform/adapter.py:1-11` — the adapter is a "STATELESS +ASSEMBLER" that owns no module content. There is **no `TYPE_MAP`**, +`INPUT_MAP`, or `OUTPUT_MAP` (deleted in the v1.11 stateless rewrite; +`modules/STANDARDS.md:212-214` confirms). A new stack type requires a +new L1 module (`modules/l1//` with `interface.json` + +`terraform/main.tf` + `README.md` + `instance.json`) + a +`modules/registry.json` entry — not an adapter change. -The `KyvernoJsonEngine._to_pcr()` translator (REQ-293) maps: -- `policy` → `ruleId` (prefixed `KJ_` per D-116) -- `result` → `result` (`pass`/`fail`/`error` → pass/fail/error; - `skip`/`skipped` → skipped) -- `message` → `message` -- `resource` → `resourceRef` + `evidence.resource` -- severity from the policy's `metadata.annotations` (see §2.6) -- `engine: "kyverno"` (per D-116 — no new enum value) +### 3.2 ECS — ready -### 2.6 Severity assignment (Nova convention) +`modules/l1/ecs-service/terraform/main.tf:1,11` — +`aws_ecs_task_definition` + `aws_ecs_service`. `interface.json:5-6` — +`type: aws:ecs:task_definition`. `registry.json:29-37` — registered. +Tests: `test_adapter.py:164-185,257-360`, `test_contract_resolver.py:61-92`. +The `microservice` L2 (`modules/l2/microservice/composition.json`) +references 6 L1 children (ecs-cluster, ecr, iam-role, alb, ecs-service, +kms-key) — the ECS pattern is fully wired end-to-end. -kyverno-json does not natively assign severities to results. Nova's -confidence signal requires a `severity` per PCR (critical/high/medium/ -low/info). The v1.25 convention: each Nova policy file declares its -severity via a `metadata.annotations` field: +### 3.3 S3 — ready -```yaml -metadata: - name: forbid-public-ingress - annotations: - nova.cloudinit.dev/severity: high -``` +`modules/l1/s3/terraform/main.tf:1` — `aws_s3_bucket` (+ versioning + +SSE). `interface.json:5-6` — `type: aws:s3:bucket`. `registry.json:2-10` +— registered. Tests: `test_adapter.py:56-110,241-257`, +`test_contract_resolver.py:36-51,92-130`. -The `KyvernoJsonEngine._to_pcr()` reads this annotation from the loaded -policy YAML (not from the scan result — the result doesn't carry it) and -applies it to every result that policy produces. Default when absent: -`info`. This keeps severity in the policy (declarative, version- -controlled) rather than in the engine adapter (imperative). The -annotation key is `nova.cloudinit.dev/severity` (matches the existing -`nova.cloudinit.dev` namespace used in `schemas/tagging-standard.json`). +### 3.4 DynamoDB — GAP (REQ-322) -## 3. The four policy targets (v1.25 scope) +**No `modules/l1/dynamodb/` directory, no `registry.json` key, no +`interface.json`, no `terraform/`, no tests.** The blockchain exchange's +ledger table needs this primitive. REQ-322 authors it: `interface.json` +(stack type `aws:dynamodb:table`), `terraform/main.tf` +(`aws_dynamodb_table` with PK + optional SK, `PAY_PER_REQUEST` default, +encryption + PITR enabled per v1.8 NFR defaults), `README.md`, +`instance.json`, + `registry.json` entry. The adapter needs no change +(stateless); the contract's `infrastructure.dynamodb` block references +this primitive. This is the single platform-side module build-out for +the milestone. -### 3.1 Consumer contract JSON (REQ-295) +### 3.5 Stale doc (not a blocker) -The payload is the parsed contract dict (the raw YAML loaded as JSON). -Policies assert the `contract.schema.json` constraints declaratively: -`require-id-pattern` (JMESPath regex `^[a-z][a-z0-9-]{2,5}$` over -`id`), `require-env-in-enum` (`environment` in `["dev","qa","prod","dr"]`), -`require-infrastructure-min-1` (`length(infrastructure) > 0`), -`forbid-unknown-fields` (keys subset of the 4 allowed). These are the -declarative equivalent of the jsonschema constraints — they let Nova -apply its own compliance posture (e.g. forbid a specific env for a -specific consumer) on top of schema validity without editing the -jsonschema. +`adapters/README.md:49-54` references the deleted `TYPE_MAP`/ +`INPUT_MAP`/`OUTPUT_MAP` — contradicts `adapter.py:1-11` + +`modules/STANDARDS.md:212-214`. REQ-321 (docs) should fix this. -**Invocation point:** `core/contract_resolver.py` pre-resolve (REQ-296). -Early-fail: if a contract policy fails, the resolver still proceeds -(the confidence signal decides the gate, consistent with the existing -`--soft-fail` Checkov pattern) — but the failing PCRs are in the -`policy` input, which lowers the score. +--- -### 3.2 Resolved Target Stack IR JSON (REQ-297) +## 4. Metric Pipeline Grounding (Post-Pilot targets) -The payload is the resolved Stack IR dict produced by -`core/contract_resolver.py` (the merged module outputs). Policies -assert over `resources[]` (the array of resolved resources): -`require-tagging-standard` (every resource's `tags` has `nova:owner` + -`nova:environment` — ports -`adapters/terraform/policy/custom_rules/nova_tagging.py`), -`forbid-public-ingress` (no resource has `public_ingress: true` — the -v1.0 demo rule, now declarative), `require-encryption-by-default` (every -S3/EBS/KMS-aliased resource carries encryption config — ports the v1.8 -D-encryption-default rule). The `~` modifier iterates `resources[]`. +### 4.1 AI Decision Accuracy — outcome backfill (REQ-317) -**Invocation point:** `core/contract_resolver.py` post-resolve (REQ-298). -Additive — the resolver's return values and exceptions are unchanged; -the PCRs are appended to the contract-policy PCRs. +`core/metrics/decision_ledger.py:210-211` documents the event chain: +`confidence.computed → ai.decision.made → attestation.recorded → +run.completed/failed`. `collector.py:262` inserts `fact_decision.outcome` +as `"pending"` — **there is no outcome-backfill step** wiring +`run.completed`/`run.failed` back into `fact_decision.outcome`. The AI +Decision Accuracy metric (`trust_snapshot.py:70-85`, `_get_ai_decision_accuracy`) +reads `decisions WHERE outcome='succeeded' ÷ total` — so it reads 0% +today (all pending). REQ-317 adds `core/metrics/outcome_backfill.py` +that reads run-manifest events and updates `fact_decision.outcome` + +`fact_decision.backfilled_at`. The PCR schema is unchanged (D-211). -### 3.3 Terraform plan JSON (REQ-300) +### 4.2 Human Escalation Frequency — `reason='confidence'` tag (REQ-318) -The payload is `terraform show -json ` output. Policies assert -over `planned_values.root_module.resources[]`: -`forbid-plaintext-secrets` (no `aws_db_instance.password` / -`aws_iam_user.login_profile.password` in plaintext — ports -`CKV_AWS_41/45/46`), `forbid-iam-wildcard` (no `Action: "*"` or -`Resource: "*"` in `aws_iam_policy.PolicyDocument` — ports -`CKV_AWS_1/40`), `require-kms-reference` (KMS keys referenced by alias, -not inline key material — ports `CKV_AWS_7/33`). These are declarative -**mirrors** of `checkov_adapter.py:RULE_MAP` — the Checkov rule stays -the source of truth for `terraform_plan` scanning; the kyverno-json -policy covers the same plan JSON with a different rule language -(defense-in-depth against engine drift). +`core/confidence_signal.py:184` — a `block` band sets +`human_override=True` in the `ai.decision.made` event. +`run_platform.sh:636` fails the pipeline on `block`. The Human +Escalation Frequency metric (`docs/metrics/human_escalation_frequency.md:11-12`) +is defined as `count(runs WHERE hitl_block=1 AND reason='confidence') ÷ +total runs`. The `reason='confidence'` discriminator is **not currently +stored** — `hitl_block` is a boolean from the manifest. REQ-318 adds +`escalation_reason: 'confidence'` to the `ai.decision.made` event when +`band == 'block'` + persists it into `fact_run` via the collector. -**Invocation point:** `run_platform.sh` Step 5 (REQ-301). After -Checkov/Wiz produce raw PCRs, the script runs `kj scan` over the plan -JSON; both PCR lists concatenate into the confidence signal's `policy` -input. When `which kj` is false, the script logs and proceeds with the -Checkov/Wiz list only. +### 4.3 Touchless Resolution Rate — denominator activates post-pilot -### 3.4 PolicyCheckResult records (meta-policies, REQ-303) +`docs/metrics/touchless_resolution_rate.md:12-15` — defined as a SQL +query over `fact_run` (`runs WHERE hitl_block=0 ÷ total runs`). The data +lands in `fact_run.hitl_block` via `collector.py:216-227`. No dedicated +emitter computes the ratio — it's a downstream query. The denominator +is 0 today (no consumer runs). The pilot run activates the denominator. -The payload is the **merged** `list[PolicyCheckResult]` produced by -checkov + wiz + the plan-JSON policies. This is the most novel target — -kyverno-json policies over the policy results themselves. -`block-on-any-critical` asserts no PCR 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" (D-119 — the -`confidence_signal.py` `PENALTY["critical"]: None` hard-override stays -as defense-in-depth). `tagging-rules-agree` cross-checks the Checkov -`NOVA_TAG_NAMING` result against the kyverno-json -`KJ_REQUIRE_TAGGING_STANDARD` result by `resourceRef`; divergence emits -an `error` PCR (D-118). +--- -**Invocation point:** after the three target policies (contract/stack- -IR/plan-JSON) produce their PCR lists, the merged list is the payload -for the meta-policies. The meta-policy PCRs are appended to the merged -list, which is what the confidence signal consumes. +## 5. kyverno-json Policy Extensibility -## 4. The `PolicyEngine` swap boundary +`adapters/kyverno-json/kyverno_json_engine.py:74-80` — the engine is +**policy-dir agnostic**: it loads whatever subdir the caller passes. +Existing subdirs: `contract/`, `stack-ir/`, `plan-json/`, `meta/`, +`regression/`. Adding a new subdir (e.g. `pilot-readiness/`, +`settlement-finality/`) requires: (1) `mkdir +adapters/kyverno-json/policies//`, (2) drop `ValidatingPolicy` +YAML/JSON files, (3) wire a caller. No engine code change needed. +Test pattern: one test file per subdir (`tests/test__policies.py`). -### 4.1 Protocol shape (REQ-291) +The pilot adds two new policy subdirs: `pilot-readiness/` +(REQ-320, no-placeholder-account) + `settlement-finality/` (REQ-315, +all-matches-committed). Both follow the established pattern. -A Python `Protocol` (PEP 544 — structural subtyping, no inheritance): -```python -class PolicyEngine(Protocol): - @property - def name(self) -> str: ... - def is_configured(self) -> bool: ... - def evaluate(self, payload: dict | str, policy_dir: Path, - contract_id: str) -> list[dict]: ... -``` -`list[dict]` (not `list[PolicyCheckResult]` — there's no dataclass; the -schema is enforced via `jsonschema` validation in tests, matching the -existing adapter pattern). The registry selects the active engine from -`config.json.policy.engine`. A `NullEngine` is the fallback when the -`policy` key is absent (emits `SKIPPED` — backward compatibility for -tests that don't set the key). +--- -### 4.2 The OPA-equivalent surface (future swap) +## 6. Env-JSON Wiring Reconciliation (REQ-319) -OPA (Open Policy Agent) is the most likely future replacement. The -mapping: -| Nova `PolicyEngine` member | kyverno-json impl | OPA equivalent | -|---|---|---| -| `name` | `"kyverno-json"` | `"opa"` | -| `is_configured()` | `which kj` | `which opa` | -| `evaluate(payload, policy_dir, contract_id)` | `kj scan --policy --payload -o json` | `opa eval -d -i 'data.nova.<...>'` | -| Policy file format | `ValidatingPolicy` (YAML) | Rego (`.rego`) | -| Result shape | `results[]` (pass/fail/error/skip) | `result` (set of violations) | -| Severity | Nova annotation `nova.cloudinit.dev/severity` | Nova convention (Rego `metadata` or a wrapper) | +`core/environments/dev.json:4` — `account_id: "000000000000"` (placeholder). +`core/environment_check.py:48-53` warns (non-fatal) when account_id is +placeholder + env != dev. `adapters/terraform/adapter.py:116-117` — +computes the state bucket as `nova-tfstate--us-east-1` +from the `AWS_ACCOUNT_ID` env var, **not** from the env JSON's +`state_backend.bucket`. This is the wiring gap: the env JSON's +`state_backend` field is currently unused by the live apply path. +REQ-319 makes the adapter read `env.state_backend.bucket` when present +(falling back to the computed name for backwards compat) + updates +`dev.json` to the real account `581513795199` + real bucket +`nova-tfstate-581513795199-us-east-1`. -The protocol is minimal (3 members) specifically so the OPA -implementation is a known quantity: an `OpaEngine` class that shells to -`opa eval`, translates the Rego violation set to PCR dicts, and -implements `is_configured()` via `which opa`. The policy *files* would -need rewriting (Rego, not ValidatingPolicy) — but the protocol, the -registry, the confidence signal, and the PCR schema are all untouched. -This is the swap boundary the user asked for ("Implemented as an -adapter since we might one day decide to replace it with something else -like OPA"). +--- -### 4.3 Why not a full plugin registry? - -A `setuptools` entry-point plugin registry (like checkov's -`--external-checks-dir`) was considered and rejected: Nova has 1 active -engine today (kyverno-json) and at most 2 in the foreseeable future -(kyverno-json + OPA). A `Protocol` + `dict` registry in -`core/policy_engine.py` is the right weight — discoverable, typed, -testable, and ~40 lines. An entry-point registry adds packaging -complexity (entry-point metadata, version resolution) for no gain at -this scale. The `register(name, factory)` method on the registry is -the extension point if a future milestone needs runtime plugin -discovery. - -## 5. Latency / MTTR impact (G-Q3 anticipation) - -NORTH_STAR.md MTTR target: < 60s p95. `run_platform.sh` Step 5 today -runs Checkov over the terraform plan (typically 5-15s for a small -stack). Adding `kj scan` over the same plan JSON adds: -- Process spawn: ~50ms (Go binary startup) -- Policy load: ~20ms (a handful of YAML files) -- Assertion evaluation: ~100-500ms (JMESPath over a small plan) -- Total: < 1s for a typical Nova stack - -The kyverno-json pass runs **in parallel** with Checkov (REQ-301 — the -script launches both and waits on both), so the wall-clock impact is -`max(checkov_time, kj_time)` ≈ checkov_time (kj is faster). The -contract + stack-IR policies run during resolve (already a fast step). -Meta-policies run over the merged list (in-memory, < 10ms). **No -measurable MTTR impact** is expected. This will be verified in P3 -VERIFY with a timing assertion. - -## 6. "Platform functions without AI" tenet (G-Q1 / D-120) - -kyverno-json is deterministic (same policy + payload → same result, -every run). It is not an LLM, not a probabilistic model, not a -"judgement" engine. The NORTH_STAR.md tenet ("the platform functions -without AI — 'AI decisions' are really automated decisions") -distinguishes AI (non-reproducible) from automation (reproducible). -kyverno-json is the latter. Adding it is **more** aligned with the -tenet than the current imperative Python in `core/env_transition.py` -and `core/regression_verify.py`, because the policy is declarative -(visible, auditable, version-controlled) rather than imperative (logic -hidden in function bodies). The `is_configured()` guard ensures the -platform functions without the binary (graceful skip → `SKIPPED` PCR -→ confidence signal proceeds). - -## 7. ECS policy catalog overlap (prior art) - -The kyverno-json catalog ships ECS policies that overlap with Nova's -L1 modules: `ecs-cluster-enable-logging`, `ecs-cluster-required- -container-insights`, `ecs-service-public-ip`, `ecs-service-required- -latest-platform-fargate`, `ecs-task-definition-fs-read-only`. These are -**reference policies**, not drop-in Nova policies — they target the -AWS ECS API shape (`type: aws_ecs_service` etc.), not Nova's Stack IR -shape. v1.25 policies target the Nova IR (REQ-297) and the terraform -plan JSON (REQ-300), not the raw AWS API. The catalog is useful as -prior art for JMESPath patterns over ECS resources — the -`ecs-service-public-ip` policy's `contains('$allowed-values', -@.assign_public_ip)` pattern informs the Nova `forbid-public-ingress` -policy shape. No catalog policies are imported directly in v1.25. - -## 8. Risks & mitigations +## 7. Risk Analysis | Risk | Likelihood | Impact | Mitigation | |---|---|---|---| -| `kj` binary not in CI image | medium | blocks P3+ tests | `is_configured()` guard + `pytest.skip` + `scripts/install-kyverno-json.sh` | -| kyverno-json output shape changes across versions | low | breaks `_to_pcr()` | pin `@latest` to a known-good commit in `install-kyverno-json.sh` after P1 smoke; defensive parsing (malformed → `error` PCR, not exception) | -| Policy explosion (4 targets × N rules) | medium | maintenance load | wave ordering (PLAN); policies co-located per target dir; meta-policy cross-check keeps the set auditable | -| Checkov + kj tagging-rule drift | medium | false `error` PCRs | `tagging-rules-agree` meta-policy emits `error` on divergence (visible, not silent); the Checkov rule stays source of truth for HCL, kj for IR | -| OPA swap turns out harder than the protocol implies | low | future milestone rework | RESEARCH §4.2 documents the OPA-equivalent surface; the protocol is the contract, not the implementation | -| `--pre-process` needed for meta-policies but undocumented behavior | low | meta-policy bugs | v1.25 meta-policies use plain assertion trees over the PCR list (no pre-process); `--pre-process` noted as a future optimization only | +| `NOVA_AWS_*` key lacks a needed IAM permission mid-pilot | Low (bootstrap succeeded → root-equivalent) | High (blocks apply) | D-207; the key has root-equivalent perms (empirically confirmed). | +| DynamoDB primitive takes longer than expected (new module) | Medium | Medium | REQ-322 is the single platform-side build-out; the `s3`/`rds` primitives are the template — straightforward. | +| Homegrown chain has a correctness bug (hash chain breaks) | Low | High | REQ-310 tests cover chain integrity, hash determinism, genesis, append/verify. | +| `deploy.yml@v1.25` ref doesn't resolve (floating tag) | Low | High | The platform's `release.yml` creates + force-moves the `v1.25` + `v1` floating tags on merge to main. The pilot contract uses `@v1.25`. | +| Settlement-finality policy false-negatives (blocks a valid promotion) | Medium | Medium | REQ-315 tests cover passing + failing fixtures; the policy is skip-when-kj-absent (graceful). | +| D-083 deferral challenged (audit ledger not tamper-evident) | Low | Low | D-204; the SQLite hash-chain + DynamoDB outbox is the pilot's audit record. Tamper-evidence is a future milestone. | -## 9. Assumptions (logged, full autonomy) +--- -- A1: `kj scan --output json` produces a stable `results[]` array shape. - Will be verified in P1 smoke test (`_smoke.json` policy + a trivial - payload); if the shape differs, `_to_pcr()` is adjusted defensively - (malformed → `error` PCR). Confidence: 0.85. -- A2: The `nova.cloudinit.dev/severity` annotation convention is - read by the engine from the policy YAML (loaded once per evaluate() - call). kyverno-json does not validate unknown annotations — they pass - through. Confidence: 0.90. -- A3: The `~` projection modifier iterates `resources[]` in the Stack - IR and `planned_values.root_module.resources[]` in the plan JSON - correctly. Verified in P2/P3 tests. Confidence: 0.85. -- A4: `go install` works in the CI image (Go toolchain available or - installable). If not, the binary-release download path is the - documented fallback in `install-kyverno-json.sh`. Confidence: 0.80. -- A5: The `NullEngine` fallback (when `policy` key absent in - config.json) keeps all existing tests passing — they don't set the - key, so they get `NullEngine` → `SKIPPED` PCRs → confidence signal - proceeds with `policy` input `[SKIPPED]` → per-input score 1.0 - (skipped counts as pass in `_per_input_score`). Confidence: 0.95 - (verified against `confidence_signal.py:84-89`). +## 8. Persona Assessment -## 10. Decisions referenced - -D-115 (install path), D-116 (engine enum reuse), D-117 (adapter -signatures unchanged), D-118 (tagging cross-check), D-119 (critical- -override defense-in-depth), D-120 (deterministic not AI). See -CLARIFY.md for the full resolution text. - -## 11. Architecture updates (deferred to RESEARCH-stage file edits) - -- `.ciagent/ARCHITECTURE.md` gains §12.7 "Policy Engine Registry" with - the registry diagram. Deferred to the RESEARCH commit (this file's - commit) — the section is authored as part of this research. -- `schemas/README.md` notes `engine: "kyverno"` is shared by the K8s - adapter and kyverno-json (distinguished by `ruleId` prefix). -- `modules/STANDARDS.md` gains a "Policy authoring standard" section - (P4, REQ-307). -- `docs/METRICS.md` notes the policy engine is swappable (P4, REQ-307). \ No newline at end of file +See `PERSONAS.md` (next section, produced by the lead-developer at the +end of RESEARCH). The active roster: backend-engineer (blockchain core ++ settlement + outcome backfill), data-engineer (DynamoDB primitive + +metrics cold store), policy-engineer (kyverno-json policies), + +blockchain-engineer (custom, phase-specific — chain consensus, order +matching, settlement finality). frontend-engineer is deactivated (no +UI in the pilot). \ No newline at end of file diff --git a/.ciagent/ROADMAP.md b/.ciagent/ROADMAP.md index 365484a..d96d98e 100644 --- a/.ciagent/ROADMAP.md +++ b/.ciagent/ROADMAP.md @@ -2308,6 +2308,10 @@ Tags: `v1.25.0` (P0) → `v1.25.5` (P5 = milestone release). `nova-tfstate-581513795199-us-east-1`. - REQ-320: `adapters/kyverno-json/policies/pilot-readiness/no-placeholder-account.json` — declarative gate preventing apply against a placeholder account. +- REQ-322: `modules/l1/dynamodb/` — new L1 primitive (interface.json + + terraform/main.tf + README.md + instance.json + registry.json entry). + The single platform-side module build-out (ECS + S3 already exist; + the adapter is stateless/registry-driven). ### Phase P4 — pilot-run-and-docs (planned, tag v1.25.4) - REQ-321: `adapters/README.md` (new consumer row) + @@ -2330,7 +2334,7 @@ Tags: `v1.25.0` (P0) → `v1.25.5` (P5 = milestone release). `milestone/v1.26-pilot-activation` → `main`; tag `v1.25.5` (= the v1.26 release per prev-minor tagging rule); create Gitea release with full milestone summary; delete all milestone branches. -- Update `REQUIREMENTS.md` (mark REQ-310..321 complete), `ROADMAP.md` +- Update `REQUIREMENTS.md` (mark REQ-310..322 complete), `ROADMAP.md` (mark v1.26 complete), `NORTH_STAR.md` (note Strategic Objectives #1 + #3 — first real consumer estate; Post-Pilot denominators activated). -- **Requirements:** REQ-310..321 (12 requirements). +- **Requirements:** REQ-310..322 (13 requirements). diff --git a/.ciagent/nova-blockchain-exchange/REQUIREMENTS.md b/.ciagent/nova-blockchain-exchange/REQUIREMENTS.md index 55d1396..f535d0d 100644 --- a/.ciagent/nova-blockchain-exchange/REQUIREMENTS.md +++ b/.ciagent/nova-blockchain-exchange/REQUIREMENTS.md @@ -60,6 +60,8 @@ contract references the Nova platform's deploy workflow (`uses: acdl/.github/workflows/deploy.yml@v1.25`) and declares the blockchain exchange stack (the AWS resources the app needs: ECS for the matching engine, DynamoDB for the ledger, S3 for block storage). +The DynamoDB L1 primitive (REQ-322) must land before this contract can +declare `dynamodb` — ECS + S3 already exist. **Must-haves:** - `contract.yaml` — id, name (`blockchain-exchange`), environment @@ -177,11 +179,43 @@ Update `adapters/README.md` (new consumer row), `docs/METRICS.md` (the - `.ciagent/ARCHITECTURE.md` — §12.8 Pilot Estate. - `.ciagent/nova-blockchain-exchange/README.md` — onboarding guide. +### REQ-322 — DynamoDB L1 primitive (platform-side) + +The blockchain exchange's ledger table needs a DynamoDB L1 primitive. +Research (RESEARCH §3) confirmed the adapter is stateless/registry- +driven (no `TYPE_MAP` — deleted in v1.11); a new stack type requires a +new L1 module, not an adapter change. The `dynamodb` primitive mirrors +the existing `s3` / `rds` primitives: `interface.json` (stack type +`aws:dynamodb:table`, inputs `table_name`/`region`/`pk`/`sk`/`billing_mode`, +outputs `table_arn`/`table_name`), `terraform/main.tf` +(`resource "aws_dynamodb_table" "this"`), `README.md`, `instance.json`, ++ a `registry.json` entry. The pilot contract's `infrastructure.dynamodb` +block references this primitive. This is the single platform-side +module build-out for the milestone (ECS + S3 already exist). + +**Must-haves:** +- `modules/l1/dynamodb/interface.json` — stack type + `aws:dynamodb:table`, inputs, outputs. +- `modules/l1/dynamodb/terraform/main.tf` — + `resource "aws_dynamodb_table" "this"` (PK + optional SK, + `billing_mode = PAY_PER_REQUEST` default, encryption + point-in-time- + recovery enabled per v1.8 NFR defaults). +- `modules/l1/dynamodb/README.md` — module doc. +- `modules/l1/dynamodb/instance.json` — sample instance. +- `modules/registry.json` — `dynamodb` entry (kind `l1`, + `terraform_dir: modules/l1/dynamodb/terraform`). +- `tests/test_adapter.py` — add `dynamodb` to `EXPECTED_L1_KEYS` + + a resolution + emission test. +- `modules/README.md` — catalog index updated. + ### Summary -12 requirements (REQ-310..321). Equities-only pilot; minimal PoA ledger; +13 requirements (REQ-310..322). Equities-only pilot; minimal PoA ledger; T+1 settlement; consumer deploy via `deploy.yml@v1.25`; 3 Post-Pilot metrics grounded (outcome backfill + escalation reason + pilot runs); 3 kyverno-json policies extending v1.25 (settlement-finality, pilot-readiness, + the existing meta-policies apply); env-JSON wiring -reconciled. \ No newline at end of file +reconciled; DynamoDB L1 primitive authored (the single platform-side +module build-out — the adapter is stateless/registry-driven, so the +primitive is a new `modules/l1/dynamodb/` module + registry entry, not +an adapter change). \ No newline at end of file