adr-check

Validate code changes against Architecture Decision Records (ADRs)

SKILL.md

GitHub

ADR 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-review skill 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:

  1. If files specified: Check those specific files
  2. If no files specified: Check recent git changes
    git status --short
    git diff --name-only HEAD~1
    
  3. If scope unclear: Ask developer to clarify

Step 2: Load ADR Context

  1. Use the ADR index as the source of truth: .claude/adr/INDEX.md (concise; full ADRs in docs/adr/)
  2. Use references/adr-validation-rules.md for 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.cs instead of through a Map{Feature}Endpoints extension

Step 3: Run Validation Checks

For each applicable ADR:

  1. Search codebase using grep/find patterns from references/adr-validation-rules.md
  2. Categorize findings as Compliant, Warning, or Violation
  3. 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:

  1. Explain why it violates the ADR
  2. Provide concrete fix with code example
  3. 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-check or code-review flags 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-spec and project-pipeline surface anticipated ADR tensions in a dedicated ADR Tensions section of spec.md
  • At code-review time — code-review Step 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-check violations 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.md to 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 creation
  • ci-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 full tests/Spaarke.ArchTests suite. A failure fails the required Router check.
  • 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 architecture and adr-audit labels
  • 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