Explicit policy precedence at the run boundary + first-class Action::Suppress#152
Merged
Conversation
… boundary
Replace the implicit "Vec position = precedence" rule for run-time
policy layering with an explicit, typed precedence on each ref.
- New nvisy_ontology::policy::PolicyRef { id: Uuid, precedence: u32 }.
Lower precedence wins (0 = most authoritative override; higher
numbers layer underneath as defaults). Required, not optional —
callers explicitly declare the layering for every ref.
- EngineInput.policy_ids: Vec<Uuid> → EngineInput.policies: Vec<PolicyRef>.
NewRun (POST /runs) request body matches.
- Pipeline.execute sorts the refs by precedence (stable; ties preserve
input order) before resolving from the policy cache, so Policies.policies
ends up in true precedence order: index 0 is highest precedence.
- Policies::all_strategies now documents the layered tiebreaker
explicitly: strategy `priority` first, then policy precedence (via
the now-meaningful Vec position), then within-policy insertion order.
Previously the precedence was hidden inside whatever order the caller
happened to pass IDs in — `vec.sort()` on the input would silently
reorder governance. Now the precedence number travels with each ref
and is load-bearing in the type.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…ping
Suppress is now a first-class action that records suppressed entities
in the audit trail and wins over equal-or-lower-priority Redact rules.
- New AuditEntryStatus::Suppressed marks entries for entities matched
by an Action::Suppress rule. The entry carries the suppressing
policy_id and the original value but no replacement.
- Evaluator rewrite: collect all matching strategies per entity, then
Suppress wins iff suppress.priority() <= best matching Redact's
priority (or no Redact matches). Compares the priority field
directly rather than slice index so ties go to Suppress regardless
of insertion order.
- Applicator skips Suppressed entries at the top of each
build_*_redactions loop — they never reach the codec.
- Four unit tests cover the precedence rules.
- Review/Alert/Block remain stubs that log and fall through to the
default-threshold path; tracking them separately.
Also slimmed RedactionMapping to { entity_id, location }, dropping the
non-functional `original: String` / `replacement: Option<String>`
fields that couldn't hold image/audio payloads. Audit values stay on
AuditEntry.value as before. A future blob-backed reversibility
extension is tracked in #151.
Also dropped #[non_exhaustive] from the four *Strategy / Action enums.
The codec matches them exhaustively; non_exhaustive forced silent
catch-all arms that hid new variants from compile-time checks. Cleared
three dead `_ =>` arms in apply.rs after the change.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…ames, file rename pass
Restructure of operation/ for consistent naming and clearer module
layout. No behavioural change.
Detection module:
- Combine entity_recognition and pattern_recognition under
operation/detection/{entity_recognition,pattern_recognition,pattern_engine,rebase_entities}.rs
- New private RebaseEntities extension trait on Entities (formerly
EntitiesExt) carries the per-span → document-relative offset shift
shared by NER and pattern detection.
- New PatternEngineRef wraps the static singleton vs config-built
engine resolution that PatternRecognition::new used to inline;
PatternRecognition::new now just calls PatternEngineRef::new(cfg).
- Document::collect_text_spans hides the locations-then-read loop both
detectors used to inline.
Deduplication file renames (verb_noun pattern):
- grouping.rs → group_entities.rs + group_key.rs (split the trait and
the hash-key type).
- strategy.rs → fuse_entities.rs (DeduplicationStrategyExt::fuse)
- conflict.rs → resolve_conflicts.rs (ConflictResolutionExt::resolve)
- calibration.rs → calibrate_entities.rs (CalibrationExt)
Engine-wide *Op suffix removed:
- ImportFileOp, ExportFileOp, ExtractionOp, GenerateContextOp,
ValidationOp, DeduplicationOp, RedactionOp → ImportFile, ExportFile,
Extraction, GenerateContext, Validation, Deduplication, Redaction.
- Workflow config types of the same name aliased at import sites
(e.g. `use nvisy_ontology::workflow::Validation as ValidationConfig`).
- validation.rs renamed to validate.rs to match verb pattern.
Visibility tightening:
- `pub mod envelope` → `mod envelope` (SharedData re-exported at
operation::SharedData; consumers switched to the shorter path).
- `pub(crate) mod redaction` → `mod redaction` (Redaction re-exported
at operation::Redaction).
Net: the operation/ tree now has consistent naming
(verb_noun.rs for trait helpers, verb.rs for ops, noun.rs for plain
types), private submodules with curated re-exports, and a detection/
folder mirroring the deduplication/ shape.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- graph/{retry,timeout,petgraph}.rs → graph/{retry_ext,timeout_ext,petgraph_ext}.rs
(the files contain extension traits/impls; the `_ext` suffix matches
the trait names RetryExt / TimeoutExt and reads better at import
sites.)
- registry/{cache,content,key,store}.rs →
registry/{resource_cache,content_handle,composite_key,registry_store}.rs
(each file's primary type is the noun in the new name; the prefixed
variants disambiguate from same-named types elsewhere.)
- detection/pattern_engine.rs: document the two PatternEngineRef
variants (Shared = process-wide singleton via
PatternEngine::instance, Owned = freshly compiled per-config
engine). Manual Deref impl stays — derive_more::Deref doesn't
support enums.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
5 tasks
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.
Summary
Two related changes to how policies are layered and how their actions execute:
Also folds in cleanup that came out of the design review: `RedactionMapping` slimmed to its useful core, `#[non_exhaustive]` dropped from the strategy/action enums.
Changes
Policy precedence (`72ec208`)
Action::Suppress implementation (`67bfd0f`)
Slimmed `RedactionMapping` (`67bfd0f`)
Removed `#[non_exhaustive]` (`67bfd0f`)
Test plan
🤖 Generated with Claude Code