Files
HealthLog/docs/audit/v1416-ai-roadmap.md
T
Marc-André BombeckandClaude Opus 4.7 a1cf9bcb37 docs: scrub PII + internal jargon + German leaks from user-facing artifacts
Retroactive cleanup of CHANGELOG.md and docs/audit/v1.4.X-summary.md
files. Removes maintainer's real name, specific health figures tied to
the live tenant, internal release-process jargon, and stray German
strings in English-only release notes. Pre-release PII-grep gate added
to the v1.4.20 release pipeline. No git history rewrite.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 15:14:28 +02:00

9.2 KiB

v1.4.16+ AI hardening roadmap

Authored: 2026-05-09 during the v1.4.15 Stage C1 cycle.

Maintainer brief, verbatim 2026-05-09:

"Es darf null Halluzinationen haben und es muss sich halt irgendwie stützen auf medizinische Dinge. Ich möchte da eine gewisse Dynamik drin haben."

("Zero hallucinations. Must ground on medical facts. I want some dynamism.")

v1.4.15 Stage C1 ships infrastructure — the schema, the wrapper, the citation enforcement, the scope-hardened prompt, the slug-drift defence. The medical-grounding work, the multi-provider redundancy, the explainability layer and the feedback loop land in v1.4.16+.

This document is the iteration roadmap the maintainer asked for.

What landed in v1.4.15 (recap)

Deliverable Commit subject
AIProvider interface + MockAIProvider + tests refactor(ai): consolidate providers behind AIProvider interface
Strict response schema + retry-once wrapper swept into docs(audit): v1.4.15 empty-states audit + i18n keys (sibling race)
Citation-from-data invariant (11 tests) feat(ai): enforce citation-from-data on every recommendation
Scope-hardened versioned prompt (PROMPT_VERSION) feat(ai): scope-hardened system prompt with refusal pattern
Slug-drift defence (chain + cache) feat(ai): fallback-chain slug discovery with 1h positive cache
Spec docs/codex-protocol-spec.md §7b (rolled into the slug-drift commit)

NOT done in v1.4.15 (deliberately deferred):

  • The /api/insights/generate route does NOT yet adopt generateInsight() — the existing UI consumes the rich legacy shape ({summary, classification, findings, correlations, dataQuality, ...}). Migrating the consumer is v1.4.16 work because it requires UI changes to <InsightAdvisorCard /> and <InsightsCard /> to render the new strict shape.
  • The scope-hardened prompt at src/lib/ai/prompts/insight-generator.ts is exported but not yet wired in. Same reason — the UI needs to consume recommendations[]-as-objects + citations[] + warnings[] before the prompt can be flipped.

v1.4.16 — Medical-reference grounding

Goal: the model no longer cites "ESH/ESC 2024 generic guidance" verbatim from the prompt. Instead it cites a small embedded quote from the guideline document, with a rendered footnote in the UI.

Subtasks

  1. Curate the reference set. A versioned bundle in src/lib/ai/references/ containing ~40 short excerpts:

    • ESH/ESC 2024 BP guidelines — adult target, hypertension stages, non-pharmacological measures.
    • AHA/ACC 2017 BP guidelines — alternative thresholds (USA-context).
    • WHO 2021 sleep / activity guidance.
    • Saint-Maurice et al. 2020 step-count dose-response.
    • DGE / DEGAM (German GP) lifestyle leaflets — already-translated plain language for the German UI.
    • DGE healthy-weight (BMI bands).
    • AASM 2015 sleep duration consensus. Each excerpt: { id, source, citation, excerpt, locale, validity }. Curated by hand (NOT scraped) — no quote longer than 200 characters to stay inside fair-use.
  2. Inject relevant excerpts as system context per request. A pre-flight step selectRelevantReferences(features) looks at the user's snapshot and selects 3-5 excerpts most relevant (BP elevated → ESH/ESC excerpts; weight gain → DGE BMI excerpts). Injected into the system prompt as a ## Relevant guidelines block with each excerpt verbatim.

  3. Schema extension. Each recommendation[].metricSource and each citations[] entry gains an optional referenceId field pointing into the bundle — the model uses an excerpt's id to back its citation. The UI then renders a footnote [1] ESH/ESC 2024, p. 17 — "..." next to the recommendation.

  4. Hallucination guard for references. Schema rejects any referenceId not in the bundle. The model cannot fabricate a citation pointing at a guideline that doesn't exist.

  5. Locale support. German excerpts shipped first — the live tenant's primary locale. English fallback on missing.

