Phase 5: Judgment policy engine (D-0026, D-0027) #8

Merged
hannah-vernon merged 1 commit from feature/policy-engine into dev 2026-08-20 17:13:47 -05:00

What does this PR do?

Implements the Judgment policy engine per D-0027 (posture, evaluation structure, provisional thresholds) and D-0026 (protected addresses).

  • DefaultPolicyEngine (viegard-default v0.1-provisional): evaluates every classification through the D-0027 chain: emergency stop -> protected-address list -> allowlists -> threshold gates (AI confidence x severity for model-backed, normalized-confidence bands for deterministic; AI can never override a deterministic Deny) -> rate caps -> circuit breaker -> dry-run/manual-approval overlay. Every stage appends a GuardrailEvaluation, pass or fail, so each Decision carries its complete explainability chain.
  • ProtectedAddressList: the D-0026 default CIDRs (IPv4 + IPv6) baked in as configurable defaults; fail-closed: unparseable addresses are treated as protected; IPv4-mapped IPv6 checks the embedded IPv4.
  • PolicyOptions at Viegard:Policy with all D-0027 provisional numbers as configurable defaults, plus a startup validator (CIDR parseability, band ordering, positive durations).
  • IGuardrailStateStore port + in-memory implementation: rolling hour/day action counters, consecutive-failure tracking, circuit breaker, repeat-offender lookback. Postgres implementation deferred (TODO.md).
  • Shipped posture: global dry-run ON, providers OFF, manual approval ON: the engine currently produces decisions and "would act" records only.

Fixes #

How was this tested?

  • dotnet build Viegard.slnx - 0 errors, 0 warnings
  • dotnet test Viegard.slnx - 232/232 pass including 18 live PostgreSQL integration tests (independent re-run after agent implementation)
  • 35 new tests, including the critical ones: a protected-IP incident is Denied even at maximum confidence/severity; dry-run converts Permit to DryRun but never weakens a Deny; emergency stop denies everything; fail-closed on malformed addresses; every decision's guardrail chain is complete
  • Host smoke boot with policy role active

Implementation drafted by a coding agent against the D-0026/D-0027 spec, independently reviewed (protected-list fail-closed and IPv4-mapped handling spot-checked) and re-verified.

Checklist

  • I have read the Contributing Guide
  • Changes are focused - one logical change per PR
  • Documentation updated (AGENT-README.md, TODO.md)
  • New dependencies: none
  • No secrets, credentials, or real infrastructure identifiers in the diff
  • No commented-out code or debug leftovers
## What does this PR do? Implements the Judgment policy engine per D-0027 (posture, evaluation structure, provisional thresholds) and D-0026 (protected addresses). - **`DefaultPolicyEngine`** (`viegard-default` v0.1-provisional): evaluates every classification through the D-0027 chain: emergency stop -> protected-address list -> allowlists -> threshold gates (AI confidence x severity for model-backed, normalized-confidence bands for deterministic; AI can never override a deterministic Deny) -> rate caps -> circuit breaker -> dry-run/manual-approval overlay. Every stage appends a `GuardrailEvaluation`, pass or fail, so each `Decision` carries its complete explainability chain. - **`ProtectedAddressList`**: the D-0026 default CIDRs (IPv4 + IPv6) baked in as configurable defaults; **fail-closed**: unparseable addresses are treated as protected; IPv4-mapped IPv6 checks the embedded IPv4. - **`PolicyOptions`** at `Viegard:Policy` with all D-0027 provisional numbers as configurable defaults, plus a startup validator (CIDR parseability, band ordering, positive durations). - **`IGuardrailStateStore`** port + in-memory implementation: rolling hour/day action counters, consecutive-failure tracking, circuit breaker, repeat-offender lookback. Postgres implementation deferred (TODO.md). - **Shipped posture:** global dry-run ON, providers OFF, manual approval ON: the engine currently produces decisions and "would act" records only. Fixes # ## How was this tested? - [x] `dotnet build Viegard.slnx` - 0 errors, 0 warnings - [x] `dotnet test Viegard.slnx` - **232/232 pass including 18 live PostgreSQL integration tests** (independent re-run after agent implementation) - [x] 35 new tests, including the critical ones: a protected-IP incident is Denied even at maximum confidence/severity; dry-run converts Permit to DryRun but never weakens a Deny; emergency stop denies everything; fail-closed on malformed addresses; every decision's guardrail chain is complete - [x] Host smoke boot with policy role active Implementation drafted by a coding agent against the D-0026/D-0027 spec, independently reviewed (protected-list fail-closed and IPv4-mapped handling spot-checked) and re-verified. ## Checklist - [x] I have read the [Contributing Guide](../CONTRIBUTING.md) - [x] Changes are focused - one logical change per PR - [x] Documentation updated (AGENT-README.md, TODO.md) - [x] New dependencies: none - [x] No secrets, credentials, or real infrastructure identifiers in the diff - [x] No commented-out code or debug leftovers
Add D-0027 policy options, protected CIDR guardrail, in-memory guardrail state, and default policy evaluation with explainable guardrail decisions.

Wire the policy role in PipelineHost with a placeholder worker for Phase 6 classification intake.

Cover protected CIDRs, policy gates, rate caps, circuit breaker, repeat-offender duration, and guardrail state behavior. Update AGENT-README and TODO with remaining follow-up work.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
hannah-vernon/viegard-sentinel!8
No description provided.