From 66e13845c0ca35fe7438ce01777c1cc967819c28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Sat, 16 May 2026 01:47:05 +0200 Subject: [PATCH] refactor(coach): narrow launch-scope metric type to the source union MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CoachLaunchScope.metric was typed as a free-form string, which would let any future caller pass a label the sources rail then silently ignores (the rail's source-toggle contract only honours the CoachScopeSource union — bp, weight, pulse, mood, compliance, plus the v1.4.23 Apple Health additions). Narrow the field to that union so the type system surfaces drift the moment a caller invents a new metric name. Also pin a one-consumer note on the setOpen export (only forwards it to the sheet's onOpenChange contract; keep the raw boolean setter for that callback shape). --- src/lib/insights/coach-launch-context.tsx | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/lib/insights/coach-launch-context.tsx b/src/lib/insights/coach-launch-context.tsx index 3dbd14c69..2b0aacdae 100644 --- a/src/lib/insights/coach-launch-context.tsx +++ b/src/lib/insights/coach-launch-context.tsx @@ -9,6 +9,8 @@ import { type ReactNode, } from "react"; +import type { CoachScopeSource } from "@/lib/ai/coach/types"; + /** * v1.4.27 R3d MB4 — Coach launch context. * @@ -35,8 +37,14 @@ export interface CoachLaunchScope { * Optional metric the user is looking at when they open the Coach. * Reserved for v1.4.28 so the drawer can pre-narrow the source rail * to the active metric; ignored in v1.4.27. + * + * v1.4.28 R3c (BK-MED-2 / BK-F-M4) — narrowed from `string` to the + * `CoachScopeSource` union so call sites cannot drift to a free-form + * label that the sources rail then silently ignores. Adding a new + * Apple-Health source to the rail (the union already covers the + * v1.4.23 additions) automatically widens the allowed values here. */ - metric?: string; + metric?: CoachScopeSource; } interface CoachLaunchValue { @@ -46,7 +54,14 @@ interface CoachLaunchValue { prefill: string | null; /** Open the drawer with an optional prefill + scope hint. */ askCoach: (prefill?: string | null, scope?: CoachLaunchScope) => void; - /** Direct setter for the open state — the drawer consumes this. */ + /** + * Direct setter for the open state — the drawer's `onOpenChange` + * consumes it on close. Kept (rather than collapsed into a + * `closeCoach()` helper) because `` forwards the + * raw setter to the Sheet's controlled-state contract, which expects + * a boolean callback. v1.4.28 R3c (BK-F-M4) audit: exactly one + * consumer, no drift. + */ setOpen: (next: boolean) => void; }