- байтовый порядок канонических форм остался тай-брейком только внутри одной доставки: на живом корпусе он решал 98,8% спорных координат и системно хранил меньшее значение, из-за чего step_count терял род и verify:archive был красным - правило перестало быть коммутативным осознанно, поэтому порядок свёртки приведён к журнальному: проход воркера прекращается на отложенной доставке, а свёртка вне порядка журнала пишет WARN - заведены счётчики PointsHeld и PointsErased — удержание полнотой и единственное направление, в котором правило теряет содержание
119 lines
10 KiB
Markdown
119 lines
10 KiB
Markdown
## 1. Правило слияния точек
|
||
|
||
- [x] 1.1 `candidate` получает флаг `incoming`; схлопывание совпавших
|
||
канонических форм поднимает флаг, а байты оставляет встреченные первыми
|
||
- [x] 1.2 `pointLess` — лексикографический порядок по паре
|
||
`(не incoming, каноническая форма)`; `pointDominates` не трогается
|
||
- [x] 1.3 `resolve` возвращает признак «победил сохранённый при наличии
|
||
пришедших»; `mergePoints` считает удержания
|
||
- [x] 1.4 `MergeStats.PointsHeld` и `PointsHeldAt`; проброс через `mergeResult`,
|
||
`SaveBuckets`, `fold.Stats`, `replay.Outcome`/`Report` и лог свёртки — без
|
||
значений точек
|
||
|
||
## 2. Порядок свёртки равен порядку журнала
|
||
|
||
- [x] 2.1 `Worker.Pass` прекращает проход на исходе `Deferred`; доставка,
|
||
оставшаяся `pending` без такого исхода, курсор двигает
|
||
- [x] 2.2 `Store.ParsedAfter` — есть ли доставка позже данной в статусе
|
||
`parsed` или `partial` (кортежный предикат; индекс — `delivery_received_at`,
|
||
цена измерена и названа в design.md)
|
||
- [x] 2.3 воркер пишет одну запись `WARN` на доставку о свёртке вне порядка
|
||
журнала; пересборка эту проверку не выполняет
|
||
|
||
## 3. Тесты
|
||
|
||
- [x] 3.1 пришедшая побеждает сохранённую при равной полноте
|
||
- [x] 3.2 столкновение внутри одной доставки решается порядком канонических
|
||
форм и не зависит от порядка элементов массива
|
||
- [x] 3.3 повторная присылка канонически совпавшего содержимого не переписывает
|
||
объект (хеш не двигается)
|
||
- [x] 3.4 полнота сильнее происхождения; счётчик удержаний растёт
|
||
- [x] 3.5 сходимость: живой путь через воркер с отложенной занятостью базы
|
||
доставкой даёт тот же отпечаток, что пересборка
|
||
- [x] 3.6 проход воркера не перешагивает отложенную доставку
|
||
- [x] 3.7 `WARN` о свёртке вне порядка журнала пишется, не содержит значений и
|
||
не пишется на `failed`-преемнице
|
||
- [x] 3.8 при разошедшихся значениях пустые ключи сохранённой точки её не
|
||
удерживают (сценарий, парный к 3.4)
|
||
|
||
## 4. Документы
|
||
|
||
- [x] 4.1 `docs/architecture.md` — «Разрешение столкновений» плюс **все**
|
||
упоминания правила: инвариант в шапке, «порядок прихода значения не имеет» в
|
||
разделе синхронизации; таблица двух правил равной полноты (точки против
|
||
сущностей) с критерием выбора
|
||
- [x] 4.2 `CLAUDE.md` — формулировка инварианта «ничего не теряем молча»:
|
||
«выигрывает более полная, при равной полноте — стоящая позже в журнале»
|
||
- [x] 4.3 `internal/store/entity.go` — комментарии, чей довод («порядок свёртки
|
||
журналу не равен») этим изменением сужен до окна видимости
|
||
- [x] 4.4 `docs/research/apple-health.md` — перемер 2026-08-04 с **методом**:
|
||
ключ со слоем, 155 доставок, 460 995 координат, 80 129 спорных, 981 полнотой,
|
||
79 148 тай-брейком, 75 494 меняют исход
|
||
- [x] 4.5 `docs/review.md` — запись о дефекте (посылка оракула умерла с ростом
|
||
корпуса) и о том, что конвейер её не ловил
|
||
- [x] 4.7 вопрос владельцу в `docs/tasks/items/journal-order-on-ingest.md`
|
||
(раздел «Вопросы» + тег `question`): решение (в) порядок журнала не
|
||
восстанавливает, а цена окна выросла с вывода слоя до значений точек
|
||
- [x] 4.6 `docs/conventions/` — промоут двух правил: «в проверке на живом
|
||
корпусе утверждается инвариант, число печатается» и «оракул сходимости
|
||
называет свою посылку рядом с собой»
|
||
|
||
## 5. Оракулы
|
||
|
||
- [x] 5.1 `task gate` зелёный
|
||
- [x] 5.2 `task verify:archive` — ноль противоречащих часов, `step_count`
|
||
накопительная, отпечаток изменился
|
||
- [x] 5.3 `task verify:archive` второй раз подряд — тот же отпечаток
|
||
- [x] 5.4 `task verify:busy` зелёный
|
||
- [x] 5.5 замер удержаний на живом архиве — число названо, не утверждено
|
||
|
||
## Приёмочные критерии из ревью предложения (профиль `design`, проход `rubric`)
|
||
|
||
Свойства, по которым судится узел рода «правило слияния версий в
|
||
хранилище-свёртке по журналу» плюс «упорядочивающий воркер очереди».
|
||
|
||
- [x] Р1 исход слияния — функция префикса журнала и только его; запрос воркера
|
||
«есть ли доставка позже» влияет на лог и MUST NOT влиять на содержимое
|
||
- [x] Р2 законные расхождения живого пути и пересборки перечислены поимённо, и
|
||
у оракула названа посылка
|
||
- [x] Р3 повторная свёртка идемпотентна на суффиксе журнала; свёртка более
|
||
ранней доставки после более поздней меняет состояние наблюдаемо
|
||
- [x] Р4 победитель — функция множества кандидатов и их происхождения, но не
|
||
порядка точек внутри доставки; отношение победы ациклично (проверка
|
||
перестановками ТРОЙКИ, не пары)
|
||
- [x] Р5 правило тотально на вырожденном входе: один кандидат, равные
|
||
канонические формы с разными байтами, не-JSON, несравнимые множества
|
||
- [x] Р6 победитель сохраняется дословно; цена лишних записей названа числом
|
||
- [x] Р7 правило структурно: не читает ни значение точки, ни род, ни витрину
|
||
- [x] Р8 барьер очереди не останавливает поток: названо, что держит очередь и
|
||
что её освобождает; канонизация и сжатие вне транзакции записи
|
||
- [x] Р9 наблюдаемость отличает «правило сработало» от «не сработало» и не
|
||
несёт значений здоровья; тест на утечку разбирает запись, а не ищет
|
||
подстроку в сыром буфере
|
||
- [x] Р10 ни одно утверждение теста не пришпилено к числу, производному от
|
||
размера корпуса: утверждается инвариант, число печатается
|
||
- [x] Р11 два правила равной полноты (точки и сущности) не расходятся молча:
|
||
различие механизмов и критерий выбора записаны
|
||
- [x] Р12 место снапшота Apple в порядке журнала — либо названо, либо явно
|
||
отложено до задачи импорта (сегодня стадия снапшота пуста)
|
||
|
||
## Критерии приёмки задачи
|
||
|
||
Приходят из `docs/tasks/items/tie-break-equal-completeness.md`, дословно. Числа
|
||
в них производны от размера корпуса — по каждому названо, чем именно измерено и
|
||
что при этом печатается, а не утверждается (`docs/review.md`, 2026-08-02).
|
||
|
||
- [x] ни одна метрика не теряет род из-за столкновения равной полноты;
|
||
`step_count` снова накопительная — оракул: `task verify:archive`, ноль
|
||
противоречащих часов
|
||
- [x] живая свёртка и пересборка дают **один** отпечаток витрины — оракул:
|
||
`healthlog reindex` против живого состояния; это же и есть страховка от
|
||
потерянной коммутативности
|
||
- [x] повторный прогон реплея даёт тот же отпечаток — оракул:
|
||
`task verify:archive`, второй прогон подряд
|
||
- [x] столкновение **внутри одной доставки** разрешается прежним байтовым
|
||
порядком — оракул: тест на двух точках одной доставки с равной полнотой
|
||
- [x] «пришедшая точка проиграла сохранённой» считается отдельно от общего
|
||
`MergeStats.Overwrites` — оракул: тест плюс прогон на живом архиве, число
|
||
сходится с замером 2026-08-04
|