TenantAtlas/specs/302-tenant-owned-surface-route-audit/spec.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

28 KiB

Feature Specification: Tenant-Owned Surface Route Audit

Feature Branch: 302-tenant-owned-surface-route-audit Created: 2026-05-14 Status: Draft Input: User description: "tenant-owned-surface-route-audit als 302"

Spec Candidate Check (mandatory - SPEC-GATE-001)

  • Problem: The repo has moved toward workspace-first managed-environment routing, but the tenant-owned admin surface set is still understood through scattered code, tests, and candidate notes. Operators and implementers cannot tell which tenant-owned surfaces are fully migrated, intentionally hidden, partially cut over, blocked by product IA decisions, or dependent on retired tenant-panel behavior without reconstructing the answer manually.
  • Today's failure: After Spec 301 restored Inventory navigation, adjacent surfaces such as Entra Groups, policies, backups, restore runs, findings, evidence, reports, diagnostics, and subordinate relation surfaces still need a repo-verified posture. Without an audit matrix, follow-up work risks either mass-enabling navigation too broadly or preserving stale hide-first assumptions as if they were product decisions.
  • User-visible improvement: The next repair can be chosen from one clear, repo-verified inventory of tenant-owned admin surfaces, including route posture, navigation posture, context source, global-search posture, RBAC posture, existing proof, blocker, and recommended next action.
  • Smallest enterprise-capable version: Produce one bounded audit artifact for admin-reachable tenant-owned surfaces and one sequenced repair-prep order. Do not enable routes, change navigation, migrate resources, delete dead code, or decide product IA for individual surfaces in this slice.
  • Explicit non-goals: No mass navigation re-enablement, no broad runtime migration, no Entra Groups cutover decision, no navigation contract split, no tenant-panel dead-code deletion, no application code changes, no new persisted state, and no new framework for route auditing.
  • Permanent complexity imported: One repo-local audit artifact in this spec package plus review tasks. No runtime models, tables, enums, services, registries, UI components, assets, jobs, or policies are introduced.
  • Why now: docs/product/spec-candidates.md lists this as the second step after admin-inventory-navigation-cutover; Spec 301 is completed and explicitly deferred this generic route audit.
  • Why not local: A local fix to one resource would hide the cross-surface drift. The current problem is incomplete shared understanding, so the narrowest correct step is a read-only repo audit and repair order.
  • Approval class: Cleanup
  • Red flags triggered: One lightweight document-only classification vocabulary is used for the audit matrix. It is not a runtime taxonomy or product state family.
  • Score: Nutzen: 2 | Dringlichkeit: 2 | Scope: 2 | Komplexitaet: 2 | Produktnaehe: 1 | Wiederverwendung: 1 | Gesamt: 10/12
  • Decision: approve

Spec Scope Fields (mandatory)

  • Scope: canonical-view
  • Primary Routes:
    • /admin/workspaces/{workspace}
    • /admin/workspaces/{workspace}/environments
    • /admin/workspaces/{workspace}/environments/{environment}
    • /admin/workspaces/{workspace}/environments/{environment}/... tenant-owned resource/page routes generated by Filament
    • retired or legacy tenant route families such as /admin/t/{tenant}/... and /admin/tenants/{tenant}/... as negative-control inputs only
  • Data Ownership: The audit covers tenant-owned and managed-environment-owned runtime records that require workspace plus managed-environment entitlement at authorization time. No product data is created or changed.
  • RBAC: The audit must record membership and capability expectations for each surface. Non-member workspace or tenant access remains deny-as-not-found. Member-without-capability remains 403 where capability checks apply. UI visibility remains non-authoritative.

For canonical-view specs, the spec MUST define:

  • Default filter behavior when tenant-context is active: Environment-bound routes resolve their managed-environment context from the canonical workspace/environment route or current trusted context helpers. Workspace-level routes must not show tenant-owned surfaces unless a valid environment context is established.
  • Explicit entitlement checks preventing cross-tenant leakage: The audit must verify whether each surface uses existing context/authorization paths such as WorkspaceScopedTenantRoutes, NavigationScope, OperateHubShell, ScopesGlobalSearchToTenant, canAccess, canViewAny, policy methods, and route middleware. Any missing or ambiguous check is documented as a repair blocker, not patched in this slice.

