A1 install path → go install (D-115) A2 engine enum → reuse kyverno, distinguish by ruleId KJ_ prefix (D-116) A3 checkov/wiz signatures unchanged; meta-policies consume merged PCR list (D-117) A4 NOVA_TAG_NAMING kept + kyverno-json mirror + tagging-rules-agree meta-policy (D-118) A5 critical-override kept as defense-in-depth behind declarative meta-policy (D-119) A6 kyverno-json is deterministic not AI; is_configured guard ensures platform functions without it (D-120) ---ci--- project: acdl phase: 0 milestone: v1.25 status: clarify ---/ci---
8.9 KiB
CLARIFY — v1.25 kyverno-json Unified Policy Engine
Autonomy: full. Ambiguities are auto-resolved with assumption logging per
config.json autonomy.level: "full"andautonomy.decision_confidence_threshold: 0.6. No human escalation.
Ambiguities Identified
A1 — kyverno-json install path (pip / go install / pinned binary release)
Ambiguity: kyverno-json is a Go project, not a Python package. Three
install paths exist: (a) pip install — not possible (no PyPI package);
(b) go install github.com/kyverno/kyverno-json/cmd/kj@latest — requires
Go toolchain in the CI image; (c) download a pinned binary release from
GitHub releases — no Go toolchain needed, but release artifacts are
platform-specific and must be checksummed.
Resolution (auto, confidence 0.85): go install (option b). A
scripts/install-kyverno-json.sh helper runs
go install github.com/kyverno/kyverno-json/cmd/kj@latest and prints
kj version. The CI image (.github/workflows/ci.yml +
.gitea/workflows/ci.yml) installs Go + kj when
config.json.policy.engine == "kyverno-json"; the install is cached via
the existing Go module cache. Rationale: go install is the upstream-
blessed path, tracks the latest stable release, avoids per-platform
binary management, and the project already accepts Go-based tooling
(checkov pulls Go-built transitive deps via pip). When which kj is
absent, KyvernoJsonEngine.is_configured() returns false → SKIPPED
PCR (mirrors the Wiz adapter pattern) — the platform functions without
the binary. Captured in REQ-293, REQ-294. Decision ID: D-115.
A2 — engine enum value: new "kyverno-json" vs reuse "kyverno"
Ambiguity: schemas/policy_check_result.schema.json already lists
engine: ["checkov", "kyverno", "opa", "wiz"]. kyverno-json is a
distinct runtime from the K8s Kyverno admission controller, but both
are "Kyverno." Two options: (a) add a new "kyverno-json" enum value
— requires schema change + checkov/wiz adapter test regression check;
(b) reuse "kyverno" and distinguish by ruleId prefix.
Resolution (auto, confidence 0.80): Reuse "kyverno" (option b).
Adding "kyverno-json" would force a schema change + a test sweep for
no semantic gain — the engine field records the policy engine family,
not the specific binary. kyverno-json PCR records carry engine: "kyverno" and ruleId prefixed KJ_<policy_name> (e.g.
KJ_REQUIRE_TAGGING_STANDARD), while the K8s adapter uses KYVERNO_
prefixes (e.g. KYVERNO_INACTIVE_TF_STACK). The two are distinguishable
in audit/telemetry by ruleId prefix and evidence payload shape (the
K8s adapter's evidence has namespace/kind; kyverno-json's has
assertion/jmespath). No schema change. Captured in REQ-293.
Decision ID: D-116.
A3 — Do checkov/wiz adapters change their signatures to feed kyverno-json?
Ambiguity: The unified-orchestrator model places kyverno-json "on
top of" checkov/wiz. Two interpretations: (a) checkov/wiz now emit a
"raw findings" intermediate (not PCR) that kyverno-json meta-policies
consume — requires changing adapt() -> list[PolicyCheckResult] to
adapt() -> list[RawFinding]; (b) checkov/wiz keep emitting PCRs as
today, and the meta-policies in adapters/kyverno-json/policies/meta/
consume the merged PCR list as their payload.
Resolution (auto, confidence 0.90): Option (b). The existing
adapt() -> list[PolicyCheckResult] signatures are unchanged. The
meta-policies consume the merged PCR list (checkov + wiz + kyverno-json
plan-JSON policies) as their input payload. This preserves the
PolicyCheckResult schema as the single inter-adapter contract
(ARCHITECTURE.md §12.6), avoids a new "RawFinding" type, and means
the existing checkov/wiz adapter tests pass unchanged. The meta-policy
block-on-any-critical.json iterates the merged list; the
tagging-rules-agree.json meta-policy cross-checks the Checkov
NOVA_TAG_NAMING result against the kyverno-json
KJ_REQUIRE_TAGGING_STANDARD result by resourceRef. Captured in
REQ-303, D-117. Decision ID: D-117.
A4 — NOVA_TAG_NAMING Checkov rule: rewrite as kyverno-json policy, keep, or both?
Ambiguity: The Checkov custom rule
adapters/terraform/policy/custom_rules/nova_tagging.py enforces the
Nova tagging standard over Terraform HCL (static scan + plan scan). The
kyverno-json milestone adds require-tagging-standard.json over the
resolved Stack IR. Three options: (a) rewrite — replace the Checkov
rule with the kyverno-json policy (loses Checkov's HCL-level coverage
and the --external-checks-dir integration); (b) keep Checkov only —
don't add a kyverno-json policy (the Stack IR is already the input to
terraform, so the Checkov rule catches it); (c) both — keep the
Checkov rule as the source of truth for HCL-level scanning AND add the
kyverno-json policy for IR-level coverage, with a meta-policy that
asserts the two agree.
Resolution (auto, confidence 0.82): Option (c) — both, with a
cross-check meta-policy. The Checkov rule stays the source of truth
for terraform_plan scanning (it reads HCL resource blocks directly);
the kyverno-json policy covers the Stack IR dict (which is the input
before terraform, so it catches IR-level violations that the
terraform adapter might mask via defaults). 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 — if the two engines disagree, the operator must
investigate before proceeding). This is the only case in v1.25 where
two engines evaluate the same concern; it is intentional — the
tagging standard is the highest-impact rule (v1.8 D-tagging-standard,
v1.10 re-verification) and merits redundancy. Captured in REQ-297,
REQ-303, REQ-299. Decision ID: D-118.
A5 — Critical-override: delegate to declarative meta-policy or keep hard-override?
Ambiguity: core/confidence_signal.py lines 144-157 hardcode
PENALTY["critical"]: None — a critical-severity fail PCR forces
score = 0, band = block regardless of the weighted-sum inputs. The
v1.25 meta-policy block-on-any-critical.json makes this declarative
(asserts no PCR in the merged list has severity: critical +
result: fail). Two options: (a) fully delegate — remove the
hard-override, rely on the meta-policy to emit a critical fail PCR
that the existing penalty logic then blocks; (b) keep both — the
meta-policy is the declarative source of truth, the hard-override is
defense-in-depth.
Resolution (auto, confidence 0.88): Option (b) — keep both. The
meta-policy is the declarative statement ("Nova blocks on any
critical finding from any engine"); the hard-override is the
imperative safety net that ensures a critical PCR can never slip
through even if the meta-policy is misconfigured or the
PolicyEngineRegistry returns a NullEngine. This is
defense-in-depth, not redundancy-for-its-own-sake: the meta-policy
runs before the confidence signal (it produces PCRs that flow in),
the hard-override runs inside the confidence signal (it is the last
gate). Removing the hard-override would make the platform's
"critical = block" guarantee depend on a single declarative policy
file — a regression in the provable-trust posture (Strategic
Objective #2). Captured in REQ-303, PROJECT.md hard-constraints.
Decision ID: D-119.
A6 — Does kyverno-json break the "platform functions without AI" tenet?
Ambiguity: NORTH_STAR.md Strategic Objective #2: "the platform functions without AI — 'AI decisions' are really automated decisions." kyverno-json is a deterministic policy engine (no ML), but it is a new runtime dependency. Does adding it violate the tenet?
Resolution (auto, confidence 0.95): No — kyverno-json is
deterministic, not AI. The tenet distinguishes "AI decisions" (LLM-
driven, non-reproducible) from "automated decisions" (rule-driven,
reproducible). kyverno-json is the latter — the same policy + payload
produces the same result on every run. 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), so the tenet
holds even in environments where kyverno-json is not installed.
Captured in PROJECT.md hard-constraints + RESEARCH.md G-Q1.
Decision ID: D-120.
Summary
6 ambiguities identified; 6 auto-resolved at full autonomy (no human escalation). All resolutions are binding and recorded as D-115..D-120. The resolutions are captured in PROJECT.md hard-constraints, REQUIREMENTS.md v1.25 sections, and will be referenced in RESEARCH.md + PLAN.md. No PROJECT.md or REQUIREMENTS.md structural changes beyond the v1.25 sections added in SPECIFY — the resolutions are already embedded in the requirement text (REQ-293, REQ-297, REQ-303, etc.) via the "Decision" annotations.