- рядом с воркером свёртки живёт горутина, раз в минуту разбирающая журнал
пассивным чекпойнтом; «журнал не разбирается» видно строкой владельцу, а не
только по `df`. Признак — пара чисел, а не флаг занятости: тот молчит под
удерживаемым читателем (`busy=0` при 6256 страницах и пяти перенесённых), а
при занятой блокировке отдаёт `-1` вместо ответа, и `-1 >= -1` читалось бы как
«разобрано целиком»
- каталог отвечает `304` на `If-None-Match`, не открывая снимок витрины. Метка
собрана из всего, от чего зависит ответ: версии витрины (`data_version` с
закреплённого соединения плюс поколение — значение локально для соединения и
не переживает переоткрытия), горизонта измерения и области действия ресурса.
Версия снимается до и после сборки: снятая после пометила бы устаревший снимок
свежим номером
- предел и дедлайн ответа отложены в задачу Read API точек вместе с измеренной
ценой первого запроса; попутно починен флаки-тест чужой задачи, искавший
значение точки в сыром буфере записи лога
- род метрики выводится сверкой минутного слоя с часовым: часовое значение
сходится с суммой минутных — накопительная, со средним — мгновенная, иначе
`unknown` и свёртка не предлагается вовсе. На живом архиве (123 доставки,
31 метрика) 7 накопительных, 9 мгновенных, противоречащих часов ноль
- `GET /api/v1/metrics` под токеном чтения отдаёт единицы, слои с границами и
род вместе с основанием измерения; род нигде не хранится — он функция витрины,
а витрина функция журнала, устаревать в нём нечему
- миграция 00009: покрывающий индекс, чтобы каталог отвечал по учётным колонкам,
не разжимая содержимое объектов
- порядок журнала при конкурентных приёмах: повторы, но после /stats;
до тех пор живём с записанным в спеке пределом
- откат релиза после наката миграции: копия файла базы перед накатом,
Down-блоки честно названы декорацией для локальной разработки
- Правило покрытия получило второй разряд (условный, как у точек), запрет
вырождения формы и счёт содержательных элементов ряда: скелет из скаляров и
ряд из null больше не затирают маршрут. Победитель внутри доставки стал
функцией множества версий — общим помощником с точками, — а провенанс
поднимается и при совпавшем хеше, иначе отложенная доставка возвращала витрину
к прежнему содержимому.
- Одно поле не того типа больше не уносит сущность, а пропуски видны в учётной
записи доставки (миграция 00008, NULL = «не измерялось»); каноническая форма
считается один раз и вне транзакции; откат бинаря поверх новой схемы отказывает
на старте; текст ошибки разбора не несёт значений из тела.
- Ревью кода профилем deep (девять проходов) нашло две регрессии и обе закрыты:
безусловный второй разряд запирал законный досчёт навсегда, а выбор победителя
был квадратичен по числу присланных версий одного ключа.
- три пропущенных прохода дозапущены, триаж оставил семь причин
- обе развилки разобраны по уже записанным решениям проекта:
второй разряд Covers как у Relate, мягкое чтение полей как у Duration
- уточнена формулировка в architecture.md про цену покрытия секции
- секции `workouts` и `stateOfMind` покрыты разбором: тренировка лежит одной
строкой вместе с маршрутом и внутренними рядами, запись — по ключу `род + id`;
миграция 00007 заводит обе таблицы и возвращает в очередь `partial`-доставки
с этими ключами
- сущность заменяется целиком, но условно: приехавшая побеждает, если не теряет
содержания сохранённой (множество ключей и длины верхнеуровневых массивов), а
при равном содержании выигрывает версия из более поздней доставки ЖУРНАЛА —
«побеждает приехавшая» было бы функцией порядка свёртки, и живая витрина
расходилась бы с пересборкой молча
- отпечаток витрины покрывает тренировки и записи и снимается одним снимком
базы; отчёт `reindex` считает «было и стало» по каждой единице хранения
- предел на размер и число заголовков доставки: спит до деплоя
- сверка живой витрины с пересборкой: отпечатки печатаются, но
сравнивать их некому — известный путь расхождения записан блокером
- Очередью служит сама таблица: доставка ждёт свёртки в статусе `pending`,
канал несёт только бит «есть работа». Переполнять нечего, падение процесса
очередь не теряет, а подбор `pending` при старте — обычный проход воркера, а
не отдельный код. Классификация исхода общая с пересборкой журнала.
- Исход разбора начал отражать доставку, а не обстоятельства: отмена и
занятость базы статус не меняют (иначе конкуренция за базу выводила бы
доставку из очереди навсегда), паника свёртки больше не валит процесс, а
учёт доставки идёт через транзакцию с повторами.
- Длинный бюджет ответа выдан маршруту приёма, а не всему серверу:
`write_timeout` в Go покрывает и чтение тела, и общий подъём снял бы защиту с
остальных маршрутов.
- целостность собранной витрины проверяется отпечатком на открытом
дескрипторе — перед необратимой подменой нужен integrity_check
- пересборка материализует учёт и список путей архива целиком: расход
памяти растёт вместе с журналом
- `healthlog reindex` собирает витрину из журнала (тела архива + учёт
доставок) в ОТДЕЛЬНЫЙ файл базы, строго по `(received_at, id)`; рабочую
базу читает без наката миграций и не трогает вовсе. Подмену делает
человек при остановленном сервисе: переименование поверх открытого
дескриптора портит базу молча.
- Журналом считается архив, а не таблица доставок: тело без учётной записи
заводится заново (метка из ULID, размер и хеш по распакованному телу),
запись без тела переносится, но не сворачивается. Оракул сходимости
встроен — два отпечатка и «объектов было/стало»; пустой журнал успехом не
считается.
- Прогон живого архива переехал на новый пакет: второго проигрывателя
журнала в проекте не осталось, а его утверждение о ключе сна перестало
быть константой, протухающей с каждой доставкой.
- формат пункта блокера жил в двух местах и разошёлся (четыре пункта против трёх, без рекомендации); теперь его описывает индекс беклога, CLAUDE.md ссылается
- три потребителя названы в паспорте поимённо: README звал их по функции, беклог по прозвищу, канонического списка не было нигде
- восстановлена фраза «спрашиваем только про необратимое» в своём абзаце — вставка про prior art её оторвала
- содержимое шагов не перечисляется: список работ жил в плане и в беклоге и расходился с каждой закрытой задачей
- десять пунктов «Отложено» заведены задачами [idea]; два из них (ретеншен, алерт) уже были в беклоге — в плане лежал дубль
- обоснование порядка расписано по шагам: это единственное, чего беклог структурно не вмещает
- половина потока (50 доставок из 104) не несёт metrics вовсе и до сих пор
числилась parsed: ретеншен, поверив статусу, срезал бы тела stateOfMind,
которых в экспорте Apple нет
- разбор перечисляет верхнеуровневые ключи data, непокрытые проглатываются
декодированием: тело 40 МиБ из непокрытой секции удерживает 0 МиБ
- статус partial и колонка delivery.uncovered_sections; миграция переводит
прежние parsed в pending — им верить нельзя
- витрина не изменилась: отпечаток совпал с прогоном до изменения
- отношение победы было нетранзитивным: полнота (частичный порядок) плюс
тай-брейк (тотальный) в попарной свёртке давали цикл, из-за которого одна
и та же доставка меняла содержимое объекта при каждой пересборке
- надмножество побеждает только при совпадении значений общих содержательных
ключей: иначе точка без единого измерения вытесняла измерение
- Less стал тотальным, isEmpty не материализует значение, имя метрики в
координате столкновения обрезается, отпечаток витрины включает units и sealed
- на живом архиве строгий no-op: 1737 объектов, содержимое совпало побайтово
- находки 48 и 49: единицы не менялись ни разу; настоящих столкновений 0.65%,
несравнимых наборов полей нет, тай-брейк берёт меньшее в 96% случаев
- edinicy-metriki-v-razreze закрыт: гипотеза не подтвердилась замером
- тай-брейк отложен до каталога рода агрегации
Дельты влиты в openspec/specs (parsing, storage), задача убрана из беклога,
план отражает сделанную часть шага 3.
Не закрыт один пункт: живая доставка с телефона не разобрана — поток молчит
с 17:13, пауза началась до перезапуска сервиса.
Триаж свёл 62 сырые находки девяти проходов к 33 причинам: 3 блокера,
4 «сейчас», 2 развилки. Все закрыты регрессионными тестами.
- схема точки сна определяется по самой точке, а не по индексу в исходном
массиве: одна пропущенная точка меняла эпизод и сводку местами
- доставка сворачивается одной транзакцией: частичное состояние было
недетерминированным (восемь прогонов — семь состояний)
- граница размера на распакованном теле: 400 КиБ gzip разворачивались в
400 МиБ мимо max_body_mb
- столкновение — расхождение канонических форм, а не байтов; WARN с
координатами объекта; payload без HTML-экранирования
- выравнивание по местной метке: получасовые зоны уводили часовую выгрузку
в minute
- доставка из одних суточных сводок больше не отвергается целиком
- единицы не переписываются молча; счётчик считает сохранённые точки
- все выходы Fold логируются, исход пишется на переживающем отмену контексте
Четыре развилки вынесены блокерами в беклог.
- находка 47: ключ (date,start,end) даёт 174 координаты сна против 170 по
метке и ноль столкновений внутри доставки против 33; value в ключе лишний
- UUID в выгрузку Apple не попадает, другой идентичности у эпизода нет
- поэпизодный sleep_analysis вернулся в scope razbor-metrik-v-obekty
- три решения опровергнуты экспериментами: повтор при SQLITE_BUSY не сходится
без _txlock=immediate (242 из 800 против 800 из 800), разбор в map[string]any
держит 197 МиБ против 54, канонизация без json.Number теряет литерал
- канонизации назначен дом: общий internal/canon вместо hae, иначе импорт
экспорта Apple потребует второй реализации и хеш-детектор станет бесполезен
- вывод слоя вернул шаг наследования, WARN сравнивается только с надёжным
заголовком; в bucket возвращены units и границы содержимого
- умолчание — делать, а не спрашивать: развилка вынимается пунктом в секцию
`блокеры` беклога, задача переформулируется на остаток и доводится до коммита
- спрашиваем только про необратимое: деплой, выкладку наружу, удаление данных
- находка ревью с пометкой «развилка» больше не останавливает работу, а
уезжает блокером с вариантами и ценой каждого
- экспорт Apple это снапшот всей истории, доставки после его даты — события
поверх; состояние пересобирается как import(экспорт) + replay(доставки)
- отсюда ретеншен архива меняется с произвольных 14 дней на «до следующего
проверенного экспорта» (~2 ГБ за квартал, измерено), а свёртка обязана быть
детерминированной — воспроизведение строго по received_at
- названы границы модели: stateOfMind в экспорт не попадает вовсе, а верхние
слои за периоды с удалёнными доставками не воскресают и досчитываться не
должны — каталог обязан говорить это честно
- пути указывают на ./data, добавлено предупреждение про умолчания в коде
- заведена задача на подтягивание умолчаний: запуск без конфига сейчас молча
создаёт пустую базу рядом с настоящей
- 20 задач: 8 высоких, 9 средних, 3 низких; две заведены идеями — сутки при
смене часового пояса и пересекающиеся источники одной метрики
- план сведён к порядку и его обоснованию, единицы работы переехали в беклог