TenantAtlas/specs/058-tenant-ui-polish/spec.md
2026-01-21 08:12:46 +01:00

13 KiB

Feature Specification: Tenant UI Polish (Dashboard + Inventory Hub + Operations)

Feature Branch: 058-tenant-ui-polish
Created: 2026-01-20
Status: Draft
Input: User description: "Feature 058 — Tenant UI Polish: Dashboard + Inventory Hub + Operations "Orders-style" (v1)"

Clarifications

Session 2026-01-20

  • Q: Coverage % definition for Inventory KPI header? → A: Coverage % = Restorable / Total (Partial remains a separate chip/number; main % stays conservative)
  • Q: Drift stale threshold (last scan older than X days)? → A: 7 days
  • Q: Inventory KPI “Active Operations” definition? → A: Show both counts: All active runs (queued + running) and Inventory-active runs (queued + running)
  • Q: How many rows in “Recent” lists by default? → A: 10

Session 2026-01-21

  • Q: Operations index "Stuck" tab in v1? -> A: No "Stuck" tab in v1

User Scenarios & Testing (mandatory)

User Story 1 - Drift-first tenant dashboard (Priority: P1)

As a tenant admin, I can open a tenant-scoped dashboard that immediately surfaces drift risk and operations health, without triggering any remote calls.

Why this priority: This is the primary entry point for day-to-day operations and should be actionable at a glance.

Independent Test: Visiting the dashboard shows drift + operations KPIs, a “needs attention” list with working CTAs, and recent lists, while confirming no outbound HTTP happens during render and any background UI updates.

Acceptance Scenarios:

  1. Given I am signed in to a tenant, When I open the Dashboard, Then I see tenant-scoped drift KPIs, operations health KPIs, and recent lists.
  2. Given there are urgent drift issues (e.g., high severity open findings), When I view the Dashboard, Then they appear in the “Needs Attention” section with a CTA that navigates to a filtered view.
  3. Given drift generation has a recent failed run, When I view the Dashboard, Then I can navigate from “Needs Attention” to the related operation run details.
  4. Given there is no drift data yet, When I view the Dashboard, Then the dashboard renders calmly with empty-state messaging and no errors.
  5. Given the last drift scan is older than 7 days, When I view the Dashboard, Then “Needs Attention” includes a “Drift stale” item with a CTA to investigate.
  6. Given there are more than 10 drift findings and operation runs, When I view the Dashboard, Then each “Recent” list shows the 10 most recent items.

User Story 2 - Inventory becomes a hub module (Priority: P2)

As a tenant admin, I can use Inventory as a “hub” with consistent sub-navigation and a shared KPI header across Inventory subpages.

Why this priority: Inventory is a high-traffic area; a hub layout reduces cognitive load and makes it easier to find the right view quickly.

Independent Test: Navigating Inventory Items / Sync Runs / Coverage keeps the same shared KPI header, the left sub-navigation is consistent, and all data remains tenant-scoped and DB-only.

Acceptance Scenarios:

  1. Given I am signed in to a tenant, When I open Inventory, Then I see hub navigation (Items / Sync Runs / Coverage) and a shared KPI header.
  2. Given I switch between Inventory subpages, When I navigate Items → Sync Runs → Coverage, Then the KPI header remains visible and consistent.
  3. Given the tenant has an inventory sync run history, When I open “Sync Runs”, Then I see only sync runs relevant to inventory synchronization.

User Story 3 - Operations index “Orders-style” (Priority: P3)

As a tenant admin, I can view Operations in an “orders-style” overview (KPIs + status tabs + table) to quickly assess activity and failures.

Why this priority: Operations is the canonical place to investigate work; better scanning and filtering reduces time-to-triage.

Independent Test: Visiting Operations index shows KPI cards and status tabs that correctly filter the table without introducing polling churn or any remote calls.

