From 1bf8f3d6619e2aa141ff257febfd313488579db7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Sun, 21 Jun 2026 20:54:39 +0200 Subject: [PATCH] refactor(coach): drop the unread streaming startedAt and reasoning state The thinking disclosure was removed, so the coach streaming bubble no longer reads startedAt or reasoning. Both fields stayed populated by the send hook but nothing consumed them. Remove them from CoachStreamingMessage and every writer, accept the additive reasoning SSE frame as an explicit no-op so the wire type stays recognised, and update the thread-test fixture. Streaming is otherwise unchanged. --- .../__tests__/message-thread.test.tsx | 10 ++-- .../insights/coach-panel/use-coach.ts | 53 +++---------------- 2 files changed, 10 insertions(+), 53 deletions(-) diff --git a/src/components/insights/coach-panel/__tests__/message-thread.test.tsx b/src/components/insights/coach-panel/__tests__/message-thread.test.tsx index aa20016d2..dd9356ed3 100644 --- a/src/components/insights/coach-panel/__tests__/message-thread.test.tsx +++ b/src/components/insights/coach-panel/__tests__/message-thread.test.tsx @@ -62,10 +62,10 @@ import { import type { CoachConversationDetailDTO } from "@/lib/ai/coach/types"; import type { CoachStreamingMessage } from "../use-coach"; -// v1.18.9 — `CoachStreamingMessage` gained `usage` / `startedAt` / -// `reasoning` (per-message token footer + thinking disclosure). The -// fixtures below predate those fields; this builder fills the defaults so -// each test only spells out the fields it cares about. +// v1.18.9 — `CoachStreamingMessage` gained a `usage` field for the +// per-message token footer. The fixtures below predate it; this builder +// fills the defaults so each test only spells out the fields it cares +// about. function streaming( partial: Partial, ): CoachStreamingMessage { @@ -77,8 +77,6 @@ function streaming( messageId: null, errorCode: null, usage: null, - startedAt: null, - reasoning: "", ...partial, }; } diff --git a/src/components/insights/coach-panel/use-coach.ts b/src/components/insights/coach-panel/use-coach.ts index 5517d5237..db101a0d6 100644 --- a/src/components/insights/coach-panel/use-coach.ts +++ b/src/components/insights/coach-panel/use-coach.ts @@ -199,19 +199,6 @@ export interface CoachStreamingMessage { * bubble; persisted bubbles read `CoachMessageDTO.tokensUsed` instead. */ usage: CoachUsage | null; - /** - * v1.18.9 — epoch-ms timestamp the send fired, so the thinking - * disclosure can show a live "thinking for N s" counter that freezes - * to a past-tense summary once the first token lands. Null before a - * turn starts. - */ - startedAt: number | null; - /** - * v1.18.9 — optional reasoning-summary text from the additive - * `reasoning` frame, rendered inside the thinking disclosure when a - * reasoning-capable provider emits it. Empty string when none. - */ - reasoning: string; } /** @@ -253,8 +240,6 @@ const EMPTY_STREAMING: CoachStreamingMessage = { messageId: null, errorCode: null, usage: null, - startedAt: null, - reasoning: "", }; export interface SendCoachMessageParams { @@ -349,10 +334,6 @@ export function useSendCoachMessage(opts: UseSendCoachMessageOptions = {}) { messageId: null, errorCode: null, usage: null, - // v1.18.9 — anchor the thinking-disclosure elapsed timer to the - // moment the send fired. - startedAt: Date.now(), - reasoning: "", }); // v1.4.47 W8 — pre-check navigator.onLine so an airplane-mode @@ -367,8 +348,6 @@ export function useSendCoachMessage(opts: UseSendCoachMessageOptions = {}) { messageId: null, errorCode: "coach.network", usage: null, - startedAt: null, - reasoning: "", }); return null; } @@ -403,8 +382,6 @@ export function useSendCoachMessage(opts: UseSendCoachMessageOptions = {}) { messageId: null, errorCode: "coach.network", usage: null, - startedAt: null, - reasoning: "", }); return null; } @@ -418,8 +395,6 @@ export function useSendCoachMessage(opts: UseSendCoachMessageOptions = {}) { messageId: null, errorCode: `coach.http.${response.status}`, usage: null, - startedAt: null, - reasoning: "", }); return null; } @@ -458,8 +433,6 @@ export function useSendCoachMessage(opts: UseSendCoachMessageOptions = {}) { messageId: null, errorCode: structured ?? `coach.http.${response.status}`, usage: null, - startedAt: null, - reasoning: "", }); // v1.16.4 — KEEP the optimistic user bubble: a rejected turn // (budget gate, 4xx) has no persisted twin coming, and dropping @@ -479,7 +452,6 @@ export function useSendCoachMessage(opts: UseSendCoachMessageOptions = {}) { let lastError: string | null = null; let messageId: string | null = null; let collectedUsage: CoachUsage | null = null; - let collectedReasoning = ""; try { while (true) { @@ -512,16 +484,10 @@ export function useSendCoachMessage(opts: UseSendCoachMessageOptions = {}) { })); break; case "reasoning": - // v1.18.9 — additive reasoning-summary frame. Append so - // multiple chunks accumulate; the disclosure renders it - // when present, else falls back to the elapsed-time label. - // Guard the text: a `reasoning` frame with no `text` must not - // append a literal "undefined". - collectedReasoning += evt.text ?? ""; - setStreaming((prev) => ({ - ...prev, - reasoning: prev.reasoning + (evt.text ?? ""), - })); + // v1.19.1 — the thinking disclosure was removed, so the + // additive `reasoning` frame is accepted off the wire and + // dropped. Kept as an explicit no-op case so the wire frame + // stays a recognised type rather than slipping to a default. break; case "done": resolvedConversationId = evt.conversationId; @@ -544,8 +510,6 @@ export function useSendCoachMessage(opts: UseSendCoachMessageOptions = {}) { collectedUsage = evt.usage ?? null; } else if (evt.type === "error") { lastError = evt.code; - } else if (evt.type === "reasoning") { - collectedReasoning += evt.text ?? ""; } } } catch (err) { @@ -554,7 +518,7 @@ export function useSendCoachMessage(opts: UseSendCoachMessageOptions = {}) { } } - setStreaming((prev) => ({ + setStreaming({ content: collectedContent, metricSource: collectedProvenance, suggestion: collectedSuggestion, @@ -562,12 +526,7 @@ export function useSendCoachMessage(opts: UseSendCoachMessageOptions = {}) { messageId, errorCode: lastError, usage: collectedUsage, - // v1.18.9 — keep the send-anchored timestamp so the disclosure can - // freeze the elapsed time to a past-tense "thought for N s" once - // the turn settles. - startedAt: prev.startedAt, - reasoning: collectedReasoning, - })); + }); if (resolvedConversationId) { // Invalidate the freshly-persisted conversation + the rail so