TenantAtlas/specs/077-workspace-nav-monitoring-hub/spec.md

163 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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 its 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 workspaces 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 platforms 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: <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, show `Tenant: <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.
### 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: <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/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.