Это не canonical шаблон, по которому надо обязательно собирать страницу. Это inspiration — каркас, который однажды собрался под validity-classifier и оказался читаемым. Берите оттуда то, что подходит вашему сервису; не пытайтесь натягивать разделы, для которых у вас нет содержания. Многие пункты ниже специфичны для validity-классификатора (батчи + бисекция + worker-pool в коде агента) и исчезают, если сервис устроен иначе.

Что описывает каркас: LLM-сервис со structured output (generateObject / responseSchema / эквивалент) и обработкой N независимых элементов. Архитектурный паттерн «гранулярность вызовов + трёхуровневая декомпозиция» — см. llm-call-granularity, llm-call-failure-classes.

Решения по интеграции (где в пайплайне, куда output, Inngest-декомпозиция, id-прокидка, стыковка с соседними фильтрами) — в companion integration decision page, не дублируются здесь. Но если интеграция уже не вопрос, отдельная decision-страница может быть лишней.

Скелет страницы

---
type: concept
status: draft
updated: <YYYY-MM-DD>
---

# <Имя сервиса>

<lead-абзац: один абзац — что разбирает страница, зачем (порт / интеграция /
онбординг), где живёт код, ссылка на companion integration decision (если есть).>

Ниже — секции по порядку. «Если применимо» означает буквально это: нет содержания — нет секции.

1. ## Что это и зачем

Один параграф: что сервис отвечает (1 предложение), какой closed-set output, какой input. Сравнить с соседними сервисами («он отвечает на X, не на Y»). Терминологическая записка — если в системе сущность называют по-разному (analyte / биомаркер / параметр / Observation), назвать прямо.

Если в пакете есть слои, которые легко спутать (например, stub’ы / data-tables рядом с типами/валидатором, или mis-naming файлов) — подсветить, чтобы не путать. Не делать этого «на всякий случай».

2. ## I/O-контракт

Таблица полей для input и output симметрично — см. wiki-io-contract-format. Формат столбцов:

  • Input: Поле | Тип | Источник | Зачем модели
  • Output: # | Поле | Тип | Что решает | Что НЕ смотрит

TS-shape — в коде, не на странице; § «Где в коде» линкует на типы. JSON-пример — опциональный companion под таблицей, когда shape вложенная (FHIR-объекты внутри bundle, sub-объекты со своими полями) или когда конкретные значения проясняют семантику быстрее описания.

Если есть input-builder (чистая функция, нормализующая источник в LLM-инпут — dedup, drop-условия, нормализация → строки) — отдельный параграф.

Если есть link-back identifier (fhirObservationId / id ресурса) — отдельный параграф: где в input, где в output, как протаскивается; pointer на llm-fhir-linkback.

Что является настоящим output-ом, который читают downstream-потребители (один enum-статус? весь объект? часть его?) — назвать прямо. Производные поля, computed в коде после LLM-ответа, отметить как computed.

3. ## Выходная схема как CoT (если применяется приём)

Только если порядок полей выходной схемы используется как structured-output-field-order-cot. Если приём не применён — секции нет, либо она просто описывает output без CoT-фрейма.

