## Summary - harden nested Filament and Livewire tenant-context handling across the backup schedule operation runs relation manager, managed-environment triage arrival continuity, the backup set policy picker table, and the Operate Hub shell - add architecture, feature, and browser coverage for nested Filament tenant-context continuity and restore-run resource behavior - add the Spec 334 artifacts (`spec.md`, `plan.md`, `tasks.md`, and the requirements checklist) ## Testing - Not run as part of this commit/push/PR workflow Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #395
9.3 KiB
Implementation Plan: Spec 334 - Nested Filament / Livewire Context Contract Hardening
- Branch:
334-nested-filament-context-contract-hardening - Date: 2026-05-24
- Spec:
specs/334-nested-filament-context-contract-hardening/spec.md - Input: User-provided Spec 334 draft + repo inspection for path truth.
Summary
Define and enforce a nested Filament/Livewire context contract so operator-critical nested surfaces do not depend on ambient Filament::getTenant() during modal, wizard, relation manager, widget, and Livewire update lifecycles.
Implement a narrow hardening slice across four confirmed surfaces, plus guard tests:
apps/platform/app/Livewire/BackupSetPolicyPickerTable.phpapps/platform/app/Filament/Resources/RestoreRunResource.php(+apps/platform/app/Filament/Concerns/ResolvesPanelTenantContext.phpseam)apps/platform/app/Filament/Resources/BackupScheduleResource/RelationManagers/BackupScheduleOperationRunsRelationManager.phpapps/platform/app/Filament/Widgets/ManagedEnvironment/ManagedEnvironmentTriageArrivalContinuity.php- new architecture guard:
apps/platform/tests/Architecture/FilamentTenantContextContractTest.php
Technical Context
- Language/Version: PHP 8.4.15, Laravel 12.52.x.
- Primary Dependencies: Filament 5.2.x, Livewire 4.1.x, Pest 4.x, Tailwind CSS 4.x.
- Storage: PostgreSQL; no schema change expected.
- Testing: Pest Feature + Livewire component tests + one architecture guard; browser smoke only for the two user-visible regression scenarios.
- Validation Lanes: confidence + browser (scoped).
- Target Platform: Laravel Sail locally; Dokploy/container deployment posture unchanged.
- Project Type: Laravel monolith under
apps/platform. - Performance Goals: DB-only context recovery; no Graph calls during UI render; no broad unscoped queries in nested surfaces.
- Constraints: No new persisted truth, migrations, packages, env vars, queue/scheduler changes, or routing architecture changes. Scale/Scope: Four confirmed nested surfaces + shared seam hardening + guard tests.
UI / Surface Guardrail Plan
- Guardrail scope: changed existing operator-facing nested surfaces (modal, wizard, relation manager, widget).
- Affected routes/pages/actions/states/navigation/panel/provider surfaces:
- Backup Set “Add policies” modal table (Livewire component).
- Restore Run Create wizard (resource create page).
- Backup Schedule Operation Runs relation manager table (nested resource detail).
- Environment dashboard triage widget (nested widget).
- No-impact class: N/A.
- Native vs custom classification summary: native Filament + Livewire surfaces; no custom UI framework.
- Shared-family relevance: environment context resolution, authorization/visibility gating, table selection semantics, wizard option closures, remembered context validation.
- State layers in scope: route-owned workspace/environment, Livewire component state (captured at mount), remembered environment context (validated only), ambient Filament tenant (fallback convenience only).
- Audience modes in scope: operator-MSP / platform operators. No customer/read-only surface changes expected.
- Decision/diagnostic/raw hierarchy plan: fail-closed states must be honest; do not show “no records” when context is invalid; no raw debug “framework state” troubleshooting exposed to operators.
- Handling modes: review-mandatory. This is a correctness hardening slice affecting authorization and scope.
- Special surface test profiles:
exception-coded-surface(context recovery) +shared-detail-family(nested relation manager) + browser smoke for two user-visible regressions. - Required tests or manual smoke:
- Feature/Livewire tests for each confirmed surface and its fail-closed behavior.
- Architecture guard preventing unsafe
Filament::setTenant(...)usage in nested surfaces. - Browser smoke for:
- Backup Set “Add policies” checkbox visibility + selection.
- Restore Run Create wizard no-crash on Livewire update transitions.
- Exception path and spread control: no broad fallback that sets tenant from arbitrary model IDs. Any referer-based recovery must validate workspace membership and environment entitlement.
- UI/Productization coverage decision: no new routes/pages; cover via tests + browser smoke + PR close-out note.
Shared Pattern & System Fit
- Cross-cutting feature marker: yes (multiple nested surfaces).
- Systems touched:
- Filament tenant resolution seam:
apps/platform/app/Filament/Concerns/ResolvesPanelTenantContext.php - Route/shell context:
apps/platform/app/Support/OperateHub/OperateHubShell.php - Workspace/environment context + remembered context:
apps/platform/app/Support/Workspaces/WorkspaceContext.php - UI gating:
apps/platform/app/Support/Rbac/UiEnforcement.php
- Filament tenant resolution seam:
- Shared abstractions reused: prefer existing helpers/policies over new framework layers.
- New abstraction introduced?: none by default. A small helper class may be introduced only if at least two confirmed surfaces would otherwise duplicate validated context recovery logic (ABSTR-001).
- Bounded deviation / spread control: any new helper must be feature-local in scope and must not become a generic “ambient context fixer” without explicit follow-up spec approval.
OperationRun UX Impact
N/A. This feature does not introduce new queued operations. It must not create new OperationRun types or change OperationRun UX policy.
Provider Boundary / Platform Core Check
Platform-core hardening only. No Graph contract, provider registry, or provider vocabulary changes.
Implementation Approach
Phase 1 — Repo truth + reproduce
- Inspect current behavior and confirm the concrete failure path(s) for:
- Add Policies modal selection checkbox hidden.
- Restore Run Create wizard crash during Livewire update.
- Document the exact call path(s) and which closures/methods access context.
Phase 2 — Define nested context resolution order
Implement (or codify within existing seams) a deterministic resolution order for managed-environment context in nested surfaces:
- Owner/domain record (owner record, component-bound record, backup set record, schedule record, widget record).
- Validated component state captured at mount (e.g., environment ID saved in the Livewire component state).
- Route-owned workspace/environment (when route params are available).
- Remembered environment only if workspace membership and entitlement validate.
- Ambient Filament tenant as fallback convenience only.
- Fail closed with a clear, honest UI state.
Prohibited:
- blindly trusting referer as authority
- unguarded
Filament::setTenant($model->...)derived from an arbitrary identifier - broad “set tenant from model id” fallback in the shared seam
Phase 3 — Apply the contract to confirmed surfaces
- BackupSetPolicyPickerTable:
- Resolve context from the BackupSet (validated) and scope policy query accordingly.
- Ensure mutation-time recheck before attaching policy IDs.
- Do not override a mismatched existing tenant; fail closed.
- RestoreRunResource:
- Ensure option closures used in wizard lifecycles can resolve environment context without route params.
- Implement validated Livewire update context recovery via the shared seam (
apps/platform/app/Support/OperateHub/OperateHubShell.php) by treating theRefererpath as a candidate (never authority) and re-validating workspace + membership + operability before using it.
- BackupScheduleOperationRunsRelationManager:
- Use owner schedule context (
$this->getOwnerRecord()) as primary context. - Avoid ambient
ManagedEnvironment::currentOrFail()-style assumptions when owner exists.
- Use owner schedule context (
- ManagedEnvironmentTriageArrivalContinuity:
- Prefer widget record context; ambient tenant only fallback.
- Use the same resolver for visibility and mutation-time checks.
Phase 4 — Guardrails
- Add
apps/platform/tests/Architecture/FilamentTenantContextContractTest.phpto:- detect unsafe
Filament::setTenant(...)patterns in nested surfaces - keep an explicit allowlist for infrastructure-only locations
- optionally classify (not fail) direct
Filament::getTenant()usage in known high-risk directories first, to avoid noisy failures
- detect unsafe
Phase 5 — Tests and validation
Add focused tests (Feature/Livewire) for:
- tenantless Add Policies picker: authorized user still sees checkboxes and can select items
- restore create wizard: does not throw when route params are missing in Livewire update lifecycle
- owner-scoped relation manager: query does not broaden when ambient tenant is null/wrong
- widget context: record context is preferred; missing context fails closed
Validation commands (narrow first):
cd apps/platform && ./vendor/bin/sail artisan test tests/Feature/Filament --filter='BackupSetPolicyPicker|RestoreRun|BackupScheduleOperationRuns|Triage' --compactcd apps/platform && ./vendor/bin/sail artisan test tests/Architecture --filter='FilamentTenantContextContract' --compact
Browser smoke (only when feature tests pass):
cd apps/platform && php vendor/bin/pest tests/Browser/Spec334NestedFilamentContextContractSmokeTest.php --compact
Deployment / Ops Impact
- Migrations: none expected.
- Env vars: none expected.
- Queues/scheduler: none expected.
- Filament assets: no new registered assets expected; deployment posture unchanged.