6.9 KiB
6.9 KiB
Research (Spec 086)
This document resolves the unknowns needed to write an implementation plan for “Retire Legacy Runs Into Operation Runs”. It is based on repository inspection (no new external dependencies).
Decisions
1) Canonical run viewer is already implemented; keep the route shape
- Decision: Use the existing tenantless canonical viewer route
admin.operations.view(path pattern/admin/operations/{run}) implemented by the Filament pageTenantlessOperationRunViewer. - Rationale: This already enforces “tenantless deep link” while still doing workspace / tenant entitlement checks server-side through
Gate::authorize('view', $run). - Alternatives considered: Create a second viewer page or route. Rejected because it would introduce duplicate UX and increase the chance of policy drift.
Repository anchors:
- Canonical viewer page:
app/Filament/Pages/Operations/TenantlessOperationRunViewer.php - Link helper:
app/Support/OperationRunLinks.php - Workspace selection middleware explicitly treats
/admin/operations/{id}as workspace-optional:app/Http/Middleware/EnsureWorkspaceSelected.php
2) OperationRun is persisted and DB-rendered; schema supports workspace-tenant and workspace-only runs
- Decision: Treat
operation_runsas the canonical persistence format for status/progress/results. - Rationale: The schema already includes
workspace_id(required) andtenant_id(nullable), enabling both tenant-plane and workspace-plane operations. - Alternatives considered: Separate tables per operation family. Rejected because it breaks the Monitoring → Operations single source of truth principle.
Repository anchors:
- Migrations:
database/migrations/2026_01_16_180642_create_operation_runs_table.php,database/migrations/2026_02_04_090030_add_workspace_id_to_operation_runs_table.php - Model:
app/Models/OperationRun.php
3) View authorization must be capability-gated per operation type (in addition to membership)
- Decision: Extend run viewing authorization to require the same capability used to start the operation type.
- Rationale: Spec 086 clarifications require: non-members get 404; members without capability get 403; and the “view” capability equals the “start” capability.
- Implementation approach (planned): Update
OperationRunPolicy::view()to:- Keep existing workspace membership and tenant entitlement checks (deny-as-not-found).
- Resolve required capability from
OperationRun->typeusing a centralized mapping helper. - If capability is known and tenant-scoped, enforce
403when the member lacks it.
Repository anchors:
- Current policy (membership + tenant entitlement only):
app/Policies/OperationRunPolicy.php - Existing capability enforcement in start surfaces (examples):
- Inventory sync start:
Capabilities::TENANT_INVENTORY_SYNC_RUNinapp/Filament/Resources/InventoryItemResource/Pages/ListInventoryItems.php - Directory groups sync start:
Capabilities::TENANT_SYNCinapp/Filament/Resources/EntraGroupResource/Pages/ListEntraGroups.php - Backup schedule run/retry:
Capabilities::TENANT_BACKUP_SCHEDULES_RUNinapp/Filament/Resources/BackupScheduleResource.php
- Inventory sync start:
4) Run identity / dedupe strategy varies by operation type
- Decision: Use existing
OperationRunServicehelpers but apply type-specific identity rules:inventory.syncanddirectory_groups.sync: while-active dedupe based on deterministic inputs (continue usingensureRun(...)-style identity).backup_schedule.run_nowandbackup_schedule.retry: unique per click (no dedupe). Create a new run each time by including a nonce in identity inputs (e.g., UUID).backup_schedule.scheduled: strict dedupe per(backup_schedule_id, scheduled_for); create a new operation typebackup_schedule.scheduledand useensureRunWithIdentity(...)keyed by schedule + intended fire-time.
- Rationale: Matches explicit spec clarifications and protects against scheduler double-fire.
- Alternatives considered:
- Keep using
ensureRun(...)for manual runs → rejected (dedupes while active). - Use legacy table unique constraints as idempotency → rejected (spec requires OperationRun is canonical).
- Keep using
Repository anchors:
ensureRun(...)andensureRunWithIdentity(...):app/Services/OperationRunService.php- Existing partial unique index for active runs:
operation_runs_active_unique_*in the migrations above.
5) Legacy run tables are real and currently written to; deterministic redirect requires an explicit mapping field
- Decision: Legacy tables remain viewable and read-only, but should not be relied on for current execution tracking.
- Rationale: Spec requires “no new legacy rows” for in-scope operations. Today, some start surfaces still create legacy rows (e.g., inventory/group sync, backup schedule runs).
- Planned design:
- Stop creating new legacy rows as part of the cutover PRs.
- Implement legacy “view” redirect behavior only when a record has a canonical mapping.
- To make redirects deterministic without a backfill, add an optional
operation_run_idFK column to legacy tables that we intend to redirect (only populated for rows created after the migration; older rows remain legacy-only view).
- Alternatives considered: Derive mapping by recomputing hashes and searching by time window. Rejected as non-deterministic and likely to pick the wrong run when identities collide historically.
Repository anchors (legacy tables):
- Inventory sync runs:
database/migrations/2026_01_07_142719_create_inventory_sync_runs_table.php - Directory group sync runs:
database/migrations/2026_01_11_120004_create_entra_group_sync_runs_table.php - Backup schedule runs:
database/migrations/**create_backup_schedule_runs**(used inBackupScheduleResource) - Restore runs (domain):
database/migrations/2025_12_10_000150_create_restore_runs_table.php
6) DB-only rendering constraint is already enforced in Monitoring pages, but Tenant configuration forms still call Graph
- Decision: Remove outbound Graph calls from configuration-form search/labels by introducing cached directory role definitions and using cached directory groups.
- Rationale: Constitution OPS-EX-AUTH-001 + Spec 086 FR-006/FR-015 require render/search/label resolution to be DB-only.
- Repository finding:
TenantResourcecurrently queries Graph for role definitions in selector callbacks.
Repository anchors:
- Graph call sites inside UI callbacks:
app/Filament/Resources/TenantResource.php(roleDefinitions search/label methods)
Open items (resolved enough for planning)
- Exact schema for the new role definition cache tables and the sync job contract will be specified in
data-model.mdand implemented in Phase PR(s). - The capability mapping for run viewing will be implemented via a centralized helper; the plan will enumerate required capabilities per in-scope operation type.