Test pivot: a fixture user with elevated BP must surface ESH/ESC excerpt #4 via the model's recommendation; a low-data user must NOT surface anything (the prompt instructs an empty recommendations[]).

v1.4.16 — Multi-provider redundancy

Goal: a single provider's outage no longer breaks insights generation.

Subtasks

  1. MultiProviderCascade wrapper. Implements AIProvider. Holds an ordered list of underlying providers (Codex, OpenAI, Anthropic, Local). Tries each in order; on transient error (5xx, 429, slug_rejected_503 from the new chain), falls through to the next. On terminal-but-permission error (401, 403), surfaces verbatim — no fall-through (the user has to fix the credential).

  2. Selection policy in admin settings. Admin can pick the cascade order in /admin/integrations. Default: user's chosen primary, then the admin OpenAI key, then any other connected per-user provider.

  3. Cost / latency budget. Cascade only kicks in on hard error, never on schema-mismatch (the strict-schema retry already handles that). Per-request total wall-clock budget: 60 s.

  4. Telemetry. Wide-Event ai.cascade.attempts: ["codex","admin-key"]

    • ai.cascade.outcome: "success" | "all-failed".

v1.4.16 — Per-recommendation explainability

Goal: every recommendation can answer "why did you tell me this?" in one click.

Schema gains recommendation[].rationale: { dataWindow, comparedTo, deviation }:

rationale: {
  dataWindow: "last7days",            // window the recommendation analysed
  comparedTo: "your last90days avg",  // baseline used for the call
  deviation: "+5 mmHg",               // magnitude of the divergence
}

UI: a small "Why?" chevron under each recommendation expands a one- line tooltip: "Your avg7 (78 bpm) is 5 bpm above your 90-day median (73 bpm). That's clinically meaningful per ±5 bpm threshold (ESH/ESC 2024)." — assembled from rationale + the linked reference excerpt.

v1.4.17 — User-feedback loop

Goal: the system prompt ratchets over time based on user signals.

  1. Per-recommendation thumb-up / thumb-down in the UI. Persisted in a new InsightFeedback table:

    id            uuid
    userId        uuid (FK)
    recommendationId   text  (the schema's stable id)
    recommendationText text  (snapshot — id is per-response, may be reused)
    helpful       bool
    createdAt     timestamp
    
  2. Aggregate dashboard for admin under /admin/ai-quality. Shows helpful-rate per recommendation type (severity, metricSource.type), per locale, per provider type, per PROMPT_VERSION.

  3. Prompt revision workflow. When a recommendation type drops below 60% helpful-rate over 100 ratings, an admin alert proposes a prompt revision — PROMPT_VERSION bumped, A/B-tested for one week, kept or reverted.

v1.4.17 — Confidence score per recommendation

Goal: every recommendation gets a calibrated confidence score the UI renders as a coloured pill (green ≥ 0.85, yellow 0.6-0.85, red < 0.6).

Schema: recommendation[].confidence: number ∈ [0, 1]. Threshold calibration done from the v1.4.17 feedback loop's ground-truth ratings.

This is NOT the same as the legacy dataQuality.confidence enum (which captures snapshot density, not per-recommendation confidence).

v1.5 — Optional "deep mode" with reasoning

The Codex protocol supports reasoning summaries (§1a of the spec). We currently emit reasoning: null to keep responses fast and cheap. v1.5 introduces an opt-in "Deep analysis" toggle in Settings that switches reasoning: { effort: "medium", summary: "concise" } on, fetches the reasoning channel separately, and renders a collapsed "Show reasoning" section under each insight. Reasoning content goes into a separate reasoning schema field — never folded into summary.

This is gated behind a per-user setting because reasoning costs ~5x more tokens.

Discoverability sanity-check

Before each iteration: re-fetch gh api repos/openai/codex/contents/codex-rs/models-manager/models.json and update DEFAULT_SLUG_FALLBACK_CHAIN. The chain was correct for the 2026-05-09 allow-list snapshot; future drift is automatically absorbed by the cache + walk, but the default order should still match upstream's "default plan" preference so a freshly-deployed instance never has to walk on its first request.

What's NOT on the roadmap

  • Self-hosted fine-tuning. Out of scope — HealthLog's user base doesn't justify training cost.
  • Image input. Snapshots are numeric; image processing belongs to a separate "doctor-report scan" feature.
  • Conversational memory. Insights are stateless per call. The multi-turn UX is owned by a future /coach route, not the insights generator.

Owner: maintainer + future Stage-C work. Cross-link from .planning/phase-C1-report.md and the v1.4.15 release notes.