TenantAtlas/specs/085-tenant-operate-hub/spec.md
2026-02-11 01:02:42 +01:00

14 KiB
Raw Blame History

Feature Specification: Tenant Operate Hub / Tenant Overview IA

Feature Branch: 085-tenant-operate-hub
Created: 2026-02-09
Status: Draft
Input: User description: "Make central Monitoring surfaces feel context-aware when entered from a tenant, without changing canonical URLs, and without weakening deny-as-not-found security boundaries."

Clarifications

Session 2026-02-09 (work order alignment)

  • Q: What is the source of truth for “Back to tenant”? → A: The active entitled tenant context (Filament tenant if present, otherwise the remembered last-tenant id for the current workspace).
  • Q: Should “Back to last tenant” be implemented as a separate feature? → A: No; remembered tenant context is used only to preserve context when navigating from a tenant into central Monitoring.
  • Q: What does “Show all tenants” do? → A: It explicitly exits tenant context to return to workspace-wide monitoring (no mixed behavior with filter resets).
  • Q: How is Monitoring reached from tenant context? → A: Tenant navigation offers a “Monitoring” group with shortcuts that open central Monitoring surfaces.
  • Q: How should stale tenant context (tenant context active but user no longer entitled) behave on Monitoring pages? → A: Monitoring renders workspace-wide (no tenant name, no “Back to tenant”), preserving deny-as-not-found for tenant pages.
  • Q: Should run detail offer a secondary escape hatch when tenant context is active? → A: Yes — show a secondary “Show all operations” link to /admin/operations.
  • Q: How should tenant Monitoring shortcuts indicate “opens central monitoring”? → A: Keep labels minimal (no “↗ Central” suffix).

User Scenarios & Testing (mandatory)

User Story 1 - Monitoring feels context-aware (Priority: P1)

As an operator, when I open central Monitoring from within a tenant, I immediately understand:

  1. whether the Monitoring view is scoped to the current tenant or to all tenants, and
  2. how to get back to the tenant I came from.

Why this priority: This is the core usability and safety problem: monitoring and tenant work should not feel like different apps, but they must not blur security boundaries.

Independent Test: With a tenant context active, a test user can open the Operations index and a run detail, see an explicit scope indicator and a deterministic “Back to tenant”, and exit to workspace-wide monitoring intentionally.

Acceptance Scenarios:

  1. Given a user is a member of a workspace and has access to at least one tenant, When they open central Monitoring (Operations), Then the page clearly shows whether tenant context is active.
  2. Given a tenant context is active, When the user navigates to a canonical monitoring detail page, Then the UI provides a single, clear “Back to tenant” affordance that returns to that tenant dashboard.
  3. Given a user is not entitled to the current tenant, When they try to access tenant-scoped pages via a direct link, Then they receive a not-found experience (deny-as-not-found), without any tenant existence hints.

User Story 2 - Canonical URLs with explicit scope (Priority: P2)

As an operator, I can use canonical Monitoring URLs at all times. When tenant context is active, Monitoring views can be tenant-filtered by default, but they must not implicitly change tenant selection.

Why this priority: Avoids mistakes and misinterpretation of data by preventing silent scoping changes.

Independent Test: With a tenant selected, open monitoring index and detail views and verify the scope is consistent and clearly communicated.

Acceptance Scenarios:

  1. Given a tenant context is active, When the user opens the monitoring index, Then the default view is tenant-scoped (or clearly offers a one-click tenant scope), and the UI visibly indicates the scope.
  2. Given no tenant context is active, When the user opens monitoring, Then the view is workspace-wide and does not imply a tenant is selected.

User Story 3 - Deep links are safe and recoverable (Priority: P3)

As an operator working inside a tenant, when I land on a canonical run detail via a deep link, I can safely return to the tenant if tenant context is still active and I am still entitled.

Why this priority: These workflows are frequent. Deep links are where users most often “lose” tenant context.

Independent Test: With a tenant context active, open a canonical run detail and verify the “Back to tenant” affordance is present and correct.

Acceptance Scenarios:

  1. Given a tenant context is active and the user is still entitled, When they open a canonical run detail, Then they see a “Back to tenant” affordance.
  2. Given tenant context is not active, When the user opens a canonical run detail, Then they see only a “Back to Operations” affordance.

