diff --git a/.ciagent/CHECKPOINT.json b/.ciagent/CHECKPOINT.json index 720e058..90b5e4b 100644 --- a/.ciagent/CHECKPOINT.json +++ b/.ciagent/CHECKPOINT.json @@ -1,32 +1,13 @@ { - "phase": 4, - "stage": "complete", - "milestone": "v1.24", - "phase_role": "final", + "phase": 0, + "stage": "specify", + "milestone": "v1.25", + "phase_role": "pre_execution", "attempts": 0, - "updated_at": "2026-08-12T02:50:00Z", + "updated_at": "2026-08-12T16:20:00Z", "project": "acdl", - "milestone_complete": true, - "tag": "v1.23.4", - "tag_line": "v1.23.x", - "requirements": ["REQ-276","REQ-277","REQ-278","REQ-279","REQ-280","REQ-281","REQ-282","REQ-283","REQ-284","REQ-285","REQ-286","REQ-287","REQ-288","REQ-289","REQ-290"], - "release": { - "forge": "gitea", - "releases_created": true, - "release_ids": { - "v1.23.0": 635, - "v1.23.1": 636, - "v1.23.2": 637, - "v1.23.3": 638, - "v1.23.4": 639 - }, - "milestone_release_id": 639, - "milestone_release_tag": "v1.23.4" - }, - "tests": { - "total": 287, - "passed": 287, - "failed": 0 - }, - "notes": "v1.24 milestone complete. Tag v1.23.4 (milestone release, id 639). 15 requirements complete (REQ-276..290). 4 phases. 287 tests pass. Consumer guide accuracy fixes + env-transition detect-and-destroy (no orphan path). Next milestone starts fresh." + "milestone_complete": false, + "tag_line": "v1.24.x", + "next_tag": "v1.24.0", + "notes": "v1.25 milestone started — kyverno-json Unified Policy Engine. 19 requirements (REQ-291..309), 6 phases. Tags on v1.24.x line. Branch gate passed: HEAD on phase/00-specify-clarify-research-plan-grill." } \ No newline at end of file diff --git a/.ciagent/PROJECT.md b/.ciagent/PROJECT.md index be9114e..c33ecf7 100644 --- a/.ciagent/PROJECT.md +++ b/.ciagent/PROJECT.md @@ -1577,3 +1577,124 @@ New requirements REQ-263..REQ-275 — see `REQUIREMENTS.md` §v1.23. Summary: consolidation (REQ-263,264), style restoration (REQ-265,266,267), image inlining (REQ-268), python-pptx generator (REQ-269,270), word-count trim + loaded-scope-term removal (REQ-271,272), CI/tests/README (REQ-273,274,275). + +## v1.25 — kyverno-json Unified Policy Engine + +> **Active milestone.** Feature milestone (the primary compliance/policy +> tool becomes kyverno-json, implemented behind a swappable adapter). +> Branch: `milestone/v1.25-kyverno-json`. Tags run on the **v1.24.x** +> patch line: `v1.24.0` (P0) → `v1.24.1..v1.24.4` (P1–P4) → `v1.24.5` +> (P5 final = milestone release). + +[Nova](https://github.com/kyverno/kyverno-json) `kyverno-json` is a +runtime from the Kyverno ecosystem that applies Kyverno policies to +**any JSON or YAML payload** — not just Kubernetes manifests. This +milestone makes kyverno-json the **primary tool of choice for +compliance / policy checks** in Nova, implemented as an **adapter** +(the `PolicyEngine` protocol) so the platform may one day replace it +with something else (e.g. OPA) without touching the confidence signal +or the pipeline. + +### Why + +Nova's policy posture today is split across three engines with three +different rule languages and three adapter shapes: + +- **Checkov** (`adapters/terraform/policy/checkov_adapter.py`) — the + runtime scanner over `terraform_plan` JSON; carries the + `NOVA_TAG_NAMING` custom rule. Imperative YAML+Python rules. +- **Wiz** (`adapters/wiz/wiz_adapter.py`) — security findings from the + Wiz API; inactive unless credentials are present. +- **Kyverno (K8s)** (`adapters/kyverno/kyverno_adapter.py`) — translates + Kyverno `PolicyReport` results; **inactive for Terraform-only stacks** + (the platform emits Terraform, not K8s manifests — D-053). + +All three emit the same `schemas/policy_check_result.schema.json` shape +that `core/confidence_signal.py` consumes engine-agnostically. The +*contract* is already right; the *orchestration* is fragmented. There is +no single place where "what Nova considers compliant" is declared — +tagging lives in a Checkov custom rule, public-ingress in Checkov's +`RULE_MAP`, env-transition destroy in `core/env_transition.py` +(imperative Python), and capability regression in +`core/regression_verify.py` (imperative Python). Each is a different +language, each drifts independently, and the K8s Kyverno adapter can't +help because it only speaks to K8s manifests. + +`kyverno-json` fixes this: one declarative policy language (Kyverno +policies with JMESPath assertions) that applies to **any** Nova +artifact — the consumer contract, the resolved Stack IR, the +Terraform plan JSON, and even the PolicyCheckResult list itself +(meta-validation). It becomes the **unified orchestrator** of compliance +checks, while Checkov and Wiz remain as raw-finding adapters that feed +*into* kyverno-json meta-policies (so Nova-specific posture rules sit +on top of, not beside, the scanner findings). + +### What the milestone delivers + +- **Swappable `PolicyEngine` protocol** (`core/policy_engine.py`) — a + Python Protocol + registry selected from `config.json` (`policy.engine`, + default `"kyverno-json"`). `KyvernoJsonEngine` implements it (shells + to the `kyverno-json` CLI); a future `OpaEngine` implements the same + protocol. The confidence signal and pipeline never import the engine + directly — they go through the registry. +- **`KyvernoJsonEngine` adapter** (`adapters/kyverno-json/`) — + `evaluate(payload, policies) -> list[PolicyCheckResult]` translates + kyverno-json native output to the existing PCR schema. Mirrors the + Checkov/Wiz adapter pattern. `is_configured()` guard skips gracefully + when the `kyverno-json` binary is absent (same pattern as the Wiz + adapter — emits `SKIPPED`, never breaks the pipeline). +- **Policies over all four Nova artifacts** under + `adapters/kyverno-json/policies/`: + - `contract/` — consumer contract JSON (shape + env-promotion rules). + - `stack-ir/` — resolved Target Stack IR (tagging standard, + public-ingress, encryption-by-default — ports of the v1.0/v1.8 + imperative rules into declarative policies). + - `plan-json/` — `terraform show -json` output (plaintext secrets, + IAM wildcards, KMS references — ports of Checkov's `RULE_MAP`). + - `meta/` — policies over the merged PolicyCheckResult list itself + (e.g. `block-on-any-critical` — the single declarative source of + truth for "critical = block", with the existing + `confidence_signal.py` hard-override kept as defense-in-depth). +- **`run_platform.sh` Step 5 wiring** — Checkov/Wiz still run and emit + raw PCRs; `KyvernoJsonEngine.evaluate()` runs plan-JSON policies in + parallel; both PCR lists merge into the confidence signal's `policy` + input. No change to `core/confidence_signal.py` (it already consumes + `list[PolicyCheckResult]` engine-agnostically). +- **Regression-gate-as-policy** (P4 — quality improvement from the + IDEATE pass): the capability checks in + `core/regression_verify.py` (CAP-013, CAP-023, CAP-024) become + declarative kyverno-json policies over the capability-inventory JSON + frontmatter. Capability regression becomes an audit artifact, not + imperative Python. +- **`policy-engineer` persona** (custom, added in RESEARCH) — owns the + policy territory; declarative-policies constraint; kyverno-json + + JMESPath frameworks. + +**Phase count:** 6 (P0 pre-execution + 4 execution + 1 final). + +**Hard constraints:** +- DO NOT change `schemas/policy_check_result.schema.json` shape in a way + that breaks existing adapters — the contract is the moat. The + `engine` enum already includes `"kyverno"` and `"opa"`; v1.25 records + carry `engine: "kyverno"` (no new enum value — decision in CLARIFY). +- DO NOT remove Checkov or Wiz adapters — they remain as raw-finding + sources feeding into kyverno-json meta-policies. +- DO NOT remove the `confidence_signal.py` `PENALTY["critical"]: None` + hard-override — it stays as defense-in-depth behind the declarative + `block-on-any-critical` meta-policy (decision in CLARIFY). +- DO NOT change `core/confidence_signal.py`'s input contract — it + already consumes `list[PolicyCheckResult]`; v1.25 only changes *who + produces* that list, not *what* the list is. +- The platform must function with `kyverno-json` absent — `is_configured()` + returns false → `SKIPPED` records → confidence signal proceeds (no + hard dependency that breaks the "platform functions without AI / + deterministic scripts" tenet — kyverno-json is deterministic, not AI). + +### Requirements + +New requirements REQ-291..REQ-309 — see `REQUIREMENTS.md` §v1.25. +Summary: engine protocol + registry (REQ-291,292), kyverno-json engine +impl (REQ-293,294), contract policies (REQ-295,296), stack-IR policies +(REQ-297,298,299), plan-JSON policies + pipeline wiring (REQ-300,301,302), +meta-policies (REQ-303), regression-gate policies (REQ-304,305), docs + +adapter README (REQ-306,307), tests (REQ-308,309). diff --git a/.ciagent/REQUIREMENTS.md b/.ciagent/REQUIREMENTS.md index 1cb8e0d..811d059 100644 --- a/.ciagent/REQUIREMENTS.md +++ b/.ciagent/REQUIREMENTS.md @@ -2196,3 +2196,295 @@ assert 20 main + 1 appendix. | REQ-288 | P3 | complete | | REQ-289 | P3 | complete | | REQ-290 | P1 | complete | + +## v1.25 — kyverno-json Unified Policy Engine + +> **Feature milestone.** `kyverno-json` becomes the primary compliance / +> policy tool, implemented behind a swappable `PolicyEngine` adapter so +> OPA (or any other engine) can replace it one day. Tags run on the +> **v1.24.x** line (milestone v1.25 → tags v1.24.0..v1.24.N). Final patch +> = milestone release. +> +> One problem, one architectural correction: +> 1. **Fragmented policy posture.** Nova's compliance rules are split +> across Checkov (imperative YAML + a Python custom rule for tagging), +> Wiz (API findings), the K8s-only Kyverno adapter (inactive for +> Terraform stacks — D-053), and imperative Python in +> `core/env_transition.py` + `core/regression_verify.py`. There is no +> single declarative place where "what Nova considers compliant" lives. +> The K8s Kyverno adapter can't help because it only speaks to K8s +> manifests, and the platform emits Terraform. +> +> The correction: `kyverno-json` (a Kyverno-ecosystem runtime that applies +> Kyverno policies to **any** JSON/YAML payload) becomes the **unified +> orchestrator** of compliance checks. Checkov and Wiz remain as +> raw-finding adapters feeding *into* kyverno-json meta-policies. The +> engine is behind a `PolicyEngine` protocol so it is replaceable. The +> confidence signal is untouched — it already consumes +> `list[PolicyCheckResult]` engine-agnostically. + +### Decisions (locked in CLARIFY, full autonomy) + +- **D-115 (C-1):** `kyverno-json` is a runtime dependency installed via + `go install github.com/kyverno/kyverno-json/cmd/kj@latest` (pinned in a + `scripts/install-kyverno-json.sh` helper; the CI image installs it). + Not a Python package — kyverno-json is a Go binary. The + `KyvernoJsonEngine.is_configured()` checks `which kj` and skips + gracefully when absent (emits `SKIPPED` PCR, mirroring the Wiz adapter). +- **D-116 (C-2):** kyverno-json PCR records carry `engine: "kyverno"` + (no new enum value). The existing `engine` enum in + `schemas/policy_check_result.schema.json` already includes `"kyverno"`; + adding `"kyverno-json"` would force a schema change + checkov_adapter + test regression for no semantic gain. The `ruleId` prefix `KJ_` + distinguishes kyverno-json rules from the K8s Kyverno adapter's + `KYVERNO_` prefix where they overlap. +- **D-117 (C-3):** Checkov and Wiz adapters keep their current + `adapt() -> list[PolicyCheckResult]` signatures. They emit PCRs as + today. The meta-policies in `adapters/kyverno-json/policies/meta/` + consume the **merged** PCR list (checkov + wiz + kyverno-json) as their + input payload, applying Nova-specific posture rules on top. No adapter + signature changes. +- **D-118 (C-4):** `NOVA_TAG_NAMING` (the Checkov custom rule in + `adapters/terraform/policy/custom_rules/nova_tagging.py`) is **kept**. + A kyverno-json mirror policy `require-tagging-standard.json` is added + in `adapters/kyverno-json/policies/stack-ir/`. The P3 meta-policy + `tagging-rules-agree.json` asserts the two engines agree on every + resource; divergence emits an `error` PCR (defense-in-depth against + rule drift). The Checkov rule stays the source of truth for + Terraform-static scanning; the kyverno-json policy covers Stack IR. + +### Category: Policy Engine Core (feat) +- **REQ-291:** `core/policy_engine.py` defines a `PolicyEngine` Python + `Protocol` (PEP 544) with three members: `name -> str`, + `is_configured() -> bool`, and + `evaluate(payload: dict | str, policy_dir: Path, contract_id: str) -> + list[dict]` (where each dict conforms to + `schemas/policy_check_result.schema.json`). A `PolicyEngineRegistry` + singleton selects the active engine from `config.json`'s new + `policy.engine` key (default `"kyverno-json"`); raises + `KeyError` on an unknown engine name. The registry exposes + `get_engine()` and `register(name, factory)`. Pure stdlib, no engine + imports at the protocol layer. +- **REQ-292:** `.ciagent/config.json` gains a new top-level `policy` + object: `{"engine": "kyverno-json", "policy_root": + "adapters/kyverno-json/policies"}`. The registry reads `policy.engine` + to select the active engine and `policy.policy_root` as the default + policy directory. Backward-compatible: if the `policy` key is absent, + the registry returns a `NullEngine` that emits only `SKIPPED` records + (so existing tests that don't set the key still pass). + +### Category: kyverno-json Engine Adapter (feat) +- **REQ-293:** `adapters/kyverno-json/kyverno_json_engine.py` implements + `KyvernoJsonEngine` satisfying the `PolicyEngine` protocol. + `is_configured()` returns `True` when `which kj` succeeds. `evaluate()` + writes the payload to a temp JSON file, invokes + `kj scan --policy --payload -o json`, + parses the native result list, and translates each entry to a PCR dict + (`engine: "kyverno"`, `ruleId` prefixed `KJ_`, severity + mapped, `result` mapped pass/fail/skip → pass/fail/skipped). When + `is_configured()` is false, `evaluate()` returns a single `SKIPPED` + PCR with `ruleId: "KJ_ENGINE_NOT_CONFIGURED"` (mirrors the Wiz + adapter's `is_configured()` guard). Native output parsing is + defensive: any kyverno-json output that doesn't match the expected + shape produces an `error` PCR, never an exception. +- **REQ-294:** `adapters/kyverno-json/__init__.py` exports + `KyvernoJsonEngine`. `adapters/kyverno-json/policies/_smoke.json` + is a single trivial policy (`require-contract-id`) used to validate + the engine round-trip end-to-end in tests. `scripts/install-kyverno-json.sh` + runs `go install github.com/kyverno/kyverno-json/cmd/kj@latest` and + prints `kj version`; documented in `adapters/kyverno-json/README.md`. + The CI image (`.github/workflows/ci.yml` + `.gitea/workflows/ci.yml`) + installs Go + kj when `policy.engine == "kyverno-json"`; the install + is cached. + +### Category: Contract Policies (feat) +- **REQ-295:** `adapters/kyverno-json/policies/contract/` holds + kyverno-json policies over consumer contract JSON. Four policies + mirroring `schemas/contract.schema.json` constraints: + `require-id-pattern.json` (`id` matches `^[a-z][a-z0-9-]{2,5}$`), + `require-env-in-enum.json` (`environment` in dev/qa/prod/dr), + `require-infrastructure-min-1.json` (`infrastructure` has ≥1 entry), + `forbid-unknown-fields.json` (only `id`/`name`/`environment`/ + `infrastructure` allowed). Each policy is a single Kyverno `Policy` + resource with one `validate.assert` rule using JMESPath against the + payload root. Policies are the declarative equivalent of the + jsonschema `required`/`pattern`/`enum` constraints — they let Nova + apply its own compliance posture on top of schema validity. +- **REQ-296:** `core/contract_resolver.py` invokes the + `PolicyEngineRegistry.get_engine().evaluate()` with the contract dict + and `policies/contract/` **before** resolving (early-fail on contract + violations) and emits a `nova.policy.evaluated` metrics event (engine + name in the event payload). Failures feed the confidence signal's + `policy` input as `fail` PCRs; the resolver does not exit — the + confidence signal decides the gate (consistent with the existing + `--soft-fail` Checkov pattern). + +### Category: Stack-IR Policies (feat) +- **REQ-297:** `adapters/kyverno-json/policies/stack-ir/` holds policies + over the resolved Target Stack IR dict. `require-tagging-standard.json` + — every resource carries `nova:owner` + `nova:environment` tags + (ports `adapters/terraform/policy/custom_rules/nova_tagging.py` logic + into a declarative Kyverno policy over the IR's `resources[]` array; + mirrors the v1.8 D-tagging-standard). `forbid-public-ingress.json` — + no resource has `public_ingress: true` (the v1.0 demo rule, now + declarative). `require-encryption-by-default.json` — every S3 bucket + + EBS volume + KMS-aliased resource carries encryption config (ports + the v1.8 D-encryption-default rule). +- **REQ-298:** `core/contract_resolver.py` invokes the engine with the + resolved Stack IR and `policies/stack-ir/` **after** resolving. The + resulting PCRs are appended to the contract-policy PCRs and fed to the + confidence signal. The resolver's existing `tests/test_contract_resolver.py` + continues to pass (the policy call is additive — it does not change + resolver return values or exceptions). +- **REQ-299:** `tests/test_stack_ir_policies.py` + fixture + `tests/fixtures/stack_ir/` — a passing IR (all tags + encryption) and + a failing IR (missing tags, public ingress, plaintext bucket). Each + policy is tested in isolation + the full `policies/stack-ir/` dir as a + bundle. Tests run the `KyvernoJsonEngine` against real `kj` when + `which kj` succeeds, and skip with a `pytest.skip("kj not installed")` + when absent (so CI without the binary doesn't fail). + +### Category: Plan-JSON Policies + Pipeline Wiring (feat) +- **REQ-300:** `adapters/kyverno-json/policies/plan-json/` holds policies + over `terraform show -json` output. `forbid-plaintext-secrets.json` + (ports `CKV_AWS_41/45/46` — no `aws_db_instance.password` / + `aws_iam_user.*` plaintext). `forbid-iam-wildcard.json` (ports + `CKV_AWS_1/40` — no `Action: "*"` or `Resource: "*"` in IAM policies). + `require-kms-reference.json` (ports `CKV_AWS_7/33` — KMS keys referenced + by alias, not inline). Each policy uses JMESPath over the plan's + `planned_values.root_module.resources[]` array. The Checkov `RULE_MAP` + in `checkov_adapter.py` is unchanged — these are declarative mirrors, + not replacements. +- **REQ-301:** `run_platform.sh` Step 5 ("runtime policy scan") gains a + parallel kyverno-json pass: after Checkov/Wiz produce raw PCRs, the + script runs `kj scan --policy adapters/kyverno-json/policies/plan-json/ + --payload -o json` and pipes through + `adapters/kyverno-json/kyverno_json_engine.py` to produce a second PCR + list. Both lists are concatenated and fed to the confidence signal's + `policy` input. The script emits a `nova.policy.evaluated` event with + both engine names. When `which kj` is false, the script logs + "kyverno-json not installed; skipping plan-json policies" and proceeds + with the Checkov/Wiz list only (no hard failure — the platform + functions without kj). +- **REQ-302:** `tests/test_plan_json_policies.py` + fixture + `tests/fixtures/plan_json/` — a passing plan JSON (no secrets, no + wildcard, KMS alias) and a failing plan JSON (plaintext password, + `Action: "*"`, inline KMS key). Tests the three policies in isolation + + as a bundle. `tests/test_run_platform_plan_json_policies.py` + asserts `run_platform.sh` has the kyverno-json Step 5 block and that + it concatenates PCR lists (pattern from `tests/test_pipeline.py:79-95` + — read script text + assert substrings). + +### Category: Meta-Policies (feat) +- **REQ-303:** `adapters/kyverno-json/policies/meta/` holds policies + whose **payload** is the merged `list[PolicyCheckResult]` itself. + `block-on-any-critical.json` — asserts no PCR in the list has + `severity: "critical"` + `result: "fail"`; if any does, the meta-policy + emits a `fail` PCR with `ruleId: "KJ_META_BLOCK_CRITICAL"` and + severity `critical`. This is the **declarative** source of truth for + "critical = block"; the `confidence_signal.py` `PENALTY["critical"]: + None` hard-override stays as defense-in-depth (D-118-adjacent + decision). `tagging-rules-agree.json` — for every resource in the + Stack IR, asserts the Checkov `NOVA_TAG_NAMING` result and the + kyverno-json `KJ_REQUIRE_TAGGING_STANDARD` result agree; divergence + emits an `error` PCR. `tests/test_meta_policies.py` covers both. + +### Category: Regression-Gate Policies (feat, quality improvement from IDEATE) +- **REQ-304:** `adapters/kyverno-json/policies/regression/` holds + policies over the capability-inventory JSON frontmatter + (`CAPABILITY_INVENTORY.md` parsed as structured data). Three policies + port the imperative checks in `core/regression_verify.py`: + `cap-013-adapter-dedup.json` (no duplicate adapter registrations), + `cap-023-metrics-collector.json` (every metric in `docs/METRICS.md` + has a grounded/derived/deferred status), `cap-024-deck-structure.json` + (deck slide structure matches the documented arc). The policies read + the parsed capability inventory as payload and emit `pass`/`fail` PCRs + per capability. The existing `core/regression_verify.py` is **kept** + (it drives the CI gate); the policies are the **declarative mirror** + that makes capability regression auditable as a policy artifact, not + imperative Python. Future milestones may switch the gate to the + policy version. +- **REQ-305:** `tests/test_regression_policies.py` + fixture + `tests/fixtures/capability_inventory.json` — a clean inventory (all + caps pass) and a drifted inventory (duplicate adapter, missing metric + status, broken deck arc). The regression gate (`pytest` suite) + continues to pass 287/287 (or new count); the new policy tests are + additive. + +### Category: Documentation (docs) +- **REQ-306:** `adapters/README.md` gains a new row for the + `kyverno-json` adapter + a new section "Policy Engine Protocol" + documenting the `PolicyEngine` Protocol, the registry, and the + swap boundary (how to add an `OpaEngine`). `adapters/kyverno-json/README.md` + documents the engine, the install path, the policy directory layout, + and the four policy categories (contract/stack-ir/plan-json/meta). +- **REQ-307:** `.ciagent/ARCHITECTURE.md` gains §12.7 "Policy Engine + Registry" with the registry diagram (engine ↔ protocol ↔ registry ↔ + config.json ↔ confidence signal). `schemas/README.md` notes the + `engine: "kyverno"` value is shared by the K8s Kyverno adapter and the + kyverno-json engine (distinguished by `ruleId` prefix). `modules/STANDARDS.md` + gains a "Policy authoring standard" section for module owners who want + to ship per-module kyverno-json policies. `docs/METRICS.md` notes the + policy engine is now swappable (Strategic Objective #2 — provable + trust via a replaceable substrate, not a vendor lock-in). + +### Category: Tests (test) +- **REQ-308:** `tests/test_policy_engine.py` — protocol conformance + (the registry returns an engine implementing all three methods), + unknown-engine `KeyError`, `NullEngine` fallback when the `policy` + key is absent, `KyvernoJsonEngine.is_configured()` returns false when + `which kj` fails (mocked). `tests/test_kyverno_json_engine.py` — + `evaluate()` returns valid PCR dicts against + `schemas/policy_check_result.schema.json` (validated with + `jsonschema`); native-output parsing is defensive (malformed kyverno-json + output → `error` PCR, not exception); `is_configured()==false` → + `SKIPPED` PCR with `KJ_ENGINE_NOT_CONFIGURED`. +- **REQ-309:** All new tests use `pytest.skip("kj not installed")` when + `which kj` is absent, so the suite passes in environments without the + binary (CI matrix: with-kj and without-kj). The full suite + (`pytest tests/`) continues to pass at 287/287 baseline + new tests + (the new tests skip without kj, so the count grows only when kj is + installed). `pyproject.toml` + `requirements-test.txt` unchanged + (kyverno-json is a Go binary, not a Python dep). + +### Out of Scope (v1.25) +- **Removing Checkov or Wiz.** Both stay as raw-finding adapters. The + unified-orchestrator model layers kyverno-json on top, not in place of. +- **`OpaEngine` implementation.** The protocol is the swap boundary; + the OPA implementation is a future milestone. RESEARCH documents the + OPA-equivalent surface so the swap is a known quantity. +- **Per-module policies.** `modules//policies/` is documented as + the future pattern in `modules/STANDARDS.md` but not populated this + milestone (policies live under `adapters/kyverno-json/policies/` + for v1.25). +- **kyverno-json as a long-running service.** v1.25 uses the CLI + (`kj scan`); the `kj serve` web-app mode is a future consideration + for lower-latency evaluation (RESEARCH notes it). +- **Replacing the K8s Kyverno adapter.** The K8s adapter + (`adapters/kyverno/`) remains documentation-only (D-053 — platform + emits Terraform). The kyverno-json engine and the K8s adapter are + siblings, not replacements. + +### v1.25 Traceability + +| REQ | Phase | Status | +|-----|-------|--------| +| REQ-291 | P1 | pending | +| REQ-292 | P1 | pending | +| REQ-293 | P1 | pending | +| REQ-294 | P1 | pending | +| REQ-295 | P2 | pending | +| REQ-296 | P2 | pending | +| REQ-297 | P2 | pending | +| REQ-298 | P2 | pending | +| REQ-299 | P2 | pending | +| REQ-300 | P3 | pending | +| REQ-301 | P3 | pending | +| REQ-302 | P3 | pending | +| REQ-303 | P3 | pending | +| REQ-304 | P4 | pending | +| REQ-305 | P4 | pending | +| REQ-306 | P4 | pending | +| REQ-307 | P4 | pending | +| REQ-308 | P1 | pending | +| REQ-309 | P1 | pending | diff --git a/.ciagent/ROADMAP.md b/.ciagent/ROADMAP.md index 3c29170..c1e473c 100644 --- a/.ciagent/ROADMAP.md +++ b/.ciagent/ROADMAP.md @@ -2156,3 +2156,93 @@ release). **DONE.** milestone). Tag `v1.22.6` (final patch = milestone release). Merge `milestone/v1.23-deck-cleanup-python-pptx` → `main`. - **Requirements:** REQ-263..275 (13 requirements). + +## v1.25 (active, tag line `v1.24.x`): kyverno-json Unified Policy Engine + +`kyverno-json` — a Kyverno-ecosystem runtime that applies Kyverno policies +to **any** JSON/YAML payload — becomes Nova's **primary compliance / +policy tool**, implemented behind a swappable `PolicyEngine` adapter so +OPA (or any other engine) can replace it one day. The unified-orchestrator +model: Checkov and Wiz remain as raw-finding adapters feeding *into* +kyverno-json meta-policies; the confidence signal is untouched (it already +consumes `list[PolicyCheckResult]` engine-agnostically). Policies cover +all four Nova artifacts: consumer contract JSON, resolved Stack IR, +Terraform plan JSON, and the merged PCR list itself (meta-validation). +The K8s-only Kyverno adapter stays documentation-only (D-053); the +kyverno-json engine and the K8s adapter are siblings, not replacements. +Quality improvement from the IDEATE pass: capability regression checks +(`core/regression_verify.py` CAP-013/023/024) become declarative +kyverno-json policies. New `policy-engineer` persona owns the policy +territory. 19 requirements (REQ-291..309), 6 phases (P0 + P1..P4 + P5 +final). Tags: `v1.24.0` (P0) → `v1.24.5` (P5 = milestone release). + +### Phase P1 — engine-core (planned, tag v1.24.1) +- REQ-291: `core/policy_engine.py` — `PolicyEngine` Protocol + + `PolicyEngineRegistry` (selects engine from `config.json.policy.engine`). +- REQ-292: `config.json` gains `policy` object + (`engine: "kyverno-json"`, `policy_root`). +- REQ-293: `adapters/kyverno-json/kyverno_json_engine.py` — + `KyvernoJsonEngine` (shells to `kj scan`; translates native output → + PCR; `is_configured()` guards on `which kj`). +- REQ-294: `adapters/kyverno-json/__init__.py` + `_smoke.json` policy + + `scripts/install-kyverno-json.sh` + CI image install. +- REQ-308: `tests/test_policy_engine.py` — protocol conformance, + registry, NullEngine fallback. +- REQ-309: `tests/test_kyverno_json_engine.py` — PCR schema validity, + defensive parsing, `pytest.skip` when kj absent. + +### Phase P2 — contract + stack-IR policies (planned, tag v1.24.2) +- REQ-295: `adapters/kyverno-json/policies/contract/` — 4 policies over + consumer contract JSON (id-pattern, env-enum, infra-min-1, + forbid-unknown-fields). +- REQ-296: `core/contract_resolver.py` invokes the engine pre-resolve + (contract policies) — early-fail, confidence signal decides the gate. +- REQ-297: `adapters/kyverno-json/policies/stack-ir/` — 3 policies over + resolved Stack IR (tagging-standard, public-ingress, encryption-by- + default — ports of v1.0/v1.8 imperative rules). +- REQ-298: `core/contract_resolver.py` invokes the engine post-resolve + (stack-IR policies); additive — existing tests pass. +- REQ-299: `tests/test_stack_ir_policies.py` + fixtures (passing + failing + IR; skip when kj absent). + +### Phase P3 — plan-JSON policies + meta-orchestration + pipeline wiring (planned, tag v1.24.3) +- REQ-300: `adapters/kyverno-json/policies/plan-json/` — 3 policies over + `terraform show -json` (plaintext-secrets, iam-wildcard, kms-reference + — ports of `checkov_adapter.py:RULE_MAP`). +- REQ-301: `run_platform.sh` Step 5 gains a parallel kyverno-json pass; + both PCR lists (checkov/wiz + kj) concatenate into the confidence + signal's `policy` input; skips gracefully when `which kj` is false. +- REQ-302: `tests/test_plan_json_policies.py` + fixtures; + `tests/test_run_platform_plan_json_policies.py` (script-substring + assertion). +- REQ-303: `adapters/kyverno-json/policies/meta/` — + `block-on-any-critical.json` (declarative critical-block; the + `confidence_signal.py` hard-override stays as defense-in-depth) + + `tagging-rules-agree.json` (asserts Checkov + kj agree on tagging). + `tests/test_meta_policies.py`. + +### Phase P4 — regression-gate policies + docs (planned, tag v1.24.4) +- REQ-304: `adapters/kyverno-json/policies/regression/` — 3 policies over + capability-inventory JSON (CAP-013/023/024) — declarative mirrors of + `core/regression_verify.py` checks. +- REQ-305: `tests/test_regression_policies.py` + fixtures (clean + + drifted inventory); regression gate still 287/287 baseline. +- REQ-306: `adapters/README.md` (new adapter row + PolicyEngine Protocol + section) + `adapters/kyverno-json/README.md`. +- REQ-307: `.ciagent/ARCHITECTURE.md` §12.7 (Policy Engine Registry) + + `schemas/README.md` + `modules/STANDARDS.md` (policy-authoring + standard) + `docs/METRICS.md` (swappable engine narrative). + +### Phase P5 — final review + audit + milestone ship (Final Phase, tag v1.24.5) +- Multi-persona code review across P1..P4 (lead-developer, backend- + engineer, data-engineer, policy-engineer). Auto-fix P0; flag P1+. +- Audit: reconstruction test (git log ↔ `.ciagent/`), branch hygiene, + commit discipline. +- Milestone ship: merge `phase/05-final-review-ship` → + `milestone/v1.25-kyverno-json` → `main`; tag `v1.24.5` (= the v1.25 + release per prev-minor tagging rule); create Gitea release with full + milestone summary; delete all milestone branches. +- Update `REQUIREMENTS.md` (mark REQ-291..309 complete), `ROADMAP.md` + (mark v1.25 complete), `NORTH_STAR.md` (note Strategic Objective #2 — + provable trust via a replaceable policy-engine substrate). +- **Requirements:** REQ-291..309 (19 requirements). diff --git a/.ciagent/config.json b/.ciagent/config.json index dc350a3..674a825 100644 --- a/.ciagent/config.json +++ b/.ciagent/config.json @@ -8,7 +8,7 @@ ], "active_project": "acdl", "active_projects": ["acdl"], - "active_milestone": "v1.24", + "active_milestone": "v1.25", "autonomy": { "level": "full", "escalation_hooks": ["deploy", "delete_data", "merge_to_main"],