20 KiB
Implementation Plan: Baseline Compare Engine Strategy Extraction
Branch: 203-baseline-compare-strategy | Date: 2026-04-13 | Spec: /Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/203-baseline-compare-strategy/spec.md
Input: Feature specification from /Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/203-baseline-compare-strategy/spec.md
Note: This plan keeps the existing baseline compare workflow, OperationRun model, and finding lifecycle intact while extracting the current Intune-specific compare behavior behind one explicit strategy seam.
Summary
Keep BaselineCompareService and CompareBaselineToTenantJob as the platform compare orchestration flow, add one deterministic compare-strategy registry keyed by canonical Baseline Scope V2, validate single-strategy compatibility before enqueue, extract current Intune-specific subject processing into an explicit IntuneCompareStrategy, and feed a strategy-neutral per-subject compare-result contract into the existing summary, finding, trust, and run-outcome writers. No new OperationRun type, no new top-level compare persistence model, and no multi-strategy-per-run orchestration are planned.
Technical Context
Language/Version: PHP 8.4.15
Primary Dependencies: Laravel 12, Filament v5, Livewire v4, Pest v4, Laravel Sail, existing BaselineCompareService, CompareBaselineToTenantJob, SubjectResolver, CurrentStateHashResolver, DriftHasher, BaselineCompareSummaryAssessor, and finding lifecycle services
Storage: PostgreSQL via existing baseline snapshots, baseline snapshot items, operation_runs, findings, and baseline scope JSON; no new top-level tables planned
Testing: Pest unit, feature, Filament Livewire, and existing browser smoke coverage run through Laravel Sail
Target Platform: Laravel web application under apps/platform with queue-backed compare execution in Sail/Docker
Project Type: web application in a monorepo (apps/platform plus apps/website)
Performance Goals: Keep compare start enqueue-only, keep strategy selection and compatibility validation in-process and cheap, add no extra remote calls on start surfaces, and preserve current compare throughput and matrix rendering behavior
Constraints: Single-strategy-per-run only, no new compare UI surface, no new OperationRun type, no new plugin framework, no silent Intune fallback, preserve current Intune compare behavior, and keep current finding lifecycle and trust semantics
Scale/Scope: One baseline compare start service, one compare job, existing workspace and tenant compare surfaces, one explicit Intune strategy, one strategy registry, one internal compare-result contract, and focused regression extensions across compare feature and Filament suites
Constitution Check
GATE: Passed before Phase 0 research. Re-checked after Phase 1 design and still passing with one explicit proportionality justification recorded below.
| Principle | Pre-Research | Post-Design | Notes |
|---|---|---|---|
| Inventory-first / snapshots-second | PASS | PASS | Compare continues to read existing inventory-backed current state and workspace-owned baseline snapshots; no new source of compare truth is introduced. |
| Read/write separation | PASS | PASS | Compare launch surfaces remain simulation-only start actions; compare still writes only existing run, finding, and audit truth. |
| Graph contract path | PASS | PASS | The feature introduces no new Microsoft Graph path; the extracted Intune strategy continues to consume existing contract-backed evidence sources. |
| Deterministic capabilities | PASS | PASS | Strategy capability matching is explicit, testable, and driven by canonical scope plus registry metadata. |
| Workspace + tenant isolation | PASS | PASS | Workspace baseline surfaces and tenant compare surfaces keep current isolation boundaries and deny-as-not-found semantics. |
| RBAC-UX authorization semantics | PASS | PASS | Existing capability checks remain authoritative for compare starts and drilldowns; unsupported or mixed scope is a compare truth outcome, not an auth shortcut. |
| Run observability / Ops-UX | PASS | PASS | Existing baseline_compare runs remain the only run truth; status/outcome ownership, summary-count rules, and terminal notification behavior remain unchanged. |
| Data minimization | PASS | PASS | No new persisted compare artifact is added; strategy-neutral context extends existing run/finding payloads only where necessary. |
| Proportionality / anti-bloat | PASS | PASS | One narrow strategy seam is justified by a current compare-truth problem and recorded in Complexity Tracking; no broader workflow framework is added. |
| No premature abstraction | JUSTIFIED | JUSTIFIED | This is a deliberate, narrow exception: one strategy contract and registry are introduced before a second production domain exists because Spec 202 makes unsupported or mixed compare scope a current-release truth problem. The alternative thin wrapper is recorded and rejected below. |
| Persisted truth / behavioral state | PASS | PASS | The new compare-result states remain internal orchestration semantics and map to existing operator-visible compare outcomes; no new top-level persisted state family is introduced. |
| UI semantics / few layers | PASS | PASS | Existing compare surfaces remain the same; the new compare-result contract feeds current summaries and explanations instead of creating a new UI interpretation framework. |
| Filament v5 / Livewire v4 compliance | PASS | PASS | The affected compare surfaces remain Filament v5 + Livewire v4 surfaces with no legacy API introduction. |
| Provider registration location | PASS | PASS | No panel or provider change is required; Laravel 11+ provider registration remains in bootstrap/providers.php. |
| Global search hard rule | PASS | PASS | No globally searchable resource is added or changed by this feature. |
| Destructive action safety | PASS | PASS | No new destructive action is introduced. Existing archive actions stay confirmed; compare start actions remain confirmed and capability-gated. |
| Asset strategy | PASS | PASS | No new assets or panel registrations are required; existing Filament asset deployment remains unchanged. |
Filament-Specific Compliance Notes
- Livewire v4.0+ compliance: The touched compare surfaces remain on Filament v5 + Livewire v4 and no deprecated Filament API is introduced by the plan.
- Provider registration location: No new panel or provider is required;
bootstrap/providers.phpremains the only relevant provider registration location. - Global search: No new searchable resource is added. Existing compare pages remain standalone pages and current global-search behavior is unaffected.
- Destructive actions: This feature adds no new destructive action. Existing destructive actions on baseline surfaces remain unchanged and must keep
->requiresConfirmation(). - Asset strategy: No panel-only or shared asset registration is needed. Deployment handling of
cd apps/platform && php artisan filament:assetsremains unchanged. - Testing plan: Extend compare feature tests, Filament start-surface tests, and focused matrix or landing coverage for unsupported or mixed-scope truth. Keep existing compare smoke coverage green.
Phase 0 Research
Research outcomes are captured in /Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/203-baseline-compare-strategy/research.md.
Key decisions:
- Keep
BaselineCompareServiceas the compare start orchestration layer andCompareBaselineToTenantJobas the async executor instead of redesigning the run lifecycle. - Add a deterministic compare-strategy registry that resolves one compatible strategy family from canonical Baseline Scope V2 before a run is enqueued.
- Extract the strategy seam at the current subject-processing boundary inside
CompareBaselineToTenantJobrather than replacing current queue, finding, or summary flows. - Reuse existing generic helpers such as
DriftHasher,CurrentStateHashResolver, finding lifecycle services, and summary assessors; move Intune-shaped normalizer selection, policy-type special cases, and subject-projection detail behindIntuneCompareStrategy. - Model the per-subject compare output as a structured internal contract instead of raw ad-hoc arrays or a new persisted compare-result table.
- Keep workspace fan-out compare as repeated tenant-owned runs with the same single-strategy validation and no new workspace umbrella run.
Phase 1 Design
Design artifacts are created under /Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/203-baseline-compare-strategy/:
research.md: architecture decisions and rejected alternatives for strategy extractiondata-model.md: internal compare strategy, strategy selection, compare-subject result, and projection contractscontracts/baseline-compare-strategy.logical.openapi.yaml: logical internal contract for compare validation, compare launch, fan-out launch, and strategy-owned subject classificationquickstart.md: implementation and verification sequence for the feature
Design decisions:
- Keep compare launch orchestration in
BaselineCompareServiceand run execution inCompareBaselineToTenantJob. - Introduce one narrow compare-support namespace under
app/Support/Baselines/Compare/for the strategy contract, registry, selection result, and compare-subject result value objects. - Keep Intune-specific section normalizers and policy-type branching inside the explicit Intune strategy rather than in platform orchestration.
- Preserve existing finding persistence, run summary assessment, and explanation surfaces by feeding them strategy-neutral projection metadata instead of raw strategy internals.
- Reject unsupported or mixed strategy scope before subject processing, and keep strategy failure truth visible through existing compare landing and canonical run-detail surfaces.
Project Structure
Documentation (this feature)
specs/203-baseline-compare-strategy/
├── plan.md
├── research.md
├── data-model.md
├── quickstart.md
├── spec.md
├── contracts/
│ └── baseline-compare-strategy.logical.openapi.yaml
└── checklists/
└── requirements.md
Source Code (repository root)
apps/platform/
├── app/
│ ├── Filament/
│ │ ├── Pages/
│ │ │ ├── BaselineCompareLanding.php
│ │ │ └── BaselineCompareMatrix.php
│ │ └── Resources/
│ │ └── BaselineProfileResource/
│ │ └── Pages/
│ │ └── ViewBaselineProfile.php
│ ├── Jobs/
│ │ └── CompareBaselineToTenantJob.php
│ ├── Services/
│ │ ├── Baselines/
│ │ │ ├── BaselineCompareService.php
│ │ │ ├── CurrentStateHashResolver.php
│ │ │ └── Evidence/
│ │ └── Drift/
│ │ ├── DriftHasher.php
│ │ └── Normalizers/
│ └── Support/
│ └── Baselines/
│ ├── BaselineCompareExplanationRegistry.php
│ ├── BaselineCompareSummaryAssessor.php
│ ├── ResolutionOutcome.php
│ ├── SubjectClass.php
│ ├── SubjectResolver.php
│ └── Compare/
│ └── [new strategy contract, registry, and result objects]
└── tests/
├── Feature/
│ ├── Baselines/
│ │ ├── BaselineComparePreconditionsTest.php
│ │ ├── BaselineCompareExecutionGuardTest.php
│ │ ├── BaselineCompareFindingsTest.php
│ │ ├── BaselineCompareMatrixCompareAllActionTest.php
│ │ ├── BaselineCompareRbacRoleDefinitionsTest.php
│ │ ├── BaselineCompareGapClassificationTest.php
│ │ └── [new strategy-selection and unsupported-scope coverage]
│ ├── BaselineDriftEngine/
│ │ ├── CompareContentEvidenceTest.php
│ │ └── CompareFidelityMismatchTest.php
│ └── Filament/
│ ├── BaselineProfileCompareStartSurfaceTest.php
│ ├── BaselineCompareLandingStartSurfaceTest.php
│ ├── BaselineCompareMatrixPageTest.php
│ └── [new unsupported or mixed-scope surface tests]
└── Browser/
└── [existing compare-related smoke tests remain green]
Structure Decision: Keep the change inside the existing baseline compare service, compare job, and compare surfaces. Add one narrow app/Support/Baselines/Compare namespace for the strategy contract, registry, selection, and result objects, but avoid a wider plugin or provider framework.
Complexity Tracking
| Violation | Why Needed | Simpler Alternative Rejected Because |
|---|---|---|
| Compare strategy contract and registry before a second production domain exists | Canonical Baseline Scope V2 makes unsupported or mixed-domain compare a current-release truth problem. The platform must stop treating Intune policy semantics as the hidden universal default now. | A thin wrapper around CompareBaselineToTenantJob would leave Intune assumptions in the platform core and would not make preflight rejection or future-domain participation honest. |
| Structured compare-subject result contract | Existing summary, finding, and run writers need domain-neutral compare output to avoid reverse-engineering Intune-shaped evidence arrays. | Passing raw arrays through the job would recreate hidden policy-only defaults and force downstream orchestration to keep inferring domain meaning from Intune payload shapes. |
Proportionality Review
- Current operator problem: Compare truth is currently reliable only because platform orchestration and Intune-specific logic are still mixed. As canonical scope broadens, unsupported or mixed compare scope cannot be rejected honestly without a clean boundary.
- Existing structure is insufficient because: The current compare job still makes policy-shaped assumptions during subject processing and evidence projection, so the platform cannot safely claim one canonical compare workflow across future subject families.
- Narrowest correct implementation: Keep the current start service, queue job, run model, finding model, and compare surfaces. Introduce one compare strategy contract, one deterministic resolver, and one strategy-neutral subject-result contract, then move only the Intune-specific processing behind the explicit strategy boundary.
- Ownership cost created: One new compare-support namespace, stricter contract discipline, additional strategy-resolution and regression coverage, and ongoing review to keep platform orchestration free of reintroduced Intune assumptions.
- Alternative intentionally rejected: A superficial wrapper around the current job or a broad compare plugin framework. The wrapper would not solve the current truth boundary, and the framework would import speculative complexity before a second real production domain exists.
- Release truth: current-release compare correctness and future-domain readiness at the specific compare seam, not generalized platform totalization
Implementation Strategy
Phase A - Strategy Selection and Compare Preconditions
- Add the compare strategy capability contract and deterministic registry keyed by canonical Baseline Scope V2.
- Validate one compatible strategy family at compare start in
BaselineCompareServicefor both tenant compare and workspace fan-out compare. - Return explicit unsupported or mixed-scope outcomes before a run is enqueued.
Phase B - Intune Strategy Extraction
- Extract the current Intune-specific subject-processing logic from
CompareBaselineToTenantJobintoIntuneCompareStrategy. - Move Intune-only normalizer selection, policy-type special cases, and subject projection details behind the new strategy boundary.
- Keep generic helpers such as
CurrentStateHashResolver,DriftHasher, and finding lifecycle orchestration reusable by the job.
Phase C - Strategy-Neutral Result Projection
- Replace raw per-subject drift arrays with a structured compare-subject result contract.
- Feed existing finding, summary, and trust writers from strategy-provided subject projection metadata rather than universal policy defaults.
- Keep existing run outcome, summary counts, and explanation surfaces stable.
Phase D - Surface Truth Hardening
- Update compare start surfaces to explain unsupported or mixed-scope preconditions through existing helper text, confirmation copy, or start-result messaging.
- Keep compare landing and canonical run detail truthful for unsupported, incomplete, ambiguous, and failed strategy outcomes without adding a new page.
Phase E - Regression and Verification
- Extend compare precondition, finding, summary, and matrix compare-all coverage for strategy resolution and unsupported or mixed-scope behavior.
- Keep current Intune compare classification, finding recurrence, and evidence contract coverage green to detect silent behavior drift.
- Validate the touched compare start surfaces and run-detail messaging with focused Filament tests and existing smoke tests.
Risk Assessment
| Risk | Impact | Likelihood | Mitigation |
|---|---|---|---|
| The strategy seam stays superficial and platform code still depends on Intune assumptions | High | Medium | Extract policy-type branching, section normalizer selection, and strategy-owned projection metadata out of the job body; review the remaining orchestration path for universal policy defaults. |
| Intune compare behavior regresses during extraction | High | Medium | Extend existing compare feature tests before and during extraction; keep current finding, gap, and summary suites green as the acceptance bar. |
| The compare-subject result contract is too weak | High | Medium | Define explicit projection, diagnostics, and availability fields in Phase 1 design and require summary/finding writers to consume them without reading strategy internals directly. |
| Unsupported or mixed-scope rejection happens too late | Medium | Medium | Resolve and validate one strategy family in BaselineCompareService before run creation and reuse the same logic for workspace fan-out compare. |
| The change expands into a general compare framework | Medium | Low | Limit the new namespace and contract to baseline compare only, keep single-strategy-per-run, and defer any multi-strategy orchestration or second-domain UI work. |
Test Strategy
- Extend
tests/Feature/Baselines/BaselineComparePreconditionsTest.phpfor unsupported strategy, mixed-strategy, and inactive subject-type rejection before run creation. - Extend
tests/Feature/Baselines/BaselineCompareMatrixCompareAllActionTest.phpto ensure workspace fan-out compare applies the same single-strategy validation and truthful rejection behavior. - Extend
tests/Feature/Baselines/BaselineCompareFindingsTest.php,BaselineCompareGapClassificationTest.php, andBaselineCompareRbacRoleDefinitionsTest.phpto prove Intune finding projection and classification behavior stay stable through the extracted strategy. - Extend
tests/Feature/Baselines/BaselineCompareExecutionGuardTest.phpandBaselineCompareSummaryAssessmentTest.phpto keepOperationRunoutcome, summary, and trust semantics unchanged. - Add focused unit coverage for the compare strategy registry, strategy selection result, and compare-subject result mapping.
- Extend
tests/Feature/Filament/BaselineProfileCompareStartSurfaceTest.php,BaselineCompareLandingStartSurfaceTest.php, andBaselineCompareMatrixPageTest.phpfor unsupported or mixed-scope start-surface truth and no-regression launch behavior. - Keep existing compare browser smoke coverage green to detect accidental surface regressions in the matrix or landing experience.