Health
Experience
How the product behaves, key flows and every state a PM or engineer can meet. Behaviour over decoration.
If a screen doesn’t help find sellable upgrades, size sell hours, hide inactive projects, or refresh, it doesn’t belong.
01
Key flows
01 · Arrive
02 · Inspect
03 · Refresh
02
Flow · Arrive
Step 1
Land on dashboard
Shared chrome: HealthLogo + quiet desktop tagline; work nav Overview · Opportunities · Dashboard · Settings; quiet Brand OS → /brand-os. One dark system. Operator UI English; report EN|NL on Upgrade.
Step 2
Read from cache
List loads from the last snapshot. Freshness lives on Refresh (hover/focus title), not a permanent header clock.
Step 3
Triage
Default sort is worst-first. Filters: All, Needs attention, Healthy, Hidden. Search by name.
Do
Show cached risk immediately so operators can act before Refresh.
Don’t
Don’t block the whole UI on a full GitHub scan on every visit.
03
Flow · Inspect
Step 1
Scan the row
Identity, Runtime, Security, chevron. One severity rail on the card edge. Long-past-EOL stacks get a caption, not a wash.
Step 2
Expand
Chevron rotates in place. Panel opens with Runtime, Security, and Upgrade (when EOL applies). Motion is feedback only.
Step 3
Act
Tune rate, effort, portfolio weight (Auto or Custom), copy update report, or share to Slack. Hide sits on Dashboard cards and Opportunities rows (no expand); Unhide via Hidden filter. Method: /estimate-method.
04
Flow · Refresh
Step 1
Operator clicks Refresh
On the filter strip under the Dashboard title/lead, text-only, same height as search. Hover/focus shows last refreshed time and that a full org scan can take several minutes. Goes busy while scanning.
Step 2
Phases
Repos (GitHub) → Runtime (endoflife.date) → Alerts (Dependabot). Active phase blinks.
Step 3
Settle
Snapshot writes. List may reveal-in. Refresh briefly shows Updated, then settles. Absolute time stays on the Refresh title.
Phase strip (specimen)
05
States
Unauthenticated
When
Auth configured and no valid session cookie.
UI
Redirect to /login (pages) or 401 JSON (APIs). Digest cron exempt.
Loading · repos
When
First load or empty cache while fetching the org list.
UI
Loading repositories from GitHub…
Loading · scan
When
Refresh is running runtime + security phases.
UI
Checking runtime and security… Sweep bar + phase strip.
Empty · filter
When
Search or filter matches nothing.
UI
“No projects match” + clear search when a query is set.
Error · fetch
When
API fails or returns non-JSON.
UI
Could not load dashboard + recovery hint (token / restart).
Partial · security
When
Dependabot or posture data unavailable.
UI
Security section explains unavailability; rest of card still usable.
Partial · runtime
When
EOL check unavailable or no runtimes detected.
UI
Runtime section says so plainly; Refresh to retry.
Success · copy / Slack
When
Clipboard or Slack share completes.
UI
Button label → Copied / Posted + one-shot confirm motion.
Restricted · Slack
When
Slack not configured or rate missing.
UI
Share disabled with clear title; never a silent no-op.
Long past EOL
When
EOL age is 2+ years past the milestone.
UI
Caption “Long past EOL” under the title, no field wash.
Healthy portfolio
When
Filter Healthy or clear security + supported runtimes.
UI
Same list chrome; healthy severity colour on status text only.
06
Interaction principles
Risk first
Deliberate refresh
Expand on demand
Still when idle
prefers-reduced-motion.Do
Design empty, loading, error, and partial before polish on the happy path.
Don’t
Don’t invent a fifth primary flow or decorative motion that delays status.