# Route Contracts: Operations Tenantless Canonical Migration

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

---

## Canonical Routes (Retained — No Changes)

### GET /admin/operations

| Property | Value |
|----------|-------|
| **Route name** | `admin.operations.index` |
| **Handler** | `App\Filament\Pages\Monitoring\Operations` |
| **Middleware** | `web`, `panel:admin`, `ensure-correct-guard:web`, `DenyNonMemberTenantAccess`, Filament middleware, `ensure-workspace-selected`, `ensure-filament-tenant-selected` |
| **Auth** | Requires authentication + workspace membership |
| **Scope** | Workspace-level (shows all runs in workspace) |
| **Response** | 200 HTML (Livewire page) |

### GET /admin/operations/{run}

| Property | Value |
|----------|-------|
| **Route name** | `admin.operations.view` |
| **Handler** | `App\Filament\Pages\Operations\TenantlessOperationRunViewer` |
| **Middleware** | `web`, `panel:admin`, `ensure-correct-guard:web`, `DenyNonMemberTenantAccess`, Filament middleware |
| **Auth** | Requires authentication + workspace membership for `$run->workspace_id` |
| **Model binding** | `{run}` resolves to `OperationRun` by ID |
| **Non-member** | 404 (deny-as-not-found) |
| **Not found** | 404 (Laravel model binding) |
| **Response** | 200 HTML (Livewire page with infolist) |

---

## Decommissioned Routes (Resource-Generated Routes Removed After Migration)

### GET /admin/t/{tenant}/operations/r/{record}

| Property | Value |
|----------|-------|
| **Route name** | `filament.admin.resources.operations.view` |
| **Status** | ❌ **REMOVED** — route no longer registered |
| **After migration** | Natural 404 |
| **Previously** | `ViewOperationRun` (Filament ViewRecord page) |

### GET /admin/t/{tenant}/operations

| Property | Value |
|----------|-------|
| **Route name** | `filament.admin.resources.operations.index` |
| **Status** | ❌ **REMOVED** — route no longer registered |
| **After migration** | Replaced by explicit convenience route `admin.operations.legacy-index` that redirects 302 → `/admin/operations` |
| **Previously** | `ListOperationRuns` (Filament ListRecords page) |

---

## Link Generation Contracts

### OperationRunLinks::view($run, $tenant)

| Property | Value |
|----------|-------|
| **Returns** | `route('admin.operations.view', ['run' => $run])` |
| **Delegates to** | `OperationRunLinks::tenantlessView($run)` |
| **Tenant parameter** | Ignored (no-op) |
| **Change** | None — already canonical |

### OperationRunLinks::tenantlessView($run)

| Property | Value |
|----------|-------|
| **Returns** | `route('admin.operations.view', ['run' => $run])` |
| **Change** | None |

### OperationRunLinks::index()

| Property | Value |
|----------|-------|
| **Returns** | `route('admin.operations.index')` |
| **Change** | None |

### OperationRunLinks::related($run, $tenant)

| Property | Value |
|----------|-------|
| **Returns** | Array of up to 11 contextual link arrays |
| **Change** | None — consumed by `TenantlessOperationRunViewer` header actions |

---

## Test Route Assertions

### Positive (must work)

| Test | Route | Expected |
|------|-------|----------|
| T-078-001 | `GET /admin/operations/{run}` | 200 (member) |
| T-078-001 | `GET /admin/operations/{run}` | 200 (run with `tenant_id = null`) |
| T-078-009 | `GET /admin/t/{tenant}/operations` | 302 redirect to `/admin/operations` |

### Negative (must 404)

| Test | Route | Expected |
|------|-------|----------|
| T-078-001 | `GET /admin/operations/{run}` | 404 (non-member) |
| T-078-002 | `GET /admin/t/{tenant}/operations/r/{record}` | 404 (any user) |
| T-078-004 | Route name `filament.admin.resources.operations.view` | Not registered |
| T-078-004 | Route name `filament.admin.resources.operations.index` | Not registered |
