feat(risk): deterministic authority gate + no-self-escalation — #5-core#4
Closed
gesh75 wants to merge 1 commit into
Closed
feat(risk): deterministic authority gate + no-self-escalation — #5-core#4gesh75 wants to merge 1 commit into
gesh75 wants to merge 1 commit into
Conversation
The per-risk-tier authority model — two orthogonal axes (severity vs authority), hard-force overrides, and the no-self-escalation ceiling. Additive new package (core/risk/); no pipeline or schema change. - Severity (NONE..CRITICAL) and Tier (AUTO<HITL<HOTL<BLOCK) as ordered IntEnums. - unify_severity(): one canonical 5-bucket scorer reconciling guards.risk_tier (3-bucket) and bundler._severity (5-bucket) — the CROSS-2 differential, richer engine wins. - classify_change(): regex-reliable AS/RD/RT detection + a documented, fail-closed spine/underlay heuristic (to be backed by clab/NetBox role at integration). - required_authority(): severity base tier, then hard-force — AS/RD/RT -> BLOCK, spine/underlay -> >= HOTL — most-restrictive wins. A twin-clean LOW-severity AS edit still BLOCKs: severity != authority is the real product change. - authorize(): the no-self-escalation ceiling (required <= max_authorized, else BLOCK). max_authorized is a plain input here; binding it to a PIV-signed, compiled-in value is the #5-ceiling / CROSS-3 follow-up — no crypto is claimed. tests/authority_test.py: 14 tests incl. the differential vs both legacy scorers, two hard-force PROPERTY tests (fabric-identity always BLOCK; spine/underlay never AUTO across all severities), and the no-self-escalation 0-violations property. 14/14 green; stress/ wedge(10)/promote/contract pass; PR-1 egress 29/29.
Reviewer's GuideIntroduces a deterministic, test-covered authority model under core/risk that unifies severity scoring, classifies fabric changes, maps severity plus change class to required authority tiers, and enforces a no-self-escalation ceiling, without altering existing pipelines or schemas. Sequence diagram for risk authority evaluation and no-self-escalationsequenceDiagram
participant Planner
participant RiskModel as core_risk
Planner->>RiskModel: classify_change(configs: list[GeneratedConfig])
RiskModel-->>Planner: ChangeClass
Planner->>RiskModel: unify_severity(batfish_errors, devices_affected, sessions_dropped, converged)
RiskModel-->>Planner: Severity
Planner->>RiskModel: required_authority(severity: Severity, change_class: ChangeClass)
RiskModel-->>Planner: Tier required
Planner->>RiskModel: authorize(required: Tier, max_authorized: Tier)
RiskModel-->>Planner: AuthorityDecision (allowed, effective, reason)
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Hey - I've left some high level feedback:
- In
classify_change, theGeneratedConfiginput is treated as a mapping (c.get(...)) despite the type hint suggesting a concrete class; consider either switching to attribute access or defining a small protocol/TypedDict interface so the expected shape is explicit and mismatches are caught earlier. - The underlay/spine heuristics (
_UNDERLAY_CONFIG_RE,_SPINE_NAME_RE,_UNDERLAY_NAME_RE) are quite broad (e.g., any OSPF/ISIS reference marks a change as underlay-touching); if this is intended as a long-term behavior, it might be worth tightening the patterns (or scoping them by context) to avoid over-classifying benign changes as underlay.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- In `classify_change`, the `GeneratedConfig` input is treated as a mapping (`c.get(...)`) despite the type hint suggesting a concrete class; consider either switching to attribute access or defining a small protocol/TypedDict interface so the expected shape is explicit and mismatches are caught earlier.
- The underlay/spine heuristics (`_UNDERLAY_CONFIG_RE`, `_SPINE_NAME_RE`, `_UNDERLAY_NAME_RE`) are quite broad (e.g., any OSPF/ISIS reference marks a change as underlay-touching); if this is intended as a long-term behavior, it might be worth tightening the patterns (or scoping them by context) to avoid over-classifying benign changes as underlay.Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
gesh75
added a commit
that referenced
this pull request
Jun 1, 2026
…authority gate, ceiling, CROSS-3 seal (#9) Squashed release of the 8-PR stack (#2..#8): the offline, hardware-rootable bounded-autonomy receipt and everything under it. AUTO disabled by default; the only un-built piece is the hardware-PIV signer (docs/PIV_HARDWARE_SIGNER_PLAN.md) — software Ed25519 runs the whole demo. - core/llm: single shared LLM egress + adapter (local-first fallback chain, retry/cooldown, 4-event hook bus; raw httpx, no anthropic SDK; air-gap fail-closed). [#2] - evidence: model-identity wedge — change.model_identity sealed into the bundle hash; honest UNKNOWN / unattested states, never a faked attribution. [#3] - core/risk: deterministic AUTO/HITL/HOTL/BLOCK authority model (severity != authority; AS/RD/RT -> BLOCK; spine/underlay -> >= HOTL) + the no-self-escalation ceiling. [#4] - core/promote: gate rule G5 enforces the ceiling from the sealed required tier; change.authority sealed (bundle_version 1.1->1.2). [#5] - core/seal: CROSS-3 detached, offline-verifiable bounded-autonomy receipt (Ed25519; gates G0..G6; refuses to seal an unbounded change). [#6] - serve: /api/preflight/run emits the seal; GET /api/seal/pubkey + POST /api/seal/verify; examiner PDF shows the seal. [#7] - docs: PIV hardware-signer plan — the final, hardware-gated CROSS-3 pass. [#8] Property-tested safety invariants: no-self-escalation 0-violations, every-seal-is-bounded, tamper detection, offline-keyless verify, air-gap fail-closed.
Owner
Author
|
Superseded by the squashed release #9, now merged to main. This PR's changes are in main; closing for tidiness (branch kept for history). |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
#5-core — per-risk-tier authority gate (stacked on the wedge PR)
The deterministic autonomy model: two orthogonal axes — Severity (NONE..CRITICAL) and Authority (AUTO<HITL<HOTL<BLOCK) — with hard-force overrides and the no-self-escalation ceiling. Additive
core/risk/package; no pipeline/schema change (keeps the sealed bundle untouched).What's here
unify_severity— one canonical 5-bucket scorer reconciling AEGIS's two existing engines (guards.risk_tier3-bucket +bundler._severity5-bucket). The CROSS-2 differential (richer engine wins) is encoded as a test.classify_change— regex-reliable AS/RD/RT detection + a documented, fail-closed spine/underlay heuristic (to be backed by containerlab labels / NetBox role at integration).required_authority— severity base tier, then hard-force: AS/RD/RT → BLOCK, spine/underlay → ≥ HOTL, most-restrictive wins. The product insight: a twin-clean LOW-severity AS edit still BLOCKs — severity ≠ authority.authorize— the no-self-escalation ceiling (required ≤ max_authorized, else BLOCK).max_authorizedis a plain input here; binding it to a PIV-signed, compiled-in value is the feat(promote): enforce the no-self-escalation ceiling at the gate — #5-ceiling #5-ceiling / CROSS-3 follow-up — no crypto is claimed (avoids the false-green the completeness critic flagged).Tests —
tests/authority_test.py, 14/14severity table · differential vs both legacy scorers · AS/RD/RT + spine/underlay classification · severity→authority mapping · two hard-force PROPERTY tests (fabric-identity always BLOCK; spine/underlay never AUTO over every severity) · no-self-escalation 0-violations property · end-to-end clean-LOW-AS-edit→BLOCK.
Regression: stress PASS, wedge 10/10, promote/contract PASS, PR-1 egress 29/29.
Next: #5-ceiling wires this into
gate.py(new rule G5) + bindsmax_authorizedto a hardware-PIV-signed ceiling → CROSS-3 SEAL (fuses the model-identity wedge + the signed authority ceiling).Summary by Sourcery
Introduce a deterministic risk authority model that separates severity from authority tiers, with hard-force overrides for critical fabric changes and a no-self-escalation ceiling.
New Features:
Tests: