# Feature Specification: Workspace-first Navigation & Monitoring Hub **Feature Branch**: `077-workspace-nav-monitoring-hub` **Created**: 2026-02-06 **Status**: Draft **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). Workspace management is workspace-scoped: non-members receive 404 (deny-as-not-found); members missing required capabilities receive 403. - Q: How should the tenant-context default filter on `/admin/operations` be 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 no `current_workspace_id` selected? → A: Redirect to `/admin/choose-workspace` and 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**: 1. **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. 2. **Given** I can manage workspaces, **When** I open "Manage workspaces", **Then** I can access workspace CRUD screens and breadcrumbs stay within the management area. 3. **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**: 1. **Given** I am a member of a workspace, **When** I visit `/admin/operations`, **Then** I can view a workspace-wide list of operations. 2. **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`. 3. **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**: 1. **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. 2. **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. 3. **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`. - **FR-003 (Canonical workspace management route)**: "Manage workspaces" MUST navigate to `/admin/workspaces` and MUST NOT be labeled simply "Workspaces". - **FR-004 (Breadcrumb correctness)**: Breadcrumbs in workspace management MUST point back to `/admin/workspaces` and 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}` - **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/operations` and 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. - **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-workspace` and 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: ` 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, show `Tenant: ` (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. ### 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_at` descending (fallback: `id` descending). - 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: ` (clickable) - `Tenant: ` (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/operations` shows 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.