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
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.mdlists this as the second step afteradmin-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.phpapps/platform/app/Support/Navigation/NavigationScope.phpapps/platform/app/Filament/Concerns/ScopesGlobalSearchToTenant.phpapps/platform/app/Support/OperateHub/OperateHubShell.phpapps/platform/app/Support/WorkspaceIsolation/TenantOwnedModelFamilies.phpapps/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.
OperationRun UX Impact (mandatory when the feature creates, queues, deduplicates, resumes, blocks, completes, or deep-links to an OperationRun; otherwise write N/A - no OperationRun start or link semantics touched)
- 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, andrepair 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.phpgit 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:
- Given the current repo surface set, When the audit is completed, Then every tenant-owned first-slice model/resource family is represented exactly once.
- 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.
- 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:
- Given a migrated surface, When it is classified, Then the matrix identifies the canonical workspace/environment route path or route-generation helper.
- 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.
- 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.
- 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:
- 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.
- Given surfaces blocked by product IA, When the audit proposes follow-up work, Then it calls out the decision needed before implementation.
- 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.mdas 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()andscopeExceptions()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, andtenant-panel-dead-code-retirementas 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:assetsrequirements 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
AuditLogevent and noOperationRun. - 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.mdexists 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-auditdid not exist before this preparation. 301-admin-inventory-navigation-cutoveris completed context and must not be rewritten.- The implementation deliverable is an audit artifact, not an application behavior change.
TenantOwnedModelFamiliesis 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-cutovernavigation-contract-split, only if the audit shows shared contract drift remains after bounded surface repairstenant-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 afteradmin-inventory-navigation-cutover. - Close alternatives deferred:
admin-directory-groups-cutoverneeds the audit/product IA evidence first;navigation-contract-splitshould happen only if drift remains after bounded repairs;tenant-panel-dead-code-retirementshould 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.