Files
healthlog/openspec/changes/archive/2026-08-04-tie-break-equal-completeness/proposal.md
T
av b278501a6e store: при равной полноте точек побеждает пришедшая доставка
- байтовый порядок канонических форм остался тай-брейком только внутри одной
  доставки: на живом корпусе он решал 98,8% спорных координат и системно хранил
  меньшее значение, из-за чего step_count терял род и verify:archive был красным
- правило перестало быть коммутативным осознанно, поэтому порядок свёртки
  приведён к журнальному: проход воркера прекращается на отложенной доставке,
  а свёртка вне порядка журнала пишет WARN
- заведены счётчики PointsHeld и PointsErased — удержание полнотой и
  единственное направление, в котором правило теряет содержание
2026-08-04 11:16:24 +03:00

6.0 KiB

Why

Тай-брейк при равной полноте точек сегодня — порядок канонических форм, и он решает 98,8% спорных координат (83 956 из 84 978 при 453 171 координате): полнота отвечает лишь в 1,2% случаев. Лексикографический порядок системно берёт меньшее значение (1 847 из 1 912, находка 49), то есть хранит устаревшую версию точки там, где HAE досчитывает задним числом (находка 10).

У дефекта появился независимый оракул: task verify:archive покраснел на master без единого коммита, с ростом корпуса. Час 2026-08-03T07:00Z метрики step_count получил четыре доставки с двумя различными значениями; победило меньшее, оно же приехавшее первым, и сверка слоёв объявила метрику мгновенной против 23 согласных часов. step_count ушёл в unknown — Read API больше не имеет права суммировать шаги.

Решение владельца от 2026-08-04: при равной полноте побеждает пришедшая точка, а не сохранённая. Значение при этом не интерпретируется — правило остаётся структурным.

What Changes

  • BREAKING (содержимое витрины): при равной полноте побеждает точка, пришедшая этой доставкой, а не сохранённая. Отпечаток витрины обязан измениться; состояние восстанавливается пересборкой из архива.
  • Порядок канонических форм остаётся тай-брейком внутри одной доставки, где провенанс общий и различать нечем.
  • Правило слияния точек перестаёт быть функцией множества и становится явной функцией порядка журнала. Отсюда — плата, которую изменение обязано внести целиком: порядок свёртки приводится к порядку журнала.
  • Фоновый воркер прекращает проход на первой отложенной доставке, а не перешагивает её. Иначе занятость базы переставляет доставки местами, и живая витрина расходится с пересборкой молча.
  • Свёртка доставки, у которой в журнале уже есть свёрнутая преемница, пишет WARN: остаточное окно (конкурентный приём делает строку учёта видимой не в порядке меток) закрыть без изменения приёма нельзя, но молчать о нём нельзя тем более.
  • Заводится счётчик «пришедшая точка проиграла сохранённой» — отдельно от общего MergeStats.Overwrites, который считает столкновения в обе стороны и различить их не даёт.
  • Схема не трогается: колонок провенанса на точку не заводится, формат payload не меняется, миграции нет.

Capabilities

New Capabilities

Новых нет.

Modified Capabilities

  • storage: тай-брейк при равной полноте точек — пришедшая побеждает сохранённую; порядок канонических форм остаётся только внутри доставки. Требование «победитель есть функция множества точек» заменяется на «функция множества точек и позиции в журнале». Новый счётчик удержаний.
  • ingest: проход воркера прекращается на первой отложенной доставке; свёртка вне порядка журнала становится наблюдаемой.
  • reindex: посылка сходимости («приём шёл последовательно») из оговорки сценария становится названным условием требования — теперь от порядка свёртки зависит не только вывод слоя, но и содержимое точек.

Impact

  • internal/store/bucket.gomergePoints, resolve, candidate, pointLess, MergeStats.
  • internal/store/delivery.go — запрос «есть ли свёрнутая доставка позже этой».
  • internal/replay/worker.go — барьер на отложенной доставке, WARN о свёртке вне порядка журнала.
  • internal/fold/fold.go — проброс нового счётчика в итог свёртки и в лог.
  • Витрина в ./data: отпечаток изменится. Пересборка — действие человека при остановленном сервисе, этим изменением не выполняется.
  • Оракулы: task verify:archive (ноль противоречащих часов, step_count снова накопительная), тест сходимости «живой приём = пересборка» с отложенной доставкой.