Cross-Cutting / Shared Pattern Reuse (mandatory when the feature touches notifications, status messaging, action links, header actions, dashboard signals/cards, alerts, navigation entry points, evidence/report viewers, or any other existing shared operator interaction family; otherwise write N/A - no shared interaction family touched)

  • Cross-cutting feature?: yes
  • Interaction class(es): navigation entry points, route generation, global search result destinations, tenant-owned detail/list surfaces, relation-manager entry points
  • Systems touched:
    • apps/platform/app/Filament/Concerns/WorkspaceScopedTenantRoutes.php
    • apps/platform/app/Support/Navigation/NavigationScope.php
    • apps/platform/app/Filament/Concerns/ScopesGlobalSearchToTenant.php
    • apps/platform/app/Support/OperateHub/OperateHubShell.php
    • apps/platform/app/Support/WorkspaceIsolation/TenantOwnedModelFamilies.php
    • apps/platform/app/Filament/Resources/*
    • apps/platform/app/Filament/Pages/*
    • apps/platform/routes/web.php
    • focused Filament feature tests under apps/platform/tests/Feature/Filament/
  • Existing pattern(s) to extend: No runtime path is extended in this slice. The audit must measure surfaces against existing shared paths: workspace-scoped tenant routes, navigation scope, tenant-scoped global search, route middleware, and tenant-owned query helpers.
  • Shared contract / presenter / builder / renderer to reuse: N/A for runtime. The audit should refer to the existing shared helpers above rather than defining a new route/navigation framework.
  • Why the existing shared path is sufficient or insufficient: Sufficiency is the question being audited per surface. The output must state whether each surface already uses the shared path, legitimately deviates, or needs a follow-up repair.
  • Allowed deviation and why: Document-only deviation. The audit may classify exceptions, but it must not create executable exception logic.
  • Consistency impact: Future repair specs should use the audit to avoid parallel local navigation/search/routing behavior.
  • Review focus: Reviewers must verify that the audit is repo-derived, that no runtime code was changed, and that follow-up recommendations are split into bounded candidates.
  • Touches OperationRun start/completion/link UX?: no
  • Shared OperationRun UX contract/layer reused: N/A
  • Delegated start/completion UX behaviors: N/A
  • Local surface-owned behavior that remains: The audit may mention tenant-owned surfaces that link to runs, but it does not change OperationRun start, completion, link, notification, or monitoring behavior.
  • Queued DB-notification policy: N/A
  • Terminal notification path: N/A
  • Exception required?: none

Provider Boundary / Platform Core Check (mandatory when the feature changes shared provider/platform seams, identity scope, governed-subject taxonomy, compare strategy selection, provider connection descriptors, or operator vocabulary that may leak provider-specific semantics into platform-core truth; otherwise write N/A - no shared provider/platform boundary touched)

  • Shared provider/platform boundary touched?: no runtime boundary change
  • Boundary classification: N/A for implementation. The audit may record provider-owned surfaces such as Entra Groups separately from platform-core route/context rules.
  • Seams affected: Read-only review of route, navigation, global search, and authorization seams.
  • Neutral platform terms preserved or introduced: Use workspace, managed environment, tenant-owned surface, route posture, navigation posture, and repair blocker.
  • Provider-specific semantics retained and why: Provider-specific names such as Entra Groups remain only as audited surface names. They do not become platform-core vocabulary.
  • Why this does not deepen provider coupling accidentally: The output is a bounded audit artifact and does not change shared contracts or runtime labels.
  • Follow-up path: document-in-feature for the audit; follow-up-spec for any product-sensitive or runtime repair.

UI / Surface Guardrail Impact (mandatory when operator-facing surfaces are changed; otherwise write N/A)

Surface / Change Operator-facing surface change? Native vs Custom Shared-Family Relevance State Layers Touched Exception Needed? Low-Impact / N/A Note
Tenant-owned surface audit artifact no runtime UI change N/A navigation, route, global search review only none no N/A - audit/prep artifact only
Existing tenant-owned admin surfaces no change in this slice Native Filament surfaces are inspected, not changed navigation, route, search shell, page, route context as audited facts only no runtime exception The audit records current posture without re-rendering or enabling surfaces

Decision-First Surface Role (mandatory when operator-facing surfaces are changed)

Surface Decision Role Human-in-the-loop Moment Immediately Visible for First Decision On-Demand Detail / Evidence Why This Is Primary or Why Not Workflow Alignment Attention-load Reduction
Tenant-owned surface audit artifact Secondary Context Product/engineering chooses the next bounded repair spec Surface classification, blocker, recommended next action Route names, resource/page classes, tests, search posture, RBAC notes Not an operator runtime surface; it guides repair sequencing Follows Admin Workspace Navigation & Tenant-owned Surface Repair candidate order Prevents repeated manual reconstruction and broad repair bundling

Audience-Aware Disclosure (mandatory when operator-facing surfaces are changed)

Surface Audience Modes In Scope Decision-First Default-Visible Content Operator Diagnostics Support / Raw Evidence One Dominant Next Action Hidden / Gated By Default Duplicate-Truth Prevention
Tenant-owned surface audit artifact internal implementer/reviewer Surface name, classification, blocker, next action Source files, test files, route names, global-search notes N/A Promote the next bounded repair Raw payloads and product data are out of scope One matrix row per surface prevents duplicate scattered posture notes

UI/UX Surface Classification (mandatory when operator-facing surfaces are changed)

Surface Action Surface Class Surface Type Likely Next Operator Action Primary Inspect/Open Model Row Click Secondary Actions Placement Destructive Actions Placement Canonical Collection Route Canonical Detail Route Scope Signals Canonical Noun Critical Truth Visible by Default Exception Type / Justification
Tenant-owned surface audit artifact Audit / Planning Repo audit matrix Choose next repair candidate Read matrix row N/A N/A N/A specs/302-tenant-owned-surface-route-audit/surface-route-audit.md N/A Workspace/environment route posture fields Tenant-owned surface Migration state, blocker, next action docs-only audit artifact

Operator Surface Contract (mandatory when operator-facing surfaces are changed)

Surface Primary Persona Decision / Operator Action Supported Surface Type Primary Operator Question Default-visible Information Diagnostics-only Information Status Dimensions Used Mutation Scope Primary Actions Dangerous Actions
Tenant-owned surface audit artifact Product owner / implementer / reviewer Decide which tenant-owned surface repair should be promoted next Internal audit matrix Which surfaces are already migrated, blocked, stale, or ready for a bounded repair? Surface classification, blocker, next action, proof status File/test/route evidence route posture, navigation posture, search posture, RBAC posture, proof status Docs-only; no TenantPilot or Microsoft tenant mutation Promote next bounded spec none

Proportionality Review (mandatory when structural complexity is introduced)

  • New source of truth?: no runtime source of truth. The audit artifact is repair-prep evidence, not product truth.
  • New persisted entity/table/artifact?: no runtime persistence. One spec-package audit artifact is expected during implementation.
  • New abstraction?: no
  • New enum/state/reason family?: no runtime family. The audit uses document-only labels: migrated, partial cutover, stale panel logic, valid context gate, valid RBAC, ambiguous product IA, dead-code dependent.
  • New cross-domain UI framework/taxonomy?: no runtime framework or taxonomy.
  • Current operator problem: The team cannot safely choose the next tenant-owned route/navigation repair without a repo-verified surface inventory.
  • Existing structure is insufficient because: Candidate notes, tests, resources, route helpers, and model-family metadata each hold part of the answer, but no single artifact records the cross-surface posture.
  • Narrowest correct implementation: Create one audit matrix and repair order from current repo evidence.
  • Ownership cost: The matrix must be maintained only when a follow-up repair changes the posture. No runtime code or framework cost is added.
  • Alternative intentionally rejected: A broad migration or route/navigation framework split is rejected because the audit must first identify the actual drift and product-decision blockers.
  • Release truth: Current-release repair preparation.

Compatibility posture

This feature assumes a pre-production environment.

Backward compatibility, legacy aliases, migration shims, historical fixtures, and compatibility-specific tests are out of scope unless explicitly required by this spec.

Canonical replacement is preferred over preservation.

Testing / Lane / Runtime Impact (mandatory for runtime behavior changes)

  • Test purpose / classification: Feature-validation review using existing Pest feature tests; no new runtime test family is required unless the audit discovers that the audit artifact itself needs a repo guard in a follow-up spec.
  • Validation lane(s): confidence for existing focused Filament tests; N/A for runtime because this slice does not change app behavior.
  • Why this classification and these lanes are sufficient: The deliverable is a repo audit. Existing route, navigation, global-search, and admin-scope tests provide evidence; any missing proof is documented as an audit gap and follow-up blocker rather than patched here.
  • New or expanded test families: none in this spec.
  • Fixture / helper cost impact: none.
  • Heavy-family visibility / justification: none.
  • Special surface test profile: standard-native-filament for any referenced existing Filament surface; no browser proof is required because no UI changes are made.
  • Standard-native relief or required special coverage: ordinary feature-test evidence and static route/resource review only.
  • Reviewer handoff: Confirm no runtime code changed, every audit row links to repo evidence, test gaps are marked as gaps, and follow-up repairs are split.
  • Budget / baseline / trend impact: none.
  • Escalation needed: document-in-feature.
  • Active feature PR close-out entry: Guardrail.
  • Planned validation commands:
    • 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
    • git diff --check

User Scenarios & Testing (mandatory)

User Story 1 - Inventory the tenant-owned admin surface set (Priority: P1)

As an implementer preparing the next repair, I need a repo-verified matrix of tenant-owned admin surfaces so I can see which surfaces are migrated, hidden, blocked, or stale before changing behavior.

Why this priority: Without the matrix, every follow-up risks repeating the broad drift that Spec 301 intentionally avoided.

Independent Test: Review surface-route-audit.md and confirm each first-slice tenant-owned family from TenantOwnedModelFamilies::firstSlice() plus named residual exceptions has a row with route posture, navigation posture, context source, global-search posture, RBAC posture, proof status, blocker, and next action.

Acceptance Scenarios:

  1. Given the current repo surface set, When the audit is completed, Then every tenant-owned first-slice model/resource family is represented exactly once.
  2. Given a tenant-owned surface with no direct primary resource, When it is subordinate or indirect, Then the audit records the parent relation or indirect surface instead of inventing a primary route.
  3. Given a surface with unclear product IA, When it is classified, Then the blocker is recorded as ambiguous product IA and the next action is a bounded follow-up decision.

User Story 2 - Classify route, navigation, search, and RBAC posture (Priority: P2)

As a reviewer, I need each surface classified against the existing route/navigation/global-search/RBAC contracts so I can distinguish valid gates from stale panel logic.

Why this priority: The audit only helps if it separates legitimate denials from implementation drift.

Independent Test: Sample at least one surface in each classification group and verify the matrix cites concrete files, route families, or tests that support the classification.

Acceptance Scenarios:

  1. Given a migrated surface, When it is classified, Then the matrix identifies the canonical workspace/environment route path or route-generation helper.
  2. Given a stale hidden surface, When it is classified, Then the matrix names the hidden navigation or panel logic and marks it as a repair candidate, not a product decision.
  3. Given a global-search capable resource, When it is classified, Then the matrix records whether it has an Edit or View page or whether global search is disabled.
  4. Given an inaccessible tenant-owned surface, When RBAC posture is classified, Then non-member 404 and member-missing-capability 403 expectations are recorded separately where applicable.

User Story 3 - Produce a sequenced repair order (Priority: P3)

As a product owner, I need a recommended follow-up order so adjacent repairs remain small and do not become a single umbrella migration.

Why this priority: The candidate group already names follow-ups, but the audit must make the next one defensible from repo evidence.

Independent Test: Confirm the audit includes one ordered follow-up list with blockers and explicitly keeps runtime fixes out of this spec.

Acceptance Scenarios:

  1. Given surfaces classified as stale panel logic or partial cutover, When the audit proposes follow-up work, Then each recommendation is scoped as a bounded spec candidate.
  2. Given surfaces blocked by product IA, When the audit proposes follow-up work, Then it calls out the decision needed before implementation.
  3. Given tenant-panel dead-code dependencies, When the audit proposes follow-up work, Then it keeps deletion after route/navigation replacement decisions.

Edge Cases

  • A surface may be tenant-owned by data but intentionally workspace-canonical by route; the audit must record both facts.
  • A surface may be tenant-owned but subordinate to a relation manager; the audit must not create an artificial top-level route recommendation.
  • A surface may be tenant-bound but not globally searchable; the audit must distinguish disabled/not-applicable search from missing search coverage.
  • A direct URL may be reachable while navigation is hidden; the audit must classify route access and navigation registration separately.
  • A test may encode stale behavior; the audit must record that as a test-contract finding without changing tests in this slice.
  • Legacy /admin/t/{tenant} and /admin/tenants/{tenant} behavior must be treated as negative-control evidence, not as a route family to revive.

Requirements (mandatory)

Functional Requirements

  • FR-302-001: The implementation MUST create specs/302-tenant-owned-surface-route-audit/surface-route-audit.md as the single audit matrix artifact.
  • FR-302-002: The audit MUST cover every first-slice tenant-owned family listed by TenantOwnedModelFamilies::firstSlice().
  • FR-302-003: The audit MUST cover documented residual or exception surfaces from TenantOwnedModelFamilies::residualRolloutInventory() and scopeExceptions() when they have admin-reachable or admin-relevant surfaces.
  • FR-302-004: Each audited row MUST include surface name, model/resource/page owner, route posture, navigation posture, context source, global-search posture, RBAC posture, existing proof, blocker, migration state, and recommended next action.
  • FR-302-005: Migration state MUST be one of: migrated, partial cutover, stale panel logic, valid context gate, valid RBAC, ambiguous product IA, or dead-code dependent.
  • FR-302-006: The audit MUST distinguish route reachability from navigation registration.
  • FR-302-007: The audit MUST distinguish workspace-home cleanliness from environment-bound admin visibility.
  • FR-302-008: The audit MUST verify global-search posture for resources using tenant-scoped search or search-disabled posture, including the Filament v5 requirement that globally searchable resources have an Edit or View page.
  • FR-302-009: The audit MUST record existing high-signal tests that prove each posture or explicitly mark proof as missing.
  • FR-302-010: The audit MUST produce a recommended repair order that keeps admin-directory-groups-cutover, navigation-contract-split, and tenant-panel-dead-code-retirement as separate follow-up candidates unless repo evidence justifies a different bounded order.
  • FR-302-011: The implementation MUST NOT modify application runtime code, routes, migrations, models, services, jobs, Filament resources, Livewire components, policies, commands, views, assets, or tests.
  • FR-302-012: Any discovered runtime defect MUST be documented as a follow-up blocker or candidate, not fixed in this spec.

Non-Functional Requirements

  • NFR-302-001: The audit must be repo-derived from current files and tests, not roadmap-aspirational.
  • NFR-302-002: The audit must be concise enough to review in one PR and structured enough for later implementation agents to use directly.
  • NFR-302-003: The feature must remain docs/spec-artifact only and must not change runtime behavior.
  • NFR-302-004: Filament v5 and Livewire v4 compliance must be preserved by not introducing legacy APIs or new runtime code.
  • NFR-302-005: No new assets are registered; deployment filament:assets requirements are unchanged.

RBAC / Security Requirements

  • SEC-302-001: The audit MUST record whether each surface's expected denial semantics are non-member 404 and member-without-capability 403, where applicable.
  • SEC-302-002: The audit MUST treat UI visibility as non-authoritative and identify server-side authorization proof separately.
  • SEC-302-003: The audit MUST flag any surface where route/context access appears to depend only on navigation hiding.

Auditability / Observability Requirements

  • AUD-302-001: This docs-only slice introduces no AuditLog event and no OperationRun.
  • AUD-302-002: The audit artifact must provide evidence references sufficient for reviewers to trace each conclusion back to repo files or tests.

Data / Truth-Source Requirements

  • DATA-302-001: No product data or database schema is changed.
  • DATA-302-002: Tenant-owned runtime truth remains in existing models/tables; the audit artifact is repair evidence only.
  • DATA-302-003: The audit must not promote document classification labels into product state or persisted truth.

Success Criteria (mandatory)

  • SC-302-001: surface-route-audit.md exists and covers the expected tenant-owned surface inventory.
  • SC-302-002: Every row has route, navigation, context, search, RBAC, proof, blocker, migration state, and next-action fields.
  • SC-302-003: The audit identifies at least one ordered follow-up path and explains why close alternatives are deferred or blocked.
  • SC-302-004: No application runtime files are changed.
  • SC-302-005: Existing focused Filament validation commands either pass or any failures are documented as pre-existing/runtime follow-up blockers.

Assumptions

  • The user explicitly requested feature number 302; specs/302-tenant-owned-surface-route-audit did not exist before this preparation.
  • 301-admin-inventory-navigation-cutover is completed context and must not be rewritten.
  • The implementation deliverable is an audit artifact, not an application behavior change.
  • TenantOwnedModelFamilies is the starting inventory, but the audit may add admin-relevant pages/resources discovered from Filament files and tests.
  • The current Laravel/Filament stack is Laravel 12, Filament 5, Livewire 4, Pest 4, and PHP 8.4.

Risks

  • The audit could become too broad if it tries to solve every route or IA problem it finds.
  • Document-only labels could be mistaken for runtime taxonomy if not clearly bounded.
  • Some tests may assert stale behavior; those must be reported as findings rather than edited in this spec.
  • A later implementation loop could accidentally patch runtime code unless the tasks keep the docs-only boundary explicit.

Out of Scope

  • Any application code change.
  • Any migration, model, service, job, policy, command, view, route, Filament resource/page, Livewire component, or test edit.
  • Broad runtime route migration.
  • Mass navigation re-enablement.
  • Entra Groups cutover implementation.
  • Navigation contract split implementation.
  • Tenant-panel dead-code deletion.
  • New global-search enablement.
  • New audit log or OperationRun behavior.

Follow-up Spec Candidates

  • admin-directory-groups-cutover
  • navigation-contract-split, only if the audit shows shared contract drift remains after bounded surface repairs
  • tenant-panel-dead-code-retirement
  • surface-specific repair specs named by surface-route-audit.md

Manual Promotion Notes

  • Selected candidate title: tenant-owned-surface-route-audit
  • Source location: docs/product/spec-candidates.md, Admin Workspace Navigation & Tenant-owned Surface Repair candidate group.
  • Why selected: User explicitly promoted it as 302; it is the recommended follow-up after admin-inventory-navigation-cutover.
  • Close alternatives deferred: admin-directory-groups-cutover needs the audit/product IA evidence first; navigation-contract-split should happen only if drift remains after bounded repairs; tenant-panel-dead-code-retirement should wait until dependencies are known.
  • Roadmap relationship: UI and Product Maturity Polish, parallel immediate repair lane for Admin Workspace Navigation & Tenant-owned Surface Repair.
  • Completed-spec guardrail result: Spec 301 has completed task markers and is treated as historical/completed context only. Specs 279-301 are not modified by this preparation.
  • Smallest viable implementation slice: Create the audit matrix and repair-prep order only; do not patch runtime.