From 473b35e997f685bae22f67dd5efb16cec9a7b05d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Fri, 26 Jun 2026 13:39:21 +0200 Subject: [PATCH] Guard that get_labs carries each reading's value and unit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The labs block already surfaces value + unit + range + date, and the Coach prompt instructs it to quote them verbatim, so labs are cited end-to-end — no gap. Pin that with a tool-level guard asserting value and unit survive get_labs, so a future change to the labs pass-through can't quietly strip the number the Coach needs to cite. --- .../ai/coach/__tests__/tools-executor.test.ts | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/src/lib/ai/coach/__tests__/tools-executor.test.ts b/src/lib/ai/coach/__tests__/tools-executor.test.ts index 79e703b5a..2c6c93551 100644 --- a/src/lib/ai/coach/__tests__/tools-executor.test.ts +++ b/src/lib/ai/coach/__tests__/tools-executor.test.ts @@ -198,6 +198,45 @@ describe("executeCoachTool", () => { expect(miss.reason).toBe("analyte_not_found"); }); + // v1.21.0 (NEW-A) — labs must reach the Coach with their value AND unit so it + // can cite the reading, not just name the biomarker. The snapshot labs block + // already carries { analyte, value, unit, rangeStatus, … }; the tool passes + // it through verbatim. This guards that the value + unit survive the tool. + it("get_labs carries each reading's value and unit (citation guarantee)", async () => { + buildCoachSnapshot.mockResolvedValue( + snapshot({ + labs: { + recent: [ + { + analyte: "LDL", + value: 120, + unit: "mg/dL", + rangeStatus: "above", + referenceLow: null, + referenceHigh: 116, + takenAt: "2026-06-01T00:00:00.000Z", + }, + ], + }, + }), + ); + const result = await executeCoachTool({ + userId: "u1", + name: "get_labs", + rawArguments: "{}", + }); + expect(result.present).toBe(true); + const data = result.data as { + recent: Array<{ value: number; unit: string; rangeStatus: string }>; + }; + expect(data.recent[0]).toMatchObject({ + analyte: "LDL", + value: 120, + unit: "mg/dL", + rangeStatus: "above", + }); + }); + it("returns the workouts section for get_workouts when present", async () => { buildCoachSnapshot.mockResolvedValue( snapshot({ workouts: { recent: [{ sport: "RUN" }], totalInWindow: 3 } }),