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

10 KiB
Raw Blame History

1. Правило слияния точек

  • 1.1 candidate получает флаг incoming; схлопывание совпавших канонических форм поднимает флаг, а байты оставляет встреченные первыми
  • 1.2 pointLess — лексикографический порядок по паре (не incoming, каноническая форма); pointDominates не трогается
  • 1.3 resolve возвращает признак «победил сохранённый при наличии пришедших»; mergePoints считает удержания
  • 1.4 MergeStats.PointsHeld и PointsHeldAt; проброс через mergeResult, SaveBuckets, fold.Stats, replay.Outcome/Report и лог свёртки — без значений точек

2. Порядок свёртки равен порядку журнала

  • 2.1 Worker.Pass прекращает проход на исходе Deferred; доставка, оставшаяся pending без такого исхода, курсор двигает
  • 2.2 Store.ParsedAfter — есть ли доставка позже данной в статусе parsed или partial (кортежный предикат; индекс — delivery_received_at, цена измерена и названа в design.md)
  • 2.3 воркер пишет одну запись WARN на доставку о свёртке вне порядка журнала; пересборка эту проверку не выполняет

3. Тесты

  • 3.1 пришедшая побеждает сохранённую при равной полноте
  • 3.2 столкновение внутри одной доставки решается порядком канонических форм и не зависит от порядка элементов массива
  • 3.3 повторная присылка канонически совпавшего содержимого не переписывает объект (хеш не двигается)
  • 3.4 полнота сильнее происхождения; счётчик удержаний растёт
  • 3.5 сходимость: живой путь через воркер с отложенной занятостью базы доставкой даёт тот же отпечаток, что пересборка
  • 3.6 проход воркера не перешагивает отложенную доставку
  • 3.7 WARN о свёртке вне порядка журнала пишется, не содержит значений и не пишется на failed-преемнице
  • 3.8 при разошедшихся значениях пустые ключи сохранённой точки её не удерживают (сценарий, парный к 3.4)

4. Документы

  • 4.1 docs/architecture.md — «Разрешение столкновений» плюс все упоминания правила: инвариант в шапке, «порядок прихода значения не имеет» в разделе синхронизации; таблица двух правил равной полноты (точки против сущностей) с критерием выбора
  • 4.2 CLAUDE.md — формулировка инварианта «ничего не теряем молча»: «выигрывает более полная, при равной полноте — стоящая позже в журнале»
  • 4.3 internal/store/entity.go — комментарии, чей довод («порядок свёртки журналу не равен») этим изменением сужен до окна видимости
  • 4.4 docs/research/apple-health.md — перемер 2026-08-04 с методом: ключ со слоем, 155 доставок, 460 995 координат, 80 129 спорных, 981 полнотой, 79 148 тай-брейком, 75 494 меняют исход
  • 4.5 docs/review.md — запись о дефекте (посылка оракула умерла с ростом корпуса) и о том, что конвейер её не ловил
  • 4.7 вопрос владельцу в docs/tasks/items/journal-order-on-ingest.md (раздел «Вопросы» + тег question): решение (в) порядок журнала не восстанавливает, а цена окна выросла с вывода слоя до значений точек
  • 4.6 docs/conventions/ — промоут двух правил: «в проверке на живом корпусе утверждается инвариант, число печатается» и «оракул сходимости называет свою посылку рядом с собой»

5. Оракулы

  • 5.1 task gate зелёный
  • 5.2 task verify:archive — ноль противоречащих часов, step_count накопительная, отпечаток изменился
  • 5.3 task verify:archive второй раз подряд — тот же отпечаток
  • 5.4 task verify:busy зелёный
  • 5.5 замер удержаний на живом архиве — число названо, не утверждено

Приёмочные критерии из ревью предложения (профиль design, проход rubric)

Свойства, по которым судится узел рода «правило слияния версий в хранилище-свёртке по журналу» плюс «упорядочивающий воркер очереди».

  • Р1 исход слияния — функция префикса журнала и только его; запрос воркера «есть ли доставка позже» влияет на лог и MUST NOT влиять на содержимое
  • Р2 законные расхождения живого пути и пересборки перечислены поимённо, и у оракула названа посылка
  • Р3 повторная свёртка идемпотентна на суффиксе журнала; свёртка более ранней доставки после более поздней меняет состояние наблюдаемо
  • Р4 победитель — функция множества кандидатов и их происхождения, но не порядка точек внутри доставки; отношение победы ациклично (проверка перестановками ТРОЙКИ, не пары)
  • Р5 правило тотально на вырожденном входе: один кандидат, равные канонические формы с разными байтами, не-JSON, несравнимые множества
  • Р6 победитель сохраняется дословно; цена лишних записей названа числом
  • Р7 правило структурно: не читает ни значение точки, ни род, ни витрину
  • Р8 барьер очереди не останавливает поток: названо, что держит очередь и что её освобождает; канонизация и сжатие вне транзакции записи
  • Р9 наблюдаемость отличает «правило сработало» от «не сработало» и не несёт значений здоровья; тест на утечку разбирает запись, а не ищет подстроку в сыром буфере
  • Р10 ни одно утверждение теста не пришпилено к числу, производному от размера корпуса: утверждается инвариант, число печатается
  • Р11 два правила равной полноты (точки и сущности) не расходятся молча: различие механизмов и критерий выбора записаны
  • Р12 место снапшота Apple в порядке журнала — либо названо, либо явно отложено до задачи импорта (сегодня стадия снапшота пуста)

Критерии приёмки задачи

Приходят из docs/tasks/items/tie-break-equal-completeness.md, дословно. Числа в них производны от размера корпуса — по каждому названо, чем именно измерено и что при этом печатается, а не утверждается (docs/review.md, 2026-08-02).

  • ни одна метрика не теряет род из-за столкновения равной полноты; step_count снова накопительная — оракул: task verify:archive, ноль противоречащих часов
  • живая свёртка и пересборка дают один отпечаток витрины — оракул: healthlog reindex против живого состояния; это же и есть страховка от потерянной коммутативности
  • повторный прогон реплея даёт тот же отпечаток — оракул: task verify:archive, второй прогон подряд
  • столкновение внутри одной доставки разрешается прежним байтовым порядком — оракул: тест на двух точках одной доставки с равной полнотой
  • «пришедшая точка проиграла сохранённой» считается отдельно от общего MergeStats.Overwrites — оракул: тест плюс прогон на живом архиве, число сходится с замером 2026-08-04