TenantAtlas/specs/203-baseline-compare-strategy/plan.md
2026-04-13 23:09:11 +02:00

226 lines
20 KiB
Markdown

# 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.php` remains 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:assets` remains 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 `BaselineCompareService` as the compare start orchestration layer and `CompareBaselineToTenantJob` as 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 `CompareBaselineToTenantJob` rather 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 behind `IntuneCompareStrategy`.
- 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 extraction
- `data-model.md`: internal compare strategy, strategy selection, compare-subject result, and projection contracts
- `contracts/baseline-compare-strategy.logical.openapi.yaml`: logical internal contract for compare validation, compare launch, fan-out launch, and strategy-owned subject classification
- `quickstart.md`: implementation and verification sequence for the feature
Design decisions:
- Keep compare launch orchestration in `BaselineCompareService` and run execution in `CompareBaselineToTenantJob`.
- 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)
```text
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)
```text
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 `BaselineCompareService` for 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 `CompareBaselineToTenantJob` into `IntuneCompareStrategy`.
- 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.php` for unsupported strategy, mixed-strategy, and inactive subject-type rejection before run creation.
- Extend `tests/Feature/Baselines/BaselineCompareMatrixCompareAllActionTest.php` to ensure workspace fan-out compare applies the same single-strategy validation and truthful rejection behavior.
- Extend `tests/Feature/Baselines/BaselineCompareFindingsTest.php`, `BaselineCompareGapClassificationTest.php`, and `BaselineCompareRbacRoleDefinitionsTest.php` to prove Intune finding projection and classification behavior stay stable through the extracted strategy.
- Extend `tests/Feature/Baselines/BaselineCompareExecutionGuardTest.php` and `BaselineCompareSummaryAssessmentTest.php` to keep `OperationRun` outcome, 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`, and `BaselineCompareMatrixPageTest.php` for 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.