docs(init): validate specification — v1.25 kyverno-json unified policy engine
Establishes the v1.25 milestone: kyverno-json becomes Nova's primary compliance/policy tool, implemented behind a swappable PolicyEngine adapter (so OPA can replace it one day). Unified-orchestrator model — checkov/wiz remain as raw-finding adapters feeding into kyverno-json meta-policies. Policies cover all 4 Nova artifacts: contract JSON, resolved Stack IR, terraform plan JSON, and the merged PCR list itself. Quality improvement from IDEATE: capability regression checks become declarative kyverno-json policies. New policy-engineer persona. 19 requirements (REQ-291..309), 6 phases (P0 + P1..P4 + P5 final). Tags on v1.24.x line: v1.24.0 (P0) → v1.24.5 (P5 = milestone release). ---ci--- project: acdl phase: 0 milestone: v1.25 status: specify ---/ci---
This commit is contained in:
@@ -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."
|
||||
}
|
||||
@@ -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).
|
||||
|
||||
@@ -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 <policy_dir> --payload <payload.json> -o json`,
|
||||
parses the native result list, and translates each entry to a PCR dict
|
||||
(`engine: "kyverno"`, `ruleId` prefixed `KJ_<policy_name>`, severity
|
||||
mapped, `result` mapped pass/fail/skip → pass/fail/skipped). When
|
||||
`is_configured()` is false, `evaluate()` returns a single `SKIPPED`
|
||||
PCR with `ruleId: "KJ_ENGINE_NOT_CONFIGURED"` (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 <tfshow.json> -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/<name>/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 |
|
||||
|
||||
@@ -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).
|
||||
|
||||
@@ -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"],
|
||||
|
||||
Reference in New Issue
Block a user