Acceptance Scenarios:

  1. Given I am signed in to a tenant, When I open Operations, Then I see KPIs, status tabs, and the operations table.
  2. Given there are active runs, When I click the “Active” tab, Then the table filters to queued + running runs only.
  3. Given there are failed runs, When I click the “Failed” tab, Then the table filters to failed runs only.
  4. Given I navigate away and back, When I return to Operations, Then the UI remains calm (no refresh loops) and loads quickly.

[Add more user stories as needed, each with an assigned priority]

Edge Cases

  • No data yet: dashboard/inventory/operations render with empty states and helpful CTAs.
  • Large tenants: KPI calculations remain fast enough to keep pages responsive.
  • Mixed outcomes: partial/failed/succeeded runs are correctly categorized and discoverable via tabs/filters.
  • Tenant switching: no cross-tenant leakage of KPIs, lists, or links.
  • Time windows: KPI windows (e.g., last 7/30 days) handle timezones consistently.
  • “Unknown” states: missing duration/end time renders gracefully (e.g., avg duration excludes non-terminal runs).

Requirements (mandatory)

Constitution alignment (required): If this feature introduces any Microsoft Graph calls, any write/change behavior, or any long-running/queued/scheduled work, the spec MUST describe contract registry updates, safety gates (preview/confirmation/audit), tenant isolation, run observability (OperationRun type/identity/visibility), and tests. If security-relevant DB-only actions intentionally skip OperationRun, the spec MUST describe AuditLog entries.

Functional Requirements

  • FR-001 (Tenant scope): System MUST ensure the Dashboard, Inventory hub, and Operations views are tenant-scoped, with no cross-tenant visibility.
  • FR-002 (DB-only surfaces): System MUST keep Dashboard, Inventory hub header, and Operations index DB-only during render and any background UI updates.
  • FR-003 (Placement policy): System MUST show KPI cards only on these entry-point pages: Dashboard, Inventory hub (shared header), and Operations index.
  • FR-004 (Inventory hub layout): System MUST provide an Inventory hub with left sub-navigation for Items, Sync Runs, and Coverage.
  • FR-005 (Inventory KPIs): Inventory hub MUST show a shared KPI header across Inventory subpages with:
    • Total Items
    • Coverage % (restorable items / total items; partial shown separately)
    • Last Inventory Sync (status + timestamp)
    • Active Operations (queued + running), showing both:
      • All active runs
      • Inventory-active runs
  • FR-006 (Inventory sync runs view): System MUST provide a “Sync Runs” view that lists only inventory synchronization runs.
  • FR-007 (Coverage chips): System MUST standardize coverage chips to this set only: Restorable, Partial, Risk, Dependencies.
  • FR-008 (Operations index KPIs): Operations index MUST show tenant-scoped KPIs:
    • Total Runs (30 days)
    • Active Runs (queued + running)
    • Failed/Partial (7 days)
    • Avg Duration (7 days, terminal runs only)
  • FR-009 (Operations tabs): Operations index MUST provide status tabs that filter the operations table: All, Active, Succeeded, Partial, Failed. No "Stuck" tab in v1.
  • FR-010 (Canonical terminology): System MUST use “Operations” as the canonical label (no legacy naming on these surfaces).
  • FR-011 (Canonical links): “View run” links MUST always navigate to the canonical operation run detail view.
  • FR-012 (Calm UI rules): System MUST avoid polling/churn in modals and avoid refresh loops; background updates should be used only where clearly necessary. Auto-refresh on Dashboard and Operations index is allowed only while active runs (queued/running) exist, and MUST stop when there are no active runs.
  • FR-013 (Drift stale rule): System MUST flag drift as “stale” when the last drift scan is older than 7 days and surface it in “Needs Attention” with an investigation CTA.
  • FR-014 (Recent list sizing): System MUST show 10 rows by default for “Recent Drift Findings” and “Recent Operations”.

OperationRun status mapping (for tabs and KPIs)

OperationRun uses two canonical fields that drive UI filters:

  • status: execution lifecycle (e.g., queued/running/completed)
  • outcome: terminal result (e.g., succeeded/partially_succeeded/failed/cancelled)

