## Summary - replace the baseline snapshot detail page with a structured summary-first rendering flow - add a presenter plus renderer registry with RBAC, compliance, and fallback renderers - add grouped policy-type browsing, fidelity and gap badges, and workspace authorization coverage - add Feature 130 spec, plan, contract, research, quickstart, and completed task artifacts ## Testing - focused Pest coverage was added for structured rendering, fallback behavior, degraded states, authorization, presenter logic, renderer resolution, and badge mapping - I did not rerun the full validation suite in this final PR step ## Notes - base branch: `dev` - feature branch: `130-structured-snapshot-rendering` Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #158
18 KiB
Implementation Plan: Structured Snapshot Rendering & Type-Agnostic Item Browser
Branch: 130-structured-snapshot-rendering | Date: 2026-03-09 | Spec: spec.md
Input: Feature specification from /specs/130-structured-snapshot-rendering/spec.md
Note: This template is filled in by the /speckit.plan command. See .specify/scripts/ for helper scripts.
Summary
Replace the current Baseline Snapshot detail experience, which mixes a raw summary JSON block with an RBAC-only repeatable section, with a normalized presenter-driven view that renders every captured policy type through a shared summary-first inspection model. Keep the feature workspace-scoped and read-only, introduce a BaselineSnapshotPresenter plus a type-renderer registry and fallback renderer, preserve richer RBAC detail as an enrichment layer rather than a privileged exception, add initial structured compliance rendering for deviceCompliancePolicy, and deliver the UI through sectioned Filament infolist entries backed by focused feature and unit tests.
Technical Context
Language/Version: PHP 8.4.15 / Laravel 12
Primary Dependencies: Filament v5, Livewire v4.0+, Tailwind CSS v4
Storage: PostgreSQL via Laravel Sail using existing baseline_snapshots, baseline_snapshot_items, and JSONB presentation source fields
Testing: Pest v4 feature and unit tests on PHPUnit 12
Target Platform: Laravel Sail web application with workspace-scoped admin panel at /admin
Project Type: Laravel monolith / Filament web application
Performance Goals: Snapshot detail remains DB-only at render time, snapshot items are eager-loaded once per page render, groups are collapsed by default for large snapshots, and technical payload disclosure remains secondary and bounded
Constraints: No schema changes; no Microsoft Graph calls; preserve immutable snapshot semantics; preserve workspace-scoped authorization and 404/403 semantics on both list and detail routes; keep raw JSON non-primary; isolate per-group rendering failures; use centralized badge semantics for fidelity and gap states; keep technical detail optional and scope-safe
Scale/Scope: One existing Filament resource detail surface, one new presenter and renderer registry layer, fallback plus initial RBAC and device-compliance enrichments, a small set of Blade infolist entry components, and focused regression tests
Constitution Check
GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.
- Inventory-first: PASS — this feature consumes existing immutable snapshot artifacts for inspection and does not alter inventory versus snapshot ownership or semantics.
- Read/write separation: PASS — the redesign is read-only and introduces no mutation flow, restore action, or audit-triggering write path.
- Graph contract path: PASS — no Microsoft Graph call is added.
- Deterministic capabilities: PASS — authorization continues to rely on the existing workspace capability registry and resolver with no raw capability strings.
- RBAC-UX planes and isolation: PASS — the feature stays in the
/adminworkspace plane, keeps/systemuntouched, and preserves deny-as-not-found for non-members plus protected in-scope access semantics. - Workspace isolation: PASS — snapshot detail remains bound to the active workspace context and existing workspace membership checks.
- RBAC-UX destructive confirmation: PASS / N/A — no destructive or mutation action is introduced.
- RBAC-UX global search: PASS — the resource remains non-globally-searchable, so global-search semantics do not change.
- Tenant isolation: PASS — no tenant-scoped read or cross-tenant aggregation is introduced.
- Run observability: PASS / N/A — no new
OperationRunor long-running workflow is introduced. - Ops-UX 3-surface feedback: PASS / N/A — no operation lifecycle change is involved.
- Ops-UX lifecycle and summary counts: PASS / N/A — no
OperationRuntransitions or summary producers are added. - Ops-UX guards and system runs: PASS / N/A — existing operations behavior is unaffected.
- Automation: PASS / N/A — no queued or scheduled behavior changes are required.
- Data minimization: PASS — the page uses existing workspace-safe snapshot metadata and technical payload disclosure stays secondary.
- Badge semantics (BADGE-001): PASS — fidelity and gap states will be added through centralized badge semantics rather than inline color mapping.
- Filament UI Action Surface Contract: PASS — the feature modifies a view-only resource detail surface, keeps the immutable-resource exemptions already declared on the resource, and upgrades the list inspection affordance from a lone
Viewrow action to clickable rows or a primary linked column so the modified surface matches the constitution. - Filament UI UX-001: PASS — the design remains a sectioned infolist-style detail view with metadata, summary, grouped browser, and technical disclosure sections.
- Filament v5 / Livewire v4 compliance: PASS — the design stays inside the existing Filament v5 and Livewire v4 admin panel.
- Provider registration (
bootstrap/providers.php): PASS — no panel provider change is introduced. - Global search resource rule: PASS — the resource remains not globally searchable, so the Edit/View global-search rule is unaffected.
- Asset strategy: PASS — no new heavy asset bundle is required; existing deploy-time
php artisan filament:assetsbehavior remains sufficient.
Project Structure
Documentation (this feature)
specs/130-structured-snapshot-rendering/
├── plan.md
├── research.md
├── data-model.md
├── quickstart.md
├── contracts/
│ └── baseline-snapshot-rendering.openapi.yaml
├── checklists/
│ └── requirements.md
└── tasks.md
Source Code (repository root)
app/
├── Filament/
│ └── Resources/
│ ├── BaselineSnapshotResource.php # MODIFY — replace raw summary primary UX
│ └── BaselineSnapshotResource/
│ └── Pages/
│ └── ViewBaselineSnapshot.php # MODIFY if page-level composition or eager-loading is needed
├── Services/
│ └── Baselines/
│ └── SnapshotRendering/
│ ├── BaselineSnapshotPresenter.php # NEW page-level builder
│ ├── SnapshotTypeRenderer.php # NEW renderer contract
│ ├── SnapshotTypeRendererRegistry.php # NEW registry/fallback resolution
│ ├── RenderedSnapshot.php # NEW normalized summary DTO
│ ├── RenderedSnapshotGroup.php # NEW group DTO
│ ├── RenderedSnapshotItem.php # NEW item DTO
│ ├── FidelityState.php # NEW fidelity enum/value object
│ ├── GapSummary.php # NEW gap summary value object
│ └── Renderers/
│ ├── FallbackSnapshotTypeRenderer.php # NEW minimum-contract renderer
│ ├── IntuneRoleDefinitionSnapshotTypeRenderer.php # NEW extracted RBAC renderer
│ └── DeviceComplianceSnapshotTypeRenderer.php # NEW initial compliance renderer
└── Support/
└── Badges/
├── BadgeDomain.php # MODIFY if new fidelity/gap domains are introduced
└── Domains/ # NEW badge mapper(s) if fidelity/gap become centralized domains
resources/
└── views/
└── filament/
└── infolists/
└── entries/
├── baseline-snapshot-summary-table.blade.php # NEW summary-first rendering block
├── baseline-snapshot-groups.blade.php # NEW grouped browser with collapsed sections
├── baseline-snapshot-technical-detail.blade.php # NEW secondary technical disclosure
└── snapshot-json.blade.php # MODIFY or reduce prominence if retained via partial reuse
tests/
├── Feature/
│ └── Filament/
│ ├── BaselineSnapshotFidelityVisibilityTest.php # MODIFY existing assertions for structured UI
│ ├── BaselineSnapshotStructuredRenderingTest.php # NEW mixed-type summary and grouped browser coverage
│ ├── BaselineSnapshotFallbackRenderingTest.php # NEW unknown-type fallback coverage
│ ├── BaselineSnapshotAuthorizationTest.php # NEW or MODIFY positive/negative workspace access coverage
│ └── BaselineSnapshotDegradedStateTest.php # NEW fidelity/gap and failure-isolation coverage
└── Unit/
└── Baselines/
└── SnapshotRendering/
├── BaselineSnapshotPresenterTest.php # NEW presenter/group aggregation coverage
├── SnapshotTypeRendererRegistryTest.php # NEW registry resolution coverage
├── FallbackSnapshotTypeRendererTest.php # NEW minimum-contract coverage
├── IntuneRoleDefinitionSnapshotTypeRendererTest.php # NEW RBAC contract compliance coverage
└── DeviceComplianceSnapshotTypeRendererTest.php # NEW compliance rendering coverage
Structure Decision: Keep the feature inside the existing Laravel/Filament monolith. The implementation centers on the existing BaselineSnapshotResource view surface, a new snapshot-rendering service layer under app/Services/Baselines, small Blade infolist entry components for summary and grouped rendering, and focused Pest feature and unit tests.
Complexity Tracking
No Constitution Check violations. No justifications needed.
| Violation | Why Needed | Simpler Alternative Rejected Because |
|---|---|---|
| — | — | — |
Phase 0 — Research (DONE)
Output:
specs/130-structured-snapshot-rendering/research.md
Key findings captured:
- Current snapshot detail uses a raw JSON summary block plus an RBAC-only repeatable section, so the shared abstraction does not exist yet.
BaselineSnapshot.summary_jsonbalready provides snapshot-level counts by policy type, fidelity counts, and gap counts suitable for a summary-first page.BaselineSnapshotItem.meta_jsonbalready provides a deterministic minimum metadata shape that supports a fallback renderer for all policy types.- Existing repo patterns favor registry or normalizer strategies, centralized badge semantics, and custom
ViewEntryBlade components inside sectioned Filament infolists.
Phase 1 — Design & Contracts (DONE)
Outputs:
specs/130-structured-snapshot-rendering/data-model.mdspecs/130-structured-snapshot-rendering/contracts/baseline-snapshot-rendering.openapi.yamlspecs/130-structured-snapshot-rendering/quickstart.md
Design highlights:
- Add a page-level
BaselineSnapshotPresenterthat loads one snapshot and returns normalized summary, group, and item DTOs for the UI. - Resolve per-type enrichment through a
SnapshotTypeRendererRegistrywith a shared fallback renderer so unsupported or new types still render. - Keep the detail page inside a sectioned Filament infolist, using custom
ViewEntryBlade components for the summary table, grouped browser, and optional technical detail disclosure. - Centralize fidelity and gap badge semantics rather than embedding ad hoc color decisions in the resource.
Phase 1 — Agent Context Update (DONE)
Run:
.specify/scripts/bash/update-agent-context.sh copilot
Phase 2 — Implementation Outline (tasks created in /speckit.tasks)
Step 1 — Introduce normalized snapshot presentation primitives
Goal: implement FR-130-03 through FR-130-09, FR-130-15 through FR-130-18, and FR-130-22.
Changes:
- Create normalized DTOs or value objects for page summary, policy-type groups, rendered items, fidelity state, and gap summaries.
- Implement a page-level presenter that consumes
BaselineSnapshotplus eager-loadeditemsand emits a deterministic presentation model. - Add a renderer interface and registry with fallback resolution so every
policy_typereceives a renderer.
Tests:
- Add unit coverage for group aggregation, ordering, and fallback resolution.
- Add unit coverage proving unknown policy types still produce valid summary and item output.
Step 2 — Refactor RBAC rendering into the shared renderer contract
Goal: implement FR-130-10 through FR-130-11 and preserve backward-compatible RBAC richness.
Changes:
- Extract the current
intuneRoleDefinitiondetail mapping fromBaselineSnapshotResourceinto a dedicated renderer class. - Preserve RBAC-specific enrichment fields such as built-in versus custom, permission block counts, identity strategy, and policy version reference.
- Ensure RBAC output also satisfies the shared item contract and group shell.
Tests:
- Add unit coverage proving the RBAC renderer produces both common fields and enrichment fields.
- Add feature coverage proving RBAC remains richly inspectable without becoming the only rendered type.
Step 3 — Add initial compliance and fallback rendering
Goal: implement FR-130-08 through FR-130-14 and FR-130-19.
Changes:
- Add an initial
deviceCompliancePolicyrenderer that emits minimum structured detail plus any safe first-pass enrichment such as platform and major compliance hints when present. - Implement the shared fallback renderer so all other policy types render via best available metadata.
- Derive explicit fidelity and gap states at group and item level using snapshot and item metadata rather than hidden omission.
Tests:
- Add unit coverage for compliance rendering and fallback minimum-contract behavior.
- Add feature coverage for unsupported or newly added types, partial fidelity, reference-only items, and empty or degraded groups.
Step 4 — Rebuild the Baseline Snapshot detail layout
Goal: implement FR-130-01 through FR-130-07, FR-130-20, and FR-130-21.
Changes:
- Replace the current raw-summary-first infolist section with a metadata section, a structured summary section, a grouped item-browser section, and a secondary technical detail section.
- Render the summary through a custom
ViewEntryor equivalent Blade component that shows one row per policy type with count, fidelity, gaps, and timing. - Render grouped items through collapsible sections that stay collapsed by default, include group header badges, and isolate failures per group.
- Keep raw JSON or technical payload inspection available only through a labeled secondary disclosure.
Tests:
- Add feature coverage proving summary-first hierarchy, collapsed-by-default groups where testable, and secondary technical payload disclosure.
- Add feature assertions proving raw JSON is no longer the primary visible content path.
Step 5 — Centralize fidelity and gap badge semantics
Goal: implement FR-130-13 through FR-130-14 and align with BADGE-001.
Changes:
- Introduce centralized badge semantics for fidelity and gap states, either through new
BadgeDomainentries and mappers or an existing compatible shared domain helper. - Replace any ad hoc state-to-color logic on the detail page with centralized badge specs.
Tests:
- Add unit coverage for fidelity and gap badge mappings.
- Add feature assertions that expected labels or badges appear for full, partial, reference-only, unsupported, and gap-present cases.
Step 6 — Preserve authorization and regression safety
Goal: satisfy the spec’s workspace-scope and regression guarantees.
Changes:
- Ensure the resource detail surface continues to enforce workspace membership and baseline-view capability semantics.
- Keep the resource non-globally-searchable and avoid introducing new scope leakage through related references or technical payloads.
- Update existing snapshot visibility tests to the new UI structure and add regression coverage for mixed-type rendering.
Tests:
- Add or update positive authorization coverage for an entitled workspace member.
- Add or update negative authorization coverage for non-members or under-entitled users.
- Add regression tests proving non-RBAC types do not disappear and one renderer failure does not break the page.
Step 7 — Final verification and polish
Goal: finish the feature within the repo’s Laravel, Filament, and testing standards.
Changes:
- Review section hierarchy, empty-state copy, collapsed defaults, and technical detail labeling against UX-001.
- Confirm the detail surface remains view-only and consistent with the resource’s immutable-action exemptions.
Tests:
- Run focused Sail-based Pest coverage for unit and feature paths touched by this redesign.
- Run Pint on dirty files through Sail during implementation.
Constitution Check (Post-Design)
Re-check result: PASS.
- Livewire v4.0+ compliance: preserved because the design remains inside the existing Filament v5 and Livewire v4 admin panel.
- Provider registration location: unchanged; the current panel provider remains registered in
bootstrap/providers.php. - Globally searchable resources: unchanged;
BaselineSnapshotResourceremains non-globally-searchable. - Destructive actions: unchanged; the redesign introduces no destructive or mutation action, so no new
->requiresConfirmation()surface is needed. - Asset strategy: unchanged; no new heavy or shared asset bundle is introduced, and current deploy-time
php artisan filament:assetsbehavior remains sufficient. - Action-surface inspect affordance: the modified list surface will use clickable rows or a primary linked column instead of a lone
Viewrow action. - Testing plan: add or update focused Pest feature and unit coverage for presenter aggregation, registry resolution, fallback rendering, compliance and RBAC contract compliance, summary-first layout, fidelity and gap badges, workspace authorization semantics, and group failure isolation.