Expand description
Pre-merge behavior-diff explainer for shieldset changes.
This module implements aperion-shield --diff, the native Rust
port of scripts/shield-diff.py (the Python prototype shipped
alongside docs/shieldset-as-code.md). Both produce a
source-compatible JSON output schema, so CI wired up against the
Python prototype keeps working unchanged when the flag flips to
aperion-shield --diff.
§Why this exists
Shieldset changes are policy changes – they need PR review like
code, but diff shieldset.before.yaml shieldset.after.yaml only
tells you the YAML changed. It does not tell you which calls in
the real corpus will now flip from allow to block, or worse,
from block to allow. That is what this mode is for.
§Pipeline
shieldset.before.yaml ─┐
shieldset.after.yaml ─┤── load Engine x2 ── evaluate corpus x2
corpus.jsonl ─┘ │
v
DecisionLine sets x2
│
v
diff rulesets (added/removed/modified/unchanged)
│
v
pair decisions by index, attribute every flip to
the changed rule(s) that fired under the after-state
│
v
render text / markdown / json§In-process, not subprocess
The Python prototype shells out to aperion-shield --check twice.
This native port skips the subprocess: both runs use the same
Engine::evaluate path the proxy uses. It is materially faster
on big corpora (no JSON re-encode + re-decode trip per line) and
removes the runtime PATH dependency the Python prototype carried.
Re-exports§
pub use evaluate::evaluate_corpus;pub use evaluate::DecisionLine;pub use evaluate::EvalOptions;
Modules§
Structs§
- Diff
Options - CLI-level options for
aperion-shield --diff. Mirrors the Python prototype 1:1 so the--format jsonoutput schema stays source-compatible. - Rule
Delta - Per-rule change: YAML-level (textual) + behavioral (corpus-level).
Mirrors
shield-diff.py::RuleDelta. Serialised in--format jsonoutput – keep the field names stable.
Enums§
- Output
Format - Output format for the diff report.
Constants§
Functions§
- diff_
rulesets - Classify every rule that appears in either ruleset. The YAML diff is rendered eagerly so we don’t pay the cost twice if the renderer is asked to embed it.
- flips_
to_ allow - How many flipped lines ended at
allow. Used by--fail-if-allows-loosened. - load_
ruleset_ yaml - Parse a shieldset YAML file into a
BTreeMap<rule_id, rule_body>whererule_bodyis the rule’s YAML node MINUS itsidfield. Used for diffing rules textually. Tolerates both the wrapped (shieldset:\n rules:) and bare (rules:) forms, matching the Python prototype. - loosening_
count - How many flipped lines moved toward a more permissive decision.
identity_verificationcounts at the same severity asapprovalbecause both gate the call before it runs upstream. - populate_
behavior - Walk paired before/after decision lists, fill in
fires_before/fires_after, build the global flip counter, attribute each flip to the rule(s) that materially changed under the after-state, and return the global flip counter. - run_
diff_ mode - Top-level entry point for
aperion-shield --diff. Returns the shell exit code: 0 for success, 1 for a policy-gate trip (e.g.--fail-if-flipped), 2 for an internal / I/O error. - yaml_
dump_ rule - Dump one rule (id + body) back to YAML for textual diffing.
Always emits
idfirst to keep the diff stable across runs.
Type Aliases§
- Flip
Counter - Aggregate counter:
(decision_before, decision_after) -> count. Lexically ordered so render order is deterministic.