## Summary - Fixes misleading “queued / running in background” message when Review Pack generation request reuses an existing ready pack (fingerprint dedupe). - Improves resilience of Filament/Livewire interactions by ensuring the Livewire intercept shim applies after Livewire initializes. - Aligns Review Pack operation notifications with Ops-UX patterns (queued + completed notifications) and removes the old ReviewPackStatusNotification. ## Key Changes - Review Pack generate action now: - Shows queued toast only when a new pack is actually created/queued. - Shows a “Review pack already available” success notification with a link when dedupe returns an existing pack. ## Tests - `vendor/bin/sail artisan test --compact tests/Feature/ReviewPack/ReviewPackGenerationTest.php` - `vendor/bin/sail artisan test --compact tests/Feature/ReviewPack/ReviewPackResourceTest.php` - `vendor/bin/sail artisan test --compact tests/Feature/LivewireInterceptShimTest.php` ## Notes - No global search behavior changes for ReviewPacks (still excluded). - Destructive actions remain confirmation-gated (`->requiresConfirmation()`). Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #133
6.4 KiB
Quickstart: 109 — Tenant Review Pack Export v1
Date: 2026-02-23
Branch: 109-review-pack-export
Prerequisites
- Sail services running (
vendor/bin/sail up -d) - Database migrated to latest
- At least one workspace with a connected tenant
- Tenant has stored reports (permission_posture and/or entra.admin_roles) and findings
Implementation Phases
Phase 1: Foundation (Data Layer + Config)
Goal: Database table, model, factory, enum additions, config entries.
-
Migration:
create_review_packs_table- See data-model.md for full schema
- Includes partial unique index on
(workspace_id, tenant_id, fingerprint)for non-expired/failed packs
-
Model:
ReviewPack- Uses
DerivesWorkspaceIdFromTenant - Casts: summary/options as array, generated_at/expires_at as datetime
- Relationships: workspace, tenant, operationRun, initiator
- Scopes: ready, expired, pastRetention, forTenant, latestReady
- Uses
-
Factory:
ReviewPackFactorywith states: queued, generating, ready, failed, expired -
Enum: Add
ReviewPackGeneratetoOperationRunType -
Enum: Add
ReviewPackStatus(standalone enum atApp\Support\ReviewPackStatus) -
Config:
config/filesystems.php→exportsdiskconfig/tenantpilot.php→review_packsection
-
Capabilities: Add
REVIEW_PACK_VIEWandREVIEW_PACK_MANAGEtoCapabilities.php -
Badge: Add
ReviewPackStatustoBadgeDomain+ createReviewPackStatusBadgemapper
Run: vendor/bin/sail artisan migrate && vendor/bin/sail artisan test --compact --filter=ReviewPack
Phase 2: Service Layer + Job
Goal: Generation logic, ZIP assembly, fingerprint computation.
-
Service:
ReviewPackServicegenerate(Tenant, User, array $options): ReviewPack— orchestrates creationcomputeFingerprint(Tenant, array $options): string— deterministic hashgenerateDownloadUrl(ReviewPack): string— signed URLfindExistingPack(Tenant, string $fingerprint): ?ReviewPack— dedupe check
-
Job:
GenerateReviewPackJob- Collects data from StoredReport, Finding, Tenant, OperationRun
- Builds file map → assembles ZIP → stores on exports disk
- Updates ReviewPack status + metadata
- Sends notification on completion/failure
-
Notification:
ReviewPackStatusNotification- Database channel
- Ready: includes view page link
- Failed: includes sanitized reason
Run: vendor/bin/sail artisan test --compact --filter=GenerateReviewPack
Phase 3: Download Controller
Goal: Signed URL download endpoint.
-
Controller:
ReviewPackDownloadController- Single
__invokemethod - Validates pack exists + status is ready
- Streams file with proper headers (Content-Type, Content-Disposition, SHA256)
- Single
-
Route:
GET /admin/review-packs/{reviewPack}/download- Named
admin.review-packs.download - Middleware:
signed
- Named
Run: vendor/bin/sail artisan test --compact --filter=ReviewPackDownload
Phase 4: Filament UI
Goal: Resource, view page, dashboard widget.
-
Resource:
ReviewPackResource- List page: table with status badge, generated_at, expires_at, file_size
- Header action: "Generate Pack" (modal with PII/operations toggles)
- Row actions: Download (ready), Expire (destructive + confirmation)
- Empty state: "No review packs yet" + "Generate first pack" CTA
- Filters: status, date range
-
View Page:
ViewReviewPack- Infolist layout with sections
- Header actions: Download, Regenerate
- Summary display, data freshness table, options used
-
Widget:
TenantReviewPackCard- Shows latest pack status + metadata
- Actions: Generate, Download (conditional)
Run: vendor/bin/sail artisan test --compact --filter=ReviewPackResource
Phase 5: Prune Command + Schedule
Goal: Retention automation.
-
Command:
tenantpilot:review-pack:prune- Marks expired, deletes files, optional hard-delete
--hard-deleteflag for DB row removal after grace period
-
Schedule: Wire in
routes/console.phpdaily()+withoutOverlapping()
-
AlertRule cleanup: Remove
sla_duefrom dropdown options
Run: vendor/bin/sail artisan test --compact --filter="ReviewPackPrune|AlertRule"
Phase 6: Integration Tests
Goal: End-to-end coverage.
- RBAC enforcement tests (404 for non-members, 403 for insufficient caps)
- Fingerprint dedupe test (duplicate generation → reuse)
- Active-run dedupe test (concurrent generation → rejection)
- Prune test (expired packs → status update + file deletion)
- Download test (signed URL → file stream with correct headers)
- Empty state + widget display tests
Run: vendor/bin/sail artisan test --compact tests/Feature/ReviewPack/
File Inventory (New Files)
database/migrations/XXXX_create_review_packs_table.php
app/Models/ReviewPack.php
database/factories/ReviewPackFactory.php
app/Support/ReviewPackStatus.php
app/Support/Badges/Mappers/ReviewPackStatusBadge.php
app/Services/ReviewPackService.php
app/Jobs/GenerateReviewPackJob.php
app/Notifications/ReviewPackStatusNotification.php
app/Http/Controllers/ReviewPackDownloadController.php
app/Filament/Resources/ReviewPackResource.php
app/Filament/Resources/ReviewPackResource/Pages/ListReviewPacks.php
app/Filament/Resources/ReviewPackResource/Pages/ViewReviewPack.php
app/Filament/Widgets/TenantReviewPackCard.php
app/Console/Commands/PruneReviewPacksCommand.php
tests/Feature/ReviewPack/ReviewPackGenerationTest.php
tests/Feature/ReviewPack/ReviewPackDownloadTest.php
tests/Feature/ReviewPack/ReviewPackRbacTest.php
tests/Feature/ReviewPack/ReviewPackPruneTest.php
tests/Feature/ReviewPack/ReviewPackResourceTest.php
tests/Feature/ReviewPack/ReviewPackWidgetTest.php
Modified Files
app/Support/OperationRunType.php — add ReviewPackGenerate case
app/Support/Auth/Capabilities.php — add REVIEW_PACK_VIEW, REVIEW_PACK_MANAGE
app/Support/Badges/BadgeDomain.php — add ReviewPackStatus case
config/filesystems.php — add exports disk
config/tenantpilot.php — add review_pack section
routes/web.php — add download route
routes/console.php — add prune schedule entry
app/Filament/Resources/AlertRuleResource.php — hide sla_due option