diff --git a/src/lib/ai/prompts/__tests__/shared-contracts-coverage.test.ts b/src/lib/ai/prompts/__tests__/shared-contracts-coverage.test.ts index e2c790a07..d9fc0f09a 100644 --- a/src/lib/ai/prompts/__tests__/shared-contracts-coverage.test.ts +++ b/src/lib/ai/prompts/__tests__/shared-contracts-coverage.test.ts @@ -67,11 +67,18 @@ const SURFACES: Record< forbiddenFiller, ], }, - // The narrative is descriptive-only, so it enforces every contract EXCEPT - // the motivating tone contract. + // v1.21.0 (coach C1 MEDIUM-1) — the retrospective narrative now composes the + // shared tone contract too, so it matches the warm house voice of the daily + // briefing beside it. Its own descriptive-never-causal guards stay intact. "period-narrative": { prompt: (l) => SYSTEM_PROMPTS_FOR_TEST[l], - contracts: [grounding, safetyGlp1, metricIdentifierBan, forbiddenFiller], + contracts: [ + grounding, + toneContract, + safetyGlp1, + metricIdentifierBan, + forbiddenFiller, + ], }, }; diff --git a/src/lib/insights/narrative/__tests__/period-narrative-generate.test.ts b/src/lib/insights/narrative/__tests__/period-narrative-generate.test.ts index 1ca917298..fa99fc7b3 100644 --- a/src/lib/insights/narrative/__tests__/period-narrative-generate.test.ts +++ b/src/lib/insights/narrative/__tests__/period-narrative-generate.test.ts @@ -25,6 +25,7 @@ import type { PeriodNarrativeContext, PeriodNarrativeResult, } from "@/lib/insights/narrative/period-narrative"; +import { toneContract } from "@/lib/ai/prompts/shared-contracts"; function readyContext( over: Partial = {}, @@ -170,6 +171,9 @@ describe("generatePeriodNarrative — descriptive generation", () => { }); expect(systemPrompt).toMatch(/never CAUSAL|DESCRIPTIVE/); expect(systemPrompt).toMatch(/no markdown/i); + // v1.21.0 (coach C1 MEDIUM-1) — the retrospective now carries the shared + // warm tone contract verbatim, matching the briefing's house voice. + expect(systemPrompt).toContain(toneContract.en); }); }); diff --git a/src/lib/insights/narrative/period-narrative-generate.ts b/src/lib/insights/narrative/period-narrative-generate.ts index b597a5511..114855493 100644 --- a/src/lib/insights/narrative/period-narrative-generate.ts +++ b/src/lib/insights/narrative/period-narrative-generate.ts @@ -104,11 +104,11 @@ Hard rules: - The supplied CONTEXT is the ONLY source of truth. Never state a number, trend, driver, or threshold that is not in it. - Be DESCRIPTIVE, never CAUSAL. Say "X moved with Y" or "X was associated with Y", never "X caused Y" or "because of X". - The listed drivers already survived statistical multiple-comparison control; restate them only as associations and keep their conservative meaning. -- No diagnosis, no medical advice, no alarm. Calm, factual, second person ("your"). +- No diagnosis, no medical advice, no alarm. Calm, factual, second person ("your"). When the period earned it, name one genuine win plainly; keep it warm and second-person, never a reflexive compliment. - 2 to 4 short sentences. Plain text only — no markdown, no headings, no bullet points, no emojis. - If the context is thin, say plainly that there is little to report this period rather than inventing detail. -${composeSharedContracts("en", ["grounding", "safetyGlp1", "metricIdentifierBan", "forbiddenFiller"])}`; +${composeSharedContracts("en", ["toneContract", "grounding", "safetyGlp1", "metricIdentifierBan", "forbiddenFiller"])}`; const SYSTEM_PROMPT_DE = `Du fasst den Gesundheits-Tracking-ZEITRAUM einer Person (eine Woche oder einen Monat) für diese Person zusammen. Prompt-Version: ${NARRATIVE_PROMPT_VERSION}. @@ -117,11 +117,11 @@ Feste Regeln: - Der bereitgestellte KONTEXT ist die EINZIGE Wahrheitsquelle. Nenne nie eine Zahl, einen Trend, einen Zusammenhang oder einen Schwellenwert, der nicht darin steht. - Sei BESCHREIBEND, nie URSÄCHLICH. Sage "X bewegte sich mit Y" oder "X war mit Y assoziiert", nie "X verursachte Y" oder "wegen X". - Die genannten Zusammenhänge haben bereits die statistische Mehrfachvergleichskorrektur überstanden; gib sie nur als Assoziationen wieder und bewahre ihre vorsichtige Bedeutung. -- Keine Diagnose, kein medizinischer Rat, keine Panik. Ruhig, sachlich, in der zweiten Person ("dein"). +- Keine Diagnose, kein medizinischer Rat, keine Panik. Ruhig, sachlich, in der zweiten Person ("dein"). Wenn der Zeitraum es hergibt, benenne einen echten Erfolg klar; bleib warm und in der zweiten Person, nie ein reflexhaftes Kompliment. - 2 bis 4 kurze Sätze. Nur Klartext — kein Markdown, keine Überschriften, keine Aufzählungen, keine Emojis. - Wenn der Kontext dünn ist, sage klar, dass es in diesem Zeitraum wenig zu berichten gibt, statt Details zu erfinden. -${composeSharedContracts("de", ["grounding", "safetyGlp1", "metricIdentifierBan", "forbiddenFiller"])}`; +${composeSharedContracts("de", ["toneContract", "grounding", "safetyGlp1", "metricIdentifierBan", "forbiddenFiller"])}`; /** * Test-only view of the composed system prompts (incl. the appended shared