Skip to content

feat(enforcement): add enforceable clause taxonomy (ENF-CLA)#19

Merged
kschlt merged 1 commit into
mainfrom
feat/enforcement-clause-taxonomy
Apr 3, 2026
Merged

feat(enforcement): add enforceable clause taxonomy (ENF-CLA)#19
kschlt merged 1 commit into
mainfrom
feat/enforcement-clause-taxonomy

Conversation

@kschlt
Copy link
Copy Markdown
Owner

@kschlt kschlt commented Apr 3, 2026

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 — a 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). A classify_policy_rule() function maps dot-separated policy paths to ClauseKind via prefix rules and returns None for unclassifiable paths, keeping the router fully backward-compatible (falls back to unfiltered granular_keys).

Each of the 5 adapters now declares supported_clause_kinds. ProvenanceEntry gains a clause_kind field populated at index build time. The 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 — preserving existing behavior for those adapters.

_build_enforcement_metadata() was expanded from 2 to all 5 adapters and now exposes supported_clause_kinds in 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. The None fallback ensures this never silently breaks routing.

What Was Tested

  • 502 unit + integration tests pass (428 at time of commit, suite has grown)
  • ClauseKind classification verified for all 9 families and for unknown/unclassifiable paths (returns None)
  • Routing behaviour verified: adapters with supported_clause_kinds only receive matching keys; adapters with empty clause_kinds still receive all keys
  • ProvenanceEntry.clause_kind population verified during index build
  • _build_enforcement_metadata() output verified for all 5 adapters

Risks

Additive changes only — no existing public API removed. The fallback to unfiltered granular_keys when classify_policy_rule() returns None means routing is never worse than before. The one area to watch: if a future adapter declares supported_clause_kinds but 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.

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.
@kschlt kschlt merged commit 0247d18 into main Apr 3, 2026
8 checks passed
@kschlt kschlt deleted the feat/enforcement-clause-taxonomy branch April 3, 2026 08:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant