226 lines
20 KiB
Markdown
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.
|