feat(enforcement): add enforceable clause taxonomy (ENF-CLA)#19
Merged
Conversation
Introduces ClauseKind — a first-class str enum of 9 semantic clause families (forbidden_import, allowed_import_surface, public_api_only, layer_boundary, forbidden_pattern, required_structure, config_invariant, workflow_policy, iac_policy) — to give the router and adapters a stable semantic target instead of ad-hoc policy key strings. classify_policy_rule() maps dot-separated policy paths to ClauseKind via prefix rules; returns None for unclassifiable paths so the router falls back to unfiltered granular_keys (backward-compatible). All 5 adapters now declare supported_clause_kinds and return ClauseKind values. ProvenanceEntry gains a clause_kind field populated during index build. PolicyRouter uses clause kinds as a secondary routing signal: adapters with non-empty supported_clause_kinds only receive keys whose classified kind matches; adapters with empty clause_kinds are unaffected. _build_enforcement_metadata() expanded from 2 to all 5 adapters and includes supported_clause_kinds in each adapter's details dict. 428 unit tests pass; ruff + mypy clean.
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.
Why
The enforcement router and adapters previously operated on ad-hoc dot-separated policy key strings with no shared semantic vocabulary. This made it impossible to route a policy rule to the right adapter based on what the rule means rather than its textual form. ENF-CLA adds a stable semantic layer that decouples routing logic from string-matching heuristics.
Approach
Introduces
ClauseKind— astrenum of 9 semantic clause families (forbidden_import,allowed_import_surface,public_api_only,layer_boundary,forbidden_pattern,required_structure,config_invariant,workflow_policy,iac_policy). Aclassify_policy_rule()function maps dot-separated policy paths toClauseKindvia prefix rules and returnsNonefor unclassifiable paths, keeping the router fully backward-compatible (falls back to unfilteredgranular_keys).Each of the 5 adapters now declares
supported_clause_kinds.ProvenanceEntrygains aclause_kindfield populated at index build time. ThePolicyRouteruses clause kinds as a secondary routing signal: adapters with non-emptysupported_clause_kindsonly receive keys whose classified kind matches; adapters with emptyclause_kindsare unaffected — preserving existing behavior for those adapters._build_enforcement_metadata()was expanded from 2 to all 5 adapters and now exposessupported_clause_kindsin each adapter's details dict.Key trade-off:
classify_policy_rule()uses prefix matching rather than a full grammar, which is fast and simple but means unusual policy key structures won't classify. TheNonefallback ensures this never silently breaks routing.What Was Tested
ClauseKindclassification verified for all 9 families and for unknown/unclassifiable paths (returnsNone)supported_clause_kindsonly receive matching keys; adapters with emptyclause_kindsstill receive all keysProvenanceEntry.clause_kindpopulation verified during index build_build_enforcement_metadata()output verified for all 5 adaptersRisks
Additive changes only — no existing public API removed. The fallback to unfiltered
granular_keyswhenclassify_policy_rule()returnsNonemeans routing is never worse than before. The one area to watch: if a future adapter declaressupported_clause_kindsbut a policy key fails to classify, that adapter will silently skip the key. This is intentional but reviewers should verify the classification coverage is adequate for the adapters that opt in.