Status: draft

Кратко: для timeline v1 закрываем минимум gap’ов — category нужно для иконок. Полный coverage всех ресурсов — на v2. Какие именно gap’ы закрываем сейчас — ниже.

Контекст

Аудит builder’ов в packages/analysis-core/src/services/narrative-to-fhir/fhir-builders/*.ts + lib/fhir-*.ts + apps/analysis-worker/.../save-fhir-resources.function.ts (см. [[../domain/fhir-resource-categories]] раздел «Что мы пишем сейчас») показал:

РесурсСтандартный кодCustom параллельноСтатус
Observation (lab)laboratory+ custom panel-categories✅ ок
Observation (narrative)LLM enum 7/9 R4 кодов✅ ок
DiagnosticReportтолько LAB hardcoded⚠️ gap (нет RAD/IMG/EC)
Conditionproblem-list-item hardcoded✅ ок (с rationale про US-Core query)
AllergyIntoleranceLLM enum (все 4 R4)✅ ок
MedicationStatement❌ не set❌ gap
Procedure❌ не set❌ gap
CarePlancustom bloodgpt.com/careplan-category⚠️ только custom, std нет
Composition (top-level)❌ не setsection codes — custom❌ gap
Immunization(resource не emit’ится)❌ gap (нет builder)

Для Timeline иконок нужно чтобы у каждого ресурса был хоть какой-то category — иначе иконка fallback’ается на resource type, теряется гранулярность (например все procedure’ы выглядят одинаково, surgical vs diagnostic не различимы).

Рассматривали

A. Закрыть всё сразу

Добавить category всем ресурсам где gap (5 мест), плюс заменить custom CarePlan system на standard.

Плюс: консистентность, полная картина для timeline. Минус: больше touch-points в коде, больше тестов, дольше до v1 timeline. Часть gap’ов (Immunization builder) — большая работа.

B. Закрыть только то что нужно для visible иконок v1

Что реально появится на timeline v1:

  • Observation lab → ✅ есть laboratory
  • Observation vital-signs → ✅ есть vital-signs
  • Condition → ✅ есть problem-list-item
  • AllergyIntolerance → ✅ есть category из LLM enum
  • MedicationStatement → ❌ gap — добавить
  • Procedure → ❌ gap — добавить
  • DiagnosticReport → ⚠️ только LAB — расширение зависит от document_type которое LLM сейчас не возвращает (см. [[uploaded-document-types-supported]]); отложить до этой фичи
  • Composition / CarePlan → AI-генерации, не на clinical timeline (см. [[timeline-page-design]] про двухтрековое разделение). Иконка нужна только на system timeline, можно использовать какой угодно код или fallback на resource type
  • Immunization → не emit’ится сейчас, не v1

Плюс: минимум изменений для unblock timeline v1. Минус: оставляет gap’ы которые могут confuse в будущем, plus в FHIR search через ?category= некоторые ресурсы не filtable.

C. Не закрывать ничего, fallback на resource type

Иконку на timeline всегда брать из FHIR resource type, не из category. Procedure → ⚙️, MedicationStatement → 💊, etc. Простая 1-1 mapping table.

Плюс: zero code changes. Минус: теряется гранулярность — Observation laboratory vs Observation vital-signs будут одинаковыми иконками, хотя сейчас они различимы. Откат гранулярности.

Выбрали: B (close-the-visible-gaps)

Почему:

  1. Timeline v1 unblock — главная цель сейчас. A слишком много touch-points.
  2. C жертвует уже работающей гранулярностью Observation lab vs vital-signs. Не приемлемо — это ровно то ради чего category вообще нужна.
  3. B минимальная работа: 2 builder’а добавить category выставление + плюс minor updates для Composition/CarePlan если нужно.

Конкретные изменения для v1

MedicationStatement — добавить patientspecified

В narrative-to-fhir/fhir-builders/medication-statement.ts, после resource construction, добавить:

resource.category = [
  {
    coding: [
      {
        system: "http://terminology.hl7.org/CodeSystem/medication-statement-category",
        code: "patientspecified",
        display: "Patient Specified",
      },
    ],
  },
];

Default patientspecified обоснован тем что наши narrative MedicationStatement в основном из выписок или со слов пациента — semantically это и есть «patient-specified». В будущем (если появится integration с pharmacy) можно расширить enum в LLM extracted schema чтобы LLM сам выбирал между community / patientspecified.

Procedure — добавить SNOMED-based category из LLM

В extract-entities.ts schema добавить поле category к ExtractedProcedureSchema:

export const ProcedureCategoryEnum = z.enum([
  "surgical",       // SNOMED 387713003
  "diagnostic",     // SNOMED 103693007
  "counselling",    // SNOMED 409063005
  "education",      // SNOMED 409073007
  "social-service", // SNOMED 410606002
  "other",
]);
 
ExtractedProcedureSchema = z.object({
  ...,
  category: ProcedureCategoryEnum,
});

В narrative-to-fhir/fhir-builders/procedure.ts mapping enum → SNOMED code. Default fallback (other или missing) → 103693007 Diagnostic procedure (большинство наших процедур из narrative — это диагностика).

Composition (top-level) — добавить assess-plan

В fhir-composition-builder.ts, при construction Composition, добавить:

composition.category = [
  {
    coding: [
      {
        system: "http://hl7.org/fhir/composition-category",  // example
        code: "assess-plan",
        display: "Assessment Plan",
      },
    ],
  },
];

Это для system timeline иконки — Composition будет различим от других resources в feed’е.

CarePlan — добавить standard assess-plan parallel к existing custom

В fhir-careplan-builder.ts:164, расширить existing custom bloodgpt.com/careplan-category parallel’но добавить standard:

category: [
  {
    coding: [
      {
        system: `${BLOODGPT_SYSTEM}/careplan-category`,
        code: "follow-up-recommendations",
        display: "Follow-up Recommendations",
      },
      {
        system: "http://terminology.hl7.org/CodeSystem/care-plan-category",
        code: "assess-plan",
        display: "Assessment Plan",
      },
    ],
  },
],

Custom system остаётся (clear-cut «это наш AI-flow»), но standard рядом для interoperability и search filterability.

Что НЕ делаем в v1

  • DiagnosticReport — оставляем LAB only. Расширение требует document_type extraction от LLM (см. [[uploaded-document-types-supported]]) — это отдельная задача.
  • Immunization builder — narrative LLM иногда возвращает content_type: immunization, но dedicated Immunization resource builder не существует. Контент сейчас попадает в Procedure/Observation. Отдельная задача (новый builder + schema). Не критично для v1.
  • Расширять Observation enum до 9 кодов R4therapy и activity в LLM enum не extracted. Не блокер для v1, но в будущем стоит расширить если появятся reference cases.
  • health-concern category для Condition (US-Core) — для self-reported concerns. Не v1; зависит от того, появится ли patient-driven data entry workflow.

Следствия

  • 4 файла нужно тронуть: medication-statement.ts, procedure.ts (+ schema), fhir-composition-builder.ts, fhir-careplan-builder.ts.
  • LLM схема ExtractedProcedureSchema расширяется новым enum field — нужен update промпта narrative_to_entities чтобы LLM возвращал классификацию.
  • Иконка-mapping таблица для timeline берёт category коды как ключи. Mapping table (отдельная UX задача) должна покрыть: laboratory, vital-signs, imaging, social-history, survey, exam, procedure (Observation); food/medication/environment/biologic (AllergyIntolerance); problem-list-item (Condition); LAB (DR); patientspecified (MS); surgical/diagnostic/counselling (Procedure); assess-plan (Composition / CarePlan).
  • FHIR search через ?category=... для clinical history queries будет работать на 5 ранее-не-фильтрабельных ресурсах (MS / Procedure / Composition / CarePlan / DR).

Открытые вопросы

  • Procedure LLM-классификация надёжная? — surgical vs diagnostic line иногда размыта (например биопсия — surgical с diagnostic intent). Может потребоваться fallback на «other» с дополнительным SNOMED через code.coding[].
  • MedicationStatement default patientspecified vs community — если в будущем появится separate pharmacy integration где medications приходят по официальному каналу, нужно будет различать. Решим тогда.
  • Immunization builder timeline — когда добавлять? Зависит от того, насколько часто immunization data появляется в narrative.

Связано

  • [[../domain/fhir-resource-categories]] — survey стандартных ValueSets + audit-таблица + gaps (источник для этого решения)
  • [[../domain/fhir-medication-statement]] — entity-page с описанием gap
  • [[../domain/fhir-procedure]] — entity-page с описанием gap
  • [[../domain/fhir-composition]] — entity-page; gap в top-level category
  • [[../domain/fhir-careplan]] — entity-page; custom-only system gap
  • [[timeline-page-design]] — потребитель этого решения
  • [[uploaded-document-types-supported]] — связан с DR.category расширением

Источники