adr-check
Validate code changes against Architecture Decision Records (ADRs)
SKILL.md.claude/skills/adr-check
GitHubADR Check
Category: Quality Last Reviewed: 2026-05-16 Reviewed By: ai-procedure-quality-r1 (Phase 2b Wave 2b-A) Exemplar rationale:
tests/Spaarke.ArchTests/is the NetArchTest project — it IS the canonical "what an ADR check actually enforces." A skill that runs against it is auditable; naming this exemplar gives the skill a verifiable anchor.
Purpose
Validate code changes against Spaarke's Architecture Decision Records (ADRs) before committing. This skill helps developers identify architectural violations early, ensuring code aligns with established constraints.
Applies When
- Developer asks to "check ADRs", "validate architecture", or "review changes against ADRs"
- Before creating a pull request
- After making significant code changes
- When reviewing code for architectural compliance
- NOT for general code review (use
code-reviewskill instead)
Workflow
Step 0: Coverage-First Contract (added 2026-07-08 for Sonnet-5)
At the finding stage, report every potential ADR violation you identify — including low-confidence ones — annotated with confidence. Sonnet 5 honors "only report significant issues" / "be conservative" literally and will silently drop violations it judges minor; that hides real ADR slips. Do not self-filter. The downstream filter is the orchestrator (task-execute Step 9.5), which applies CLAUDE.md §6.5 (fix / exception / amend) per violation. Your job here is coverage, not the accept/waive decision.
Step 1: Identify Scope
Determine what code to validate:
- If files specified: Check those specific files
- If no files specified: Check recent git changes
git status --short git diff --name-only HEAD~1 - If scope unclear: Ask developer to clarify
Step 2: Load ADR Context
- Use the ADR index as the source of truth:
.claude/adr/INDEX.md(concise; full ADRs indocs/adr/) - Use
references/adr-validation-rules.mdfor grep/pattern-based checks (where available)
Quick reference of key constraints:
| ADR | Key Constraint | Check For |
|---|---|---|
| ADR-001 | BFF endpoints in Minimal API; no Functions / Durable Task inside the BFF | [Function]/[FunctionName]/[HttpTrigger] inside Sprk.Bff.Api; Functions or DurableTask packages in the BFF csproj; Functions duplicating BFF auth/correlation |
| ADR-052 | Where background work runs — decided per workload (BFF / Functions / Container Apps Jobs) | Background work with no host decision in the Placement Justification; a new hand-rolled timer BackgroundService; a Functions project outside src/server/functions/, referencing Sprk.Bff.Api, or using MSAL confidential-client / OBO |
| ADR-002 | No plugins; invariants server-side | Any IPlugin/plugin project; invariant enforced only in client wizard (WP-2); invariant table written via Xrm.WebApi (WP-3); fail-open security (WP-6) |
| ADR-006 | PCF over webresources | New .js files in webresources |
| ADR-007 | Graph isolation | Microsoft.Graph outside Infrastructure |
| ADR-008 | Endpoint filters | Global UseAuthorization middleware |
| ADR-009 | Redis-first | IMemoryCache for cross-request caching |
| ADR-010 | DI minimalism | Interfaces with single implementation |
| ADR-013 (refined 2026-05-20) | AI architecture — facade discipline for CRUD→AI consumption | CRUD code injecting IOpenAiClient, IPlaybookService, or other AI-internal interfaces directly (must consume via Services/Ai/PublicContracts/ facades); AI work proposed in BFF without checking refined ADR-013 decision criteria |
| ADR-021 | Fluent v9 design system | @fluentui/react (v8), hard-coded colors, missing FluentProvider |
| ADR-028 | Spaarke Auth v2 client contract | Raw fetch(... headers: { Authorization: \Bearer ${...}` }), tokenBridge, window.__SPAARKE_BFF_TOKEN__, BridgeStrategy/XrmStrategy/MsalSilentStrategyreferences,accessToken: stringtyped props,PublicClientApplicationinstantiated outside@spaarke/auth, ClientSecretCredentialfor app-only when MI available, MSAL authority/commonor/organizations` |
| ADR-028 A4 | Secret-free confidential credential | .WithClientSecret(...) on any BFF-identity client (use MI-FIC assertion or KV certificate); credentials constructed per call site instead of via the shared provider; IConfidentialClientApplication built per-request instead of singleton-cached. 🔴 E-3 is CLOSED (2026-08-24, auth-v4 task 033) and its enumerated site list is EMPTY — report every BFF-identity .WithClientSecret(...) as a violation; there is no longer any site for which citing E-3 is correct. (Previously this row read "Cite exception E-3 (do not report as a violation) for the transitional sites it enumerates" — that clause is spent.) The only remaining carve-out is E-1 (per-customer SPE owning apps — other applications' identities), plus PowerBi:ClientSecret while task 042 stays deferred. DefaultAzureCredential is not a valid fix on OBO paths — it cannot perform the exchange. Also flag: a secret listed beneath MI-FIC in Graph:Credentials:Order (it silently re-arms fall-through), and RequireSecretFreeIdentity absent/false outside Development. |
Note: This table is not exhaustive. The index pass over .claude/adr/INDEX.md (Conventions) is what reaches the ADRs it omits.
Step 2.5: Load BFF Hygiene Constraints (Conditional)
If any file in scope is under:
src/server/api/Sprk.Bff.Api/**src/server/shared/Spaarke.Core/**src/server/shared/Spaarke.Dataverse/**
Then ALSO load and apply .claude/constraints/bff-extensions.md — this is the binding pre-merge checklist for BFF additions per root CLAUDE.md §10. Treat each MUST rule in that file as an ADR-equivalent compliance check. The 5-rule pre-merge checklist (Section A) is mandatory; rules in Sections B–E apply by change type.
Flag as Violation (not Warning) any addition that:
- Lacks a Placement Justification in the PR/design.md (§10 imperative)
- Introduces a new direct CRUD→AI dependency (must use
Services/Ai/PublicContracts/facade) - Adds a NuGet package without publish-size verification or CVE check
- Adds endpoints directly in
Program.csinstead of through aMap{Feature}Endpointsextension
Step 3: Run Validation Checks
For each applicable ADR:
- Search codebase using grep/find patterns from
references/adr-validation-rules.md - Categorize findings as Compliant, Warning, or Violation
- Note specific file paths and line numbers
Step 4: Generate Report
Output structured report using the format in Output Format section.
Step 5: Suggest Fixes
For each violation:
- Explain why it violates the ADR
- Provide concrete fix with code example
- Reference the ADR document for full context
Step 5.5: Surface Challenge Paths (per CLAUDE.md §6.5)
For each Violation reported, ALSO surface the three resolution paths from the ADR Conflict Resolution Protocol so the reviewer can choose intentionally rather than defaulting to silent compliance:
FOR EACH violation:
EMIT a "Resolution Paths" block alongside the fix:
Path A — Project-scoped exception
Applies when: the ADR remains correct generally but this project has a
narrow, documented reason to deviate.
Action: document in design.md/spec.md "ADR Tensions" section + cite in PR.
Path B — ADR amendment
Applies when: context has changed; the ADR's prior decision is no longer
correct as written.
Action: propose ADR amendment (concise + full versions) + link from this
project; merge ADR change before or alongside the code.
Path C — Pivot to comply (default suggestion)
Applies when: an ADR-compliant approach meets the requirement equally
well or better.
Action: use the fix suggested in Step 5.
REVIEWER CHOOSES which path applies. Do not assume Path C.
Why this matters: A violation list that only shows fixes implicitly says "comply or fail." That's wrong when path A or B is the better outcome. Surfacing all three paths forces the human to choose intentionally instead of defaulting to whichever is mechanically easiest.
ADR Conflict Resolution Protocol — enforcement and limits (moved verbatim from root CLAUDE.md §6.5 on 2026-10-07)
Root
CLAUDE.md§6.5 keeps the principle, the three paths, "no fourth path" and the required output format. The rest moved here.
When this protocol fires. Trigger conditions:
- An agent recognizes that strict compliance with an ADR will produce a worse technical outcome than a documented exception or amendment would
adr-checkorcode-reviewflags a violation that the implementer believes is justified- During spec authoring, the design surfaces a requirement that conflicts with an existing ADR's MUST/MUST NOT rule
- During task execution, an ADR constraint blocks a legitimate implementation need
Where this protocol is enforced:
- At design time —
design-to-specandproject-pipelinesurface anticipated ADR tensions in a dedicated ADR Tensions section ofspec.md - At code-review time —
code-reviewStep 6 (ADR Compliance Check) accepts a reasoned exception (path A) cited in the PR description; otherwise flags as Critical - At task-execute Step 9.5 —
adr-checkviolations either are fixed (path C), formalized as exceptions (path A), or trigger amendment workflow (path B); silent retry-until-clean is not the loop - At ADR-check — this skill's output includes a "Challenge Path" section alongside violations (Step 5.5), prompting the human to choose a resolution rather than just accepting the violation list as final
What this protocol is NOT:
- Not a license to ignore ADRs casually — the bar for path A/B is "documented + rationale + reviewer approval"
- Not an excuse to bypass auth, security, or compliance ADRs without explicit human sign-off
- Not retroactive — code that violated an ADR silently before this protocol existed is still in violation; this protocol applies to new decisions going forward
Anti-patterns this catches:
- ❌ "ADR says no, so I'll write worse code to comply" — surface as path B candidate
- ❌ "I violated the ADR but it's fine, the reviewer won't notice" — silent violation, forbidden
- ❌ "The ADR is wrong but I don't want to amend it" — surface as path B; the cost of amendment is part of the work
- ❌ "I'll comply now and document the exception later" — exception MUST be documented at the point of decision, not deferred
Binding for ≥ 6 months from 2026-06-29 (added by spaarkeai-compose-r1); reviewed by the next major procedure-quality audit.
Conventions
- Check the ADRs mapped to the touched resource types (adr-aware Rule 1), the code-review Step 6 always-check set, and make one pass over
.claude/adr/INDEX.mdto add any ADR whose title or rule names a concept in the diff (the mapping table does not reach every ADR — this pass is the backstop). List the ADRs considered and why each is in or out. Done = each applicable ADR reported Compliant / Warning / Violation. Grep-checking every ADR's rules is for an explicit full scan and a project's 090 wrap-up - Report warnings for potential issues that need human judgment
- Provide specific file paths and line numbers for violations
- Reference ADR documents by full path:
/docs/adr/ADR-XXX-*.md - Suggest running ArchTests after fixes:
dotnet test tests/Spaarke.ArchTests/Spaarke.ArchTests.csproj
Resources
| Resource | Purpose |
|---|---|
references/adr-validation-rules.md |
Detailed validation rules and grep patterns for each ADR |
Output Format
## ADR Validation Report
**Scope:** [files/changes being validated]
**Date:** [timestamp]
### ✅ Compliant ADRs
- ADR-001: Minimal API BFF runtime
- ADR-007: Graph isolation
- [list all compliant ADRs]
### ⚠️ Warnings (Review Required)
- **ADR-010:** Found [N] interfaces with single implementation
- `src/server/Services/IFooService.cs` → `FooService.cs`
- **Recommendation:** Consider registering `FooService` directly unless testing seam needed
### ❌ Violations (Resolution Required — see Challenge Paths)
- **ADR-007:** Graph types found outside Infrastructure layer
- **File:** `src/server/api/Sprk.Bff.Api/Api/FileAccessEndpoints.cs:42`
- **Code:** `Microsoft.Graph.DriveItem item = ...`
- **Path A (Exception)**: justify in PR description + ADR Tensions section if there's a project-scoped reason this Graph type can't route through `SpeFileStore`
- **Path B (Amendment)**: propose ADR-007 amendment if the Infrastructure-only rule is no longer correct in general
- **Path C (Comply — default)**: Replace with `FileHandleDto` and route through `SpeFileStore`
Per CLAUDE.md §6.5, the reviewer chooses path A / B / C — do not default to silent C if the implementation legitimately needs A or B.
### Summary
| Status | Count |
|--------|-------|
| ✅ Compliant | [N] |
| ⚠️ Warnings | [N] |
| ❌ Violations | [N] |
### Next Steps
1. Fix violations listed above
2. Review warnings with team if needed
3. Re-run validation: invoke `adr-check` skill
4. Run NetArchTest: `dotnet test tests/Spaarke.Bff.Api.ArchTests/`
Examples
Example 1: Check recent changes
Input:
Developer: "Check my changes against ADRs before I commit"
Output:
Claude runs git diff --name-only, identifies changed files, validates each against ADRs, and produces a structured report.
Example 2: Check specific file
Input:
Developer: "Validate src/server/api/Sprk.Bff.Api/Api/DocumentsEndpoints.cs against ADRs"
Output: Claude focuses validation on the specified file, checking for Graph types, authorization patterns, caching usage, etc.
Example 3: Full project scan
Input:
Developer: "Run a full ADR compliance check on the solution"
Output: Claude scans all source directories, producing a comprehensive report across all ADRs.
Error Handling
| Situation | Response |
|---|---|
| No files specified and no git changes | Ask developer what to validate |
| File path doesn't exist | Report error, ask for correct path |
| Unclear whether finding is violation or warning | Report as warning, explain uncertainty |
Related Skills
code-review- General code quality review (not architecture-focused)push-to-github- Include ADR check as part of PR creationci-cd- CI pipeline status and ADR validation in CI
CI/CD Integration
Local vs CI ADR Validation
This skill performs local ADR validation. The same checks run in CI via GitHub Actions:
| Location | Tool | When |
|---|---|---|
| Local | This skill (/adr-check) |
Before commit/push |
| CI | NetArchTest (Spaarke.ArchTests) |
On every PR and push to master |
| Weekly | adr-audit.yml workflow |
Monday 9 AM UTC |
CI Pipeline ADR Checks
The CI Router runs the ADR tests twice:
- Tier 1
arch-tests(ci-tier1-blocking.yml, blocking): the fulltests/Spaarke.ArchTestssuite. A failure fails the requiredRoutercheck. - Tier 2
adr-compliance(ci-tier2-advisory.yml, advisory): reported in the single "Tier 2 Advisory Report" PR comment.
(The legacy sdap-ci.yml code-quality job and its adr-pr-comment were retired 2026-10-14.)
Weekly ADR Audit
The adr-audit.yml workflow runs weekly and:
- Creates/updates GitHub issue with
architectureandadr-auditlabels - Groups violations by ADR number
- Auto-closes issue when all violations resolved
- Provides remediation guidance
Troubleshooting CI ADR Failures
# View PR ADR check status
gh pr checks | grep -i "code-quality"
# View ADR test results
gh run view {run-id} --log --job=code-quality
# Download test results artifact
gh run download {run-id} --name adr-test-results
# Trigger ADR audit manually
gh workflow run adr-audit.yml
Local Validation Before Push
RECOMMENDED: Run this skill before pushing to catch violations early:
1. Make code changes
2. Run /adr-check (this skill)
3. Fix any violations
4. Run NetArchTest locally for additional validation:
dotnet test tests/Spaarke.ArchTests/Spaarke.ArchTests.csproj
5. Push changes
6. CI will re-run same checks
Failure Modes & Recovery
| Failure | Cause | Prevention / Recovery |
|---|---|---|
| Reviewer marks PR "ADR-clean" but production hits the violated rule at runtime | ADR check only ran on diff, not on full reachable code paths from diff | For changes touching API surface or DI registration, expand check to all callers (use grep -rn for usages). NetArchTest catches these structurally — run it locally. |
| Skill flags an ADR violation that's actually approved/grandfathered | ADR has an exception that's not documented in the ADR itself | Update the ADR with the exception explicitly (Exceptions: section). Agent should respect documented exceptions. |
| ADR-jobs or a newer ADR missed during review | ADR added recently but not in this skill's checklist | Run /ai-procedure-maintenance to propagate. The validation checklist must always cover every active ADR in docs/adr/. |
| NetArchTest passes but skill reports a violation (or vice versa) | Skill's grep patterns drift from NetArchTest rule definitions | Treat NetArchTest as authoritative (it's mechanical). Update skill grep patterns to match. File a follow-up if a rule needs both forms. |
Operator Notes
- Be thorough: every ADR that applies to the change, found through the mapping AND the index pass, even when the change seems small
- Be specific: always include file paths and line numbers
- Be actionable: provide concrete fixes, not just problem descriptions
- When in doubt, report as warning rather than skipping
- Encourage running automated NetArchTest for additional validation