From ae607f1ceb62045565e4ca6147f057eb80fe1ecf Mon Sep 17 00:00:00 2001 From: Anton Vakhrushev Date: Tue, 4 Aug 2026 07:59:40 +0300 Subject: [PATCH] =?UTF-8?q?tasks:=20=D0=B7=D0=B0=D0=B2=D0=B5=D0=B4=D0=B5?= =?UTF-8?q?=D0=BD=D0=B0=20=D0=B7=D0=B0=D0=B4=D0=B0=D1=87=D0=B0=20=D0=BE=20?= =?UTF-8?q?=D0=B7=D0=B0=D0=BC=D0=B5=D0=BD=D0=B5=20=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=B8=D0=BB=D0=B0=20=D0=BF=D0=BE=D0=BB=D0=BD=D0=BE=D1=82=D1=8B?= =?UTF-8?q?=20=D0=BD=D0=B0=20last=20wins?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - первый шаг — замер: в 1 022 координатах, где полноту решило превосходство полей, была ли более полная точка более поздней; от исхода ветвится всё - «экспорт — источник правды» ограничено двумя рамками: по дате снапшота и по типам, которых в экспорте нет вовсе (stateOfMind) --- docs/tasks/BACKLOG.md | 1 + .../items/last-wins-over-completeness.md | 85 +++++++++++++++++++ 2 files changed, 86 insertions(+) create mode 100644 docs/tasks/items/last-wins-over-completeness.md diff --git a/docs/tasks/BACKLOG.md b/docs/tasks/BACKLOG.md index de2ad7c..b6eae58 100644 --- a/docs/tasks/BACKLOG.md +++ b/docs/tasks/BACKLOG.md @@ -37,6 +37,7 @@ - [Устаревание нижнего слоя после экспорта](items/lower-layer-expiry.md) — Нижний слой растёт на ~100 тысяч координат в сутки, а после экспорта Apple он избыточен - [[idea] Выгрузка в parquet отдельной командой](items/parquet-export.md) — Дверь для тяжёлой аналитики снаружи: DuckDB читает и parquet, и файл SQLite — спешить некуда, но и закрывать не нужно - [Заголовки доставки в архиве рядом с телом](items/delivery-headers-in-archive.md) — Заголовки живут только в базе — потеря базы навсегда ломает вывод слоя при пересборке +- [Правило полноты против last wins](items/last-wins-over-completeness.md) — Полнота решает 1,2% спорных координат, и неизвестно, была ли более полная точка более поздней — от этого зависит, нужна ли она вообще ## инфра - [Активный алерт «данных нет N часов»](items/stream-silence-alert.md) — Пропажу потока сейчас замечает человек, а не сервис diff --git a/docs/tasks/items/last-wins-over-completeness.md b/docs/tasks/items/last-wins-over-completeness.md new file mode 100644 index 0000000..60f7913 --- /dev/null +++ b/docs/tasks/items/last-wins-over-completeness.md @@ -0,0 +1,85 @@ +# Правило полноты против last wins + +- **Секция:** ядро +- **Зачем:** Полнота решает 1,2% спорных координат, и неизвестно, была ли более полная точка более поздней — от этого зависит, нужна ли она вообще +- **Теги:** goal:merge-robustness, sprint:2026-08-03 + +Правило слияния перестаёт зависеть от того, чей набор полей богаче, — либо +зависит ровно там, где замер показал, что без этого теряются данные. Сегодня +неизвестно, какой из двух случаев верен. + +## Откуда задача + +Владелец предложил 2026-08-04 держаться стратегии **last wins**: экспорт Apple +Health — база снапшота, новые доставки HAE затирают предыдущие. Это отменяет +`critical`-инвариант `CLAUDE.md` «при столкновении выигрывает более полная +точка, а не последняя», и потому меняется не молча, а этой задачей. + +Соседняя задача [tie-break-equal-completeness](tie-break-equal-completeness.md) +двигает то же правило в ту же сторону, но осторожнее: она меняет только +тай-брейк при **равной** полноте, оставляя саму полноту первичной. Эта задача +решает, надо ли снимать и её. + +## Замер — первый шаг, и от него ветвится всё остальное + +Из 84 978 спорных координат живого архива (замер 2026-08-04, `tmp/diag`): + +| | координат | +| --- | --- | +| решено полнотой | 1 022 (1,2%) | +| упало на тай-брейк | 83 956 (98,8%) | + +Вопрос ровно один: **в этих 1 022 случаях более полная точка была более поздней +или более ранней?** + +- **Всегда более поздней** — полнота ничего не решает сверх порядка, `last wins` + строго проще и ничего не теряет. Ветка полноты удаляется, инвариант в + `CLAUDE.md` переписывается. +- **Иногда более ранней** — значит HAE присылает обеднённые версии задним + числом, и `last wins` будет молча стирать поля. Тогда полнота остаётся, а + граница её применения записывается числом: сколько таких случаев, у каких + метрик, какие поля пропадали. + +Замер обязан различать **точки метрик** и **сущности** (`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`, ноль противоречащих часов + +## Рамки + +Схему не трогаем. Отпечаток витрины изменится — пересборка обязательна и +делается человеком при остановленном сервисе; подмена файла базы необратима и в +задаче не выполняется. Берётся **после** +[tie-break-equal-completeness](tie-break-equal-completeness.md): та меняет то же +место, и замер до её вливания ответит на вопрос про уже неактуальное правило. + +Связано: находки 10, 47, 49, 53; `docs/architecture.md` → «Разрешение +столкновений»; `docs/review.md`, запись 2026-08-04.