Edge Cases

  • User has no workspace selected: Monitoring must not show cross-workspace data; user must select a workspace first.
  • User has workspace access but zero tenant access: Monitoring must still work in workspace-wide mode, without tenant selection.
  • Users tenant access is revoked while they have a deep link open: subsequent tenant-scoped navigation must be deny-as-not-found.
  • User opens a bookmarked canonical run detail directly: the UI must provide a deterministic “Back” behavior without inventing tenant context.
  • Tenant context is active, but entitlement was revoked: Monitoring must not leak tenant identity; tenant return affordance must not appear (or must be safe).
  • Monitoring views must remain view-only render surfaces: rendering must not trigger outbound calls.

Requirements (mandatory)

Target State (hard decision)

This spec adopts a single, deterministic interpretation:

  • Monitoring URLs are canonical and do not change with tenant context.

  • Tenant context makes Monitoring feel scoped (scope indicators, default filters, and deterministic exits) without implicit tenant switching.

  • Operations index: /admin/operations

  • Operations run detail: /admin/operations/{run}

  • Alerts: /admin/alerts

  • Audit log: /admin/audit-log

Tenant plane remains under /admin/t/{tenant} for tenant dashboards and workflows. Monitoring views are central, but when tenant context is active they become tenant-filtered by default and provide a deterministic “Back to tenant” affordance.

Constitution alignment (required): This feature is information architecture + navigation behavior. It MUST NOT introduce new outbound calls for monitoring pages. If it introduces or changes any write/change behavior (e.g., starting workflows), it MUST maintain existing safety gates (preview/confirmation/audit), tenant isolation, run observability, and tests.

Constitution alignment (RBAC-UX): This feature changes how users reach surfaces; it MUST preserve and test authorization semantics:

  • Non-member / not entitled to workspace scope OR tenant scope → deny-as-not-found (404 semantics)
  • Member but missing capability → forbidden (403 semantics)

Constitution alignment (OPS-EX-AUTH-001): Authentication handshakes may perform synchronous outbound communication on auth endpoints. This MUST NOT be used for Monitoring pages.

Constitution alignment (BADGE-001): If any status/severity/outcome badges are added or changed on hub pages, their meaning MUST be centralized and covered by tests.

Constitution alignment (UI Action Surfaces): If this feature adds/modifies any admin or tenant UI surfaces, the “UI Action Matrix” MUST be updated and action gating MUST remain consistent (confirmation for destructive-like actions; server-side authorization for mutations).

Functional Requirements

  • FR-085-001: Tenant navigation MUST offer a “Monitoring” group with shortcuts to central Monitoring surfaces:

    • Runs (Operations) → /admin/operations
    • Alerts → /admin/alerts
    • Audit Log → /admin/audit-log These shortcuts MUST NOT introduce new tenant-scoped monitoring URLs.
  • FR-085-002: The Operations index (/admin/operations) MUST show a clear scope indicator in the page header:

    • Scope: Workspace — all tenants when no tenant context is active
    • Scope: Tenant — <tenant name> when tenant context is active
  • FR-085-003: Canonical Monitoring URLs MUST NOT implicitly change tenant context. Tenant context MAY influence default filters on Monitoring views.

  • FR-085-004: When tenant context is active on the Operations index, the default tenant filter MUST be set to the current tenant, and the UI MUST make this tenant scoping obvious.

  • FR-085-005: The Operations index MUST provide two explicit CTAs when tenant context is active:

    • Show all tenants (explicitly exits tenant context and returns to workspace-wide monitoring)
    • Back to <tenant name> (navigates to tenant dashboard)
  • FR-085-006: The run detail (/admin/operations/{run}) MUST provide a deterministic “Back” affordance:

    • If tenant context is active AND the user is still entitled: ← Back to <tenant name> (tenant dashboard) AND a secondary Show all operations (to /admin/operations)
    • Else: Back to Operations (Operations index)
  • FR-085-007: “Back to tenant” MUST be based only on active entitled tenant context (Filament tenant, or remembered tenant for the current workspace). It MUST NOT be inferred from arbitrary deep-link parameters.

  • FR-085-008: Deny-as-not-found MUST remain: users not entitled to workspace or tenant scope MUST receive a not-found experience (404 semantics), with no tenant existence hints.

  • FR-085-009: Monitoring views (/admin/operations and /admin/operations/{run}) MUST remain view-only render surfaces and MUST NOT trigger outbound calls during render.

  • FR-085-010: If tenant context is active but the user is not entitled to that tenant, Monitoring pages MUST behave as workspace-wide views:

    • Scope indicator MUST show Scope: Workspace — all tenants
    • No tenant name MUST be displayed
    • No “Back to ” affordance MUST be rendered
    • Direct access to tenant pages MUST continue to be deny-as-not-found

