Skip to content

feat(enforce): staged enforcement — policy checks at commit, push, and CI#10

Merged
kschlt merged 2 commits intomainfrom
feat/staged-enforcement
Mar 22, 2026
Merged

feat(enforce): staged enforcement — policy checks at commit, push, and CI#10
kschlt merged 2 commits intomainfrom
feat/staged-enforcement

Conversation

@kschlt
Copy link
Copy Markdown
Owner

@kschlt kschlt commented Mar 22, 2026

Why

ADR policies were only checked at CI time, which means violations are caught too late — after code is already pushed and waiting in a pipeline. Developers need fast, targeted feedback at the point where a decision is relevant: a missing status field at commit time, an undocumented architecture change before push, a full structural audit in CI. This implements that tiered model.

Approach

Three new modules form the core:

  • enforce/stages.py — defines EnforcementLevel (commit/push/ci) and maps policy types to their appropriate stage. The classification rule: fast grep-detectable checks (imports, python patterns, required fields) belong at commit; architecture-level checks belong at push; comprehensive structural checks belong at CI. Architecture and config checks are classified now but execute as no-ops — reserved for the ENF task so the contract is clear but not yet paid.

  • enforce/validator.pyStagedValidator uses git diff --cached (staged files) or git diff @{upstream}..HEAD (push) to fetch only the relevant file set, then runs offline grep-based policy checks against accepted ADRs. No network calls; the full CI pass scans all project files.

  • enforce/hooks.pyHookGenerator writes idempotent sentinel-marked sections into .git/hooks/pre-commit and .git/hooks/pre-push. Re-running is safe: the sentinel block is replaced in-place so existing hook content (e.g. from other tools) is preserved. Approval workflow calls generate() automatically so hooks stay in sync as rules are added.

Three new CLI commands expose this: enforce (run a check at any level), setup-enforcement (install/update hooks), and enforce-status (show what hooks are installed and their source policy counts). adr-kit init --with-enforcement installs hooks during project setup.

What Was Tested

  • tests/unit/test_staged_enforcement.py: classifier maps each policy type to the correct enforcement level; StagedValidator correctly selects the commit vs push file set; all three CI modes scan the full file list; check counts reflect classified (not yet executed) architecture checks.
  • tests/unit/test_hook_generator.py: hooks are written with correct sentinel markers; re-running is idempotent (sentinel block replaced, not duplicated); pre-existing hook content outside the sentinel is preserved; executable bit is set on hook files.
  • mypy type errors in cli.py and validator.py fixed as part of pre-PR quality gate.

Risks

The hook installer writes directly to .git/hooks/ — projects using Husky, pre-commit framework, or other hook managers could have content silently overwritten if the sentinel block insertion logic doesn't correctly detect the existing section boundary. The sentinel approach mitigates this for re-runs of adr-kit itself, but first-run on a repo with a pre-existing hook of the same name will overwrite non-sentinel content. Worth reviewing the idempotency logic in hooks.py before merge.

Architecture and config checks are intentionally inert (classified but not executed). This is documented in the module docstring. The ENF task will implement them — no behavior change expected at that point, only the currently-empty check runners will be filled in.

kschlt added 2 commits March 23, 2026 00:00
…idator

Implements the STG task: ADR policy checks now run automatically at the right
workflow stage rather than only at CI time. A classification model maps policy
types to stages (imports/python/patterns → commit, architecture → push,
required_structure/config → ci), StagedValidator fetches the appropriate file
set via git and runs offline grep-based checks, and HookGenerator writes
idempotent sentinel-marked sections into .git/hooks/pre-commit and
.git/hooks/pre-push. Approval workflow auto-calls generate() so hooks stay in
sync as rules are added. Three new CLI commands (enforce, setup-enforcement,
enforce-status) plus --with-enforcement on init. Architecture and config checks
are classified but not yet executed — reserved for the ENF task.
Add threading import at module level for Thread return type annotation,
and annotate adrs list with ADR type in _load_accepted_adrs.
@kschlt kschlt merged commit b3c3144 into main Mar 22, 2026
8 checks passed
@kschlt kschlt deleted the feat/staged-enforcement branch March 22, 2026 23:13
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