Implements Spec 077 refinements: workspace Global Mode and navigation/context-bar redundancy cleanup.
Summary
- Global Mode: `/admin/workspaces` is workspace-optional (lists only member workspaces); explicit allowlist in `EnsureWorkspaceSelected`.
- Navigation cleanup: workspace switching is topbar-only; no sidebar “Switch workspace”; removes redundant “Manage workspaces” entry from context-bar.
- Context bar: when no workspace selected, tenant picker is disabled with guidance; on tenant-scoped routes `/admin/t/{tenant}/…` the tenant indicator is read-only (Filament tenant menu remains primary).
- Authorization: workspace creation is policy-driven (`WorkspacePolicy::create()`), enforced in `ChooseWorkspace` via Gate.
Safety / Compliance
- Livewire v4.0+ compliant (Filament v5).
- Panel provider registration remains in `bootstrap/providers.php` (no changes required).
- Global search: no new globally searchable resources added; no behavior changes introduced.
- Destructive actions: none added/changed.
- Assets: no new assets registered; deploy process unchanged (if assets are registered elsewhere, ensure `php artisan filament:assets` runs in deploy as usual).
Tests
- `vendor/bin/sail bin pint --dirty`
- `vendor/bin/sail artisan test --compact tests/Feature/Workspaces tests/Feature/Monitoring tests/Feature/OpsUx tests/Feature/Filament/WorkspaceContextTopbarAndTenantSelectionTest.php`
Spec artifacts
- `specs/077-workspace-nav-monitoring-hub/{spec,plan,tasks}.md`
- `specs/077-workspace-nav-monitoring-hub/contracts/routes.md`
Co-authored-by: Ahmed Darrazi <ahmeddarrazi@MacBookPro.fritz.box>
Reviewed-on: #94
14 KiB
Feature Specification: Workspace-first Navigation & Monitoring Hub
Feature Branch: 077-workspace-nav-monitoring-hub
Created: 2026-02-06
Status: Implemented
Input: User description: "Workspace-first navigation and monitoring hub for an enterprise admin suite: remove workspace navigation ambiguity, lock canonical operations deep links, apply tenant context only as default filters, and enforce strict 404/403 access semantics without information leakage."
Clarifications
Session 2026-02-06
- Q: What is the authorization plane + status-code rule for
/admin/workspaces("Manage workspaces")? → A: Tenant plane (/admin, Entra users)./admin/workspacesis Global Mode (workspace-optional). Index lists only the user’s workspaces; per-record access for non-members is 404 (deny-as-not-found); protected actions/screens return 403 when unauthorized. - Q: Should
/admin/workspacesrequire an activecurrent_workspace_id? → A: No./admin/workspacesis Global Mode (workspace-optional). The index lists only workspaces the user is a member of; per-record access for non-members remains 404. - Q: How should the tenant-context default filter on
/admin/operationsbe implemented? → A: Server-side default state with a removable filter chip; URL remains/admin/operations. - Q: What happens when a user visits a workspace-scoped page (e.g.
/admin/operations) with nocurrent_workspace_idselected? → A: Redirect to/admin/choose-workspaceand return to the originally requested URL after selection. - Q: If tenant context is active but the tenant is not in the current workspace (e.g., user switches workspaces), what should happen? → A: Auto-clear tenant context and continue on tenantless workspace pages.
User Scenarios & Testing (mandatory)
User Story 1 - Switch workspace without ambiguity (Priority: P1)
As an operator/admin, I need to switch my active workspace (portfolio) using a clear, single-purpose entry point, so that I never confuse "switch workspace" with "manage workspaces".
Why this priority: Workspace context is foundational. If it’s confusing, every other module becomes harder to use and support.
Independent Test: A user can find "Switch workspace", select a workspace they are a member of, and the application context updates while workspace management remains separate.
Acceptance Scenarios:
- Given I am signed in and belong to multiple workspaces, When I choose "Switch workspace", Then I see only workspaces I am a member of and can select one.
- Given I can manage workspaces, When I open "Manage workspaces", Then I can access workspace CRUD screens and breadcrumbs stay within the management area.
- Given I cannot manage workspaces, When I look at navigation, Then I do not see "Manage workspaces".
User Story 2 - Use Monitoring hub from canonical links (Priority: P2)
As an operator, I need monitoring pages (starting with Operations) to be reachable via stable, shareable links that never depend on tenant context, so that support, alerts, and notifications can deep-link reliably.
Why this priority: Monitoring must be dependable across contexts; deep links are critical for incident response and support.
Independent Test: Visiting the canonical operations URLs works with and without tenant context, and the system enforces membership checks.
Acceptance Scenarios:
- Given I am a member of a workspace, When I visit
/admin/operations, Then I can view a workspace-wide list of operations. - Given I have an active tenant context, When I visit
/admin/operations, Then operations are pre-filtered to that tenant but the URL remains/admin/operations. - Given I have a run link
/admin/operations/{run}, When I open it, Then I see the run detail regardless of tenant context.
User Story 3 - Navigate and search without leaking inaccessible data (Priority: P3)
As a user, I should never learn about workspaces/tenants/runs I cannot access through navigation labels, breadcrumbs, counts, or global search results.
Why this priority: Preventing information leakage is a core enterprise requirement and reduces risk in multi-tenant MSP environments.
Independent Test: An unauthorized user receives not-found responses for out-of-scope resources and does not see them in search.
Acceptance Scenarios:
- Given I am not a member of a workspace, When I attempt to access that workspace’s monitoring data or runs, Then I receive a not-found response.
- Given I am a workspace member but lack a capability for a protected workspace-scoped screen or action, When I attempt to access it directly, Then I receive a forbidden response.
- Given I use global search, When I search for entities outside my scope, Then they do not appear in results (no partial hints).
Edge Cases
- User is a member of zero workspaces.
- User loses workspace membership while having an active session.
- Tenant context is active but the tenant does not belong to the current workspace.
- A run is referenced by an external deep link after it was deleted or moved.
- User can view monitoring but cannot perform mutations (e.g., cancel/retry) if those actions exist.
Requirements (mandatory)
Constitution alignment (required): This feature changes navigation and authorization behavior but does not introduce new external API calls or background jobs. Any mutation actions added later (e.g., cancel/retry) must follow the platform’s safety gates (confirmation/audit) and be covered by authorization tests.
Constitution alignment (RBAC-UX):
- Authorization plane(s) involved:
- Tenant plane (Entra users) only.
- Platform plane (
/system) is out of scope for this feature.
- Authorization planes:
- Workspace-level pages (e.g., monitoring hub, workspace management) are governed by workspace membership and workspace capabilities.
- Tenant context is secondary and must not change canonical routing for monitoring pages.
- Isolation model note (workspace scope):
- “Workspace-scoped” monitoring is an explicit, access-checked aggregation scope over the managed tenants that belong to the selected workspace.
- All reads remain bounded to the current workspace; there is no cross-workspace monitoring view in this feature.
- 404 vs 403 semantics (strict):
- Non-member / not entitled to the workspace scope → 404 (deny-as-not-found)
- Workspace member but missing the required capability for a protected screen/action → 403
- Server-side enforcement: Navigation visibility must not be treated as authorization; all access control is enforced on the server for every protected page and every mutation.
- Global search non-leakage: Global search must not show titles, counts, or partial matches for inaccessible workspaces/tenants/runs. Inaccessible entities behave as not-found.
Functional Requirements
-
FR-001 (One label, one meaning): The application MUST provide two distinct, clearly-labeled entry points:
- "Switch workspace" for selecting the active workspace context.
- "Manage workspaces" for workspace CRUD/administration.
-
FR-002 (Canonical workspace switch route): "Switch workspace" MUST navigate to
/admin/choose-workspace.- UX note: "Switch workspace" is a global context control and MUST NOT be registered as a sidebar navigation item.
-
FR-003 (Canonical workspace management route): "Manage workspaces" MUST navigate to
/admin/workspacesand MUST NOT be labeled simply "Workspaces". -
FR-004 (Breadcrumb correctness): Breadcrumbs in workspace management MUST point back to
/admin/workspacesand must not send users to the workspace switcher. -
FR-005 (Monitoring is workspace-level): Monitoring pages MUST be workspace-scoped and reachable without tenant context.
-
FR-006 (Canonical Operations URLs): Operations MUST remain canonical and tenantless:
- index:
/admin/operations - detail:
/admin/operations/{run}
- index:
-
FR-007 (Tenant context affects defaults, not routing): If tenant context is active, the operations index MUST default to showing runs for that tenant using server-side default filter state, and users MUST be able to clear that default to view workspace-wide operations. The canonical URL MUST remain
/admin/operationsand the default MUST present a visible, removable filter chip (no required querystring parameters). -
FR-008 (Tenant shortcut to operations): Tenant detail screens MUST offer a "Recent operations" summary and a "View all operations" call-to-action that leads to the canonical operations index.
-
FR-009 (Membership gating): Users MUST be a member of a workspace to access workspace-scoped pages. Non-members MUST receive a not-found response.
-
FR-010 (Capability gating for management): Workspace-scoped management/mutations MUST be restricted to users with the appropriate capability/capabilities (from the canonical registry). Unauthorized workspace members MUST receive a forbidden response.
- Canonical capabilities used by this feature:
workspace.manage(Capabilities::WORKSPACE_MANAGE): create/edit workspace fields.workspace_membership.manage(Capabilities::WORKSPACE_MEMBERSHIP_MANAGE): add/remove members and change roles.
- Canonical capabilities used by this feature:
-
FR-011 (Monitoring hub IA): The sidebar MUST provide a "Monitoring" area that is the canonical home for Operations now, with reserved surfaces for future Alerts and Audit Log.
-
FR-012 (Deep-link stability): Any monitoring entity intended for support workflows MUST have a stable deep link that does not depend on tenant context.
-
FR-013 (No workspace selected): If a user visits a workspace-scoped page without a selected workspace context, the system MUST redirect to
/admin/choose-workspaceand then return the user to their originally requested URL after a successful selection. -
FR-014 (Invalid tenant context): If tenant context is active but the tenant does not belong to the current workspace, the system MUST auto-clear tenant context and continue on workspace-level pages without tenant scoping.
-
FR-077-016 (Header context bar): The header MUST provide an always-available context bar for Suite navigation:
- FR-077-016-A (Workspace visible): If a workspace is selected, show
Workspace: <name>and allow the user to open the existing workspace switcher (/admin/choose-workspace). - FR-077-016-B (Tenant accessible on tenantless pages): The header MUST surface tenant context even on tenantless pages (e.g.,
/admin/operations). If there is an active tenant context, showTenant: <tenant name>(fallback to a safe identifier). If there is no active tenant but there is a last-selected tenant in the current workspace session, show it. - FR-077-016-C (No implicit switching): Canonical pages MUST NOT silently switch tenant or workspace. The context bar is an explicit control only.
- FR-077-016-D (No leakage): Tenant picker contents MUST include only tenants the user is entitled to view within the current workspace. Unauthorized tenant selection via direct URL MUST remain deny-as-not-found (404).
- FR-077-016-E (Filament-native): Implementation MUST use Filament v5 mechanisms (topbar/user-menu render hooks + Filament tenancy) and Livewire v4 where needed.
- FR-077-016-A (Workspace visible): If a workspace is selected, show
Key Entities (include if feature involves data)
- Workspace: Primary context container for a customer/portfolio.
- Workspace Membership: The relationship that entitles a user to a workspace.
- Managed Tenant: Secondary context within a workspace; used for scoping defaults and tenant workflows.
- Operation Run: A record representing an operational execution that belongs to a workspace and may be associated with a tenant.
- Capability: A named permission that gates management/mutation behavior.
Success Criteria (mandatory)
Measurable Outcomes
- SC-001 (Reduced confusion): In a moderated test with new users, at least 90% correctly choose the right destination (switch vs manage) on first attempt.
- SC-002 (Faster workspace switching): Users can switch to a known workspace in under 15 seconds without using search.
- SC-003 (Reliable deep links): Support can open
/admin/operations/{run}successfully regardless of tenant context in 100% of tested cases. - SC-004 (No leakage regressions): Security regression tests confirm 0 instances of inaccessible workspaces/tenants/runs appearing in navigation or global search.
Acceptance details (pinned)
Recent operations summary (FR-008)
- Show the most recent 5 operation runs for the current tenant, ordered by
created_atdescending (fallback:iddescending). - Display, at minimum:
type(label),status,outcome,created_at(or since), and a link to the run detail. - Provide a "View all operations" CTA that navigates to canonical
/admin/operations(no tenant prefix / no required query params).
Header context bar (FR-077-016)
- The header shows a stable, compact context bar:
Workspace: <name>(clickable)Tenant: <name>(picker)
- Tenant picker is available on tenantless pages.
- No automatic tenant selection occurs when opening canonical URLs.
Mandatory Tests (pinned)
- T-077-016-1 (Tenant dropdown on tenantless pages): With a selected workspace and an active tenant context, visiting
/admin/operationsshows the tenant picker and selecting a tenant navigates to tenant home. - T-077-016-2 (Security filtering): Only entitled tenants within the current workspace appear in the picker; posting /navigating to an unauthorized tenant results in 404.
- T-077-016-3 (No implicit switching): Visiting
/admin/operations/{run}from a deep link MUST NOT auto-switch tenant.