Tab filters MUST map exactly as:

  • All: no status/outcome filter
  • Active: status IN (queued, running)
  • Succeeded: status = completed AND outcome = succeeded
  • Partial: status = completed AND outcome = partially_succeeded
  • Failed: status = completed AND outcome = failed

Notes:

  • No “Stuck” tab in v1.
  • Runs with outcome = cancelled appear under All only (unless a future “Cancelled” tab is added).
  • Any legacy status/outcome values must already be normalized before reaching this UI (out of scope for this feature).

KPI window definitions (timestamp basis)

All KPI windows are tenant-scoped and DB-only.

  • Total Runs (30 days): count OperationRuns by created_at within the last 30 days (includes all statuses/outcomes).
  • Active Runs: current count where status IN (queued, running) (no time window).
  • Failed/Partial (7 days): count terminal runs where status = completed AND outcome IN (failed, partially_succeeded) and completed_at is within the last 7 days.
  • Avg Duration (7 days): average of (completed_at - started_at) for runs where status = completed, started_at and completed_at are present, and completed_at is within the last 7 days.

Inventory coverage classification (Restorable/Partial/Risk/Dependencies)

Coverage chips and KPI aggregation MUST derive from the existing “policy type meta” and dependency capability signals (DB-only):

  • inventory_items.policy_type
  • config('tenantpilot.supported_policy_types') meta fields:
    • restore (e.g., enabled / preview-only)
    • risk (e.g., medium / medium-high / high)
  • Dependency support computed via the existing coverage dependency resolver (based on contracts/config).

Definitions:

  • Restorable: inventory items whose policy type meta has restore = enabled
  • Partial: inventory items whose policy type meta has restore = preview-only
  • Risk: inventory items whose policy type meta has risk IN (medium-high, high)
  • Dependencies: inventory items whose policy type supports dependencies per the existing dependency capability resolver

Notes:

  • This feature does not redefine coverage semantics; it standardizes UI rendering and KPI aggregation based on the existing policy type meta.
  • If a policy type is unknown/missing meta, it MUST be treated conservatively (non-restorable) for KPI aggregation.

Assumptions:

  • Drift findings, inventory items, and operation runs already exist as tenant-scoped data sources.
  • “Coverage %” is Restorable/Total; Partial is shown separately (e.g., chips/secondary metric). If total is 0, coverage shows as not available.
  • “Drift stale” default threshold is 7 days.
  • “Recent” list default size is 10.
  • Auto-refresh behavior (DB-only): Dashboard and Operations index auto-refresh only while active runs exist; otherwise it stops.
  • Creating/generating drift is out of scope unless it can be performed as an explicit, enqueue-only user action that results in an operation run.

Key Entities (include if feature involves data)

  • Tenant: The scope boundary for all dashboards and lists in this feature.
  • Operation Run: A tenant-scoped record of work execution, including status, timestamps, and outcomes used for Operations KPIs and recent lists.
  • Drift Finding: A tenant-scoped record representing detected drift, including severity and state (open/closed) used for Dashboard KPIs and “Needs Attention”.
  • Inventory Item: A tenant-scoped record representing inventory coverage and totals used in the Inventory hub.

Success Criteria (mandatory)

Measurable Outcomes

  • SC-001 (Task speed): A tenant admin can reach “what needs attention” (drift/ops) from the dashboard within 30 seconds.
  • SC-002 (Discoverability): A tenant admin can find inventory sync runs within 2 clicks from Inventory.
  • SC-003 (Triage efficiency): A tenant admin can filter Operations to “Active” or “Failed” within 1 click and identify a run to investigate within 60 seconds.
  • SC-004 (Calm UI): No refresh loops are observed on Dashboard, Inventory hub pages, or Operations index during normal navigation.
  • SC-005 (Safety): Viewing Dashboard, Inventory hub pages, and Operations index does not trigger any outbound HTTP.