UI Action Matrix (mandatory when UI surfaces are changed)

Surface Location Header Actions Inspect Affordance (List/Table) Row Actions (max 2 visible) Bulk Actions (grouped) Empty-State CTA(s) View Header Actions Create/Edit Save+Cancel Audit log? Notes / Exemptions
Central Operations (index) /admin/operations Scope indicator; Show all tenants (when tenant context active); deterministic back affordance Linked run rows to open run detail N/A N/A N/A N/A N/A No Must not implicitly change tenant context; default tenant filter when tenant context active
Central Operations (run detail) /admin/operations/{run} ← Back to <tenant> (when tenant context active + entitled) OR Back to Operations; secondary Show all operations allowed when tenant context active + entitled N/A N/A N/A N/A N/A N/A No Must not reveal tenant identity when user is not entitled
Tenant navigation shortcuts Tenant sidebar N/A N/A N/A N/A N/A N/A N/A No “Monitoring” group with central shortcuts

Key Entities (include if feature involves data)

  • Workspace: A security and organizational boundary for operations and monitoring.
  • Tenant: A managed environment within a workspace; access is entitlement-based.
  • Monitoring (Operations): Central monitoring views that can be workspace-wide or tenant-scoped when tenant context is active.
  • Operation Run: A tracked execution of an operational workflow; viewable via canonical run detail.
  • Alert: An operator-facing signal about an issue or state requiring attention.
  • Audit Event: An immutable record of important user-triggered actions and sensitive operations.

Success Criteria (mandatory)

Measurable Outcomes

  • SC-085-001: In a usability walkthrough, 90% of operators can correctly identify whether Operations is scoped to a tenant or to all tenants within 10 seconds of opening /admin/operations.
  • SC-085-002: With tenant context active, operators can return to the tenant dashboard from /admin/operations and /admin/operations/{run} in ≤ 1 click.
  • SC-085-003: Support tickets tagged “lost tenant context / where am I?” decrease by 30% within 30 days after rollout.
  • SC-085-004: Authorization regression checks show zero cases where a non-entitled user can infer existence of a tenant or view tenant-scoped monitoring data.

Engineering Acceptance Outcomes

  • SC-085-005: When tenant context is active, /admin/operations and /admin/operations/{run} clearly show tenant scope and a “Back to ” affordance.
  • SC-085-006: When tenant context is not active, /admin/operations/{run} shows “Back to Operations” and no “Back to tenant”.
  • SC-085-007: Viewing Monitoring pages does not initiate outbound network requests or start background work as a side effect of rendering.

Test Plan (mandatory)

  1. Operations index scope label + CTAs (tenant context)

    • With tenant context active and user entitled, request /admin/operations.
    • Assert the page indicates Scope: Tenant — <tenant name>.
    • Assert Show all tenants and Back to <tenant name> are available.
  2. Operations index scope label (no tenant context)

    • With no tenant context active, request /admin/operations.
    • Assert the page indicates Scope: Workspace — all tenants.
  3. Run detail back affordance (tenant context)

    • With tenant context active and user entitled, request /admin/operations/{run}.
    • Assert ← Back to <tenant name> is available.
    • Assert secondary Show all operations is available and links to /admin/operations.
  4. Run detail back affordance (no tenant context)

    • With no tenant context active, request /admin/operations/{run}.
    • Assert only Back to Operations is available.
  5. Deny-as-not-found regression

    • As a user without tenant entitlement, request /admin/t/{tenant}.
    • Assert deny-as-not-found behavior (404 semantics) and that no tenant identity hints are revealed via Monitoring CTAs.
  6. Stale tenant context behaves workspace-wide

    • With tenant context active but user not entitled, request /admin/operations.
    • Assert scope indicates workspace-wide and no tenant name or “Back to tenant” is present.
  7. No outbound calls on render

    • Assert rendering /admin/operations and /admin/operations/{run} does not initiate outbound network calls and does not start background work from a view-only GET.