Merge branch 'fix/prompt-structure' into release/v1.27.5

This commit is contained in:
Marc-André Bombeck committed 2026-07-05 15:11:03 +02:00
commit ecb54a8d1b
4 files changed
+23 -68

No files matched your search

+6 -11
View File
@@ -303,19 +303,18 @@ export default function InsightsPage() {
const analyticsQuery = useAnalyticsQuery();
const analytics = analyticsQuery.data as AnalyticsData | undefined;
// v1.21.2 (A4 / A5 / A6) — the dashboard snapshot is the single server source
// for the ambient narrative the hero + briefing surface: the Tension Verdict
// and return-to-baseline ride its `healthScore`, the briefing recall +
// forward-look rides `briefingMemory`. The hero score number itself stays on
// the analytics payload (unchanged); only these three additive, server-
// resolved DTO fields are lifted from the snapshot. Reads the same shared
// v1.21.2 (A5 / A6) — the dashboard snapshot is the single server source
// for the ambient narrative the hero surfaces: the Tension Verdict and
// return-to-baseline ride its `healthScore`. The hero score number itself
// stays on the analytics payload (unchanged). Reads the same shared
// `queryKeys.dashboardSnapshot()` cell the dashboard warms, so the insights
// visit pays at most one extra round-trip and usually a warm cache hit.
// (The A4 recall/forward-look block left the briefing card — the card now
// opens straight on the day's signals.)
const snapshotQuery = useDashboardSnapshot(isAuthenticated);
const snapshotHealthScore = snapshotQuery.data?.healthScore ?? null;
const heroTension = snapshotHealthScore?.tension ?? null;
const heroReturnToBand = snapshotHealthScore?.returnToBand ?? null;
const briefingMemory = snapshotQuery.data?.briefingMemory ?? null;
// v1.12.6 — the page owns the ONE overview derived batch. The wellness
// strip (above the briefing) and the vitals grid (below it) both read this
@@ -483,10 +482,6 @@ export default function InsightsPage() {
// v1.25.3 — failure class points the empty-state hint at the right
// lever (raise the response timeout vs re-check the provider).
generationFailureClass={advisor.generationFailureClass}
// v1.21.2 (A4) — server-resolved recall + forward-look off the dashboard
// snapshot. Both strings are already localised; null leaves the card's
// memory block unrendered.
memory={briefingMemory}
/>
) : null,
vitals: <VitalsDashboard batch={dashboardDerived} layout={layout} />,
+7 -51
View File
@@ -25,7 +25,6 @@ import { EmptyState } from "@/components/ui/empty-state";
import { ListRow } from "@/components/ui/list-row";
import { Skeleton } from "@/components/ui/skeleton";
import { SectionHeading } from "@/components/insights/section-heading";
import { AskCoachAction } from "@/components/insights/ask-coach-action";
import { useTranslations, useFormatters } from "@/lib/i18n/context";
import { formatUpdatedLabel } from "@/lib/i18n/relative-time";
import { stripChartTokens } from "@/lib/insights/chart-tokens";
@@ -140,20 +139,6 @@ interface DailyBriefingProps {
* comparison toggle migrates here from the hero in commit 5.
*/
metaSlot?: React.ReactNode;
/**
* v1.21.2 (A4) — server-resolved callback + forward-look. The briefing
* recalls the prior period and points ahead ("as I noted Monday, your sleep
* was short; it's recovered since — worth watching tonight"). Both strings
* are already localised and figure-anchored by the resolver; the card only
* renders them. One callback per surface — absent (the common case) when no
* prior narrative recall is on file.
*/
memory?: {
/** The recall of the prior-period read, anchored to a figure. */
recall: string;
/** The forward-look pointing ahead. */
forward: string;
} | null;
}
const METRIC_ICON: Record<
@@ -368,7 +353,6 @@ export function DailyBriefing({
generationFailed = false,
generationFailureClass = null,
metaSlot,
memory = null,
}: DailyBriefingProps) {
const { t } = useTranslations();
const fmt = useFormatters();
@@ -424,33 +408,11 @@ export function DailyBriefing({
// looser `space-y-4`, which read as taller than its neighbours on
// the overview. One token, no new spacing scale.
<div className="space-y-3">
{/* v1.21.2 (A4) — callback + forward-look. Recalls the prior
period and points ahead, both server-resolved and
figure-anchored. Leads the card so the briefing opens by
closing the loop on what it noted last, then looks forward. */}
{memory && (
<div
data-slot="daily-briefing-memory"
className="border-border/60 bg-card/40 space-y-1 rounded-md border px-3 py-2"
>
<p
data-slot="daily-briefing-memory-recall"
className="text-muted-foreground text-xs leading-snug"
>
{t("insights.briefing.memory.recall", {
text: memory.recall,
})}
</p>
<p
data-slot="daily-briefing-memory-forward"
className="text-foreground/80 text-xs leading-snug"
>
{t("insights.briefing.memory.forward", {
text: memory.forward,
})}
</p>
</div>
)}
{/* The v1.21.2 recall/forward-look block is gone: the card
opens straight on "signals of the day". The narrative
memory read as a second briefing paragraph above the
structured list and pulled the card down; the server
still resolves `briefingMemory` for other consumers. */}
{/* v1.4.27 B1 — the leading narrative paragraph dropped.
The hero strip subtitle on `/insights` already renders
the same `briefing.paragraph` text directly above this
@@ -465,7 +427,7 @@ export function DailyBriefing({
<div className="space-y-2">
<p
data-slot="daily-briefing-signals-title"
className="text-muted-foreground text-xs font-semibold tracking-wide uppercase"
className="text-foreground text-xs font-semibold tracking-wide uppercase"
>
{t("insights.dailyBriefing.signalsTitle")}
</p>
@@ -483,7 +445,7 @@ export function DailyBriefing({
<div className="space-y-2">
<p
data-slot="daily-briefing-findings-title"
className="text-muted-foreground text-xs font-semibold tracking-wide uppercase"
className="text-foreground text-xs font-semibold tracking-wide uppercase"
>
{t("insights.dailyBriefing.keyFindingsTitle")}
</p>
@@ -565,12 +527,6 @@ export function DailyBriefing({
)}
</div>
)}
{/* v1.21.0 (C4 H2) — hand off to the Coach for the whole
picture. The briefing spans every metric, so no scope: the
default all-source snapshot reads best here. */}
<div className="flex justify-end">
<AskCoachAction question={t("insights.coach.seed.briefing")} />
</div>
</div>
) : noProvider ? (
// v1.15.20 — no provider configured anywhere: a regenerate CTA
+2 -2
View File
@@ -219,9 +219,9 @@ Synthese statt Aufzählung: die Geschichte dessen, was die Daten bedeuten, zähl
},
{
id: "output",
en: `OUTPUT FORMAT: Reply with valid JSON only, in exactly this schema. The "summary" field holds the complete assessment as English flowing prose (2-4 sentences):
en: `OUTPUT FORMAT: Reply with valid JSON only, in exactly this schema. The "summary" field holds the complete assessment in English: 1-3 short paragraphs of 1-3 sentences each, separated by a blank line written as \\n\\n INSIDE the JSON string. A steady one-liner stays a single paragraph:
{ "summary": "..." }`,
de: `AUSGABEFORMAT: Antworte ausschließlich mit validem JSON in genau diesem Schema. Das Feld "summary" enthält die komplette Einschätzung als deutscher Fließtext (2-4 Sätze):
de: `AUSGABEFORMAT: Antworte ausschließlich mit validem JSON in genau diesem Schema. Das Feld "summary" enthält die komplette Einschätzung auf Deutsch: 1-3 kurze Absätze mit je 1-3 Sätzen, getrennt durch eine Leerzeile als \\n\\n INNERHALB des JSON-Strings. Ein stabiler Einzeiler bleibt EIN Absatz:
{ "summary": "..." }`,
},
];
+8 -4
View File
@@ -259,8 +259,10 @@ gib die obige Verweigerung zurück.`,
warm, and motivating. Lead with NOW; draw on history for context,
but the user opens this to learn what is happening TODAY and what
one thing they can do about it. Fields:
- paragraph: a connected 70-160 word read of TODAY the user sees
at the top of /insights. SENTENCE 1 is the day's VERDICT in plain
- paragraph: a 70-160 word read of TODAY the user sees at the top
of /insights, written as 2-3 SHORT PARAGRAPHS separated by a
blank line (\\n\\n inside the JSON string) — never one unbroken
block. SENTENCE 1 is the day's VERDICT in plain
words — the overall picture, NOT a number ("Today reads like a
recovery day", "A steady day, nothing demanding your attention").
Then weave the 2-3 most salient signals into ONE story: lead from
@@ -452,8 +454,10 @@ gib die obige Verweigerung zurück.`,
JETZT; nutze die Historie als Kontext, aber der Nutzer öffnet das, um
zu erfahren, was HEUTE passiert und welche eine Sache er dagegen tun
kann. Felder:
- paragraph: ein zusammenhängender 70-160 Wörter langer Tages-Read,
den der Nutzer oben auf /insights liest. SATZ 1 ist das URTEIL des
- paragraph: ein 70-160 Wörter langer Tages-Read, den der Nutzer
oben auf /insights liest — geschrieben als 2-3 KURZE ABSÄTZE,
getrennt durch eine Leerzeile (\\n\\n innerhalb des JSON-Strings),
nie als ein durchgehender Block. SATZ 1 ist das URTEIL des
Tages in klaren Worten — das Gesamtbild, KEINE Zahl ("Heute liest
sich wie ein Erholungstag", "Ein ruhiger Tag, nichts, das deine
Aufmerksamkeit braucht"). Dann verwebe die 2-3 wichtigsten Signale