TenantAtlas/specs/245-customer-health-score/plan.md
ahmido 86505483bf
Some checks failed
Main Confidence / confidence (push) Failing after 52s
feat(customer-health): add decision card to tenant/workspace detail (spec 245) (#283)
Add Customer Health decision card to tenant & workspace detail pages (spec 245).

What I changed:
- Render a decision-first Customer Health card on tenant and workspace detail pages.
- Reuse `WorkspaceHealthSummaryQuery` and preserve `window` query param.
- Update attention widget link text to "Review health details" and include `?window=`.
- Add/adjust tests to cover new behavior and explainability.
- Run Pint formatting.

Compare URL: https://git.cloudarix.de/ahmido/TenantAtlas/compare/dev...245-customer-health-score

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #283
2026-04-27 08:30:01 +00:00

18 KiB

Implementation Plan: Customer Health Score

Branch: 245-customer-health-score | Date: 2026-04-27 | Spec: /Users/ahmeddarrazi/Documents/projects/wt-plattform/specs/245-customer-health-score/spec.md Input: Feature specification from /Users/ahmeddarrazi/Documents/projects/wt-plattform/specs/245-customer-health-score/spec.md

Summary

  • Add one bounded CustomerHealth support namespace that derives workspace-health summaries from existing onboarding, provider, telemetry, OperationRun, findings, and review-pack truth.
  • Reuse the existing /system dashboard, existing dashboard window selector, and existing system widget family to show both aggregate health counts and an attention-needed workspace list.
  • Reuse the existing system tenant and workspace residual detail pages as decision-first follow-up surfaces by adding one read-only customer-health card above the existing diagnostics.
  • Keep the slice derived-only, Livewire v4-compatible, Filament v5-native, and free of migrations, new persistence, new panel providers, global-search changes, destructive actions, or asset changes.

Technical Context

Language/Version: PHP 8.4 (Laravel 12)
Primary Dependencies: Laravel 12 + Filament v5 + Livewire v4 + Pest; existing ProductTelemetrySummaryQuery, ProviderConnectionStateProjector, StuckRunClassifier, BadgeRenderer, SystemConsoleWindow, system directory links, and system operations links
Storage: N/A - no new persisted health truth
Testing: Pest unit + feature tests only
Validation Lanes: fast-feedback, confidence
Target Platform: Sail-backed Laravel admin panel under /system
Project Type: web
Performance Goals: derive health summaries inline for the current system dashboard without background jobs, remote calls, or broad per-tenant page reconstruction
Constraints: no new score table, no customer-facing surface, no CRM or billing workflow expansion, no direct /admin deep links, and no new badge taxonomy
Scale/Scope: six fixed first-slice dimensions, one derived summary query, two dashboard widgets, one shared decision-first follow-up card on the existing system detail pages, and focused unit plus feature proof only

First-Slice Dimension Inventory

The first slice is locked to these six dimensions only:

  1. Onboarding readiness — point-in-time signal derived from existing onboarding-readiness truth
  2. Provider connection health — point-in-time signal derived from existing provider consent and verification truth
  3. Operational stability — windowed signal derived from failed and stuck OperationRun truth using the selected SystemConsoleWindow
  4. Governance pressure — point-in-time signal derived from active high-severity, overdue, or exception-warning findings truth
  5. Review-pack readiness — narrow mixed signal derived from recent review-pack request context plus the latest relevant ReviewPack state
  6. Engagement freshness — windowed signal derived from existing product_usage_events

Any change to this dimension inventory requires an explicit spec update before implementation expands or swaps the slice.

Health-Level Resolution Rules

  • Reuse existing SystemHealth levels only: ok, warn, critical, unknown
  • Overall level precedence is fixed for v1: critical > warn > unknown > ok
  • Windowed dimensions must honor the selected dashboard time window
  • Point-in-time dimensions must not pretend to share the same time basis as windowed dimensions
  • Missing or stale source truth must remain explicit and must never silently collapse to ok
  • Archived workspaces are excluded from active counts, and archived tenants do not contribute source truth into active workspace-health derivation
  • Attention-needed workspace ordering is fixed for v1: overall severity desc, non-ok dimension count desc, workspace name asc, then workspace id asc

Review-Pack Readiness Rule

Recent means the currently selected dashboard window. Request context comes from existing review-pack request telemetry when available, falling back to a ReviewPack created for the same workspace inside that same window.

Condition Review-pack readiness level Notes
No request context and no relevant pack activity in the selected window unknown The first slice does not infer periodic review obligations from silence
Request context exists in the selected window and no relevant pack is usable yet warn Covers queued, running, or not-yet-materialized review-pack generation
Latest relevant pack in the selected window is ready and not expired ok A usable recent review pack exists
Latest relevant pack in the selected window is failed or expired critical Recent review-pack work ended unusably and needs attention

UI / Surface Guardrail Plan

  • Guardrail scope: changed surfaces
  • Native vs custom classification summary: native Filament dashboard plus existing custom system widget family and the existing residual system detail pages
  • Shared-family relevance: dashboard signals/cards, compact attention list, system-safe deep links, existing SystemHealth badges
  • State layers in scope: page, widget, window query, detail-link state, residual detail follow-up state
  • Handling modes by drift class or surface: review-mandatory
  • Repository-signal treatment: review-mandatory
  • Special surface test profiles: standard-native-filament
  • Required tests or manual smoke: functional-core, state-contract, follow-up-detail smoke
  • Exception path and spread control: none
  • Active feature PR close-out entry: Guardrail
  • List-surface review standard: /Users/ahmeddarrazi/Documents/projects/wt-plattform/docs/product/standards/list-surface-review-checklist.md applies to CustomerHealthTopWorkspaces; accepted compact-widget exceptions are no persistence trio, no bulk actions, no row click, and no empty-state CTA

Shared Pattern & System Fit

  • Cross-cutting feature marker: yes
  • Systems touched: App\Filament\System\Pages\Dashboard, App\Filament\System\Pages\Directory\ViewTenant, App\Filament\System\Pages\Directory\ViewWorkspace, App\Filament\System\Widgets\ControlTowerHealthIndicator, App\Filament\System\Widgets\ControlTowerTopOffenders, App\Support\ProductTelemetry\ProductTelemetrySummaryQuery, App\Services\Providers\ProviderConnectionStateProjector, App\Support\SystemConsole\StuckRunClassifier, existing Finding truth, existing ReviewPack truth, and platform-plane system link helpers
  • Shared abstractions reused: dashboard widget composition, dashboard time-window semantics, BadgeRenderer for SystemHealth, system-safe link helpers, existing residual system detail pages, and existing source-truth owners for provider, telemetry, operations, findings, and review packs
  • New abstraction introduced? why?: one bounded CustomerHealth support namespace with a fixed dimension catalog and one derived summary query is justified because the repo already has isolated source truths but no shared workspace-health derivation path
  • Why the existing abstraction was sufficient or insufficient: existing abstractions are sufficient for rendering and navigation but insufficient for deriving one explainable cross-domain workspace summary
  • Bounded deviation / spread control: no page-local health arithmetic, no second badge language, no persistence, and no customer-success surface. All health derivation must converge on the single CustomerHealth namespace.
  • Platform-safe link contract: every attention-needed workspace row must expose exactly one platform-safe next link. If a more specific platform-plane route is not appropriate, the row falls back to the existing system tenant-detail context instead of rendering with no link. Dashboard links into residual detail pages must preserve the selected time window so the follow-up card explains the same top drivers as the dashboard.

OperationRun UX Impact

  • Touches OperationRun start/completion/link UX?: no
  • Central contract reused: N/A
  • Delegated UX behaviors: N/A
  • Surface-owned behavior kept local: N/A
  • Queued DB-notification policy: N/A
  • Terminal notification path: N/A
  • Exception path: none

Provider Boundary & Portability Fit

  • Shared provider/platform boundary touched?: yes
  • Provider-owned seams: provider consent and verification outcomes on ProviderConnection
  • Platform-core seams: workspace-health dimensions, overall SystemHealth level, dominant reason labels, dashboard widget copy, and system-safe next links
  • Neutral platform terms / contracts preserved: customer health, workspace health, attention needed, operational stability, engagement freshness, review readiness
  • Retained provider-specific semantics and why: Microsoft verification and consent states remain provider-owned inputs because they are existing current-release truth already modeled on provider connections
  • Bounded extraction or follow-up path: document-in-feature only if one provider-specific dominant reason still needs text-only explanation instead of a platform-safe link in v1

Constitution Check

GATE: Must pass before implementation begins. Re-check after design changes.

  • Inventory-first / snapshots-second: PASS - the slice derives health from existing observed truths only
  • Read/write separation: PASS - the slice is read-only
  • Graph contract path: PASS - no new Graph calls are added
  • RBAC-UX / workspace isolation / tenant isolation: PASS - system dashboard access rules remain authoritative and no tenant/admin viewer is introduced
  • Shared pattern reuse / XCUT-001: PASS - dashboard widget family, badge semantics, and system link helpers are reused explicitly
  • Proportionality / PROP-001 and ABSTR-001: PASS - one bounded derived-query path is the narrowest reusable solution
  • Persisted truth / PERSIST-001: PASS - no new persistence is introduced
  • UI semantics / UI-SEM-001: PASS - the slice adds decision-support summaries only and does not create a new workflow hub
  • Filament-native UI / UI-FIL-001: PASS - the operator-facing impact stays on the existing dashboard widget family
  • Livewire v4 / Filament v5: PASS - the slice remains fully inside the current Filament v5 + Livewire v4 stack
  • Provider registration location: PASS - no provider registration changes are introduced; Laravel 11+ provider registration stays in bootstrap/providers.php
  • Global search rule: PASS - no resource or global-search changes are introduced
  • Destructive actions: PASS - none added
  • Asset strategy: PASS - no new assets are required, so deployment behavior for filament:assets remains unchanged
  • Test governance / TEST-GOV-001: PASS - proof remains in focused unit + feature lanes only

Test Governance Check

  • Test purpose / classification by changed surface: Unit for dimension rules and summary derivation; Feature for /system dashboard rendering, explainability, authorization, and residual detail follow-up rendering
  • Affected validation lanes: fast-feedback, confidence
  • Why this lane mix is the narrowest sufficient proof: the slice is server-driven, read-only, and widget-based; browser automation would duplicate what focused unit and feature tests already prove
  • Narrowest proving command(s):
    • export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Unit/Support/CustomerHealth/CustomerHealthDimensionCatalogTest.php tests/Unit/Support/CustomerHealth/WorkspaceHealthSummaryQueryTest.php
    • export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/System/CustomerHealth/CustomerHealthDashboardWidgetsTest.php tests/Feature/System/CustomerHealth/CustomerHealthExplainabilityTest.php tests/Feature/System/CustomerHealth/CustomerHealthAuthorizationTest.php
  • Fixture / helper / factory / seed / context cost risks: reuse existing workspaces, tenants, provider connections, onboarding sessions, telemetry events, operation runs, findings, and review packs; avoid browser setup and avoid new heavy support fixtures
  • Expensive defaults or shared helper growth introduced?: no
  • Heavy-family additions, promotions, or visibility changes: none
  • Surface-class relief / special coverage rule: standard-native-filament relief
  • Closing validation and reviewer handoff: reviewers should confirm unknown handling, dominant reason explainability, platform-safe linking, absence of new persistence, and explicit selected-window semantics
  • Disclosure-ladder proof: reviewers should also confirm that default-visible dashboard content stays operator-first, raw or support-grade detail remains behind linked surfaces, and the two widgets do not duplicate the same visible decision summary
  • Budget / baseline / trend follow-up: none expected beyond ordinary feature-local upkeep
  • Review-stop questions: did the implementation add a persisted score model, customer-facing route, or second badge taxonomy; do unknown and recent-review-pack-request cases degrade honestly?
  • Escalation path: reject-or-split if the slice expands into CRM, billing, predictive scoring, or a new portfolio workbench page
  • Active feature PR close-out entry: Guardrail
  • Test-governance outcome: keep

Project Structure

Documentation (this feature)

specs/245-customer-health-score/
├── checklists/
│   └── requirements.md
├── spec.md
├── plan.md
└── tasks.md

Source Code (repository root)

apps/platform/
├── app/
│   ├── Filament/
│   │   └── System/
│   │       ├── Pages/
│   │       │   ├── Dashboard.php
│   │       │   └── Directory/
│   │       │       ├── ViewTenant.php
│   │       │       └── ViewWorkspace.php
│   │       └── Widgets/
│   │           ├── CustomerHealthKpis.php
│   │           └── CustomerHealthTopWorkspaces.php
│   ├── Services/
│   │   └── Providers/ProviderConnectionStateProjector.php
│   └── Support/
│       ├── CustomerHealth/
│       │   ├── CustomerHealthDimensionCatalog.php
│       │   └── WorkspaceHealthSummaryQuery.php
│       ├── ProductTelemetry/ProductTelemetrySummaryQuery.php
│       └── SystemConsole/
├── resources/views/filament/system/pages/directory/
│   ├── view-tenant.blade.php
│   └── view-workspace.blade.php
├── resources/views/filament/system/widgets/customer-health-top-workspaces.blade.php
└── tests/
    ├── Unit/Support/CustomerHealth/
    │   ├── CustomerHealthDimensionCatalogTest.php
    │   └── WorkspaceHealthSummaryQueryTest.php
    └── Feature/System/CustomerHealth/
    ├── CustomerHealthDetailDecisionTest.php
        ├── CustomerHealthAuthorizationTest.php
        ├── CustomerHealthDashboardWidgetsTest.php
        └── CustomerHealthExplainabilityTest.php

Structure Decision: Single Laravel web application. The implementation adds one bounded support namespace and two dashboard widgets only.

Complexity Tracking

No constitution violations are required. The slice adds one derived summary path only and introduces no new persistence, no new state family, and no new workflow surface.

Rollout & Risk Controls

  • Start on the existing /system dashboard only; do not introduce a new portfolio or customer-health page in v1.
  • Keep the first-slice dimension inventory fixed at six. Any added dimension requires a spec update.
  • Keep review-pack readiness narrow and operational, not programmatic or compliance-heavy.
  • Use explicit copy or visual grouping to distinguish point-in-time signals from windowed signals.
  • Guarantee one platform-safe next link per attention-needed workspace row by falling back to the existing system tenant-detail context when a more specific platform-plane route is not appropriate.

Implementation Outline

  • Add App\Support\CustomerHealth\CustomerHealthDimensionCatalog as the single source for first-slice dimension labels, order, and level precedence rules.
  • Add App\Support\CustomerHealth\WorkspaceHealthSummaryQuery to derive per-workspace summaries from existing onboarding, provider, telemetry, operations, findings, and review-pack truth.
  • Add App\Filament\System\Widgets\CustomerHealthKpis for aggregate health counts.
  • Add App\Filament\System\Widgets\CustomerHealthTopWorkspaces plus a small Blade view for the attention-needed list.
  • Add one read-only customer-health decision card to the existing system tenant and workspace residual detail pages using the existing summary query and the same dominant drivers as the dashboard.
  • Register both widgets on the existing App\Filament\System\Pages\Dashboard and reuse existing system-safe link helpers only.

Implementation Phases

  1. Foundation: fixed dimension catalog + derived workspace summary query with core unknown-handling and review-pack-readiness rules + unit proof
  2. Dashboard summary: aggregate KPI widget + system dashboard registration + feature proof
  3. Attention list: compact unhealthy-workspace widget + system-safe links + explainability proof
  4. Detail follow-up: residual system tenant/workspace detail card + preserved window context + follow-up rendering proof
  5. Safety hardening: archived workspace plus tenant exclusions, deterministic ordering hardening, and authorization proof

Constitution Check (Post-Design)

Re-check result: PASS. The plan stays derived-only, reuses existing system dashboard and health presentation semantics, keeps Filament v5 + Livewire v4 intact, leaves provider registration in bootstrap/providers.php untouched, introduces no global-search or asset changes, adds no destructive actions, and keeps proof in narrow unit + feature lanes only.