# Implementation Plan: 163 — Baseline Subject Resolution and Evidence Gap Semantics Foundation **Branch**: `163-baseline-subject-resolution` | **Date**: 2026-03-24 | **Spec**: `specs/163-baseline-subject-resolution/spec.md` **Input**: Feature specification from `specs/163-baseline-subject-resolution/spec.md` **Note**: This template is filled in by the `/speckit.plan` command. See `.specify/scripts/` for helper scripts. ## Summary Introduce an explicit backend subject-resolution contract for baseline compare and baseline capture so the system can classify each subject before resolution, select the correct local model path, and persist precise operator-safe gap semantics instead of collapsing structural, operational, and transient causes into broad `policy_not_found` style states. The implementation will extend existing baseline scope, inventory policy-type metadata, compare and capture jobs, baseline evidence-gap detail parsing, and OperationRun context persistence rather than introducing a parallel execution stack, with a bounded runtime support guard that prevents baseline-supported types from entering compare or capture on a resolver path that cannot truthfully classify them. ## Technical Context **Language/Version**: PHP 8.4 **Primary Dependencies**: Laravel 12, Filament v5, Livewire v4 **Storage**: PostgreSQL via existing application tables, especially `operation_runs.context` and baseline snapshot summary JSON **Testing**: Pest v4 on PHPUnit 12 **Target Platform**: Dockerized Laravel web application running through Sail locally and Dokploy in deployment **Project Type**: Web application **Performance Goals**: Preserve DB-only render behavior for Monitoring and tenant review surfaces, add no render-time Graph calls, and keep evidence-gap interpretation deterministic and lightweight enough for existing run-detail and landing surfaces **Constraints**: - No new render-time remote work and no bypass of `GraphClientInterface` - No change to `OperationRun` lifecycle ownership, notification channels, or summary-count rules - No new operator screen; existing surfaces must present richer semantics - Existing development-only run payloads may be deleted or regenerated if that simplifies migration to the new structured contract - Baseline-supported configuration must not overpromise runtime capability **Scale/Scope**: Cross-cutting backend semantic work across baseline compare and capture pipelines, support-layer parsers and translators, OperationRun context contracts, tenant and canonical read surfaces, and focused Pest coverage for deterministic classification and development-safe contract cleanup ## Constitution Check *GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.* - Inventory-first: PASS — the design keeps inventory as last-observed truth and distinguishes inventory-backed evidence from policy-backed evidence rather than conflating them. - Read/write separation: PASS — this feature changes classification and persisted run semantics inside existing compare and capture flows; it does not add new write or restore actions. - Graph contract path: PASS — no new Graph contract or direct endpoint use is introduced; existing capture and sync services remain the only remote paths. - Deterministic capabilities: PASS — subject-class derivation, resolution outcome mapping, and support-capability guards are explicitly designed to be deterministic and testable. - RBAC-UX: PASS — existing `/admin` and tenant-context authorization boundaries remain unchanged; only read semantics improve. - Workspace isolation: PASS — no new workspace leakage is introduced and canonical run-detail remains tenant-safe. - RBAC confirmations: PASS — no new destructive actions are added. - Global search: PASS — unaffected. - Tenant isolation: PASS — all compare, capture, inventory, and run data remain tenant-bound and entitlement-checked. - Run observability: PASS — compare and capture continue to use existing `OperationRun` types; this slice enriches context semantics only. - Ops-UX 3-surface feedback: PASS — no new toast, progress, or terminal-notification channels are added. - Ops-UX lifecycle: PASS — `OperationRun.status` and `OperationRun.outcome` remain service-owned; only context enrichment changes. - Ops-UX summary counts: PASS — no non-numeric values are moved into `summary_counts`; richer semantics live in context and read models. - Ops-UX guards: PASS — focused regression tests can protect classification determinism and development cleanup behavior without relaxing existing CI rules. - Ops-UX system runs: PASS — unchanged. - Automation: PASS — existing queue, retry, and backoff behavior stays intact; transient outcomes are classified more precisely, not re-executed differently. - Data minimization: PASS — the new gap detail contract stores classification and stable identifiers, not raw policy payloads or secrets. - Badge semantics (BADGE-001): PASS — if structural, operational, or transient labels surface as badges, they must route through centralized badge or presentation helpers rather than ad hoc maps. - UI naming (UI-NAMING-001): PASS — the feature exists to replace implementation-first broad error prose with domain-first operator meaning. - Operator surfaces (OPSURF-001): PASS — existing run detail and tenant review surfaces remain operator-first and diagnostics-secondary. - Filament UI Action Surface Contract: PASS — action topology stays unchanged; this is a read-surface semantics upgrade. - Filament UI UX-001 (Layout & IA): PASS — existing layouts remain, but sections become more semantically truthful. No exemption required. ## Project Structure ### Documentation (this feature) ```text specs/163-baseline-subject-resolution/ ├── plan.md ├── research.md ├── data-model.md ├── quickstart.md ├── contracts/ │ └── openapi.yaml ├── checklists/ │ └── requirements.md └── tasks.md ``` ### Source Code (repository root) ```text app/ ├── Filament/ │ ├── Pages/ │ │ └── BaselineCompareLanding.php │ ├── Resources/ │ │ ├── OperationRunResource.php │ │ └── BaselineSnapshotResource.php │ └── Widgets/ ├── Jobs/ │ ├── CompareBaselineToTenantJob.php │ └── CaptureBaselineSnapshotJob.php ├── Services/ │ ├── Baselines/ │ │ ├── BaselineCompareService.php │ │ ├── BaselineCaptureService.php │ │ ├── BaselineContentCapturePhase.php │ │ └── Evidence/ │ ├── Intune/ │ │ └── PolicySyncService.php │ └── Inventory/ │ └── InventorySyncService.php ├── Support/ │ ├── Baselines/ │ ├── Inventory/ │ ├── OpsUx/ │ └── Ui/ ├── Livewire/ └── Models/ config/ ├── tenantpilot.php └── graph_contracts.php tests/ ├── Feature/ │ ├── Baselines/ │ ├── Filament/ │ └── Monitoring/ └── Unit/ └── Support/ ``` **Structure Decision**: Web application. The work stays inside existing baseline jobs and services, support-layer value objects and presenters, current Filament surfaces, and focused Pest coverage. No new top-level architecture area is required. ## Complexity Tracking No constitution violations are required for this feature. ## Phase 0 — Outline & Research (DONE) Outputs: - `specs/163-baseline-subject-resolution/research.md` Key decisions captured: - Introduce a first-class subject-resolution contract in the backend instead of solving the problem with UI-only relabeling. - Persist both `subject_class` and `resolution_outcome` because they answer different operator questions. - Keep foundation-backed subjects eligible only when the runtime can truthfully classify them through an inventory-backed or limited-capability path. - Add a runtime consistency guard during scope or resolver preparation so `baseline_compare.supported` cannot silently overpromise structural capability. - Preserve transient reasons such as throttling and capture failure as precise operational outcomes rather than absorbing them into structural taxonomy. - Treat broad legacy gap shapes as development-only cleanup candidates rather than a compatibility requirement for the new runtime contract. ## Phase 1 — Design & Contracts (DONE) Outputs: - `specs/163-baseline-subject-resolution/data-model.md` - `specs/163-baseline-subject-resolution/contracts/openapi.yaml` - `specs/163-baseline-subject-resolution/quickstart.md` Design highlights: - The core semantic unit is a `SubjectDescriptor` that is classified before resolution and yields a deterministic `ResolutionOutcomeRecord`. - `OperationRun.context` remains the canonical persisted contract for compare and capture evidence-gap semantics, but new runs store richer subject-level objects instead of reason plus raw string only. - The runtime support guard sits before compare and capture execution so unsupported structural mismatches are blocked or reclassified before misleading `policy_not_found`-style outcomes are emitted. - Existing detail and landing surfaces are updated for the new structured gap contract, and development fixtures or stale local run data are regenerated instead of driving a permanent compatibility layer. - Compare and capture share the same root-cause vocabulary, but retain operation-specific outcome families where needed. ## Phase 1 — Agent Context Update (REQUIRED) Run: - `.specify/scripts/bash/update-agent-context.sh copilot` ## Constitution Check — Post-Design Re-evaluation - PASS — the design remains inside existing compare and capture operations and does not add new remote-call paths or lifecycle mutations. - PASS — inventory-first semantics are strengthened because inventory-backed subjects are no longer mislabeled as missing policy records. - PASS — operator surfaces stay on existing pages and remain DB-only at render time. - PASS — development cleanup is explicit and bounded; the new contract remains the only forward-looking runtime shape. - PASS — no Action Surface or UX-001 exemptions are needed because action topology and layouts remain intact. ## Phase 2 — Implementation Plan ### Step 1 — Subject classification and runtime capability foundation Goal: implement FR-001 through FR-003, FR-008, FR-015, and FR-016 by creating a deterministic subject-resolution foundation shared by compare and capture. Changes: - Introduce a dedicated subject-resolution support layer under `app/Support/Baselines/` that defines: - subject classes - resolution paths - resolution outcomes - operator action categories - structural versus operational versus transient classification - Extend `InventoryPolicyTypeMeta` and related metadata accessors so baseline support can express whether a type is policy-backed, inventory-backed, foundation-backed, or limited. - Add a runtime capability guard used by `BaselineScope`, `BaselineCompareService`, and `BaselineCaptureService` so types only enter compare or capture on a truthful path. - Keep the guard deterministic and explicit in logs or run context when support is limited or excluded. Tests: - Add unit tests for subject-class derivation, resolution-path derivation, and runtime-capability guard behavior. - Add golden-style tests covering supported, limited, and structurally invalid foundation types. ### Step 2 — Capture-path resolution and gap taxonomy upgrade Goal: implement FR-004 through FR-010 on the capture side so structural resolver mismatches are no longer emitted as generic missing-policy cases. Changes: - Refactor `BaselineContentCapturePhase` so it resolves subjects through the new subject contract rather than assuming a policy lookup for all subjects. - Replace broad `policy_not_found` capture gaps with precise structured outcomes such as: - policy record missing - inventory record missing - foundation-backed via inventory path - resolution type mismatch - unresolvable subject - Preserve existing transient outcomes like `throttled`, `capture_failed`, and `budget_exhausted` unchanged except for richer structured metadata. - Persist new structured gap-subject objects for new runs and remove any requirement to keep broad legacy reason shapes alive for future writes. Tests: - Add feature and unit coverage for capture-path classification across policy-backed, inventory-backed, foundation-backed, duplicate, invalid, and transient cases. - Add deterministic replay coverage proving unchanged capture inputs produce unchanged outcomes. - Add regressions proving structural foundation subjects no longer produce new generic `policy_not_found` gaps. ### Step 3 — Compare-path resolution and evidence-gap detail contract Goal: implement FR-004 through FR-014 on the compare side by aligning current-evidence resolution, evidence-gap reasoning, and persisted run context with the new contract. Changes: - Refactor `CompareBaselineToTenantJob` so baseline item interpretation and current-state resolution produce explicit `resolution_outcome` records rather than only count buckets and raw subject keys. - Add structured evidence-gap subject records under `baseline_compare.evidence_gaps.subjects` for new runs, including subject class, resolution path, resolution outcome, reason code, operator action category, and retryability or structural flags. - Preserve already precise compare reasons such as `missing_current`, `ambiguous_match`, and role-definition-specific gap families while separating them from structural non-policy-backed outcomes. - Ensure baseline compare reason translation remains aligned with the new detailed reason taxonomy instead of flattening distinct root causes. Tests: - Add feature tests for mixed compare runs containing structural, operational, transient, and successful subjects. - Add deterministic compare tests proving identical inputs yield identical resolution outcomes. - Add regressions for evidence-gap persistence shape and compare-surface rendering against the new structured contract. ### Step 4 — Development cleanup and operator-surface adoption Goal: implement FR-011 through FR-014 and the User Story 3 acceptance scenarios by moving existing read surfaces to the new gap contract and treating stale development data as disposable. Changes: - Extend `BaselineCompareEvidenceGapDetails`, `BaselineCompareStats`, `OperationRunResource`, `BaselineCompareLanding`, and any related Livewire gap tables so they read the new structured gap subject records consistently. - Add an explicit development cleanup mechanism for stale local run payloads, preferably a dedicated development-only Artisan command plus fixture regeneration steps, so old broad string-only gap subjects can be purged instead of preserved. - Introduce operator-facing labels that answer root cause before action advice while keeping diagnostics secondary. - Keep existing pages and sections, but expose structural versus operational versus transient semantics consistently across dense and detailed surfaces. - Update snapshot and compare summary surfaces where old broad reason aggregations would otherwise misread the new taxonomy. Tests: - Add or update Filament feature tests for canonical run detail and tenant baseline compare landing against the new structured run shape. - Add cleanup-oriented tests proving the development cleanup mechanism removes or invalidates stale broad-reason run payloads without extending production semantics. ### Step 5 — Focused validation pack and rollout safety Goal: protect the foundation from semantic regressions and make follow-on fidelity work safe. Changes: - Add a focused regression pack spanning compare, capture, capability guard, and development-safe contract cleanup. - Review every touched reason-label and badge usage to ensure structural, operational, and transient meanings remain centralized. - Document the new backend contract shape in code-level PHPDoc and tests so follow-on specs can build on stable semantics. - Keep rollout bounded to baseline compare and capture semantics without adding renderer-richness work from Spec 164. Tests: - Run the focused Pest pack in `quickstart.md`. - Add one regression proving no render-time Graph calls occur on affected run-detail or landing surfaces.