mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-15 06:52:04 +00:00
fix(insights): strip metric tokens in recommendation prose + DE component labels
Two related leak fixes traced to the same root: every other AI-prose
surface ran user-visible text through `stripChartTokens()`, but the
recommendation card's always-visible text was rendered directly. The
model embeds `metric:WEIGHT` and friends in the rec text expecting an
inline mini-chart, but the chart slot lives in the EXPANDED rationale
block — so the token had nowhere to be consumed and leaked verbatim
to the DOM.
- `<RecommendationCard>` now wraps `norm.text` in
`stripChartTokens()`, matching the contract every other
rec / finding / summary surface already uses.
- DE Health-Score component labels surfaced English nouns ("Mood",
"Medis", "BD") instead of full German. Rename to "Blutdruck",
"Stimmung", "Einnahmetreue", "Gewicht" so the four sub-bars read
natively German. The "Mut" the maintainer reported on prod was
voice-to-text rendering "Mood".
- i18n-integrity test pins the four component labels so a copy-paste
regression can't reintroduce the leak.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
4d3b12936a
commit
59a4a89b62
4 files changed
+72
-4
No files matched your search
@@ -243,4 +243,36 @@ describe("<RecommendationCard>", () => {
|
||||
);
|
||||
expect(html).not.toContain("data-feedback-thumb=");
|
||||
});
|
||||
|
||||
/**
|
||||
* v1.4.22 A6 — strip chart-tokens from the always-visible
|
||||
* recommendation text. W1a §3 traced the production "metric:WEIGHT"
|
||||
* leak Marc saw at the bottom of the advisor card to this exact
|
||||
* render path: every other AI-prose surface ran through
|
||||
* `stripChartTokens()`, the recommendation text didn't. Pin the
|
||||
* strip so a future copy-paste regression can't reintroduce the
|
||||
* leak.
|
||||
*/
|
||||
it("strips literal chart tokens (metric:WEIGHT et al) from the rec text", () => {
|
||||
const recWithToken = {
|
||||
...recBase,
|
||||
text: "Increase fluids. metric:WEIGHT",
|
||||
};
|
||||
const html = render(<RecommendationCard rec={recWithToken} index={0} />);
|
||||
expect(html).not.toContain("metric:WEIGHT");
|
||||
expect(html).toContain("Increase fluids.");
|
||||
});
|
||||
|
||||
it("strips lowercase / mixed-case model output from the rec text", () => {
|
||||
const recWithLowercaseToken = {
|
||||
...recBase,
|
||||
text: "Watch the trend. metric:blood_pressure_sys today",
|
||||
};
|
||||
const html = render(
|
||||
<RecommendationCard rec={recWithLowercaseToken} index={0} />,
|
||||
);
|
||||
expect(html).not.toContain("metric:blood_pressure_sys");
|
||||
expect(html).toContain("Watch the trend.");
|
||||
expect(html).toContain("today");
|
||||
});
|
||||
});
|
||||
@@ -11,6 +11,7 @@ import type {
|
||||
import { HealthChart } from "@/components/charts/health-chart";
|
||||
import { MoodChart } from "@/components/charts/mood-chart";
|
||||
import { getMedicalReferenceById } from "@/lib/ai/medical-references";
|
||||
import { stripChartTokens } from "@/lib/insights/chart-tokens";
|
||||
import type { Locale } from "@/lib/i18n/config";
|
||||
import {
|
||||
RecommendationFeedback,
|
||||
@@ -333,7 +334,9 @@ export function RecommendationCard({
|
||||
{norm.severity}
|
||||
</Badge>
|
||||
)}
|
||||
<p className="text-sm leading-snug">{norm.text}</p>
|
||||
<p className="text-sm leading-snug">
|
||||
{stripChartTokens(norm.text)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in new issue
Block a user