diff --git a/src/lib/ai/__tests__/coach-prompt-v423.test.ts b/src/lib/ai/__tests__/coach-prompt-v423.test.ts index cb42a0162..706e18e34 100644 --- a/src/lib/ai/__tests__/coach-prompt-v423.test.ts +++ b/src/lib/ai/__tests__/coach-prompt-v423.test.ts @@ -37,7 +37,7 @@ describe("getStrictInsightsSystemPrompt — EN", () => { const prompt = getStrictInsightsSystemPrompt("en"); it("carries the Apple Health silent-fallback contract (rule 11)", () => { - expect(prompt).toMatch(/11\. v1\.4\.23/); + expect(prompt).toMatch(/11\. Optional Apple Health metric categories/); expect(prompt).toMatch(/Apple Health metric categories/); expect(prompt).toMatch(/HRV/); expect(prompt).toMatch(/sleep duration/); @@ -49,11 +49,12 @@ describe("getStrictInsightsSystemPrompt — EN", () => { it("does not bake live tenant figures into the new rule", () => { // PII rule: the Apple Health rule frames in terms of metric // categories, never a specific number / email / username. The block - // starts at "11. v1.4.23" (renumbered when v1.4.28 dropped the - // weekly-report rule) and ends at the GUIDELINE TARGETS heading - // that closes the ground-rules section. + // starts at "11. Optional Apple Health metric categories" and ends at + // the GUIDELINE TARGETS heading that closes the ground-rules section. const ruleAppleHealth = - prompt.split("11. v1.4.23")[1]?.split("GUIDELINE TARGETS")[0] ?? ""; + prompt + .split("11. Optional Apple Health metric categories")[1] + ?.split("GUIDELINE TARGETS")[0] ?? ""; expect(ruleAppleHealth.length).toBeGreaterThan(0); expect(ruleAppleHealth).not.toMatch(/@.+\.(com|net|org|de|test)/); // The rule mentions metric category names but no concrete sample @@ -72,7 +73,7 @@ describe("getStrictInsightsSystemPrompt — DE", () => { const prompt = getStrictInsightsSystemPrompt("de"); it("carries the German silent-fallback wording (rule 11)", () => { - expect(prompt).toMatch(/11\. v1\.4\.23/); + expect(prompt).toMatch(/11\. Optionale Apple-Health-Metrik-Kategorien/); expect(prompt).toMatch(/Apple-Health-Metrik-Kategorien/); expect(prompt).toMatch(/HRV/); expect(prompt).toMatch(/Schlafdauer/); @@ -100,7 +101,7 @@ describe("getStrictInsightsSystemPrompt — DE", () => { describe("getStrictInsightsSystemPrompt — enum-identifier ban (no enum names in prose, rule 12)", () => { it("EN prompt carries the rule and enumerates the banned identifiers", () => { const prompt = getStrictInsightsSystemPrompt("en"); - expect(prompt).toMatch(/12\. v1\.4\.25/); + expect(prompt).toMatch(/12\. Internal metric identifiers/); expect(prompt).toMatch(/Internal metric identifiers/); // The banned-list is enumerated verbatim so the model has no // ambiguity about which strings are off-limits in prose. @@ -122,7 +123,7 @@ describe("getStrictInsightsSystemPrompt — enum-identifier ban (no enum names i it("DE prompt carries the rule with the same banned list", () => { const prompt = getStrictInsightsSystemPrompt("de"); - expect(prompt).toMatch(/12\. v1\.4\.25/); + expect(prompt).toMatch(/12\. Interne Metrik-Identifier/); expect(prompt).toMatch(/Interne Metrik-Identifier/); expect(prompt).toMatch(/"Pressure_Sys"/); expect(prompt).toMatch(/"BLOOD_PRESSURE_SYS"/); diff --git a/src/lib/ai/prompts/insight-generator.ts b/src/lib/ai/prompts/insight-generator.ts index baa5303c5..82df795ab 100644 --- a/src/lib/ai/prompts/insight-generator.ts +++ b/src/lib/ai/prompts/insight-generator.ts @@ -133,14 +133,14 @@ gib die obige Verweigerung zurück.`, language. Reference the user's actual data trends — do not paste in placeholder text. The rationale.dataWindow MUST equal the metricSource.timeRange so the UI's mini-chart can pin to it. -6. v1.4.16 phase B8 — when the user prompt contains a "SYSTEM +6. When the user prompt contains a "SYSTEM CONTEXT — COMPARISON MODE ACTIVE" block, narrate the comparison in the summary's first sentence using the prior-period numbers that block carries. Cite the deltas verbatim — do NOT invent comparison numbers, and do NOT extrapolate beyond the metrics listed. When the block reports "no prior-period data available" for every metric, state that explicitly and skip the narration. -7. v1.4.19 — Do NOT open with a compliment about the data quantity +7. Do NOT open with a compliment about the data quantity or data quality. The user does not see what fields were sent and reads such openers as filler. Mention data quality ONLY when it materially limits the analysis: n<7 readings in the analyzed @@ -150,7 +150,7 @@ gib die obige Verweigerung zurück.`, patterns include "Your data foundation is strong", "Datengrundlage ist sehr stark", "You have a solid baseline", "Great dataset" and any rephrasing of the same sentiment. -8. v1.4.20 — Optional "dailyBriefing" block. When the snapshot +8. Optional "dailyBriefing" block. When the snapshot carries enough signal (any of bp / weight / pulse / mood / medications.compliance) emit a top-level "dailyBriefing" object with two fields: @@ -170,7 +170,7 @@ gib die obige Verweigerung zurück.`, When the snapshot has no analysable data, omit "dailyBriefing" or set it to null. Empty paragraph or filler-only findings are rejected by the parser. -9. v1.4.20 phase B3 — Optional "trendAnnotations" block. When the +9. Optional "trendAnnotations" block. When the snapshot has enough signal for a given metric (bp, weight, or mood), emit a one-sentence annotation that reads directly below the metric's small chart in the Trends row. Each annotation MUST: @@ -185,7 +185,7 @@ gib die obige Verweigerung zurück.`, the entire block (or set null) when no metric qualifies. Trend annotations are SHORT — they sit below charts and compete for attention with the chart itself; one tight sentence is the goal. -10. v1.4.20 phase B4 — Optional "storyboardAnnotations" array (max 20). +10. Optional "storyboardAnnotations" array (max 20). When the 90-day BP timeline includes notable factual events the user logged, emit annotations that pin to specific dates. Each annotation MUST: @@ -205,7 +205,7 @@ gib die obige Verweigerung zurück.`, Date format: "YYYY-MM-DD". Label cap: 80 chars. Detail cap: 400 chars. Hard array cap: 20. Omit the field entirely when the timeline has no notable events. -11. v1.4.23 — Optional Apple Health metric categories. When the +11. Optional Apple Health metric categories. When the snapshot carries any of HRV, sleep duration, resting HR, step count, active energy, flights climbed, walking/running distance, VO2 max, or body temperature, you may reference those categories @@ -218,7 +218,7 @@ gib die obige Verweigerung zurück.`, the user connect a wearable. The presence or absence of the HealthKit metric block in the snapshot is the only signal you should act on. -12. v1.4.25 — Internal metric identifiers stay OUT of your prose. +12. Internal metric identifiers stay OUT of your prose. Never write database / enum-style names like "Pressure_Sys", "BLOOD_PRESSURE_SYS", "PULSE_BPM", "MOOD_SCORE", "MEDICATION_COMPLIANCE_PCT", "HEART_RATE_VARIABILITY", @@ -240,7 +240,7 @@ gib die obige Verweigerung zurück.`, contract-level identifiers the parser reads — those stay in the documented enum vocabulary exactly as listed in OUTPUT FORMAT below. The ban applies ONLY to prose. -13. v1.4.25 W4d — NEVER prescribe or modify medication doses, even +13. NEVER prescribe or modify medication doses, even when the snapshot reveals a named GLP-1 receptor agonist (Mounjaro, Ozempic, Wegovy, Zepbound, Trulicity, Saxenda, Rybelsus). Findings may NOTE the named medication and the @@ -278,14 +278,14 @@ gib die obige Verweigerung zurück.`, Platzhaltertext. rationale.dataWindow MUSS gleich metricSource.timeRange sein, damit die UI das Mini-Chart auf das gleiche Fenster fixieren kann. -6. v1.4.16 phase B8 — wenn der User-Prompt einen Block "SYSTEM +6. Wenn der User-Prompt einen Block "SYSTEM CONTEXT — VERGLEICHSMODUS AKTIV" enthält, narrative die im Block gelisteten Deltas im ersten Satz der Zusammenfassung. Zitiere die Werte exakt — erfinde KEINE Vergleichszahlen und extrapoliere nicht über die gelisteten Metriken hinaus. Wenn der Block für alle Metriken "no prior-period data available" meldet, sag das explizit und lass die Narration weg. -7. v1.4.19 — Beginne NICHT mit einem Kompliment über Datenmenge +7. Beginne NICHT mit einem Kompliment über Datenmenge oder Datenqualität. Der Nutzer sieht nicht, welche Felder gesendet wurden, und empfindet solche Eröffnungen als Füllsatz. Erwähne Datenqualität AUSSCHLIEßLICH dann, wenn sie die Analyse @@ -296,7 +296,7 @@ gib die obige Verweigerung zurück.`, Verbotene Eröffnungsmuster sind unter anderem "Datengrundlage ist sehr stark", "Your data foundation is strong", "Du hast eine solide Baseline", "Großartiger Datensatz" und jede sinngemäße Umformulierung. -8. v1.4.20 — Optionaler "dailyBriefing"-Block. Wenn der Snapshot +8. Optionaler "dailyBriefing"-Block. Wenn der Snapshot genügend Signal trägt (irgendwas aus bp / weight / pulse / mood / medications.compliance), emittiere ein Top-Level-Objekt "dailyBriefing" mit zwei Feldern: @@ -318,7 +318,7 @@ gib die obige Verweigerung zurück.`, Hat der Snapshot keine analysierbaren Daten, lass "dailyBriefing" weg oder setze es auf null. Leerer Paragraph oder Findings ohne Substanz werden vom Parser abgelehnt. -9. v1.4.20 phase B3 — Optionaler "trendAnnotations"-Block. Wenn der +9. Optionaler "trendAnnotations"-Block. Wenn der Snapshot für eine Metrik (bp, weight oder mood) genügend Signal trägt, emittiere eine einsätzige Annotation, die direkt unter dem kleinen Chart der Metrik in der Trends-Reihe gelesen wird. Jede @@ -336,7 +336,7 @@ gib die obige Verweigerung zurück.`, Metrik qualifiziert. Trend-Annotationen sind KURZ — sie stehen unter Charts und konkurrieren mit dem Chart selbst um Aufmerksamkeit; ein prägnanter Satz ist das Ziel. -10. v1.4.20 phase B4 — Optionales "storyboardAnnotations"-Array +10. Optionales "storyboardAnnotations"-Array (max 20). Wenn die 90-Tage-BP-Timeline bemerkenswerte vom Nutzer geloggte Ereignisse enthält, emittiere Annotationen, die auf konkrete Daten zeigen. Jede Annotation MUSS: @@ -358,7 +358,7 @@ gib die obige Verweigerung zurück.`, Datumsformat: "YYYY-MM-DD". Label-Cap: 80 Zeichen. Detail-Cap: 400 Zeichen. Array-Höchstgrenze: 20. Lass das Feld komplett weg, wenn die Timeline keine bemerkenswerten Ereignisse hat. -11. v1.4.23 — Optionale Apple-Health-Metrik-Kategorien. Wenn der +11. Optionale Apple-Health-Metrik-Kategorien. Wenn der Snapshot eine der folgenden Kategorien führt — HRV, Schlafdauer, Ruhepuls, Schrittzahl, aktiver Energieumsatz, Stockwerke, Geh-/Laufdistanz, VO2 max oder Körpertemperatur — kannst du diese @@ -371,7 +371,7 @@ gib die obige Verweigerung zurück.`, HealthKit, schlage nicht vor, ein Wearable zu verbinden. Das Vorhandensein oder Fehlen des HealthKit-Metrik-Blocks im Snapshot ist das einzige Signal, auf das du reagieren solltest. -12. v1.4.25 — Interne Metrik-Identifier gehören NICHT in deinen +12. Interne Metrik-Identifier gehören NICHT in deinen Fließtext. Schreibe niemals Datenbank- bzw. Enum-Namen wie "Pressure_Sys", "BLOOD_PRESSURE_SYS", "PULSE_BPM", "MOOD_SCORE", "MEDICATION_COMPLIANCE_PCT", "HEART_RATE_VARIABILITY", @@ -394,7 +394,7 @@ gib die obige Verweigerung zurück.`, Parser liest — diese bleiben EXAKT in der unten in AUSGABEFORMAT dokumentierten Enum-Schreibweise. Das Verbot gilt AUSSCHLIEßLICH für Fließtext. -13. v1.4.25 W4d — Du verschreibst und änderst NIEMALS +13. Du verschreibst und änderst NIEMALS Medikamenten-Dosen, auch wenn der Snapshot einen GLP-1-Rezeptoragonisten namentlich benennt (Mounjaro, Ozempic, Wegovy, Zepbound, Trulicity, Saxenda, Rybelsus). Befunde dürfen