Some checks failed
Main Confidence / confidence (push) Failing after 54s
Add `CustomerReviewWorkspace` page for tenant pre-filtered reviews Add customer workspace links to `EvidenceSnapshotResource`, `ReviewPackResource`, and `TenantReviewResource` Implement audit logging for `TenantReviewOpened` and `ReviewPackDownloaded` actions Update ReviewPack download controller to enforce tenant-scoped RBAC Add tests for ReviewPack download authorization and audit logging Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #289
166 lines
12 KiB
Markdown
166 lines
12 KiB
Markdown
# Research — Customer Review Workspace v1
|
||
|
||
**Date**: 2026-04-27
|
||
**Spec**: [spec.md](spec.md)
|
||
|
||
This document resolves the planning decisions that shape the smallest safe implementation slice for Spec 249.
|
||
|
||
## Decision 1 — Place the new surface as a native admin reviews page
|
||
|
||
**Decision**: Implement the customer-safe workspace as a new native Filament page under the existing admin reviews family, with the planned route shape `/admin/reviews/workspace`. Do not create a new panel, a public/customer portal shell, or a new Resource just to host the view.
|
||
|
||
**Rationale**:
|
||
- The repo already has native workspace-level read-only pages for reporting and monitoring.
|
||
- The existing review, review-pack, and evidence Resources already own tenant-scoped detail and proof routes.
|
||
- A dedicated page keeps the first slice calm and customer-safe without overloading an operator-oriented registry.
|
||
|
||
**Evidence**:
|
||
- [../../apps/platform/app/Filament/Pages/Reviews/ReviewRegister.php](../../apps/platform/app/Filament/Pages/Reviews/ReviewRegister.php) already provides the workspace review register pattern.
|
||
- [../../apps/platform/app/Filament/Pages/Monitoring/EvidenceOverview.php](../../apps/platform/app/Filament/Pages/Monitoring/EvidenceOverview.php) already provides a workspace-scoped read-only page pattern with tenant prefilters.
|
||
- [../../apps/platform/bootstrap/providers.php](../../apps/platform/bootstrap/providers.php) already registers the existing panel providers. No new provider registration is needed.
|
||
|
||
**Alternatives considered**:
|
||
- Extend `ReviewRegister` into a dual-persona page.
|
||
- Rejected: it already carries operator-oriented filters and export semantics, which would blur the customer-safe default path.
|
||
- Create a new customer portal or new identity plane.
|
||
- Rejected: outside the bounded v1 scope and unnecessary because the current admin plane plus readonly-capable roles already exists.
|
||
|
||
## Decision 2 — Reuse `TenantReviewRegisterService` as the entitlement and query seam
|
||
|
||
**Decision**: Prefer extending or reusing [../../apps/platform/app/Services/TenantReviews/TenantReviewRegisterService.php](../../apps/platform/app/Services/TenantReviews/TenantReviewRegisterService.php) for workspace access checks, entitled-tenant discovery, and the base review query before adding any new helper.
|
||
|
||
**Rationale**:
|
||
- The service already centralizes workspace membership and entitled tenant selection for the current review register.
|
||
- Reusing it keeps entitlement logic in one place and avoids new raw tenant-role queries inside the page.
|
||
- It is the narrowest existing seam that can be extended toward latest-published-per-tenant behavior.
|
||
|
||
**Evidence**:
|
||
- `authorizedTenants(...)` already derives tenant scope from the canonical role/capability map.
|
||
- `query(...)` already scopes tenant reviews to the current workspace and eager-loads `tenant`, `evidenceSnapshot`, and `currentExportReviewPack`.
|
||
- `canAccessWorkspace(...)` already exposes the workspace-membership check needed for deny-as-not-found page gating.
|
||
|
||
**Alternatives considered**:
|
||
- Build a new persisted workspace summary table.
|
||
- Rejected: violates the no-new-persistence rule for a derived read surface.
|
||
- Recreate entitlement logic directly inside the page class.
|
||
- Rejected: duplicates existing membership and capability behavior.
|
||
|
||
## Decision 3 — Derive the default path from the latest published tenant review per entitled tenant
|
||
|
||
**Decision**: The default workspace page should derive each tenant summary from the latest published `TenantReview` for that entitled tenant, with eager-loaded `currentExportReviewPack` and `evidenceSnapshot` relationships.
|
||
|
||
**Rationale**:
|
||
- The spec requires the default path to stay customer-safe and exclude draft, failed, and other internal-only states.
|
||
- The current `TenantReview` model already distinguishes published reviews and holds the summary relationships the new page needs.
|
||
- This keeps the page read-oriented and avoids a separate customer-review lifecycle.
|
||
|
||
**Evidence**:
|
||
- [../../apps/platform/app/Models/TenantReview.php](../../apps/platform/app/Models/TenantReview.php) already exposes `published()` and `currentExportReviewPack()`.
|
||
- [../../apps/platform/app/Services/TenantReviews/TenantReviewService.php](../../apps/platform/app/Services/TenantReviews/TenantReviewService.php) already stores review summary payloads, evidence basis, and export readiness.
|
||
- Existing review composition already emits `finding_outcomes` and accepted-risk related payloads through the review artifact family.
|
||
|
||
**Alternatives considered**:
|
||
- Surface draft or ready reviews when no published review exists.
|
||
- Rejected: leaks internal lifecycle meaning into the customer-safe path.
|
||
- Create a second customer-review publication model.
|
||
- Rejected: duplicates review truth and imports unnecessary workflow complexity.
|
||
|
||
## Decision 4 — Keep page state Livewire-safe and tenant-prefilter aware
|
||
|
||
**Decision**: Tenant launch context, requested tenant filters, and any remembered page state must live in public, query-backed, or session-backed state, following the existing workspace-page patterns. Do not keep required filter state in private properties.
|
||
|
||
**Rationale**:
|
||
- Existing workspace pages already show how canonical admin pages preserve tenant prefilters and survive Livewire follow-up requests.
|
||
- This repo has already hit Livewire state-reset issues when tenant context lived in non-hydrated private properties.
|
||
- The workspace page needs tenant prefiltering from review, evidence, and related entry points.
|
||
|
||
**Evidence**:
|
||
- [../../apps/platform/app/Filament/Pages/Reviews/ReviewRegister.php](../../apps/platform/app/Filament/Pages/Reviews/ReviewRegister.php) uses canonical admin filter-state sync on mount.
|
||
- [../../apps/platform/app/Filament/Pages/Monitoring/EvidenceOverview.php](../../apps/platform/app/Filament/Pages/Monitoring/EvidenceOverview.php) documents its page-state contract for tenant prefilters and remembered search/filter state.
|
||
- Repo memory already records that private page state can reset during Livewire actions on admin canonical pages.
|
||
|
||
**Alternatives considered**:
|
||
- Keep launch context in a private property only.
|
||
- Rejected: too brittle across Livewire requests.
|
||
- Use only client-side state.
|
||
- Rejected: breaks server-side truth and shareable canonical page behavior.
|
||
|
||
## Decision 5 — Reuse artifact-truth and redaction seams for customer-safe disclosure
|
||
|
||
**Decision**: Reuse [../../apps/platform/app/Support/Ui/GovernanceArtifactTruth/ArtifactTruthPresenter.php](../../apps/platform/app/Support/Ui/GovernanceArtifactTruth/ArtifactTruthPresenter.php), `SurfaceCompressionContext`, and [../../apps/platform/app/Support/RedactionIntegrity.php](../../apps/platform/app/Support/RedactionIntegrity.php) for outcome, freshness, and redaction-safe wording.
|
||
|
||
**Rationale**:
|
||
- These seams already normalize review, review-pack, and evidence truth into operator-safe summaries.
|
||
- Reusing them preserves vocabulary and prevents a second customer-review explanation system.
|
||
- `RedactionIntegrity` already owns the repo’s protected-value and support-diagnostics notes.
|
||
|
||
**Evidence**:
|
||
- `ArtifactTruthPresenter::for(...)` already supports `TenantReview`, `ReviewPack`, and `EvidenceSnapshot`.
|
||
- `ReviewRegister`, `EvidenceOverview`, `TenantReviewResource`, and `ReviewPackResource` already depend on these truth envelopes.
|
||
- `RedactionIntegrity` already defines reusable disclosure notes for protected values and support diagnostics.
|
||
|
||
**Alternatives considered**:
|
||
- Introduce a customer-only presenter or status taxonomy.
|
||
- Rejected: duplicates shared artifact truth and increases review drift risk.
|
||
- Inline page-local disclosure strings only.
|
||
- Rejected: likely to diverge from existing review and pack semantics.
|
||
|
||
## Decision 6 — Keep review-pack consumption on the existing signed download path
|
||
|
||
**Decision**: Pack consumption should stay on the existing signed route and download controller, with the workspace page only generating or surfacing the already-authorized download path through [../../apps/platform/app/Services/ReviewPackService.php](../../apps/platform/app/Services/ReviewPackService.php).
|
||
|
||
**Rationale**:
|
||
- The repo already has a real signed download route and a dedicated download controller.
|
||
- Reusing that path keeps the new page consumption-only and avoids inventing a customer-specific download endpoint.
|
||
- The page must not trigger pack generation or regeneration.
|
||
|
||
**Evidence**:
|
||
- [../../apps/platform/app/Services/ReviewPackService.php](../../apps/platform/app/Services/ReviewPackService.php) already generates signed download URLs.
|
||
- [../../apps/platform/routes/web.php](../../apps/platform/routes/web.php) already exposes `/admin/review-packs/{reviewPack}/download` as the signed download route.
|
||
- [../../apps/platform/app/Http/Controllers/ReviewPackDownloadController.php](../../apps/platform/app/Http/Controllers/ReviewPackDownloadController.php) already enforces pack readiness and expiry constraints.
|
||
|
||
**Alternatives considered**:
|
||
- Add a new workspace-page-specific download endpoint.
|
||
- Rejected: duplicates current signed download behavior.
|
||
- Offer generate/regenerate from the workspace page.
|
||
- Rejected: out of scope and not customer-safe for v1.
|
||
|
||
## Decision 7 — Reuse the current audit pipeline and add new action IDs only if needed
|
||
|
||
**Decision**: Reuse `WorkspaceAuditLogger` and `AuditActionId` for any explicit artifact access or download events surfaced by the new page, and only add new stable action IDs if the existing review/export path does not already provide a truthful event.
|
||
|
||
**Rationale**:
|
||
- The repo already has a canonical workspace-scoped audit path.
|
||
- This slice needs auditability for explicit artifact consumption, not a new access-analytics subsystem.
|
||
- Stable action IDs are preferable to page-local logging if an additional event is truly required.
|
||
|
||
**Evidence**:
|
||
- [../../apps/platform/app/Services/TenantReviews/TenantReviewService.php](../../apps/platform/app/Services/TenantReviews/TenantReviewService.php) already logs review creation/refresh through `WorkspaceAuditLogger`.
|
||
- [../../apps/platform/app/Services/ReviewPackService.php](../../apps/platform/app/Services/ReviewPackService.php) already logs review-pack export activity.
|
||
- [../../apps/platform/app/Support/Audit/AuditActionId.php](../../apps/platform/app/Support/Audit/AuditActionId.php) is the stable audit action registry.
|
||
|
||
**Alternatives considered**:
|
||
- Add a new customer-review audit table.
|
||
- Rejected: violates the no-new-persistence rule.
|
||
- Emit page-render audits for every visit.
|
||
- Rejected: too noisy and not aligned with the explicit-artifact-access requirement.
|
||
|
||
## Decision 8 — Keep the slice Filament-native, asset-light, and non-searchable
|
||
|
||
**Decision**: Keep the slice on the existing Filament v5 / Livewire v4 stack, do not add a new Resource or global-search entry, and plan for no new asset bundle unless implementation proves otherwise.
|
||
|
||
**Rationale**:
|
||
- The feature is a new page over existing truth, not a new object family.
|
||
- Existing review, pack, and evidence Resources already disable global search because they are tenant-scoped.
|
||
- A native page avoids a second shell and keeps the deploy story unchanged.
|
||
|
||
**Evidence**:
|
||
- `TenantReviewResource`, `ReviewPackResource`, and `EvidenceSnapshotResource` already set global search off.
|
||
- Panel providers are already registered in [../../apps/platform/bootstrap/providers.php](../../apps/platform/bootstrap/providers.php).
|
||
- The repo’s Filament guidance already expects provider registration to remain in `bootstrap/providers.php` and assets to stay minimal unless explicitly registered.
|
||
|
||
**Alternatives considered**:
|
||
- Add a new searchable Resource just for the workspace page.
|
||
- Rejected: the surface is a page-level dashboard, not a new record type.
|
||
- Add a custom asset bundle or custom portal shell up front.
|
||
- Rejected: unnecessary for the first read-only slice. |