docs(P00): research findings — v1.26 (PoA blockchain, deploy model, DynamoDB gap, personas)

---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---
This commit is contained in:
Jon Chery
2026-08-12 21:16:42 +00:00
parent 50e77e6314
commit 707d8a1e39
6 changed files with 392 additions and 503 deletions
+3 -3
View File
@@ -1,17 +1,17 @@
{ {
"phase": 0, "phase": 0,
"stage": "clarify", "stage": "research",
"milestone": "v1.26", "milestone": "v1.26",
"phase_role": "pre_execution", "phase_role": "pre_execution",
"attempts": 0, "attempts": 0,
"updated_at": "2026-08-12T21:12:00Z", "updated_at": "2026-08-12T21:13:00Z",
"project": "acdl", "project": "acdl",
"projects": ["acdl", "nova-blockchain-exchange"], "projects": ["acdl", "nova-blockchain-exchange"],
"active_milestone": "v1.26", "active_milestone": "v1.26",
"milestone_branch": "milestone/v1.26-pilot-activation", "milestone_branch": "milestone/v1.26-pilot-activation",
"phase_branch": "phase/00-specify-clarify-research-plan", "phase_branch": "phase/00-specify-clarify-research-plan",
"tag_line": "v1.25.x", "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": { "pre_run": {
"flaky_test_fixed": "8c68d68 test(metrics): fix attestation-event test freshness time-bomb", "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_*", "acdl_to_nova_migration": "f844fea chore(bootstrap): migrate ACDL_* env vars to NOVA_*",
+151 -114
View File
@@ -1,132 +1,169 @@
--- ---
project: acdl project: acdl
milestone: v1.25 milestone: v1.26
generated_at: 2026-08-12 generated_at: 2026-08-12
generator: lead-developer generator: lead-developer
verification_toolchain: 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" 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_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" 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/policy_engine.py adapters/kyverno-json/ 2>/dev/null || python3 -m py_compile core/policy_engine.py" 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: | 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 + milestone. Four active personas: lead-developer (coordination +
docs + ARCHITECTURE.md §12.7), backend-engineer (core/policy_engine.py docs + ARCHITECTURE.md §12.8), backend-engineer (confidence_signal.py
protocol + registry + contract_resolver.py wiring + run_platform.sh escalation reason + outcome_backfill.py + run_platform.sh wiring +
Step 5 + pipeline tests), policy-engineer (adapters/kyverno-json/ env-JSON state_backend reconciliation), data-engineer (DynamoDB L1
engine + policies across all 4 target dirs + meta-policies + policy primitive + metrics cold store outcome backfill), policy-engineer
tests + adapter README + STANDARDS.md policy-authoring section), (kyverno-json pilot-readiness + settlement-finality policies), +
data-engineer (config.json policy object + schemas/README.md note + blockchain-engineer (custom, phase-specific — chain core + order
capability-inventory JSON fixture for regression policies). engine + settlement). frontend-engineer is deactivated (no UI).
frontend-engineer stays deactivated (no UI). The policy-engineer is a Territory enforcement: warn (the pilot is cross-territory by
new custom persona created for this milestone's policy domain (see nature — the consumer repo + the platform repo share the milestone).
RESEARCH.md §4 — kyverno-json + JMESPath is a distinct framework from
backend-engineer's fastify/hono).
--- ---
# 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 > Generated by the lead-developer at the end of RESEARCH. Assesses the
> milestone: the work is a swappable policy-engine protocol + a new > project domains, activates/deactivates personas, creates custom
> adapter + policies across 4 Nova artifacts + pipeline wiring + docs. > personas for domains beyond the default four, aligns frameworks +
> The policy-engineer is a new custom persona — kyverno-json + JMESPath > territory + constraints to the actual project structure.
> is a specialized domain that doesn't fit backend-engineer's
> fastify/hono frameworks or data-engineer's drizzle/postgresql.
## Active personas ## Active Roster (5)
### lead-developer ### 1. lead-developer (active)
- **Domain:** coordination + docs - **active:** true
- **Frameworks:** [] - **phase_specific:** false
- **Constraints:** ["pragmatic", "battle-tested defaults", "docs match code", "swap boundary is the moat"] - **reason:** Coordinates task decomposition + resolves conflicts between
- **Territory:** engineering personas. Owns the milestone narrative (PROJECT.md,
- `.ciagent/ARCHITECTURE.md` (§12.7 Policy Engine Registry — NEW) ROADMAP.md, ARCHITECTURE.md §12.8). Final architectural decisions when
- `.ciagent/PROJECT.md` (v1.25 section) personas disagree (e.g. where the outcome-backfill emitter lives).
- `.ciagent/REQUIREMENTS.md` (v1.25 section) - **domain:** project coordination, milestone narrative, cross-persona
- `.ciagent/ROADMAP.md` (v1.25 section) conflict resolution.
- `.ciagent/PLAN.md`, `.ciagent/RESEARCH.md`, `.ciagent/CLARIFY.md`, - **frameworks:** none (coordination role).
`.ciagent/GRILL.md`, `.ciagent/PERSONAS.md` - **territory:** `.ciagent/`, `docs/METRICS.md`, `adapters/README.md`,
- `docs/METRICS.md` (swappable engine narrative — REQ-307) `modules/README.md`, `modules/STANDARDS.md`.
- **Reason:** Owns the milestone coordination + the architecture - **constraints:** does not write Python/Terraform (delegates to
narrative. The swap boundary (PolicyEngine protocol) is the moat per backend/data-engineer); does not author policies (delegates to
Strategic Objective #2 — the lead-developer owns the boundary policy-engineer); does not author chain code (delegates to
description in ARCHITECTURE.md §12.7 and the docs/METRICS.md note. blockchain-engineer).
No Python policy code (backend-engineer + policy-engineer territory).
No UI (frontend-engineer deactivated).
### backend-engineer ### 2. backend-engineer (active)
- **Domain:** backend (Python + bash + pipeline wiring) - **active:** true
- **Frameworks:** ["boto3", "terraform"] - **phase_specific:** false
- **Constraints:** ["api-first", "strict-typing", "engine-agnostic confidence signal", "fail-soft when kj absent"] - **reason:** Owns the platform-side Python changes: confidence signal
- **Territory:** escalation reason (REQ-318), outcome-backfill emitter (REQ-317),
- `core/policy_engine.py` (NEW — PolicyEngine Protocol + PolicyEngineRegistry + NullEngine) env-JSON state_backend wiring (REQ-319), adapter test updates for
- `core/contract_resolver.py` (MODIFIED — invoke registry pre/post resolve) DynamoDB (REQ-322), regression CAP-025 (REQ-316).
- `scripts/run_platform.sh` (MODIFIED — Step 5 kyverno-json parallel pass) - **domain:** core Python (confidence_signal.py, metrics/, adapter.py,
- `scripts/install-kyverno-json.sh` (NEW) regression_verify.py, contract_resolver.py), run_platform.sh wiring.
- `tests/test_policy_engine.py` (NEW — protocol conformance, registry, NullEngine) - **frameworks:** Python 3.12, pytest, boto3, SQLite, DynamoDB.
- `tests/test_run_platform_plan_json_policies.py` (NEW — script-substring assertion) - **territory:** `core/confidence_signal.py`, `core/metrics/`,
- `.github/workflows/ci.yml` + `.gitea/workflows/ci.yml` (MODIFIED — Go + kj install) `adapters/terraform/adapter.py`, `core/regression_verify.py`,
- **Reason:** Owns the Python protocol layer + the pipeline wiring. The `core/environments/`, `scripts/run_platform.sh`, `tests/test_adapter.py`,
`PolicyEngine` Protocol + `PolicyEngineRegistry` are Python structural- `tests/test_confidence_signal.py`, `tests/test_outcome_backfill.py`,
typing constructs (PEP 544) — backend-engineer's strict-typing `tests/test_regression_pilot.py`.
constraint. The `contract_resolver.py` wiring + `run_platform.sh` - **constraints:** does not change `schemas/policy_check_result.schema.json`
Step 5 are backend territory. Does NOT write kyverno-json policy (v1.25 moat, D-211); does not change `schemas/contract.schema.json`
files (policy-engineer territory) — only the Python that *invokes* the (no schema breaks, D-213); does not author Terraform modules
engine. Does NOT modify the confidence signal (it already consumes (delegates to data-engineer for DynamoDB); does not author policies
`list[PolicyCheckResult]` engine-agnostically — PROJECT.md hard- (delegates to policy-engineer); does not author chain code (delegates
constraint). to blockchain-engineer).
### policy-engineer ### 3. data-engineer (active)
- **Domain:** policy (declarative compliance rules) - **active:** true
- **Frameworks:** ["kyverno-json", "jmespath", "kyverno ValidatingPolicy"] - **phase_specific:** false
- **Constraints:** ["declarative-policies", "no-imperative-rules", "schema-validated", "severity-via-annotation", "assertion-trees-not-foreach"] - **reason:** Owns the DynamoDB L1 primitive (REQ-322) — the single
- **Territory:** platform-side module build-out. Owns the metrics cold store
- `adapters/kyverno-json/` (NEW — engine impl + __init__.py + README) outcome-backfill integration (REQ-317, the `fact_decision.outcome`
- `adapters/kyverno-json/kyverno_json_engine.py` (NEW — KyvernoJsonEngine) column + `backfilled_at` timestamp). Owns the env-JSON data updates
- `adapters/kyverno-json/policies/` (NEW — all 4 target dirs: contract/, stack-ir/, plan-json/, meta/, regression/) (REQ-319, `core/environments/*.json` account_id + state_backend.bucket).
- `adapters/kyverno-json/policies/_smoke.json` (NEW) - **domain:** Terraform modules (`modules/l1/`), schema definitions
- `adapters/README.md` (MODIFIED — new adapter row + PolicyEngine Protocol section) (`interface.json`), registry (`modules/registry.json`), metrics cold
- `tests/test_kyverno_json_engine.py` (NEW — PCR schema validity, defensive parsing) store (`metrics/nova_metrics.db`, `core/metrics/collector.py`).
- `tests/test_stack_ir_policies.py` (NEW) - **frameworks:** Terraform, JSON, SQLite, DynamoDB, boto3.
- `tests/test_plan_json_policies.py` (NEW) - **territory:** `modules/l1/dynamodb/`, `modules/registry.json`,
- `tests/test_meta_policies.py` (NEW) `modules/README.md`, `core/environments/*.json`,
- `tests/test_regression_policies.py` (NEW) `core/metrics/collector.py`, `tests/test_adapter.py` (DynamoDB
- `tests/fixtures/stack_ir/`, `tests/fixtures/plan_json/`, `tests/fixtures/capability_inventory.json` (NEW) emission test).
- `modules/STANDARDS.md` (MODIFIED — Policy authoring standard section — REQ-307) - **constraints:** does not change the adapter (stateless, v1.11);
- **Reason:** The policy-engineer owns the declarative policy artifacts. follows the v1.8 NFR defaults (encryption + deletion protection +
kyverno-json's `ValidatingPolicy` + assertion trees + JMESPath is a PITR); follows the module standards (`modules/STANDARDS.md`).
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.
### data-engineer ### 4. policy-engineer (active, custom — added in v1.25)
- **Domain:** data (config schema + structured fixtures) - **active:** true
- **Frameworks:** ["jsonschema", "yaml"] - **phase_specific:** false
- **Constraints:** ["schema-first", "type-safe config", "backward-compatible additions"] - **reason:** Owns the kyverno-json policy authoring for the pilot:
- **Territory:** settlement-finality (REQ-315), pilot-readiness (REQ-320). Extends
- `.ciagent/config.json` (MODIFIED — new `policy` object: engine + policy_root) v1.25's policy engine to the securities domain.
- `schemas/policy_check_result.schema.json` (READ-ONLY — no change per D-116) - **domain:** declarative policies (kyverno-json ValidatingPolicy YAML),
- `schemas/README.md` (MODIFIED — note engine: "kyverno" shared by K8s adapter + kj) JMESPath assertions, policy tests.
- `tests/fixtures/capability_inventory.json` (NEW — clean + drifted inventory fixtures for regression policies) - **frameworks:** kyverno-json, JMESPath, JSON, pytest.
- **Reason:** The `config.json.policy` object is a schema-first addition - **territory:** `adapters/kyverno-json/policies/pilot-readiness/`,
(new top-level key with `engine` + `policy_root` fields). The `adapters/kyverno-json/policies/settlement-finality/`,
capability-inventory JSON fixtures for the regression-gate policies `tests/test_settlement_finality_policy.py`,
(REQ-304) are structured data — the data-engineer owns the fixture `tests/test_pilot_readiness_policy.py`.
shape. The `policy_check_result.schema.json` is read-only (D-116 — no - **constraints:** policies are declarative (no imperative Python);
enum change); the data-engineer documents the `engine: "kyverno"` `is_configured()` guard skips gracefully when `kj` absent; follows
sharing in `schemas/README.md`. No migrations (no database). No Python the v1.25 policy-authoring standard (`modules/STANDARDS.md` policy
(backend-engineer + policy-engineer territory). 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 - **active:** false
- **Reason:** ACDL has no frontend (no package.json — confirmed in - **phase_specific:** false
config.json personas.personas[frontend-engineer].reason). v1.25 adds - **reason:** The pilot has no UI — the blockchain exchange is a
no UI work — the policy engine is backend + policy artifacts only. backend service (matching engine + settlement). The consumer repo
Deactivated per the v1.15+ convention. 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).
+4 -2
View File
@@ -1759,13 +1759,15 @@ the pilot end-to-end.
### Requirements ### Requirements
New requirements REQ-310..REQ-321 — see New requirements REQ-310..REQ-322 — see
`.ciagent/nova-blockchain-exchange/REQUIREMENTS.md` §v1.26. Summary: `.ciagent/nova-blockchain-exchange/REQUIREMENTS.md` §v1.26. Summary:
blockchain core (REQ-310), order engine (REQ-311), settlement blockchain core (REQ-310), order engine (REQ-311), settlement
(REQ-312), consumer contract (REQ-313), deploy invocation (REQ-314), (REQ-312), consumer contract (REQ-313), deploy invocation (REQ-314),
settlement-finality policy (REQ-315), pilot regression CAP (REQ-316), settlement-finality policy (REQ-315), pilot regression CAP (REQ-316),
outcome backfill (REQ-317), escalation reason (REQ-318), env-JSON 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 ### Hard constraints
+192 -380
View File
@@ -1,438 +1,250 @@
# Nova — v1.25 Research Findings # Nova — v1.26 Research Findings
> Phase: research (pre-execution). Milestone: v1.25 (kyverno-json Unified > Phase: research (pre-execution). Milestone: v1.26 (Live Pilot Estate
> Policy Engine). Status: research. Researcher: ci-researcher. > Activation). Status: research. Researcher: ci-researcher.
> Autonomy: full. > Autonomy: full.
## 1. Problem domain ---
Nova's compliance/policy posture is fragmented across three engines with ## 1. Domain — Homegrown PoA Blockchain for Securities Settlement
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).
`kyverno-json` is the correction: a Kyverno-ecosystem runtime that applies ### 1.1 Why a homegrown chain (not Ethereum/Solana/Hyperledger)
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.
## 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 ### 1.2 PoA consensus single validator (pilot)
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.
### 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
``` Equities settle T+1 (trade date + 1 business day). The pilot's
kyverno-json scan [flags] 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: ### 1.4 Equities-only scope (D-200)
--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
```
The `KyvernoJsonEngine.evaluate()` implementation (REQ-293) invokes: Bonds (T+2), derivatives (varying), and options (exercise models) have
``` different settlement models. A pilot should demonstrate the Nova
kj scan --policy <policy_dir> --payload <payload.json> --output json platform's gates over the simplest case (equities T+1) before
``` expanding. "All types of securities" is the product vision; v1.26 is
and parses the JSON `results[]` array. The `--pre-process` flag is the pilot (equities first). Future milestones add other security types
available for JMESPath pre-projection (noted for the meta-policy use case with their settlement models.
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).
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) ### 2.1 The reusable `deploy.yml@v1.25` workflow
belonging to the `json.kyverno.io` API group, kind `ValidatingPolicy`,
version `v1alpha1`:
```yaml The platform's `.github/workflows/deploy.yml` is a `workflow_call`
apiVersion: json.kyverno.io/v1alpha1 a reusable workflow that a consumer repo invokes via
kind: ValidatingPolicy `uses: acdl/.github/workflows/deploy.yml@v1.25`. Inputs: `contract`
metadata: (default `.nova/contract.yml`), `mode` (default `full`; enum
name: <policy-name> # becomes the KJ_<policy-name> ruleId prefix `full|plan-only|check-only|decommission`), `environment` (override).
spec: The workflow checks out the consumer repo + the platform repo, runs
rules: `scripts/run_platform.sh`, and records the apply decision +
- name: <rule-name> attestation in the Decision Ledger. Secrets: `NOVA_AWS_*`
identifier: <jmespath> # optional — path to the unique entry id (account + access key + secret) + `NOVA_LAMBDA_URL` (error reporting).
match: # assertion tree — which payload entries
any: # the rule applies to
- <assertion>
exclude: # optional — exclude matching entries
any:
- <assertion>
context: # optional — named bindings available to
- name: <binding> # the rule's assertions ($<binding>)
variable: <value>
validate:
message: "<human-readable>" # optional per-rule message
assert:
all: # all assertions must hold
- check: <assertion-tree>
message: "<per-check>"
# OR
any: # at least one assertion must hold
- check: <assertion-tree>
```
Key differences from K8s Kyverno policies: The pilot consumer (`nova-blockchain-exchange`) invokes this workflow
- **Always cluster-scoped** — no `namespace` field. with `mode: full` for `dev` (D-209). The `.gitea/workflows/deploy.yml`
- **No `forEach`, pattern operators, anchors, or wildcards.** Iteration mirror is byte-identical (the platform's deploy workflow is
is done via the `~` projection modifier in assertion trees (see §2.4). forge-agnostic — Gitea + GitHub).
- **Assertion trees** with JMESPath expressions replace Kyverno's
pattern-matching syntax (see §2.4).
### 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 `scripts/run_platform.sh:431-455` — the `--apply` (or `mode: full`)
`check` (the assertion tree — a nested JMESPath projection) and an path runs `terraform apply -auto-approve` after the HITL gate
optional `message`. **All comparisons happen in the leaves of the tree.** (`: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): **Gap (noted in RESEARCH §4):** the `--apply` path exits before the
```yaml outbox write (Step 8). The pilot runs the full pipeline (not `--apply`
validate: alone), so the outbox write happens. The `run.completed` event lands in
assert: the JSONL Decision Ledger (not the DynamoDB outbox) — this is by design
all: (the outbox is the platform-run evidence stream; the Decision Ledger is
- message: "serviceAccountName 'default' is not allowed" the cold store for metrics).
check:
spec:
(serviceAccountName == 'default'): false
```
The `(expression)` syntax evaluates a JMESPath expression; the result ### 2.3 Contract schema — multi-module manifest
becomes the current object for descendants; the leaf value is compared
to the expected value.
**Iteration via the `~` modifier.** The `~` prefix on a key applies `schemas/contract.schema.json:7,24-48` — required fields: `id`,
descendant assertions to **each element** of an array/map individually `name`, `environment`, `infrastructure`. The `infrastructure` block is
(rather than comparing the whole array). Given `foo.bar: [1,2,3]`: `minProperties: 1` with `patternProperties` accepting any module name
```yaml key. Multi-module manifest is supported: one contract can declare
check: `infrastructure: { microservice: {...}, dynamodb: {...}, s3: {...} }`.
foo: The constraint is the `modules/registry.json` (the module must be
~.bar: # iterate each element registered), not the schema.
(@ < `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).
**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 ## 3. Platform Module Readiness (the critical finding)
with the projection syntax. Not needed for Nova payloads (no `(key)`
fields), noted for completeness.
### 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 `adapters/terraform/adapter.py:1-11` — the adapter is a "STATELESS
minimum): ASSEMBLER" that owns no module content. There is **no `TYPE_MAP`**,
- `policy`: the policy metadata.name `INPUT_MAP`, or `OUTPUT_MAP` (deleted in the v1.11 stateless rewrite;
- `rule`: the rule name `modules/STANDARDS.md:212-214` confirms). A new stack type requires a
- `result`: `"pass"` | `"fail"` | `"error"` | `"skip"` (lowercase) new L1 module (`modules/l1/<name>/` with `interface.json` +
- `message`: the assertion message (or engine error message) `terraform/main.tf` + `README.md` + `instance.json`) + a
- `resource`: the matched payload entry (the `identifier` value, or the `modules/registry.json` entry — not an adapter change.
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)
The `KyvernoJsonEngine._to_pcr()` translator (REQ-293) maps: ### 3.2 ECS — ready
- `policy``ruleId` (prefixed `KJ_<policy_name>` 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)
### 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 ### 3.3 S3 — ready
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:
```yaml `modules/l1/s3/terraform/main.tf:1``aws_s3_bucket` (+ versioning +
metadata: SSE). `interface.json:5-6``type: aws:s3:bucket`. `registry.json:2-10`
name: forbid-public-ingress — registered. Tests: `test_adapter.py:56-110,241-257`,
annotations: `test_contract_resolver.py:36-51,92-130`.
nova.cloudinit.dev/severity: high
```
The `KyvernoJsonEngine._to_pcr()` reads this annotation from the loaded ### 3.4 DynamoDB — GAP (REQ-322)
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. 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). `adapters/README.md:49-54` references the deleted `TYPE_MAP`/
Policies assert the `contract.schema.json` constraints declaratively: `INPUT_MAP`/`OUTPUT_MAP` contradicts `adapter.py:1-11` +
`require-id-pattern` (JMESPath regex `^[a-z][a-z0-9-]{2,5}$` over `modules/STANDARDS.md:212-214`. REQ-321 (docs) should fix this.
`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.
**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 ### 4.1 AI Decision Accuracy — outcome backfill (REQ-317)
`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[]`.
**Invocation point:** `core/contract_resolver.py` post-resolve (REQ-298). `core/metrics/decision_ledger.py:210-211` documents the event chain:
Additive — the resolver's return values and exceptions are unchanged; `confidence.computed → ai.decision.made → attestation.recorded →
the PCRs are appended to the contract-policy PCRs. 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 <tfplan>` output. Policies assert `core/confidence_signal.py:184` — a `block` band sets
over `planned_values.root_module.resources[]`: `human_override=True` in the `ai.decision.made` event.
`forbid-plaintext-secrets` (no `aws_db_instance.password` / `run_platform.sh:636` fails the pipeline on `block`. The Human
`aws_iam_user.login_profile.password` in plaintext — ports Escalation Frequency metric (`docs/metrics/human_escalation_frequency.md:11-12`)
`CKV_AWS_41/45/46`), `forbid-iam-wildcard` (no `Action: "*"` or is defined as `count(runs WHERE hitl_block=1 AND reason='confidence') ÷
`Resource: "*"` in `aws_iam_policy.PolicyDocument` — ports total runs`. The `reason='confidence'` discriminator is **not currently
`CKV_AWS_1/40`), `require-kms-reference` (KMS keys referenced by alias, stored** — `hitl_block` is a boolean from the manifest. REQ-318 adds
not inline key material — ports `CKV_AWS_7/33`). These are declarative `escalation_reason: 'confidence'` to the `ai.decision.made` event when
**mirrors** of `checkov_adapter.py:RULE_MAP` — the Checkov rule stays `band == 'block'` + persists it into `fact_run` via the collector.
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).
**Invocation point:** `run_platform.sh` Step 5 (REQ-301). After ### 4.3 Touchless Resolution Rate — denominator activates post-pilot
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.
### 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- ## 5. kyverno-json Policy Extensibility
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.
## 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/<name>/`, (2) drop `ValidatingPolicy`
YAML/JSON files, (3) wire a caller. No engine code change needed.
Test pattern: one test file per subdir (`tests/test_<name>_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 `core/environments/dev.json:4``account_id: "000000000000"` (placeholder).
mapping: `core/environment_check.py:48-53` warns (non-fatal) when account_id is
| Nova `PolicyEngine` member | kyverno-json impl | OPA equivalent | placeholder + env != dev. `adapters/terraform/adapter.py:116-117`
|---|---|---| computes the state bucket as `nova-tfstate-<AWS_ACCOUNT_ID>-us-east-1`
| `name` | `"kyverno-json"` | `"opa"` | from the `AWS_ACCOUNT_ID` env var, **not** from the env JSON's
| `is_configured()` | `which kj` | `which opa` | `state_backend.bucket`. This is the wiring gap: the env JSON's
| `evaluate(payload, policy_dir, contract_id)` | `kj scan --policy <dir> --payload <json> -o json` | `opa eval -d <dir> -i <json> 'data.nova.<...>'` | `state_backend` field is currently unused by the live apply path.
| Policy file format | `ValidatingPolicy` (YAML) | Rego (`.rego`) | REQ-319 makes the adapter read `env.state_backend.bucket` when present
| Result shape | `results[]` (pass/fail/error/skip) | `result` (set of violations) | (falling back to the computed name for backwards compat) + updates
| Severity | Nova annotation `nova.cloudinit.dev/severity` | Nova convention (Rego `metadata` or a wrapper) | `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? ## 7. Risk Analysis
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
| Risk | Likelihood | Impact | Mitigation | | Risk | Likelihood | Impact | Mitigation |
|---|---|---|---| |---|---|---|---|
| `kj` binary not in CI image | medium | blocks P3+ tests | `is_configured()` guard + `pytest.skip` + `scripts/install-kyverno-json.sh` | | `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). |
| 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) | | 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. |
| 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 | | Homegrown chain has a correctness bug (hash chain breaks) | Low | High | REQ-310 tests cover chain integrity, hash determinism, genesis, append/verify. |
| 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 | | `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`. |
| 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 | | 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). |
| `--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 | | 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. ## 8. Persona Assessment
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`).
## 10. Decisions referenced See `PERSONAS.md` (next section, produced by the lead-developer at the
end of RESEARCH). The active roster: backend-engineer (blockchain core
D-115 (install path), D-116 (engine enum reuse), D-117 (adapter + settlement + outcome backfill), data-engineer (DynamoDB primitive +
signatures unchanged), D-118 (tagging cross-check), D-119 (critical- metrics cold store), policy-engineer (kyverno-json policies), +
override defense-in-depth), D-120 (deterministic not AI). See blockchain-engineer (custom, phase-specific — chain consensus, order
CLARIFY.md for the full resolution text. matching, settlement finality). frontend-engineer is deactivated (no
UI in the pilot).
## 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).
+6 -2
View File
@@ -2308,6 +2308,10 @@ Tags: `v1.25.0` (P0) → `v1.25.5` (P5 = milestone release).
`nova-tfstate-581513795199-us-east-1`. `nova-tfstate-581513795199-us-east-1`.
- REQ-320: `adapters/kyverno-json/policies/pilot-readiness/no-placeholder-account.json` - REQ-320: `adapters/kyverno-json/policies/pilot-readiness/no-placeholder-account.json`
— declarative gate preventing apply against a placeholder account. — 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) ### Phase P4 — pilot-run-and-docs (planned, tag v1.25.4)
- REQ-321: `adapters/README.md` (new consumer row) + - 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 `milestone/v1.26-pilot-activation``main`; tag `v1.25.5` (= the
v1.26 release per prev-minor tagging rule); create Gitea release with v1.26 release per prev-minor tagging rule); create Gitea release with
full milestone summary; delete all milestone branches. 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 (mark v1.26 complete), `NORTH_STAR.md` (note Strategic Objectives #1
+ #3 — first real consumer estate; Post-Pilot denominators activated). + #3 — first real consumer estate; Post-Pilot denominators activated).
- **Requirements:** REQ-310..321 (12 requirements). - **Requirements:** REQ-310..322 (13 requirements).
@@ -60,6 +60,8 @@ contract references the Nova platform's deploy workflow
(`uses: acdl/.github/workflows/deploy.yml@v1.25`) and declares the (`uses: acdl/.github/workflows/deploy.yml@v1.25`) and declares the
blockchain exchange stack (the AWS resources the app needs: ECS for blockchain exchange stack (the AWS resources the app needs: ECS for
the matching engine, DynamoDB for the ledger, S3 for block storage). 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:** **Must-haves:**
- `contract.yaml` — id, name (`blockchain-exchange`), environment - `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/ARCHITECTURE.md` — §12.8 Pilot Estate.
- `.ciagent/nova-blockchain-exchange/README.md` — onboarding guide. - `.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 ### 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 T+1 settlement; consumer deploy via `deploy.yml@v1.25`; 3 Post-Pilot
metrics grounded (outcome backfill + escalation reason + pilot runs); metrics grounded (outcome backfill + escalation reason + pilot runs);
3 kyverno-json policies extending v1.25 (settlement-finality, 3 kyverno-json policies extending v1.25 (settlement-finality,
pilot-readiness, + the existing meta-policies apply); env-JSON wiring pilot-readiness, + the existing meta-policies apply); env-JSON wiring
reconciled. 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).