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
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
CustomerHealthsupport namespace that derives workspace-health summaries from existing onboarding, provider, telemetry,OperationRun, findings, and review-pack truth. - Reuse the existing
/systemdashboard, 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:
- Onboarding readiness — point-in-time signal derived from existing onboarding-readiness truth
- Provider connection health — point-in-time signal derived from existing provider consent and verification truth
- Operational stability — windowed signal derived from failed and stuck
OperationRuntruth using the selectedSystemConsoleWindow - Governance pressure — point-in-time signal derived from active high-severity, overdue, or exception-warning findings truth
- Review-pack readiness — narrow mixed signal derived from recent review-pack request context plus the latest relevant
ReviewPackstate - 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
SystemHealthlevels 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-
okdimension 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
SystemHealthbadges - 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.mdapplies toCustomerHealthTopWorkspaces; 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, existingFindingtruth, existingReviewPacktruth, and platform-plane system link helpers - Shared abstractions reused: dashboard widget composition, dashboard time-window semantics,
BadgeRendererforSystemHealth, 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
CustomerHealthsupport 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
CustomerHealthnamespace. - 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
SystemHealthlevel, 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-featureonly 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-001andABSTR-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:assetsremains 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
/systemdashboard 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.phpexport 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-splitif 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
/systemdashboard 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\CustomerHealthDimensionCatalogas the single source for first-slice dimension labels, order, and level precedence rules. - Add
App\Support\CustomerHealth\WorkspaceHealthSummaryQueryto derive per-workspace summaries from existing onboarding, provider, telemetry, operations, findings, and review-pack truth. - Add
App\Filament\System\Widgets\CustomerHealthKpisfor aggregate health counts. - Add
App\Filament\System\Widgets\CustomerHealthTopWorkspacesplus 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\Dashboardand reuse existing system-safe link helpers only.
Implementation Phases
- Foundation: fixed dimension catalog + derived workspace summary query with core unknown-handling and review-pack-readiness rules + unit proof
- Dashboard summary: aggregate KPI widget + system dashboard registration + feature proof
- Attention list: compact unhealthy-workspace widget + system-safe links + explainability proof
- Detail follow-up: residual system tenant/workspace detail card + preserved window context + follow-up rendering proof
- 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.