# Код HealthKit кладётся реестром рядом, а не полем внутри точки - Дата: 2026-08-03 - Источник: openspec/changes/archive/2026-08-03-slovar-kategorialnyh-znachenij/design.md ## Решение Стабильный код HealthKit для локализованной строки хранится **отдельной строкой таблицы `category_value`** с ключом `(метрика, поле, значение)`, а не полем `value_code` внутри точки, как рисовал `architecture.md`. Словарь и таблица синонимов живут в бинаре (`internal/healthkit`), а не в базе. Наблюдение входит в отпечаток витрины, выведенный код — **нет**. ## Почему Рассматривались три формы, и отвергнутые названы вместе с ценой. **Поле внутри точки** — отвергнуто. Цитата источника: «Точка хранится **исходными байтами**; дописать в неё ключ можно только пересериализацией, а она теряет литерал (`1.0` → `1`, целые больше 2^53 сдвигаются, невалидный UTF-8 → U+FFFD) — ровно то, от чего `Point.Raw` защищает. Побайтовая врезка в чужой JSON — фокус, а не решение. Параллельный массив кодов в `bucket` завёл бы производную величину в путь слияния и хеширования: правило полноты, тай-брейк и `content_hash` пришлось бы учить носить код, не давая ему влиять на исход. Правка на поверхности `critical`-инвариантов ради нуля новых сведений — код есть **функция** от того, что уже лежит». **Код нигде не хранится, выводится на чтении** — отвергнуто по одной причине: «тогда код недостижим ничем, кроме бинаря. Владелец сегодня читает витрину `sqlite` на хосте (`Read API` ещё нет), а вся задача затевается против того, что „клиент угадывает словарь“. Реестр без кода сообщает только „такая строка была“ — это половина ответа». **Словарь в базе, а не в бинаре** — отвергнуто: «словарь стал бы входом, которого нет в журнале, и `import + replay` перестал бы задавать состояние однозначно. `stateOfMind` уже единственная дыра в журнале; вторую заводить незачем». **Код вне отпечатка** — обратная сторона того же решения: «Ключ и провенанс — функция журнала; `code` — функция журнала **и версии словаря в бинаре**. Включи его в отпечаток, и он перестал бы отвечать на свой единственный вопрос („дал ли повтор журнала то же состояние“) ровно тогда, когда его задают: всякое пополнение словаря — а оно объявлено рабочим циклом — давало бы расхождение при побайтно совпавшем журнале, и человек, принимающий необратимое решение о подмене базы, читал бы это как дефект». Prior art: FHIR `ConceptMap` (отображение «чужая система значений → своя») и `CodeSystem` с `replaced-by` для устаревших имён — те же два отношения, разведённые по разным сущностям. Форма взята, реализация FHIR отвергнута ценой. ## Последствия - `+` Инвариант «точки хранятся дословно» не тронут вовсе: точка не меняется ни байтом, обратное преобразование возможно всегда. - `+` Пути слияния, тай-брейка и `content_hash` не знают о кодах — правки на поверхности `critical`-инвариантов не потребовалось. - `+` Пополнение словаря меняет десяток строк реестра, а не каждый объект с фазами сна; отпечаток при этом не двигается, потому что код в него не входит. - `−` Потребитель обязан делать соединение по `(метрика, поле, значение)` вместо чтения одного поля. Форма ответа Read API это скроет, когда он появится. - `−` Код в базе отстаёт от словаря в бинаре для строк, переставших приезжать. Лечится пересборкой; на сходимость не влияет. - `−` Ключ реестра зафиксирован миграцией `00010`: смена формы ключа стоит второй миграции и пересборки.