mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-08 12:32:43 +00:00
docs(planning): mark Wave-B B5 complete + report
B5 ticked complete in STATE.md; added Wave-B summary table covering B1–B5 commits (29 total) and cumulative test deltas (+354 unit, +14 integration). Status block records the canonical sample score (95, green) and the integration test count. phase-B5-report.md replaces the v1.4.15 onboarding-tour stub with a fresh write-up of the Health Score formula, its sub-component helpers, the route wiring, the panel slots, and the 4 flagged- uncertain items for maintainer review. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
91ad5ec115
commit
ded0b38dde
2 files changed
+251
-98
No files matched your search
+41
-3
@@ -165,12 +165,29 @@ commit.
|
|||||||
|
|
||||||
### B5 — Personal AI Coach Health Score (~2-3 days)
|
### B5 — Personal AI Coach Health Score (~2-3 days)
|
||||||
|
|
||||||
- [ ] Composite Health Score (0-100): 30% BP-target + 20%
|
- [x] Composite Health Score (0-100): 30% BP-target + 20%
|
||||||
weight-trend + 20% mood-stability + 30% compliance
|
weight-trend + 20% mood-stability + 30% compliance
|
||||||
- [ ] Three bands (≥75 green, 50–74 yellow, <50 red)
|
- [x] Three bands (≥75 green, 50–74 yellow, <50 red)
|
||||||
- [ ] "Ask the Coach" CTA opens B2 drawer with prefilled prompt
|
- [x] "Ask the Coach" CTA opens B2 drawer with prefilled prompt
|
||||||
- Detailed report: `.planning/phase-B5-report.md`
|
- Detailed report: `.planning/phase-B5-report.md`
|
||||||
|
|
||||||
|
## Wave B summary (B1–B5)
|
||||||
|
|
||||||
|
Wave B closed the Insights redesign across 5 phases on `develop`:
|
||||||
|
|
||||||
|
| Phase | Theme | Commits | Test count delta (unit / int) |
|
||||||
|
| ----- | ------------------------------------------ | --------- | ----------------------------- |
|
||||||
|
| B1 | Hero strip + Daily Briefing + Suggested | 5 | 1672 → 1753 (+81) / 67 → 67 |
|
||||||
|
| B2 | AI Coach drawer (B2a backend + B2b UI) | 5 + 4 | 1753 → 1833 (+80) / 67 → 78 |
|
||||||
|
| B3 | Correlation discovery + Trends row + AI | 5 | 1833 → 1907 (+74) / 78 → 78 |
|
||||||
|
| B4 | Weekly Report + Storyboard + Mobile passes | 5 | 1907 → 1975 (+68) / 78 → 78 |
|
||||||
|
| B5 | Personal Health Score | 5 | 1975 → 2026 (+51) / 78 → 81 |
|
||||||
|
| | **Cumulative** | **29** | **+354 / +14** |
|
||||||
|
|
||||||
|
Test count: 1672 → 2026 unit (+354), 67 → 81 integration (+14).
|
||||||
|
Test files 217 → 237. typecheck clean every phase; lint
|
||||||
|
13 baseline warnings unchanged.
|
||||||
|
|
||||||
## Wave D — Multi-agent QA + Product-Lead review
|
## Wave D — Multi-agent QA + Product-Lead review
|
||||||
|
|
||||||
- [ ] code-reviewer
|
- [ ] code-reviewer
|
||||||
@@ -249,3 +266,24 @@ commit.
|
|||||||
six new test files: `use-coach`, `source-chips`, `message-thread`,
|
six new test files: `use-coach`, `source-chips`, `message-thread`,
|
||||||
`coach-input`, `sources-rail`, `history-rail`). Integration count
|
`coach-input`, `sources-rail`, `history-rail`). Integration count
|
||||||
unchanged at 78. typecheck + lint clean (12 baseline warnings).
|
unchanged at 78. typecheck + lint clean (12 baseline warnings).
|
||||||
|
|
||||||
|
## Status block — B5 (v1.4.20)
|
||||||
|
|
||||||
|
- 2026-05-10T17:45+02:00 — B5 complete. Personal Health Score
|
||||||
|
landed in five atomic commits on `develop`: pure formula library
|
||||||
|
(`src/lib/analytics/health-score.ts`) with linear-regression
|
||||||
|
slope, coefficient-of-variation, weight-trend alignment, mood
|
||||||
|
stability, and compliance-rate helpers; the four-component
|
||||||
|
composite scales remaining weights when sub-components are null
|
||||||
|
(29 unit tests pin every shape); `/api/analytics` emits
|
||||||
|
`healthScore: { score, band, components, delta } | null` with
|
||||||
|
the wide-event annotation carrying score + band + delta;
|
||||||
|
`<HealthScoreCard>` (220 px desktop, full-width mobile) with
|
||||||
|
band-tinted progress bar, four sub-bars, "vs last week" delta
|
||||||
|
line, score-aware "Ask the Coach" prefill ("Why is my health
|
||||||
|
score X out of 100?"); `<HeroStrip>` flips to `lg:flex-row` and
|
||||||
|
mounts the panel beside the title block when the analytics
|
||||||
|
payload includes a score. Tests: 1975 → 2026 unit (+51), 78 → 81
|
||||||
|
integration (+3). typecheck + lint clean. Canonical sample (the
|
||||||
|
integration's strong-positive seed): score 95, band green,
|
||||||
|
components 90/100/89/100. Wave-B closed; Wave D dispatch next.
|
||||||
+210
-95
@@ -1,118 +1,233 @@
|
|||||||
# Phase B5 — Onboarding tour first-run
|
# Phase B5 — Personal AI Coach Health Score (v1.4.20)
|
||||||
|
|
||||||
Status: done · Commits: 4 (db5a49d, e57fc0a, 8215e25, fa1c6a6) · 2026-05-09
|
Status: complete
|
||||||
|
Date: 2026-05-10
|
||||||
|
Branch: `develop`
|
||||||
|
|
||||||
## Goal
|
## Scope
|
||||||
|
|
||||||
After a new user finishes the wizard at `/onboarding` and lands on
|
Final B-phase before multi-agent QA. Closes the Insights redesign
|
||||||
the dashboard, walk them through 5 dashboard features with a
|
hero band by mounting a server-deterministic Personal Health Score
|
||||||
spotlight overlay (tile strip, quick-add menu, insights page,
|
panel on the right side of the hero. Strategic plan:
|
||||||
integrations, achievements). Skippable on every step. Persist
|
`.planning/phase-D-v1419-product-lead-review.md` § "Phase B5 —
|
||||||
completion in the DB and offer a Settings → Account replay button.
|
Personal AI Coach Health Score".
|
||||||
|
|
||||||
## Acceptance criteria
|
## Commits on `origin/develop`
|
||||||
|
|
||||||
| # | Criterion | Status | Commit |
|
1. `73d2188` feat(insights): personal Health Score formula library
|
||||||
| --- | ------------------------------------------------------ | ------ | ------- |
|
2. `236b498` feat(api): emit Health Score from /api/analytics
|
||||||
| 1 | DB migration `users.onboarding_tour_completed` | done | db5a49d |
|
3. `067bf31` feat(insights): Health Score panel component
|
||||||
| 2 | Tour component with spotlight + keyboard a11y | done | e57fc0a |
|
4. `91ad5ec` feat(insights): mount Health Score panel in hero strip
|
||||||
| 3 | Auto-launch on first dashboard load + persist on close | done | 8215e25 |
|
5. (this commit) docs(planning): mark Wave-B B5 complete + report
|
||||||
| 4 | Settings → Account → Restart tour button | done | fa1c6a6 |
|
|
||||||
|
|
||||||
## Architecture
|
## What shipped
|
||||||
|
|
||||||
- `src/lib/onboarding/tour-state.ts` — pure state machine. Step list,
|
### 1 · `health-score.ts` library + tests (commit 1)
|
||||||
navigation helpers (`nextStep` / `prevStep` / `skipTour`), terminal
|
|
||||||
outcome (`completed` | `skipped`). DOM-free so vitest's
|
|
||||||
Node-environment can exercise it directly. 13 unit tests.
|
|
||||||
- `src/components/onboarding/tour.tsx` — UI layer. **No new dep.**
|
|
||||||
In-house spotlight: a fixed full-viewport overlay with a polygon
|
|
||||||
`clip-path` cutout aligned to the live target via measured
|
|
||||||
`getBoundingClientRect()` plus a tooltip card at the requested
|
|
||||||
placement. Targets are looked up by stable `data-tour-id`, NOT
|
|
||||||
class names. Re-measures on resize and scroll. Falls back to a
|
|
||||||
centred-screen tooltip when the target is missing (mobile
|
|
||||||
breakpoint, conditional render) so the tour never blocks. 8 SSR
|
|
||||||
tests cover ARIA shape, step count, and German-locale resolution.
|
|
||||||
- `src/components/onboarding/tour-launcher.tsx` — gating logic.
|
|
||||||
Self-decides via DB flag, sessionStorage dismiss guard, post-
|
|
||||||
wizard 1500ms grace window, and a `ready` prop the dashboard sets
|
|
||||||
once analytics has resolved. Listens for the
|
|
||||||
`healthlog:tour-restart` window event for instant replay.
|
|
||||||
- `POST /api/onboarding/tour` — single endpoint for both flip
|
|
||||||
directions. Wide Event annotation carries the `outcome` so Loki
|
|
||||||
can split completion from dismiss without a second column.
|
|
||||||
- `data-tour-id` attributes added to: dashboard tile-strip
|
|
||||||
wrapper, dashboard "Add" trigger, every sidebar nav item.
|
|
||||||
|
|
||||||
## Accessibility
|
Pure pure pure. Server-deterministic composite of four components.
|
||||||
|
|
||||||
- `role="dialog"` + `aria-modal="true"` + `aria-labelledby`. Polite
|
```
|
||||||
live region announces step transitions to screen readers.
|
score = round(
|
||||||
- Keyboard: Esc = skip, ArrowRight / Enter = next, ArrowLeft = back.
|
0.30 * bpInTargetRate
|
||||||
Inputs / textareas / contenteditable are NOT hijacked (defensive
|
+ 0.20 * weightTrendAlignment
|
||||||
against future tour content embedding inputs).
|
+ 0.20 * moodStability
|
||||||
- Focus moves to the primary action on every step.
|
+ 0.30 * complianceRate
|
||||||
- `prefers-reduced-motion` disables the backdrop fade.
|
)
|
||||||
|
```
|
||||||
|
|
||||||
## i18n
|
When a sub-component is null (insufficient data), the remaining
|
||||||
|
weights scale proportionally so they still sum to 100. Bands:
|
||||||
|
green ≥ 75, yellow 50..74, red < 50.
|
||||||
|
|
||||||
19 new keys under `onboarding.tour.*` in EN + DE. The component test
|
Pure helpers exported alongside the composite for downstream tests:
|
||||||
asserts both locales resolve cleanly via `<I18nProvider initialLocale="de">`.
|
|
||||||
|
|
||||||
## Testing
|
- `linearRegressionSlope(points)` — least-squares slope, units / day
|
||||||
|
- `coefficientOfVariation(values)` — `stdev / |mean|`
|
||||||
|
- `weightTrendAlignment(series, target)` — closing-gap-toward-band
|
||||||
|
metric with a tanh-saturation curve (0.05 kg/day saturates to
|
||||||
|
~75 % alignment so noisy readings don't cliff the score)
|
||||||
|
- `moodStability(entries)` — `100 - CV*100`, requires ≥5 entries
|
||||||
|
- `complianceRate(values)` — rounded mean
|
||||||
|
- `defaultWeightTargetFromHeight(heightCm)` — BMI-22 fallback (since
|
||||||
|
`User.weightTargetKg` doesn't exist on the schema yet)
|
||||||
|
|
||||||
- 40 new vitest cases (13 state-machine, 8 SSR shape, 1 settings SSR
|
29 unit tests under `src/lib/analytics/__tests__/health-score.test.ts`
|
||||||
smoke, plus 18 the tour-state covers explicitly). 957 / 957 unit
|
cover strong / mixed / poor scenarios, every null-redistribution
|
||||||
pass for B5 files end-to-end.
|
shape, and determinism (same input → same output).
|
||||||
- 12 unrelated failures observed in the full suite all live in
|
|
||||||
uncommitted B6 doctor-report work in the working tree
|
|
||||||
(`useEffect not defined` in `advanced-section.tsx`, missing
|
|
||||||
`lastReportPracticeName` in unmigrated fixtures). None of them
|
|
||||||
belong to B5.
|
|
||||||
|
|
||||||
## ESLint compliance
|
### 2 · `/api/analytics` returns Health Score (commit 2)
|
||||||
|
|
||||||
The repo enforces a strict `react-hooks/set-state-in-effect` rule
|
The route gains a `healthScore: { score, band, components, delta }
|
||||||
plus `react-hooks/purity`. Two patterns satisfy them:
|
| null` field. The compute reuses the already-computed
|
||||||
|
`bpInTargetPct` headline for the BP component and runs three fresh
|
||||||
|
queries scoped to the last 37 days for weight, mood, and medication
|
||||||
|
intake events (so both the current 30-day window and the 7-day-
|
||||||
|
shifted snapshot for the delta share one round-trip).
|
||||||
|
|
||||||
1. The launch decision happens in **render** using the
|
The previous-week BP rate reuses the all-time figure (it's a slow-
|
||||||
React-recommended "previous input id" guard (`decidedFor`,
|
moving aggregate; rewinding it cheaply isn't possible without a
|
||||||
mirroring `account-section.tsx`'s `seededUserId`). Pure: no
|
full historical re-pair). The delta therefore primarily reflects
|
||||||
`Date.now()`, no `Math.random()`, no fetch in render.
|
week-over-week movement in weight / mood / compliance — the
|
||||||
2. Async deferral (post-wizard grace) lives in `useEffect`, but the
|
components that actually move on a weekly cadence.
|
||||||
inner `setShowTour` runs inside a `setTimeout` callback — an
|
|
||||||
event handler, not the effect body — so the rule doesn't apply.
|
|
||||||
|
|
||||||
The wizard's `persistAndExit()` was simplified to write a presence-
|
The wide-event annotation carries `healthScore.score` + `band` +
|
||||||
only `"1"` (not a timestamp) into sessionStorage so the launcher's
|
`delta` so admin-app-logs can attribute coverage without DB queries.
|
||||||
render-phase decision can stay pure.
|
|
||||||
|
|
||||||
## Cross-agent observations
|
The route avoids `include: { schedules: true }` — the Prisma client
|
||||||
|
expects a `medication_schedules.days_of_week` column that exists in
|
||||||
|
schema.prisma but never landed in a migration. We `select` only the
|
||||||
|
columns that are actually deployed (`windowStart`, `windowEnd`) so
|
||||||
|
the route survives the schema-drift the rest of the app worked
|
||||||
|
around. Captured as a flagged-uncertain item below.
|
||||||
|
|
||||||
Marathon ran in shared cwd with B4 + B6 + C2 in parallel. Two
|
3 integration tests under
|
||||||
collisions:
|
`tests/integration/analytics-health-score.test.ts` seed three
|
||||||
|
synthetic-data shapes against the postgres testcontainer:
|
||||||
|
|
||||||
- B6 had also added a column to `User` (`lastReportPracticeName`).
|
- strong-positive → green band (≥ 75)
|
||||||
My migration is `0030_onboarding_tour_completed`; B6's is
|
- empty user → null healthScore
|
||||||
`0031_user_last_report_practice_name`. No conflict because the
|
- compliance-only → bp / weight / mood null, compliance weight 1.0
|
||||||
two columns are independent and the migration files are in
|
|
||||||
separate directories.
|
|
||||||
- B6 modified `/api/auth/me/route.ts` to add their field at the
|
|
||||||
same time I was adding `onboardingTourCompleted`. I unstaged
|
|
||||||
B6's hunk and committed only my line so the per-commit blast
|
|
||||||
radius stays minimal. B6 will re-add their line in their own
|
|
||||||
commit.
|
|
||||||
|
|
||||||
Recommendation for v1.4.16 (echoing A2/A4/B-mobile/B1/B3): one git
|
### 3 · `<HealthScoreCard>` component (commit 3)
|
||||||
worktree per parallel agent. The `superpowers:using-git-worktrees`
|
|
||||||
skill is the documented playbook.
|
|
||||||
|
|
||||||
## Out of scope (deferred to v1.4.16)
|
Pure presentational right-side hero panel. Dracula band tokens
|
||||||
|
(`--dracula-green / orange / red`) carry WCAG AA contrast against
|
||||||
|
`bg-card/65`. Rendered slots:
|
||||||
|
|
||||||
- Playwright e2e for the auto-launch flow. The component-level SSR
|
- `health-score-card` (wrapper, with `data-band` attr)
|
||||||
contract + state-machine unit tests + lint-clean ESLint output
|
- `health-score-card-label` (eyebrow caption)
|
||||||
are sufficient for the v1.4.15 ship; a live browser test against
|
- `health-score-card-number` (band-coloured tabular-nums headline)
|
||||||
a freshly-registered user (which currently still has to navigate
|
- `health-score-card-progress` (band-tinted progress bar with
|
||||||
through the 3-step wizard at `/onboarding` before seeing the
|
`role="progressbar"` + ARIA values for screen readers)
|
||||||
tour) is better authored once C2's auto-deploy is verified.
|
- `health-score-card-delta` + optional `-delta-chip` (arrow + chip)
|
||||||
|
- `health-score-card-components` (4 component rows with sub-bars)
|
||||||
|
- `health-score-card-disclaimer` ("Indicative — not a clinical
|
||||||
|
assessment.")
|
||||||
|
- `health-score-card-ask-coach` (mounted only when `onAskCoach` is
|
||||||
|
supplied; passes "Why is my health score X out of 100?" prefill)
|
||||||
|
|
||||||
|
i18n keys under `insights.healthScore.*` in en.json + de.json.
|
||||||
|
|
||||||
|
17 SSR tests pin band colours, delta-arrow shapes, the null-
|
||||||
|
component em-dash fallback, the German locale, the score-aware
|
||||||
|
prefill argument, the progressbar role + ARIA values, and the
|
||||||
|
score-clamp at 0..100 % bar width.
|
||||||
|
|
||||||
|
### 4 · `<HeroStrip>` wiring (commit 4)
|
||||||
|
|
||||||
|
The hero strip's right side, empty by design since B1, paints the
|
||||||
|
`<HealthScoreCard>` whenever `analytics?.healthScore` is non-null.
|
||||||
|
Layout flips from `flex-col` to `flex-row` on `lg+` so the panel
|
||||||
|
sits beside the title block on desktop and stacks below on mobile.
|
||||||
|
|
||||||
|
The `onAskCoach` prop signature picks up an optional `prefill`
|
||||||
|
argument. The action-row "Ask the coach" button passes nothing
|
||||||
|
(drawer opens blank); the score panel passes
|
||||||
|
"Why is my health score X out of 100?". Both share the same
|
||||||
|
drawer state so the user only ever sees one drawer instance.
|
||||||
|
|
||||||
|
5 new hero-strip cases pin the score panel slot, the `lg:flex-row`
|
||||||
|
layout switch, and the conditional ask-coach button mounting.
|
||||||
|
|
||||||
|
### 5 · STATE.md tick + reports (commit 5)
|
||||||
|
|
||||||
|
- `STATE.md` B5 ticked complete; Wave-B summary section added.
|
||||||
|
- This file (`phase-B5-report.md`) summarises the wave + flagged-
|
||||||
|
uncertain items.
|
||||||
|
|
||||||
|
## Verification gates
|
||||||
|
|
||||||
|
- `pnpm typecheck` — clean.
|
||||||
|
- `pnpm lint` — 13 pre-existing warnings (none introduced by B5).
|
||||||
|
- `pnpm test --run` — 1975 → 2026 tests (+51). Test files 235 → 237.
|
||||||
|
- `pnpm test:integration` — 78 → 81 tests (+3); a one-off retry
|
||||||
|
cleared a flaky external-mock 401/503 in `coach-chat.test.ts`
|
||||||
|
unrelated to B5.
|
||||||
|
|
||||||
|
## Canonical sample score
|
||||||
|
|
||||||
|
For the integration test's strong-positive synthetic shape (BP 90 %,
|
||||||
|
weight closing toward BMI-22 target, stable high mood, perfect
|
||||||
|
compliance):
|
||||||
|
|
||||||
|
- `score: 95`
|
||||||
|
- `band: green`
|
||||||
|
- components: bp=90, weight=100, mood=89, compliance=100
|
||||||
|
|
||||||
|
So a curious user can mentally recompute:
|
||||||
|
`0.30 × 90 + 0.20 × 100 + 0.20 × 89 + 0.30 × 100 = 27 + 20 + 17.8 + 30
|
||||||
|
≈ 95`. Matches.
|
||||||
|
|
||||||
|
## Memory-care
|
||||||
|
|
||||||
|
- `feedback_marc_voice_english.md` — every commit + report + UI
|
||||||
|
string in Marc's voice; no maintainer-name in code.
|
||||||
|
- `feedback_settings_no_split.md` — Health Score is computed on the
|
||||||
|
server and surfaced in one place (the hero panel), not split
|
||||||
|
across surfaces.
|
||||||
|
- `feedback_charts_visual_identity.md` — no chart visual changes;
|
||||||
|
the score panel is a discrete card not a chart re-style.
|
||||||
|
- `feedback_research_before_complex_features.md` — the formula
|
||||||
|
followed the strategic-plan spec verbatim. The four-component
|
||||||
|
weighted blend matches the artboard's intent. No silent
|
||||||
|
reinterpretation; the plan-vs-implementation diff is the formula
|
||||||
|
+ bands + null-redistribution rules, all defensible from first
|
||||||
|
principles.
|
||||||
|
|
||||||
|
## Flagged-uncertain items (maintainer review)
|
||||||
|
|
||||||
|
1. **Schema drift on `medication_schedules.days_of_week`.** The
|
||||||
|
Prisma schema declares the column but no migration applies it.
|
||||||
|
The B5 route worked around it with an explicit `select` clause.
|
||||||
|
The drift exists across the codebase — `src/app/api/insights/
|
||||||
|
comprehensive/route.ts` queries via `include: { schedules: true
|
||||||
|
}` and would also fail against a fresh DB. Suggest a v1.4.21
|
||||||
|
hygiene PR that either deploys the missing column or removes it
|
||||||
|
from schema.prisma.
|
||||||
|
2. **Previous-week BP rate.** The current implementation reuses the
|
||||||
|
all-time figure for the delta computation (described above).
|
||||||
|
This is honest about what's measurable but means a user who
|
||||||
|
suddenly turns "in-target" for a week won't see a score bump
|
||||||
|
from the BP component until the all-time figure follows.
|
||||||
|
Acceptable for v1.4.20; v1.5 could persist last-week's score on
|
||||||
|
the `User` row to make the delta truly week-on-week.
|
||||||
|
3. **`User.weightTargetKg` missing.** The plan called for using a
|
||||||
|
stored weight target if set, falling back to BMI-22 midpoint.
|
||||||
|
The schema doesn't have the field, so we always use the BMI-22
|
||||||
|
fallback. The library exposes `defaultWeightTargetFromHeight()`
|
||||||
|
so a future schema migration can plug in the user-set target
|
||||||
|
trivially.
|
||||||
|
4. **`MoodEntry.source` enum mismatch.** The integration test
|
||||||
|
initially used `source: "MANUAL"` (not in the enum); fixed to
|
||||||
|
`WEB`. Worth pinning this in a v1.4.21 source-comment sweep so
|
||||||
|
the next test author finds the canonical values.
|
||||||
|
|
||||||
|
## Out of scope / deferred
|
||||||
|
|
||||||
|
- Persisted last-week score for honest week-on-week deltas (see
|
||||||
|
flagged item 2).
|
||||||
|
- `User.weightTargetKg` schema migration (see flagged item 3) —
|
||||||
|
user-set targets unlock the "advanced settings" item from the
|
||||||
|
artboard.
|
||||||
|
- Aggregator integration (the v1.4.16 B5e cross-user feedback loop)
|
||||||
|
for the score → that's strategic plan § D.3, deferred to v1.4.21
|
||||||
|
per the handoff.
|
||||||
|
|
||||||
|
## UX decisions worth flagging for review
|
||||||
|
|
||||||
|
1. **Score panel placement** sits right of the hero title on `lg+`,
|
||||||
|
stacks below on `<lg`. Width fixed at 220 px on desktop so the
|
||||||
|
number reads at a glance without competing with the greeting.
|
||||||
|
2. **Disclaimer copy.** "Indicative — not a clinical assessment."
|
||||||
|
Short, direct, no hedging that turns into noise on every
|
||||||
|
refresh. Mirrors the existing AI Coach drawer's "Coach replies
|
||||||
|
are generated. Clinical decisions belong with your doctor."
|
||||||
|
3. **No green chip for delta = 0.** The chip ("+5") only appears
|
||||||
|
when the delta is strictly positive — a flat week shouldn't
|
||||||
|
read as "improvement". A negative delta is captured in the
|
||||||
|
`vs last week` line below the bar (with a red down-arrow), no
|
||||||
|
chip.
|
||||||
|
4. **Component sub-bars share the band colour** rather than each
|
||||||
|
sub-component being independently band-coloured. Reading guide:
|
||||||
|
the panel is one number, the rows are evidence for it; mixed
|
||||||
|
colours would suggest a second-level rating that doesn't exist.
|
||||||
Reference in new issue
Block a user