TenantAtlas/specs/302-tenant-owned-surface-route-audit/surface-route-audit.md
ahmido d072b0107b feat(specs/302): tenant owned surface route audit (#357)
Implements platform feature branch `302-tenant-owned-surface-route-audit`.

Target branch: `platform-dev`.

Follow-up integration path after merge:

`platform-dev` → `dev`.

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #357
2026-05-14 21:14:59 +00:00

27 KiB

Tenant-Owned Surface Route Audit

Spec: 302 tenant-owned-surface-route-audit Date: 2026-05-14 Branch: 302-tenant-owned-surface-route-audit Scope: repo-derived audit artifact only

Scope Boundary

This audit records current route, navigation, context, global-search, and RBAC posture for admin-relevant tenant-owned surfaces. It does not change application runtime behavior.

Out of scope for this spec:

  • application runtime code
  • application tests
  • routes
  • migrations
  • assets
  • policies
  • jobs
  • Filament resources, pages, clusters, widgets, or Livewire components

Any runtime defect or product decision discovered here is documented as a blocker or follow-up candidate instead of being patched in this spec.

Audit Method

Primary repo sources:

  • apps/platform/app/Support/WorkspaceIsolation/TenantOwnedModelFamilies.php
  • apps/platform/app/Filament/Concerns/WorkspaceScopedTenantRoutes.php
  • apps/platform/app/Support/Navigation/NavigationScope.php
  • apps/platform/app/Filament/Concerns/ScopesGlobalSearchToTenant.php
  • apps/platform/app/Filament/Concerns/ResolvesPanelTenantContext.php
  • apps/platform/app/Support/OperateHub/OperateHubShell.php
  • apps/platform/routes/web.php
  • apps/platform/app/Filament/Resources/
  • apps/platform/app/Filament/Pages/
  • focused tests under apps/platform/tests/Feature/Filament/

Filament documentation was checked through Laravel Boost for the current stack. Relevant conclusions for this audit:

  • Filament v5 targets Livewire v4.0+ in this app; no Livewire v3 patterns are introduced.
  • shouldRegisterNavigation() controls navigation registration only and is not an authorization boundary.
  • A globally searchable Filament resource needs an Edit or View page to provide truthful destinations.
  • Destructive Filament actions should execute through Action::make(...)->action(...) and include ->requiresConfirmation(). This spec adds or changes no actions.
  • URL-only actions should not be treated as confirmation-protected mutations.

Classification Vocabulary

Each audited row has exactly one migration state:

  • migrated: route, environment navigation, context, search posture, RBAC, and proof are aligned for the current intended admin contract.
  • partial cutover: substantial route/context migration exists, but proof or adjacent entry-point behavior remains incomplete.
  • stale panel logic: current behavior still preserves an older hide-first or panel-split assumption that conflicts with repo-real admin access.
  • valid context gate: a surface is intentionally contextual/subordinate and does not need a standalone route, but still requires trusted workspace/environment context.
  • valid RBAC: the current posture is primarily an authorization exception rather than a route/navigation migration target.
  • ambiguous product IA: implementation cannot safely proceed until the product role of the surface is decided.
  • dead-code dependent: cleanup depends on earlier route/navigation decisions and should not be performed first.

First-Slice Surface Matrix

Surface Model/resource/page owner Route posture Navigation posture Context source Global-search posture RBAC posture Existing proof Blocker Migration state Recommended next action
Policy Policy / PolicyResource Admin URLs use WorkspaceScopedTenantRoutes under /admin/workspaces/{workspace}/environments/{environment}/policies; list and view pages exist. Environment-only via NavigationScope::shouldRegisterEnvironmentNavigation(); workspace home stays clean. WorkspaceScopedTenantRoutes, ResolvesPanelTenantContext, tenant-owned query helpers. Disabled by resource and registry; PolicyResource has a View page if search is later re-enabled. canViewAny() requires tenant membership plus tenant.view; row/detail access uses tenant-owned resolution. PanelNavigationSegregationTest, TenantOwnedResourceScopeParityTest, AdminTenantSurfaceParityTest, PolicyResourceAdminSearchParityTest, GovernanceArtifactAdminPanelRegistrationTest. None for route audit. migrated Keep as reference contract for future tenant-owned resources.
PolicyVersion PolicyVersion / PolicyVersionResource Admin URLs use WorkspaceScopedTenantRoutes; list and view pages exist. Environment-only via NavigationScope; workspace home stays clean. WorkspaceScopedTenantRoutes, ResolvesPanelTenantContext, tenant-owned query helpers. Disabled by resource and registry; View page exists if search is later re-enabled. canViewAny() requires tenant membership plus tenant.view; detail resolution is tenant-owned. PanelNavigationSegregationTest, TenantOwnedResourceScopeParityTest, AdminTenantSurfaceParityTest, PolicyVersionAdminSearchParityTest, GovernanceArtifactAdminPanelRegistrationTest. None for route audit. migrated Keep search disabled until a separate policy-version search spec defines destination copy and ranking.
BackupSchedule BackupSchedule / BackupScheduleResource Admin URLs use WorkspaceScopedTenantRoutes; list, create, and edit pages exist. Environment-only via NavigationScope; workspace home stays clean. WorkspaceScopedTenantRoutes, ResolvesPanelTenantContext, tenant-owned query helpers. Not applicable in registry. Tenant membership and capabilities are enforced through resource methods and action-level UI enforcement. PanelNavigationSegregationTest, TenantOwnedResourceScopeParityTest, GovernanceArtifactAdminPanelRegistrationTest. None for route audit. migrated No follow-up in this candidate group.
BackupSet BackupSet / BackupSetResource Admin URLs use WorkspaceScopedTenantRoutes; list, create, view pages exist; BackupItemsRelationManager is subordinate. Environment-only via NavigationScope; workspace home stays clean. WorkspaceScopedTenantRoutes, ResolvesPanelTenantContext, tenant-owned query helpers. Not applicable in registry. Tenant membership/capability checks protect list/detail and restore/export actions. PanelNavigationSegregationTest, TenantOwnedResourceScopeParityTest, AdminSharedSurfacePanelParityTest, GovernanceArtifactAdminPanelRegistrationTest. None for route audit. migrated Keep BackupItem as subordinate relation-manager coverage; do not invent standalone route.
RestoreRun RestoreRun / RestoreRunResource Admin URLs use WorkspaceScopedTenantRoutes; list, create, view pages exist. Environment-only via NavigationScope; workspace home stays clean. WorkspaceScopedTenantRoutes, ResolvesPanelTenantContext, tenant-owned query helpers. Not applicable in registry. Tenant membership/capability gates protect tenant-owned restore history and restore mutation flows. PanelNavigationSegregationTest, TenantOwnedResourceScopeParityTest, GovernanceArtifactAdminPanelRegistrationTest. None for route audit. migrated Keep separate from workspace-owned OperationRunResource.
Finding Finding / FindingResource Admin URLs use WorkspaceScopedTenantRoutes; list and view pages exist. Environment-only via NavigationScope; workspace home stays clean. WorkspaceScopedTenantRoutes, ResolvesPanelTenantContext, tenant-owned query helpers. Not applicable in registry. Tenant membership/capability checks protect finding visibility and workflows. PanelNavigationSegregationTest, TenantOwnedResourceScopeParityTest, GovernanceArtifactAdminPanelRegistrationTest, finding RBAC/evidence feature coverage. None for route audit. migrated No follow-up in this candidate group.
FindingException FindingException / FindingExceptionResource Admin URLs use WorkspaceScopedTenantRoutes; list and view pages exist. Environment-only via NavigationScope; workspace home is expected to stay clean. WorkspaceScopedTenantRoutes, ResolvesPanelTenantContext, tenant-owned query helpers. Disabled by resource and registry; View page exists if later re-enabled. Tenant membership/capability checks protect risk-exception visibility and decision actions. PanelNavigationSegregationTest sidebar assertions, GovernanceArtifactAdminPanelRegistrationTest, exception/action guard tests. High-signal navigation dataset covers sidebar text but not the class-level environment-visible dataset. partial cutover Add to the next navigation-contract proof pass or a bounded governance-artifact navigation parity check; no runtime change in this audit.
EvidenceSnapshot EvidenceSnapshot / EvidenceSnapshotResource Admin URLs use WorkspaceScopedTenantRoutes; list and view pages exist. Environment-only via NavigationScope; environment sidebar proof exists by text but not in the primary class dataset. WorkspaceScopedTenantRoutes, ResolvesPanelTenantContext, tenant-owned query helpers. Disabled by resource and registry; View page exists if later re-enabled. canViewAny() / canView() require tenant access plus evidence.view; generation action requires evidence.manage. GovernanceArtifactAdminPanelRegistrationTest, GovernanceArtifactEnvironmentContextTest, governance artifact deep-link tests, browser governance smoke coverage. Proof is split across governance tests rather than the core navigation segregation dataset. partial cutover Create a small governance-artifact environment navigation parity follow-up only if reviewers want one canonical proof lane.
InventoryItem InventoryItem / InventoryItemResource Admin URLs use WorkspaceScopedTenantRoutes; list and view pages exist. Environment-only via NavigationScope; workspace home stays clean after Spec 301. WorkspaceScopedTenantRoutes, explicit tenant query scope for detail, ResolvesPanelTenantContext. Not applicable in registry. Tenant membership plus resource authorization; cross-tenant detail returns 404. PanelNavigationSegregationTest, TenantOwnedResourceScopeParityTest, Inventory coverage/admin parity tests, GovernanceArtifactAdminPanelRegistrationTest. None after Spec 301. migrated Keep as completed reference for the candidate group.
EntraGroup EntraGroup / EntraGroupResource Direct admin list/detail routes exist and are tenant-scoped through remembered canonical tenant context; resource does not use WorkspaceScopedTenantRoutes. shouldRegisterNavigation() returns false on the admin panel, even when an environment context exists. ScopesGlobalSearchToTenant, OperateHubShell::activeEntitledTenant(), InteractsWithTenantOwnedRecords, remembered workspace tenant. Scoped and enabled; View page exists and search results resolve to scoped detail URLs. Direct list/detail/global search are tenant-scoped; no-context and cross-tenant access return no results or 404. EntraGroupAdminScopeTest, EntraGroupGlobalSearchScopeTest, TenantOwnedResourceScopeParityTest, PanelNavigationSegregationTest admin-hidden dataset. Product IA is unresolved: groups may belong in primary Directory navigation, secondary Identity lane, or contextual-only entry points. Current admin-hidden test is stale if groups should be environment-visible. ambiguous product IA Promote admin-directory-groups-cutover next. Decide the Directory/Groups role, then align navigation, route helper posture, tests, and search-detail destinations.
EnvironmentReview EnvironmentReview / EnvironmentReviewResource Admin URLs use WorkspaceScopedTenantRoutes; list and view pages exist. Environment-only via NavigationScope; workspace home stays clean. WorkspaceScopedTenantRoutes, ResolvesPanelTenantContext, tenant-owned query helpers. Disabled by resource and registry. Tenant access and review/evidence capabilities gate creation and visibility. GovernanceArtifactAdminPanelRegistrationTest, GovernanceArtifactEnvironmentContextTest, customer-review tests. None for route audit. migrated No candidate-group follow-up; keep customer-review paths as separate review-domain work if needed.
StoredReport StoredReport / StoredReportResource Admin URLs use WorkspaceScopedTenantRoutes; list and view pages exist. Environment-only via NavigationScope; navigation proof is governance-specific, not in the core segregation dataset. WorkspaceScopedTenantRoutes, ResolvesPanelTenantContext, tenant-owned query helpers. Disabled by resource and registry. canViewAny() and canView() require report-type capabilities and matching workspace/environment. GovernanceArtifactAdminPanelRegistrationTest, GovernanceArtifactEnvironmentContextTest, dashboard report-link tests. Proof is split across governance/report tests rather than the core navigation segregation dataset. partial cutover Optionally include with EvidenceSnapshot in a bounded governance-artifact environment navigation parity proof.

Residual, Subordinate, and Exception Surfaces

Surface Model/resource/page owner Route posture Navigation posture Context source Global-search posture RBAC posture Existing proof Blocker Migration state Recommended next action
BackupItem BackupItem via BackupSetResource::BackupItemsRelationManager Subordinate relation-manager surface under backup-set detail; no standalone primary route should be invented. Inherits BackupSet visibility. Parent BackupSet tenant context. Not applicable. Inherits backup-set tenant membership/capability gates. TenantOwnedModelFamilies::residualRolloutInventory(), backup-set relation manager declaration. None. valid context gate Keep subordinate; add proof only if relation-manager behavior changes.
InventoryLink InventoryLink via InventoryItemResource related-link affordances Subordinate metadata under inventory item. Inherits InventoryItem visibility. Parent InventoryItem tenant context. Not applicable. Inherits inventory item scope and link target authorization. TenantOwnedModelFamilies::residualRolloutInventory(), inventory related-context tests. None. valid context gate Keep subordinate; no standalone route.
EntraRoleDefinition EntraRoleDefinition via Entra admin-role reports/findings Indirect read paths through reports, findings, and role-posture evidence. No standalone navigation in this slice. Report/finding/evidence parent context. Not applicable. Must inherit parent report/finding capability gates. TenantOwnedModelFamilies::residualRolloutInventory(), stored-report and finding evidence coverage. Directory/admin-role IA may need to align with the Entra Groups decision. ambiguous product IA Keep indirect unless admin-directory-groups-cutover creates a broader Directory/Admin Roles lane.
ManagedEnvironmentPermission ManagedEnvironmentPermission via diagnostics, onboarding, required-permissions pages Contextual pages under /admin/workspaces/{workspace}/environments/{environment}/... or onboarding draft routes; no standalone resource. Diagnostics/required-permissions are contextual and not primary sidebar entries. Route model binding, workspace middleware, selected tenant middleware, page-local access checks. Not applicable. Requires workspace membership and environment access; diagnostics mutation actions are capability-gated and confirmation-protected. routes/web.php, EnvironmentDiagnostics, EnvironmentRequiredPermissions, onboarding tests. None for tenant-owned route audit. valid context gate Keep contextual; future diagnostics specs should maintain 404 for invalid environment context.
FindingExceptionDecision FindingExceptionDecision via FindingExceptionResource detail/history Subordinate to finding exception aggregate. Inherits FindingException visibility. Parent finding-exception tenant context. Not applicable. Inherits finding-exception capability gates and action authorization. TenantOwnedModelFamilies::residualRolloutInventory(), finding-exception workflow tests. None. valid context gate Keep subordinate; no standalone route.
FindingExceptionEvidenceReference FindingExceptionEvidenceReference via FindingExceptionResource evidence sections Subordinate to finding exception aggregate. Inherits FindingException visibility. Parent finding-exception tenant context. Not applicable. Inherits finding-exception visibility plus evidence/report link authorization. TenantOwnedModelFamilies::residualRolloutInventory(), governance evidence tests. None. valid context gate Keep subordinate; no standalone route.
ProviderConnectionResource ProviderConnectionResource Workspace-admin canonical viewer that references tenant defaults; explicit first-slice exception. Workspace/integration navigation, not tenant-owned environment navigation. Workspace membership plus remembered tenant entitlement for destination actions. Not applicable to first-slice tenant-owned search. Capability gating remains required on destination/action paths. TenantOwnedModelFamilies::scopeExceptions(), provider-connection scope/UI enforcement tests. None for tenant-owned route audit. valid RBAC Do not migrate into the tenant-owned family unless provider IA changes.
OperationRunResource / tenantless operation viewer Workspace-owned operation monitoring plus tenant deep links Workspace operation routes under /admin/workspaces/{workspace}/operations...; tenant-owned destinations must be checked before rendering. Workspace monitoring navigation; OperationRunResource itself is hidden from resource nav. Workspace route context plus operation owner/deep-link tenant checks. Not applicable. Workspace membership plus tenant entitlement on tenant-owned deep links. TenantOwnedModelFamilies::scopeExceptions(), operation monitoring tests. None for tenant-owned route audit. valid RBAC Keep separate from RestoreRunResource; no route migration in this candidate group.
AlertDeliveryResource AlertDeliveryResource Mixed workspace-owned and tenant-bound semantics; explicit deferred family. Workspace alert navigation, not tenant-owned first-slice navigation. Workspace context plus tenant capability checks for tenant-bound mutations. Not applicable. Workspace membership and tenant-specific mutation capability checks remain required. TenantOwnedModelFamilies::scopeExceptions(), alert delivery/action tests. Deferred family; not enough product scope in this audit to classify all alert delivery tenant semantics. partial cutover Leave for a bounded alert-delivery route/RBAC audit if alert surfaces become part of this candidate group.

Non-Resource Environment Pages

Surface Model/resource/page owner Route posture Navigation posture Context source Global-search posture RBAC posture Existing proof Blocker Migration state Recommended next action
Environment dashboard EnvironmentDashboard Canonical route /admin/workspaces/{workspace}/environments/{environment:slug} via ManagedEnvironmentLinks::viewUrl(). Environment landing page, not a resource nav row. Route model binding, workspace member middleware, selected tenant middleware. Not applicable. Tenant dashboard widgets/actions use tenant access and capabilities. routes/web.php, dashboard feature tests, PanelNavigationSegregationTest canonical environment route. None. migrated Keep as canonical environment context anchor.
Inventory coverage InventoryCoverage page Admin URL resolves to /admin/workspaces/{workspace}/environments/{environment}/inventory/coverage style Filament page route; falls back to /admin without tenant. Environment-only via NavigationScope; workspace home stays clean. ResolvesPanelTenantContext, route parameters, remembered tenant. Not applicable. canAccess() requires tenant membership plus tenant.view. PanelNavigationSegregationTest, inventory coverage/admin parity tests. None after Spec 301. migrated Keep in Inventory proof lane.
Environment diagnostics EnvironmentDiagnostics page Explicit route /admin/workspaces/{workspace}/environments/{environment:slug}/diagnostics. Hidden from primary navigation; contextual only. Route model binding and ResolvesPanelTenantContext. Not applicable. Requires authenticated environment context; repair actions require tenant.manage, are destructive, and use confirmation. routes/web.php, diagnostics page code, diagnostics/access-scope tests. None for route audit. valid context gate Keep contextual; do not add primary navigation without product decision.
Required permissions EnvironmentRequiredPermissions page Explicit workspace/environment slug route in page registration; hidden from discovery/navigation. Hidden from primary navigation; linked from diagnostics/onboarding/reporting contexts. Route parameters, workspace context, page-local scoped-tenant resolution. Not applicable. canAccess() and mount abort invalid tenant access with 404. EnvironmentRequiredPermissions tests and permissions-report tests. None for route audit. valid context gate Keep contextual.
Access scopes ManagedEnvironmentResource\Pages\ManageEnvironmentAccessScopes Explicit route /admin/workspaces/{workspace}/environments/{environment:slug}/access-scopes. Contextual management page, not tenant-owned resource nav. Route model binding plus workspace/environment middleware. Not applicable. Workspace membership and environment access checks protect access-scope management. routes/web.php, access-scope feature tests. None for route audit. valid context gate Keep contextual.
Managed environment onboarding ManagedEnvironmentOnboardingWizard Explicit /admin/onboarding and /admin/onboarding/{onboardingDraft} routes. Hidden from primary navigation; entry from environment creation flow. Workspace/onboarding draft context until an environment exists. Not applicable. Authenticated workspace user; later environment access assigned through onboarding flow. routes/web.php, onboarding tests. Not a tenant-owned surface until environment creation completes. valid context gate Keep outside tenant-owned route repair.

Repair Order

  1. admin-directory-groups-cutover

    Decide the admin role for Directory / Entra Groups before touching runtime behavior. Current repo truth shows scoped direct admin access and scoped global search, while admin navigation remains explicitly hidden. The cutover spec should choose one contract: primary environment navigation, secondary Identity/Directory lane, or contextual-only entry points. It should then align EntraGroupResource routes, navigation tests, global-search destinations, and detail access with that decision.

  2. Governance artifact navigation proof pass, if reviewers require one canonical proof lane

    Evidence snapshots, stored reports, and finding exceptions already use workspace/environment routes and tenant context gates, but their proof is spread across governance tests and sidebar text assertions. A small follow-up can add or normalize class-level navigation proof without changing product behavior. This should not be bundled into the Entra Groups cutover.

  3. Alert delivery route/RBAC audit, only if alerts enter the tenant-owned candidate group

    AlertDeliveryResource is an explicit deferred family with mixed workspace-owned and tenant-bound behavior. It should remain out of this tenant-owned surface repair sequence unless product scope pulls alert delivery into managed-environment navigation or tenant mutation flows.

  4. navigation-contract-split, conditional

    Promote only if drift remains after Inventory, this audit, and Entra Groups. The target should be a test/contract split between workspace-home clean-sidebar assertions and environment-bound tenant-owned navigation assertions.

  5. tenant-panel-dead-code-retirement

    Keep after route/navigation dependency decisions. Current negative-control tests already assert retired /admin/t/{tenant} and /admin/tenants/{tenant} entry routes are unavailable; deletion should wait until active surfaces and tests no longer depend on tenant-panel compatibility assumptions.

Unresolved Blockers

  • Entra Groups product IA is the only blocker that prevents the next runtime repair from being purely mechanical.
  • Governance artifacts have no confirmed runtime blocker, but their proof is distributed across several test families instead of one core navigation dataset.
  • Alert delivery remains intentionally deferred and should not be treated as first-slice tenant-owned work.

Filament, RBAC, and Deployment Review

  • Livewire v4.0+ compliance is preserved: no Livewire or Filament runtime APIs were changed.
  • Provider registration is unchanged; Laravel provider registration remains in apps/platform/bootstrap/providers.php.
  • Globally searchable tenant-owned resource: EntraGroupResource has a View page. PolicyResource, PolicyVersionResource, FindingExceptionResource, EvidenceSnapshotResource, EnvironmentReviewResource, and StoredReportResource are disabled for global search. Other first-slice resources are not applicable for global search in the registry.
  • Destructive actions were not added or changed. Existing inspected destructive diagnostics actions use ->action(...) and ->requiresConfirmation().
  • No assets were registered or changed; deployment filament:assets requirements are unchanged.
  • UI visibility is treated as non-authoritative. Server-side RBAC/context proof is recorded separately from navigation posture.

Validation Evidence

  • cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Filament/PanelNavigationSegregationTest.php tests/Feature/Filament/AdminTenantSurfaceParityTest.php tests/Feature/Filament/AdminSharedSurfacePanelParityTest.php tests/Feature/Filament/TenantOwnedResourceScopeParityTest.php tests/Feature/Filament/EntraGroupAdminScopeTest.php tests/Feature/Filament/EntraGroupGlobalSearchScopeTest.php tests/Feature/Filament/PolicyResourceAdminSearchParityTest.php tests/Feature/Filament/PolicyVersionAdminSearchParityTest.php
    • Result: passed, 53 tests, 124 assertions, 18.56s.
  • Browser smoke test: not run. This spec changes only Markdown audit artifacts and does not alter Filament UI, Livewire interactions, navigation behavior, forms, tables, actions, modals, dashboards, operation drilldowns, workspace/tenant runtime context, or any user-facing flow.
  • git diff --check
    • Result: passed, no whitespace errors reported.
  • git diff --check --no-index /dev/null <spec-file> for each file in specs/302-tenant-owned-surface-route-audit/
    • Result: no whitespace warnings reported for the untracked spec package files.
  • git status --short
    • Result: only specs/302-tenant-owned-surface-route-audit/ is untracked/changed; no application runtime files are modified.