# VERIFY — P1 engine-core (v1.25)

> 4-layer verify gate: structural, behavioral, security, quality.
> Phase: P1. Requirements: REQ-291..294, 308, 309. Result: PASS.

## Structural

- `core/policy_engine.py` exists, implements `PolicyEngine` Protocol
  (PEP 544, `@runtime_checkable`), `PolicyEngineRegistry` with
  `register()` + `get_engine()`, `NullEngine` fallback.
- `adapters/kyverno-json/kyverno_json_engine.py` exists, exports
  `KyvernoJsonEngine` with `name`, `is_configured()`, `evaluate()`.
- `adapters/kyverno-json/__init__.py` loads the engine by file path
  (the dir name has a hyphen — not a valid Python package name).
- `adapters/kyverno-json/policies/_smoke.json` exists (trivial policy
  for round-trip validation).
- `scripts/install-kyverno-json.sh` exists (go install kj@latest).
- `.ciagent/config.json` has the `policy` object
  (`engine: kyverno-json`, `policy_root`).
- `.gitea/workflows/ci.yml` + `.github/workflows/ci.yml` have the
  Go + kj install step (best-effort, tests skip when kj absent).
- `tests/test_policy_engine.py` (10 tests) +
  `tests/test_kyverno_json_engine.py` (16 tests) exist.

## Behavioral

- `pytest tests/test_policy_engine.py tests/test_kyverno_json_engine.py`:
  **24 passed, 2 skipped** (kj not installed — expected;
  `pytest.skip("kj not installed")`).
- `NullEngine` satisfies the `PolicyEngine` Protocol (G-Q8a —
  `isinstance(NullEngine(), PolicyEngine)` is True). Proves the swap
  boundary is real without implementing OPA.
- `KyvernoJsonEngine.is_configured()` returns `False` when
  `which kj` is absent → `evaluate()` returns a single
  `KJ_ENGINE_NOT_CONFIGURED` SKIPPED PCR (distinct `ruleId` from
  NullEngine's `NULL_ENGINE_INACTIVE` — G-Q4).
- PCR records validate against `schemas/policy_check_result.schema.json`
  (via `jsonschema.validate` in tests).
- Defensive parsing: malformed kyverno-json output → `error` PCR
  (`KJ_ENGINE_ERROR`), never an exception.
- Severity annotation reading (G-Q10a): policies with
  `nova.cloudinit.dev/severity: high` produce PCRs with `severity: high`;
  policies without the annotation default to `info`.
- Registry: `get_engine()` returns the configured engine; unknown
  engine name raises `KeyError`; `policy` key absent → `NullEngine`.
- No regression: `pytest tests/test_confidence_signal.py
  tests/test_adapter.py tests/test_checkov_adapter.py
  tests/test_kyverno_adapter.py tests/test_contract_resolver.py` —
  **132 passed** (unchanged).

## Security

- No new secrets, no new network calls in the engine core (the engine
  shells to a local binary; the binary makes no network calls for
  `scan`).
- `is_configured()` guard ensures the platform runs without the binary
  (no hard dependency that could be exploited as a DoS vector).
- The engine writes the payload to a temp file (`tempfile.NamedTemporaryFile`)
  and unlinks it in a `finally` block (no leftover payload on disk).
- No `shell=True` in the `subprocess.run` call (command is a list —
  no shell injection surface).

## Quality

- `python3 -m py_compile` passes on all new Python files.
- The `PolicyEngine` Protocol is minimal (3 members) — the swap
  boundary is the moat (NORTH_STAR Strategic Objective #2).
- The `NullEngine` proves a second implementation exists (structural
  conformance) — the OPA swap is a known quantity (RESEARCH §4.2).
- Tests use `pytest.skip` when `which kj` is absent, so the CI matrix
  passes with or without the binary (the suite is green in both cases).

## Must-have checklist

- [x] `PolicyEngine` Protocol + `PolicyEngineRegistry` + `NullEngine`
      (REQ-291)
- [x] `config.json.policy` object (REQ-292)
- [x] `KyvernoJsonEngine` adapter (REQ-293)
- [x] `__init__.py` + `_smoke.json` + `install-kyverno-json.sh` + CI
      install (REQ-294)
- [x] `test_policy_engine.py` — protocol conformance, registry,
      NullEngine fallback (REQ-308)
- [x] `test_kyverno_json_engine.py` — PCR schema validity, defensive
      parsing, skip-without-kj (REQ-309)

**Verdict: PASS** — all P1 must-haves met, no regressions, 24 new
tests pass (2 skip-without-kj), 132 existing tests unchanged.