Policy Exception Effects

Audience: Policy authors, reviewers, operators, and governance owners.
Scope: How exception definitions are authored, resolved, and surfaced by the Policy Engine during evaluation, including precedence rules, metadata flow, and simulation/diff behaviour.

Exception effects let teams codify governed waivers without compromising determinism. This guide explains the artefacts involved, how the evaluator selects a single winning exception, and where downstream consumers observe the applied override.


1 · Exception Building Blocks

ArtefactDescription
Exception EffectDeclared inside a policy pack (exceptions.effects). Defines the override behaviour plus governance metadata. See effect fields in §2.
Routing TemplateOptional mapping (exceptions.routingTemplates) used by Authority to route approvals/MFA. Effects reference templates by id.
Exception InstanceStored outside the policy pack (Authority/API). Captures who requested the waiver, scope filters, metadata, and creation time.

Effects are validated at bind time (PolicyBinder), while instances are ingested alongside policy evaluation inputs. Both are normalized to case-insensitive identifiers to avoid duplicate conflicts.


2 · Effect Fields

FieldRequiredPurposeNotes
idStable identifier ([A-Za-z0-9-_]+).Must be unique per policy pack.
nameFriendly label for consoles/reports.Forwarded to verdict metadata if present.
effectBehaviour enum: suppress, defer, downgrade, requireControl.Case-insensitive.
downgradeSeverity⚠️Target severity for downgrade.Must map to DSL severities (high, medium, etc.). Validation enforced in PolicyBinder (policy.exceptions.effect.downgrade.missingSeverity).
requiredControlId⚠️Control catalogue key for requireControl.Required when effect is requireControl.
routingTemplateConnects to an Authority approval flow.CLI/Console resolve to authorityRouteId.
maxDurationDaysSoft limit for temporary waivers.Must be > 0 when provided.
descriptionRich-text rationale.Displayed in approvals centre (optional).

Authoring invalid combinations returns structured errors with JSON paths, preventing packs from compiling (see src/Policy/__Tests/StellaOps.Policy.Tests/PolicyBinderTests.cs:33). Routing templates additionally declare authorityRouteId and requireMfa flags for governance routing.


3 · Exception Instances & Scope

Instances are resolved from Authority or API collections and injected into the evaluation context (PolicyEvaluationExceptions). Each instance contains:

FieldSourceUsage
idAuthority storagePropagated to annotations and appliedException.exceptionId.
effectIdLinks to pack-defined effectMust resolve to a known effect; otherwise ignored.
scope.ruleNamesOptional listLimits to specific rule identifiers.
scope.severitiesOptional list (severity.normalized)Normalized against the evaluator’s severity string.
scope.sourcesOptional advisory sources (GHSA, NVD, …)Compared against the advisory context.
scope.tagsOptional SBOM tagsMatched using sbom.has_tag(...).
createdAtRFC3339 UTC timestampUsed as tie-breaker when specificity scores match.
metadataArbitrary key/value bagCopied to verdict annotations (exception.meta.*).

Scopes are case-insensitive and trimmed. Empty scopes behave as global waivers but still require routing and metadata supplied by Authority workflows.


4 · Resolution & Specificity

Only one exception effect is applied per finding. Evaluation proceeds as follows:

  1. Filter instances whose effectId resolves to a known effect.
  2. Discard instances whose scope does not match the candidate finding (rule name, severity, advisory source, SBOM tags).
  3. Score remaining instances for specificity:
    • ruleNames1000 + (count × 25)
    • severities500 + (count × 10)
    • sources250 + (count × 10)
    • tags100 + (count × 5)
  4. Highest score wins. Ties fall back to the newest createdAt, then lexical id (stable sorting).

These rules guarantee deterministic selection even when multiple waivers overlap. See src/Policy/__Tests/StellaOps.Policy.Engine.Tests/PolicyEvaluatorTests.cs:209 for tie-break coverage.


5 · Effect Behaviours

EffectStatus impactSeverity impactWarnings / metadata
suppressForces status suppressed.No change.exception.status=suppressed.
deferForces status deferred.No change.exception.status=deferred.
downgradeNo change.Sets severity to configured downgradeSeverity.exception.severity annotation.
requireControlNo change.No change.Adds warning Exception '<id>' requires control '<requiredControlId>'. Annotation exception.requiredControl.

All effects stamp shared annotations: exception.id, exception.effectId, exception.effectType, optional exception.effectName, optional exception.routingTemplate, plus exception.maxDurationDays. Instance metadata is surfaced both in annotations (exception.meta.<key>) and the structured AppliedException.Metadata payload for downstream APIs. Behaviour is validated by unit tests (src/Policy/__Tests/StellaOps.Policy.Engine.Tests/PolicyEvaluatorTests.cs:130 & src/Policy/__Tests/StellaOps.Policy.Engine.Tests/PolicyEvaluatorTests.cs:169).


6 · Explain, Simulation & Outputs

Example verdict excerpt (JSON):

{
  "status": "suppressed",
  "severity": "Critical",
  "annotations": {
    "exception.id": "exc-001",
    "exception.effectId": "suppress-critical",
    "exception.effectType": "Suppress",
    "exception.status": "suppressed",
    "exception.meta.requestedBy": "alice"
  },
  "appliedException": {
    "exceptionId": "exc-001",
    "effectId": "suppress-critical",
    "effectType": "Suppress",
    "originalStatus": "blocked",
    "appliedStatus": "suppressed",
    "metadata": {
      "effectName": "Rule Critical Suppress",
      "requestedBy": "alice"
    }
  }
}

7 · Operational Notes


8 · Testing References


9 · Compliance Checklist


Last updated: 2025-10-27 (Sprint 25).