From 56e51ebed2e9bb49fd3410c6d8986557050321a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Sun, 27 Sep 2026 13:00:07 +0200 Subject: [PATCH] fix(coach): catch a German dose change that names the dose MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The outbound screen matched "Erhöhe auf 2,4 mg" but not "Erhöhe deine Dosis auf 2,4 mg": the German change verbs had no slot for an object between the verb and the target, unlike the English "increase your dose to". The raise and lower patterns now accept one. --- .../safety/__tests__/outbound-screen.test.ts | 22 +++++++++++++++++++ src/lib/ai/safety/outbound-screen.ts | 12 ++++++++-- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/src/lib/ai/safety/__tests__/outbound-screen.test.ts b/src/lib/ai/safety/__tests__/outbound-screen.test.ts index 969fa964f..9860f0300 100644 --- a/src/lib/ai/safety/__tests__/outbound-screen.test.ts +++ b/src/lib/ai/safety/__tests__/outbound-screen.test.ts @@ -65,6 +65,28 @@ describe("screenModelOutput — dose-prescription, all six locales", () => { } }); +describe("screenModelOutput — a German change verb with its object", () => { + it.each([ + "Erhöhe deine Dosis auf 2,4 mg pro Woche.", + "Steigere die Dosierung um 0,5 mg.", + "Reduziere deine Dosis auf 5 mg.", + "Senke die Tagesdosis auf 25 mg.", + ])("blocks %s", (text) => { + const d = screenModelOutput(text, "de", CONVERSATIONAL_CONTRACTS); + expect(d.block).toBe(true); + expect(d.reason).toBe("dose_prescription"); + }); + + it("still passes a restatement that names the dose", () => { + const d = screenModelOutput( + "Deine Dosis liegt seit drei Wochen bei 7,5 mg.", + "de", + CONVERSATIONAL_CONTRACTS, + ); + expect(d.block).toBe(false); + }); +}); + // ── risk-score fabrication ─────────────────────────────────────────────── const RISK_VIOLATIONS: Record = { diff --git a/src/lib/ai/safety/outbound-screen.ts b/src/lib/ai/safety/outbound-screen.ts index 1313aef36..f9a7e9896 100644 --- a/src/lib/ai/safety/outbound-screen.ts +++ b/src/lib/ai/safety/outbound-screen.ts @@ -75,6 +75,14 @@ const DOSE_UNIT = * "NIGDY nie zalecać konkretnej wartości") plus the standard clinical * titration verbs of each language. */ +/** + * The object a German change verb may carry before its target ("erhöhe + * deine Dosis auf 2,4 mg"). English allows "your dose" in the same slot; + * without it the German imperative with an object slipped through. + */ +const DE_DOSE_OBJECT = + "(?:\\s+(?:(?:deine|die|ihre|eure|seine)\\s+)?(?:dosis|dosierung|medikation|wochendosis|tagesdosis))?"; + const DOSE_PATTERNS: Record = { en: [ // step/move/increase/raise/bump/titrate/go up to|by N unit @@ -99,11 +107,11 @@ const DOSE_PATTERNS: Record = { ], de: [ new RegExp( - `\\b(?:erhöh\\w*|steiger\\w*|setz\\w*\\s+(?:hoch|rauf)|geh\\w*\\s+(?:hoch|rauf))\\s+(?:auf|um)\\s+[\\d.,]+\\s*${DOSE_UNIT}\\b`, + `\\b(?:erhöh\\w*|steiger\\w*|setz\\w*\\s+(?:hoch|rauf)|geh\\w*\\s+(?:hoch|rauf))${DE_DOSE_OBJECT}\\s+(?:auf|um)\\s+[\\d.,]+\\s*${DOSE_UNIT}\\b`, "i", ), new RegExp( - `\\b(?:reduzier\\w*|senk\\w*|verringer\\w*|nimm\\s+(?:weniger|runter))\\s+(?:auf|um)\\s+[\\d.,]+\\s*${DOSE_UNIT}\\b`, + `\\b(?:reduzier\\w*|senk\\w*|verringer\\w*|nimm\\s+(?:weniger|runter))${DE_DOSE_OBJECT}\\s+(?:auf|um)\\s+[\\d.,]+\\s*${DOSE_UNIT}\\b`, "i", ), new RegExp(