# Research: Stored Reports Surface v1 **Date**: 2026-05-06 **Branch**: `277-stored-reports-surface` ## Decision 1: Use one tenant-panel Filament resource with list and view pages - **Decision**: Implement the new surface as one tenant-scoped Filament resource with a register and a read-only view page. - **Rationale**: The repo already uses tenant-panel Filament resources with clickable-row inspection and infolist-style view pages for read-only registry surfaces. That keeps the slice inside the current UI contract, keeps Livewire behavior native, and avoids a bespoke report-view shell. - **Alternatives considered**: - Custom dashboard page or widget-only viewer: rejected because it would create a parallel navigation and interaction model for a standard read-only registry surface. - Edit-page-as-view workaround: rejected because the constitution requires inspection surfaces to use view/infolist semantics rather than disabled edit forms. ## Decision 2: Reuse `StoredReport` and `ArtifactTruthPresenter` instead of creating new lifecycle truth - **Decision**: Keep `StoredReport` as the only persisted truth and reuse `ArtifactTruthPresenter::forStoredReport()` for current versus historical retained state. - **Rationale**: The presenter already computes retained lifecycle truth by comparing the current row to the latest same-family row for the tenant. A second status column, mirror record, or new presenter layer would duplicate truth the repo already has. - **Alternatives considered**: - Persist a `current` flag or second lifecycle column on `stored_reports`: rejected because current versus historical is already derivable from existing rows. - Introduce a new stored-report-specific presenter or envelope system: rejected because `ArtifactTruthPresenter` already covers stored reports. ## Decision 3: Add explicit `permission_posture.view` instead of piggybacking on unrelated capabilities - **Decision**: Introduce one bounded `permission_posture.view` capability alongside the existing `entra_roles.view` capability. - **Rationale**: The spec requires family-aware browse and detail visibility. Reusing `provider.view`, `review_pack.view`, or `evidence.view` would blur ownership between the stored-report surface and downstream consumers. - **Alternatives considered**: - Reuse provider or evidence capabilities: rejected because the stored-report surface would become coupled to unrelated read paths. - Introduce one generic `stored_report.view` capability: rejected because the repo already has two concrete families with separate product meanings, and family-aware filtering is part of the feature value. ## Decision 4: Support exactly two family summary branches and keep all other families out of v1 - **Decision**: Render explicit summary branches only for `permission_posture` and `entra.admin_roles`, and keep any unexpected report family entirely outside v1 browse and detail scope. - **Rationale**: The repo has exactly two concrete stored-report families today. The narrowest correct implementation is explicit presentation for those families only. A local catch-all renderer would still need an authorization story and would widen the surface contract beyond the two repo-real families. - **Alternatives considered**: - Generic report-schema registry or plugin renderer: rejected because two concrete cases do not justify a framework. - Local catch-all renderer for unexpected families: rejected because it widens the browse/detail contract without a bounded authorization story. ## Decision 5: Canonical drilldown stays on the one confirmed widget seam - **Decision**: Make the stored-report detail route the canonical drilldown target for `AdminRolesSummaryWidget` only. - **Rationale**: The widget is the clearest current repo-real missing launch point. The codebase does not currently expose broad Filament proof links to stored reports, so the package should not invent new local report cards or pseudo-view links just to claim convergence. - **Alternatives considered**: - Widget-only link with no register: rejected because it would leave stored-report browsing fragmented. - Add new stored-report links to every evidence or review surface up front: rejected because repo truth does not currently show broad operator-facing stored-report launch affordances there. ## Decision 6: Keep proof in focused feature tests and skip browser by default - **Decision**: Plan feature coverage for register behavior, entitlement behavior, detail presentation, and widget drilldown, with no default browser lane. - **Rationale**: The surface is native Filament list and view behavior plus a widget link. The main risks are authorization and disclosure rules, not browser-only choreography. - **Alternatives considered**: - Browser smoke as a default requirement: rejected because the slice does not introduce a custom interaction model. - Large unitized presentation layers: rejected because that would create indirection just to make testing easier. ## Final Research Outcome - One native tenant-panel Filament resource is the narrowest correct surface shape. - Existing stored-report truth and artifact-truth lifecycle semantics are sufficient; no new persistence or lifecycle framework is needed. - `permission_posture.view` is the only new capability required. - Summary rendering stays explicit for the two repo-real families and avoids a registry. - Canonical drilldown starts and ends in v1 with the admin-roles widget seam. - Focused feature tests are the honest default proving path.