diff --git a/src/components/insights/insight-status-card.tsx b/src/components/insights/insight-status-card.tsx
index a7f31f60c..6718a4178 100644
--- a/src/components/insights/insight-status-card.tsx
+++ b/src/components/insights/insight-status-card.tsx
@@ -17,6 +17,7 @@ import { cn } from "@/lib/utils";
import { useFeatureFlags } from "@/hooks/use-feature-flags";
import { AskCoachIconButton } from "@/components/insights/ask-coach-action";
import type { CoachLaunchScope } from "@/lib/insights/coach-launch-context";
+import type { AssessmentStatus } from "@/lib/insights/status-shared";
// ─── Types ────────────────────────────────────────────────
@@ -62,6 +63,12 @@ interface InsightStatusCardProps {
* card is being assembled, not stuck. The client polls until text lands.
*/
preparing?: boolean;
+ /**
+ * Explicit producer/hook state. Legacy scalar props remain supported for
+ * direct mounts while routes roll forward, but this discriminator owns
+ * branch ordering whenever it is present.
+ */
+ assessment?: AssessmentStatus;
/**
* v1.21.0 (C4 H2) — opt-in "Ask the Coach about this assessment" hand-off.
* When `coachQuestion` is set (the metric-aware caller supplies it), the
@@ -88,6 +95,7 @@ export function InsightStatusCard({
updatedAt,
loading = false,
preparing = false,
+ assessment,
coachQuestion,
coachScope,
coachAutoSend,
@@ -99,12 +107,22 @@ export function InsightStatusCard({
// suppressed in full so the layout collapses naturally.
if (!flags.insightStatus) return null;
+ const displayText =
+ assessment?.kind === "generated" ||
+ assessment?.kind === "screened-fallback" ||
+ assessment?.kind === "revalidating"
+ ? assessment.text
+ : text;
+ const isPreparing =
+ assessment?.kind === "preparing" ||
+ (!assessment && preparing && hasProvider && !displayText);
+
// v1.8.3 — preparing: the read-only route enqueued a generation and the
// client is polling. Render the loading skeleton geometry with a
// preparing caption so the card reads as "assembling", not stuck. Only
// shown when a provider is configured (the route returns the no-key
// fallback otherwise).
- if (preparing && hasProvider && !text) {
+ if (isPreparing) {
return (
+
+
+
+
+ {message}
+
+
+ );
+ }
+
+ const isNoProvider =
+ assessment?.kind === "no-provider" ||
+ (!assessment && !hasProvider && !displayText);
+ if (isNoProvider) {
return (
@@ -187,7 +238,7 @@ export function InsightStatusCard({
);
}
- if (!text) {
+ if (!displayText) {
return (
@@ -255,7 +306,7 @@ export function InsightStatusCard({
token surfaces verbatim. The producer side now strips too;
this consumer-side wrap keeps existing caches clean while
they roll forward. */}
-
+
diff --git a/src/components/insights/metric-status-card.tsx b/src/components/insights/metric-status-card.tsx
index 7a6a30956..ef7532727 100644
--- a/src/components/insights/metric-status-card.tsx
+++ b/src/components/insights/metric-status-card.tsx
@@ -66,6 +66,7 @@ export function MetricStatusCard({
text={status?.text ?? null}
hasProvider={status?.hasProvider ?? false}
updatedAt={status?.updatedAt ?? null}
+ assessment={status?.assessment}
coachQuestion={coachQuestion}
coachScope={coachSource ? { metric: coachSource } : undefined}
coachAutoSend
diff --git a/src/components/insights/slug-insight-status-card.tsx b/src/components/insights/slug-insight-status-card.tsx
index d73bd2080..f64bcca14 100644
--- a/src/components/insights/slug-insight-status-card.tsx
+++ b/src/components/insights/slug-insight-status-card.tsx
@@ -71,6 +71,7 @@ export function SlugInsightStatusCard({
text={status?.text ?? null}
hasProvider={status?.hasProvider ?? false}
updatedAt={status?.updatedAt ?? null}
+ assessment={status?.assessment}
coachQuestion={t("insights.coach.assessmentPrompt", {
metric: t(coach.labelKey),
})}