# 🔬 Измерить, нужно ли правило полноты рядом с LWW

- **Тип:** research
- **Категория:** Ядро
- **Зачем:** Полнота решает 1,2% спорных координат, и неизвестно, была ли более полная точка более поздней — от этого зависит, нужна ли она вообще
- **Теги:** goal:merge-robustness, sprint:2026-08-03

Правило слияния перестаёт зависеть от того, чей набор полей богаче, — либо
зависит ровно там, где замер показал, что без этого теряются данные. Сегодня
неизвестно, какой из двух случаев верен.

Двигает строку «Завершения» цели: «Правило выбора между версиями измерено: полнота либо нужна, либо снята».

## Откуда задача

Владелец предложил 2026-08-04 держаться стратегии **LWW** («выигрывает
последняя»): экспорт Apple
Health — база снапшота, новые доставки HAE затирают предыдущие. Это отменяет
`critical`-инвариант `CLAUDE.md` «при столкновении выигрывает более полная
точка, а не последняя», и потому меняется не молча, а этой задачей.

Соседняя задача «Тай-брейк при равной полноте» двигала то же правило в ту же
сторону, но осторожнее: она поменяла только тай-брейк при **равной** полноте,
оставив саму полноту первичной. Она сделана 2026-08-04 — решение записано в
[ADR о тай-брейке по порядку журнала](../../adr/ADR-2026-08-04-tie-break-po-poryadku-zhurnala.md).
Эта задача решает, надо ли снимать и саму полноту.

## Замер — первый шаг, и от него ветвится всё остальное

Из 84 978 спорных координат живого архива (замер 2026-08-04, `tmp/diag`):

| | координат |
| --- | --- |
| решено полнотой | 1 022 (1,2%) |
| упало на тай-брейк | 83 956 (98,8%) |

Вопрос ровно один: **в этих 1 022 случаях более полная точка была более поздней
или более ранней?**

- **Всегда более поздней** — полнота ничего не решает сверх порядка, LWW
  строго проще и ничего не теряет. Ветка полноты удаляется, инвариант в
  `CLAUDE.md` переписывается.
- **Иногда более ранней** — значит HAE присылает обеднённые версии задним
  числом, и LWW будет молча стирать поля. Тогда полнота остаётся, а
  граница её применения записывается числом: сколько таких случаев, у каких
  метрик, какие поля пропадали.

Замер обязан различать **точки метрик** и **сущности** (`workouts`,
`stateOfMind`): у сущностей отношение другое — покрытие, код другой
(`internal/store/winner.go`), и он не мерялся вовсе.

Оба исхода — законный результат задачи. Исход «полнота нужна» не считается
провалом и не отменяет предложение владельца: он его уточняет границей.

## Две рамки, без которых «экспорт — источник правды» ломает работающее

Обе выведены при постановке и в замере не нуждаются:

1. **По времени.** Экспорт — снапшот на дату выгрузки; доставки HAE после этой
   даты обязаны его перекрывать, иначе новые данные не доедут. Совместимо с
   инвариантом «хранилище — свёртка по журналу»: `import(экспорт) +
   replay(доставки по received_at)`.
2. **По типам.** `stateOfMind` в экспорте Apple отсутствует ни одним типом
   (измерено, `docs/research/apple-health.md`). Для него единственный источник —
   доставки HAE, и объявить экспорт источником правды для него нельзя.

## Критерии приёмки

- для 1 022 координат, где полнота решила исход, названо число: в скольких из
  них более полная точка была более поздней — оракул: прогон замера на живом
  архиве, число воспроизводится вторым прогоном
- тот же вопрос отвечён отдельно для сущностей (`workouts`, `stateOfMind`) —
  оракул: тот же прогон, отдельная колонка
- правило слияния приведено к исходу замера, и `CLAUDE.md` говорит то же, что
  делает код — оракул: глазами, сверка формулировки инварианта с реализацией
- повторный прогон живого архива даёт тот же отпечаток, живая свёртка равна
  пересборке — оракул: `task verify:archive` дважды подряд
- ни одна метрика не потеряла род из-за изменения правила — оракул:
  `task verify:archive`, ноль противоречащих часов

## Рамки

Схему не трогаем. Отпечаток витрины изменится — пересборка обязательна и
делается человеком при остановленном сервисе; подмена файла базы необратима и в
задаче не выполняется. Тай-брейк при равной полноте уже влит, поэтому замер
отвечает про действующее правило, а не про снятое.

Связано: находки 10, 47, 49, 53; `docs/architecture.md` → «Разрешение
столкновений»; `docs/review.md`, запись 2026-08-04.
