ADR-018: Compliance.Core and Cross-Cutting Shared Extractions
Status
Accepted — delivered on branch fix/thorough-review-findings as part of the thorough-review hardening wave (no release version cut yet). Extends ADR-016 (the original monolith→multi-project split).
Date
2026-05-31
Context
A repository-wide thorough review flagged a cluster of DRY / SOLID architecture issues (the ARC-* findings) where the same logic was reimplemented across assemblies and could silently drift. The most acute:
- ARC-01 — the GDPR and EU AI Act compliance packs are near-verbatim parallel hierarchies (loader, validator, builders, reporters, PDF renderers, model records). A fix to shared mechanics had to be made twice and had already drifted.
- ARC-02/03/04/05/07/08/11, MNT-02/03/05 — duplicated tree-walk depth caps, PDF helpers, calibration statistics, tool-call assertion checks, model-pricing key matching, OWASP/MITRE leaf scoring, workspace-root discovery, and the memory god-class.
- ARC-10 — the umbrella re-declares each embedded sub-project's external
PackageReferences by hand (becausePrivateAssets="all"suppresses transitive propagation), with no compile/CI signal when a sub-project adds a dependency the umbrella forgets to mirror (SEC-02 was a concrete instance).
The hard constraint: the compliance gates were carefully calibrated over a prior arc (GDPR 5/5; EU AI Act with documented carve-outs — Pillar 1 / Art 5 at 0.65/0.35, GPAI at 0.60/0.25). No refactor may change any scoring weight, pass threshold, pillar/article definition, aggregation rule, or judge constant.
Decision
Create
AgentEval.Compliance.Core— a new embedded sub-project holding the regulation-neutral building blocks shared by both compliance packs. The wave seeds it with the provably-identical, self-contained, calibration-neutral types (CompositeExtensions,Recommendation,CriticalFindingExtractor); the remaining entangled files (models, loaders, validators, builders, runners, renderers, calibration) migrate into it incrementally as test-gated follow-ups.Consolidate cross-cutting logic into single owners, each close to its domain and validated by the existing test suites (behaviour preserved, not changed):
EvalTreeLimits(Abstractions) — one tree-walk depth cap referenced by the producer and every consumer.EvalReportHelpers(Abstractions) — shared QuestPDF-free report helpers.ModelKeyMatcher(Abstractions) — one longest-first model-pricing key matcher.CalibrationMath(Core) — shared judge/evaluator statistics.WorkflowToolCallChecks(Core) — shared order-independent tool-call assertion checks.AgenticCategoryResolver(Evals.Agentic),RedTeamComplianceLeaf(RedTeam),MemoryScenarioContextBuilder(Memory),WorkspaceRootDiscovery.CanonicaliseExistingDirectory(DataLoaders).
Add
UmbrellaDependencyClosureTests— a build-time guard that parses the umbrella + every embedded sub-project.csprojand fails when a sub-project runtimePackageReferenceis not re-declared on the umbrella. This is the missing compile/CI signal for ARC-10."Safe extraction + documented residual" for the large ARCs. Where a complete merge would be high-risk on calibrated/tested code (ARC-01 full pack merge, ARC-02 layout skeletons, ARC-05 fluent-builder unification), extract only the provably-safe, behaviour-preserving slice now and record the deeper structural merge as a tracked vNext residual rather than forcing a risky change.
Consequences
Positive
- Shared logic has one owner; a fix lands once. The most drift-prone duplications (depth cap, calibration math, leaf scoring, pricing match) can no longer diverge.
- The umbrella's NuGet dependency closure is now guarded — a forgotten transitive dependency fails a test instead of shipping a broken/silently-vulnerable package.
- Zero behaviour/calibration change: the GDPR/EU-AI-Act compliance tests and the full suite pass identically; an independent adversarial re-review confirmed the wave is behaviour-preserving and calibration-safe.
Negative / costs
- One more embedded sub-project (
AgentEval.Compliance.Core) in the umbrella'sPrivateAssets="all"list (and in the closure guard). Compliance.Coreis intentionally a partial extraction; the compliance packs still hold near-identical model/builder/runner files pending the incremental migration documented per finding.