# Research: Baseline Compare Engine Strategy Extraction ## Decision: Keep `BaselineCompareService` and `CompareBaselineToTenantJob` as the platform compare orchestration path ### Rationale The current compare workflow already has the right platform-owned responsibilities in the right places: `BaselineCompareService` performs start-surface preconditions, run setup, and workspace fan-out orchestration, while `CompareBaselineToTenantJob` owns queued execution, result persistence, and run completion. Replacing that lifecycle would expand scope and risk without improving the core problem this spec is trying to solve. ### Alternatives considered - Introduce a brand-new compare orchestrator service and new queue workflow: rejected because it would duplicate stable run and finding behavior before proving the narrower extraction seam. - Push more orchestration into Filament pages: rejected because start surfaces must stay enqueue-only and server-side compare truth must remain outside page code. ## Decision: Resolve one compare strategy family from canonical Baseline Scope V2 before a run is enqueued ### Rationale Spec 202 makes canonical scope explicit. That gives the compare start path a deterministic input contract for strategy selection. Resolving one strategy family before enqueue is the narrowest way to reject unsupported or mixed scope truthfully and enforce the single-strategy-per-run rule before any subject processing begins. ### Alternatives considered - Resolve strategy lazily inside the compare job after the run starts: rejected because unsupported or mixed scope would then produce avoidable queued work and less honest operator feedback. - Allow the compare job to pick the first compatible strategy silently: rejected because it would recreate the same hidden platform default that this spec is trying to remove. ## Decision: Extract the strategy seam at the subject-processing boundary inside `CompareBaselineToTenantJob` ### Rationale The narrowest real domain seam in the current code is where the compare job turns baseline items, current-state evidence, and subject resolution into drift classification, diagnostics, and finding projection data. That is where Intune-specific normalizer selection, policy-type special cases, and evidence shaping currently live. Extracting there preserves the existing queue lifecycle and reuse of generic helpers while removing domain assumptions from the platform core. ### Alternatives considered - Extract only a thin wrapper around the whole job: rejected because the job body would still contain platform-visible Intune assumptions. - Extract at the `BaselineCompareService` level only: rejected because the service already behaves like platform orchestration; the Intune-shaped logic mostly lives deeper in execution. ## Decision: Reuse current generic helpers and move only Intune-shaped logic behind `IntuneCompareStrategy` ### Rationale Exploration shows that several current compare helpers are already strategy-neutral enough to survive the extraction: `CurrentStateHashResolver`, `DriftHasher`, finding lifecycle handling, and summary/trust assessment logic. The most Intune-shaped code is the section normalizer selection, policy-type branching, summary-kind choice, RBAC role-definition special cases, and subject projection detail. Reusing the generic helpers keeps the extraction narrow and avoids replacing proven infrastructure. ### Alternatives considered - Rebuild all compare helpers under a new generic compare package: rejected because it would replatform too much stable code for little value. - Leave Intune-specific normalizers and evidence shaping in the job while adding only a nominal strategy interface: rejected because the platform would still own hidden Intune behavior. ## Decision: Model compare subject output as an explicit internal result contract, not as raw arrays and not as a new persisted entity ### Rationale Current compare processing already passes around raw drift arrays and evidence payloads. That shape is too weak for a true domain boundary because it forces downstream orchestration to infer meaning from Intune-shaped keys. A structured internal compare-subject result contract is the narrowest way to make summaries, findings, and diagnostics consume strategy-neutral data. It remains internal and derived, so it does not create a new persisted compare truth layer. ### Alternatives considered - Keep raw drift arrays as the only contract: rejected because the platform would keep reverse-engineering domain logic from strategy internals. - Create a new persisted compare result table: rejected because existing `OperationRun`, finding, and evidence persistence already hold the long-lived product truth. ## Decision: Keep workspace fan-out compare as repeated tenant-owned runs with the same strategy validation ### Rationale The workspace compare matrix already starts normal tenant-owned compare runs for the visible assignment set and explicitly avoids a workspace umbrella run. The extraction should preserve that model and apply the same strategy validation used by tenant-local compare starts. That keeps operator semantics stable and avoids inventing new batch-run persistence. ### Alternatives considered - Add a new workspace umbrella compare run for fan-out: rejected because it would add new operational truth that this spec does not need. - Validate fan-out compare differently from tenant compare: rejected because it would create inconsistent compare semantics between two existing launch surfaces. ## Decision: Surface unsupported or mixed strategy failures through existing compare launch and review semantics ### Rationale This feature is not a UI redesign. The existing baseline detail, compare matrix, tenant compare landing, and canonical run-detail surfaces already own the operator story. The right change is to make those surfaces show truthful compatibility or failure meaning through existing alerts, helper text, summaries, and diagnostics instead of inventing a new compare-administration page. ### Alternatives considered - Add a dedicated strategy diagnostics page: rejected because it would add UI breadth for a workflow that already has a natural home. - Hide unsupported or mixed scope behind generic precondition failures: rejected because the operator needs explicit truth about why compare could not run. ## Decision: Treat the new compare strategy seam as a deliberate, narrow proportionality exception to the default anti-abstraction bias ### Rationale The constitution normally rejects new strategy systems before two concrete production cases exist. This feature qualifies for a narrow exception because Baseline Scope V2 is already shipping a broader compare-input contract, and the current compare engine still embeds one domain's assumptions in the platform core. Without the seam, current-release compare truth becomes less honest the moment scope extends beyond the hidden Intune default. ### Alternatives considered - Defer the seam until the second production domain exists: rejected because that would either block safe expansion or force a future domain to copy or distort the current compare engine. - Build a broad multi-domain compare framework now: rejected because it would over-correct and import far more complexity than the current release needs.