Когда применяется: лид-абзац про порядок-полей-как-CoT с ссылкой на pattern-страницу; таблица полей (# | Поле | Тип | Что решает | Что НЕ смотрит). Подчеркнуть, что является финальным аутпутом (статус / id / etc.), а что — служебные поля рассуждения. Производные computed-поля явно отметить.

4. ## Три уровня обработки (если есть батчинг)

Если сервис делает один LLM-вызов на запрос — этой секции нет, всё описывается в одной § «Один LLM-вызов». Три уровня появляются, когда есть разделение один LLM-вызов ⊂ один батч ⊂ один прогон — типично при N независимых элементов с групповой обработкой.

Подсекции — по факту: что у этого сервиса реально есть.

  • ### Один LLM-вызов — то, что выполняется на этом уровне у этого сервиса. Если используется generateObject — назвать; если другая библиотека или собственная обёртка — назвать её. Retry/timeout/fallback — назвать там, где это живёт: в коде агента, в SDK-врапере, или в инфра-слое (LLM-прокси). Если retry-policy уже на прокси — не описывать как «есть в коде».
  • ### Один батч — preprocess + один вызов + postprocess валидатор. Указать, что именно валидатор проверяет в этом сервисе. Нюансы пустых / опциональных полей — буллитами, если есть.
  • ### Один прогон — chunk → fan-out → каждый батч → собрать финальный результат + метаданные. Если worker-pool / бисекция / retry-loop реализованы в коде агента — это анти-паттерн (no-self-rolled-queues), при порте → Inngest; companion integration decision § Inngest-декомпозиция.
  • ### Константы — env-override-таблица только для констант, реально использующихся. Альтернативно — описывать константу там, где она используется, без отдельной таблицы. Не выставленные константы упоминать, если это open-вопрос (например, maxOutputTokens — см. gemini-doom-loop).

5. ## Классы сбоев

Применить таксономию llm-call-failure-classes (транспорт / схема / семантика) — но только в той мере, в какой эти классы видны в коде сервиса. Канонически:

  • Транспорт должен жить на прокси, не в коде агента. Если в коде есть transport-retry — это карри-овер, отметить как «кандидат на вынос».
  • Схема (schema-cleaning, превентивно до вызова) — тоже на прокси.
  • Семантика — остаётся в коде агента (validator → reject → retry-with-hint → degrade). Это главный класс, который описывается в секции.

Подчеркнуть: все классы дают partial-результат, прогон не валится.

6. ## Зависимости / самодостаточность (если есть что сказать)

Что импортирует / не импортирует — описывать, если это аргумент за переносимость (мало cross-cuts → легко портится). Port-gotchas (import.meta.url / __dirname под CJS-бандлом, имя файла врёт) — если они есть и реально кусают. Pointer на companion integration page § Следствия для подробного разбора. Не делать секцию, если зависимостей нечего обсуждать.

7. ## Где в коде

Таблица Артефакт | Расположение. Расположение — ссылка на код (на конкретной ветке), не голая строка пути: чтобы из других страниц можно было кликнуть и попасть в файл. Артефакты — те, что есть: оркестрация, публичный orchestrator, input-builder, промпт-файл, типы, валидатор, eval-suite, CLI, модель-резолвер, адаптеры, ветка/автор. Если артефакта нет — не упоминать.

8. ## Диаграммы (если помогают)

Диаграммы — там, где они проясняют. Без шаблонного лида про «ASCII-черновики». Что бывает полезно:

  • ### D1 — control flow одного прогона — control flow от точки входа до финального аутпута. Над диаграммой — короткий лид: какие классы сбоев видны в этом control flow для этого сервиса. Подчеркнуть «partial-результат, прогон не валится» — если это так.
  • ### D2 — модель данных — поток от source-of-truth через input-builder через LLM-вызовы в финальный output-тип. TBD-куски (куда персистится, например) — ссылкой на decision-страницу (которая обсуждает вопрос), не голым TBD-маркером.

D3 «где воткнуть в пайплайн» — не сюда. Это архитектурная сшивка → companion integration decision page. Здесь — pointer.

9. ## Открытые вопросы

Только важные открытые вопросы — те, что действительно нерешены и реально кусают. Не делать секцию ради секции. Если интеграционные вопросы не проблемные, отдельная companion integration decision page может не понадобиться; если проблемные — выносить туда, не дублировать здесь.

Если есть смысл — разделить по адресату («к автору сервиса» / «к команде по пайплайну»), но не насильно.

10. ## Связано

Фокусировано, не все связанные страницы подряд. Включать то, что по смыслу стоит посмотреть после этой страницы. Не повторять ссылки, уже процитированные выше в тексте (особенно базовые — failure-classes, granularity и т.п.) — если упомянуты в lead-абзаце или в § 5 «Классы сбоев», нет смысла дублировать в § «Связано».

Соседние сервисы — да, как ориентир. Технические нюансы, на которые уже сослались — нет.

11. ## Источники

Branch + commit HEAD + spec-файлы + plan-файл + автор (того, кто это собрал / писал код / промпт). Если есть MD-файлы рядом с кодом (SPEC.md, DEBUG_METHODOLOGY.md) — линковать. Цитаты из митингов / Slack / fireflies — как footnotes ([^N]) к конкретным claim’ам в теле, определения здесь. См. wiki/CLAUDE.md § «Источники — формат ссылок».

Граница: что — здесь, что — в companion integration decision page

Когда integration действительно вопрос — отдельная companion decision page по wiki/CLAUDE.md § «Граница: что входит в decision, а что в concept». Когда integration не вопрос (сервис готов и работает, всё стыкуется) — companion decision не нужен, всё сидит в concept-странице или вообще нигде. Не плодить decision-страницы для не-проблем.

Concept-страница (эта)Companion integration decision (если вопрос)
Внутренности (что отвечает, схема output, валидатор, как обрабатывается вызов / батч / прогон)данет
Константыданет
File pathsда (§ «Где в коде»)нет
D1 / D2данет
D3 (где в пайплайне)нет — pointerда
Где живёт output (ephemeral / FHIR / Postgres)нет — pointerда
Idempotency между перезапускаминет — pointerда
Inngest-декомпозициянет — pointerда
Стыковка с соседними фильтрами (TTL / triage / retrieval)нет — pointerда
Companion-страница: каркасКонтекст → Вопрос 1 → Вопрос 2 → … → Позиции → Что нужно для разрешения → Следствия → Открытые вопросы → Связано → Источники

Companion-decision создавать сразу при первом сомнении (status: draft), переходит в active когда фиксируется.

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

  • Один сервис = одна concept-страница vs одна companion decision — норм; что делать, если несколько сервисов делят companion (общая интеграция-история) — пока не встречалось. TBD по факту, если возникнет.

Связано

  • validity-classifier — каноничный пример, из которого собран каркас (смотреть если что-то непонятно)
  • llm-call-granularity — паттерн «1 / N / батчами» + трёхуровневая декомпозиция
  • biomarker-actuality-integration — пример companion integration decision page

Источники

  • validity-classifier (origin/feat/v2-5) — служила образцом при сборе шаблона. Автор сервиса: Артур.
  • wiki/CLAUDE.md § «Граница: что входит в decision, а что в concept», § «Формат страницы», § «Типы страниц», § «Источники — формат ссылок»
  • Discussion: chain 6bfe41d1; ревизия 12 мая 2026 — критика «слишком частный кейс / слишком прескриптивный шаблон» (Ильдар).