TenantAtlas/specs/078-operations-tenantless-canonical/data-model.md
ahmido d56ba85755 Spec 078: Operations tenantless canonical detail (#95)
Implements Spec 078 operations tenantless canonical migration.

Highlights:
- Canonical run detail at `/admin/operations/{run}` renders with standard Filament chrome + sidebar and reuses `OperationRunResource::infolist()` (schema-based, Filament v5).
- Legacy tenant-scoped resource pages removed; legacy URLs return 404 as required.
- Added full spec test pack under `tests/Feature/078/` and updated existing tests.
- Added safe refresh/header actions wiring and KPI header guard when tenant context is null.

Validation:
- `vendor/bin/sail artisan test --compact tests/Feature/078/` (pass)
- `vendor/bin/sail bin pint --dirty` (pass)

Notes:
- Livewire v4+ compliant (Filament v5).
- Panel providers remain registered in `bootstrap/providers.php` (Laravel 11+ standard).

Co-authored-by: Ahmed Darrazi <ahmeddarrazi@MacBookPro.fritz.box>
Reviewed-on: #95
2026-02-07 09:07:26 +00:00

4.5 KiB

Data Model: Operations Tenantless Canonical Migration

Feature: 078-operations-tenantless-canonical
Date: 2026-02-06


Overview

This feature does not introduce new models or migrations. It reorganizes how existing models are rendered and routed.

Entities (Existing — No Changes)

OperationRun

Field Type Notes
id bigint (PK) Auto-increment
workspace_id bigint (FK) Required. Authorization boundary.
tenant_id bigint (FK, nullable) Null for workspace-level runs (e.g., onboarding).
type string Operation type slug (e.g., policy.sync, restore.execute).
status enum OperationRunStatus: Queued, Running, Completed, Cancelled.
outcome enum (nullable) OperationRunOutcome: Succeeded, PartiallySucceeded, Failed.
context jsonb Contains target_scope, verification_report, etc.
summary_counts jsonb {total, processed, succeeded, failed, skipped}
failure_summary jsonb (nullable) Sanitized failure details.
initiated_by bigint (FK, nullable) User who started the run.
started_at timestamp (nullable)
completed_at timestamp (nullable)
created_at timestamp
updated_at timestamp

Relationships: belongsTo Workspace, belongsTo Tenant (nullable), belongsTo User (initiated_by)

WorkspaceMembership (Authorization Boundary)

The OperationRunPolicy::view() checks:

  1. User must be a member of $run->workspace_id
  2. Returns Response::denyAsNotFound() if not a member

No changes to this model or policy logic.

Routing Changes (No Model Impact)

Routes Removed

Route Name Pattern Handler
filament.admin.resources.operations.index GET /admin/t/{tenant}/operations ListOperationRuns (resource-generated)
filament.admin.resources.operations.view GET /admin/t/{tenant}/operations/r/{record} ViewOperationRun

Route Added

Route Name Pattern Handler
admin.operations.legacy-index GET /admin/t/{tenant}/operations Redirect 302 to /admin/operations

Routes Retained (Unchanged)

Route Name Pattern Handler
admin.operations.index GET /admin/operations Operations.php
admin.operations.view GET /admin/operations/{run} TenantlessOperationRunViewer

Files Deleted

File Reason
app/Filament/Resources/OperationRunResource/Pages/ViewOperationRun.php Replaced by TenantlessOperationRunViewer
app/Filament/Resources/OperationRunResource/Pages/ListOperationRuns.php Replaced by Operations.php
app/Livewire/Monitoring/OperationsDetail.php Dead code
resources/views/livewire/monitoring/operations-detail.blade.php Dead code (blade for OperationsDetail)

Files Modified

File Change
app/Filament/Resources/OperationRunResource.php getPages() returns []
app/Filament/Pages/Operations/TenantlessOperationRunViewer.php Reuse infolist via schema, add related links
resources/views/filament/pages/operations/tenantless-operation-run-viewer.blade.php Replace hand-coded HTML with {{ $this->infolist }}
app/Filament/Widgets/Operations/OperationsKpiHeader.php Hide stats when no tenant context

Test Files Requiring Updates

File Change Required
tests/Feature/Verification/VerificationAuthorizationTest.php Replace OperationRunResource::getUrl('view') with route('admin.operations.view')
tests/Feature/OpsUx/FailureSanitizationTest.php Replace OperationRunResource::getUrl('view') with canonical route; replace ViewOperationRun mount
tests/Feature/OpsUx/CanonicalViewRunLinksTest.php Update guard regex to account for headless resource
tests/Feature/Verification/VerificationReportViewerDbOnlyTest.php Replace ViewOperationRun mount with TenantlessOperationRunViewer
tests/Feature/Verification/VerificationReportRedactionTest.php Replace ViewOperationRun mount with TenantlessOperationRunViewer
tests/Feature/Verification/VerificationReportMissingOrMalformedTest.php Replace ViewOperationRun mount with TenantlessOperationRunViewer
tests/Feature/Monitoring/OperationsCanonicalUrlsTest.php Remove ListOperationRuns test; add route-not-registered assertion
tests/Feature/Monitoring/OperationsTenantScopeTest.php Remove ListOperationRuns test