- отношение победы было нетранзитивным: полнота (частичный порядок) плюс
тай-брейк (тотальный) в попарной свёртке давали цикл, из-за которого одна
и та же доставка меняла содержимое объекта при каждой пересборке
- надмножество побеждает только при совпадении значений общих содержательных
ключей: иначе точка без единого измерения вытесняла измерение
- 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 логируются, исход пишется на переживающем отмену контексте
Четыре развилки вынесены блокерами в беклог.
- internal/fold — свёртка по идентификатору доставки, тело из архива: тот же
код, каким пойдёт пересборка витрины
- приём зовёт свёртку на context.WithoutCancel с собственным дедлайном;
исход разбора на код ответа не влияет
- слой доставки хранится в delivery.derived_layer и наследуется от
ПРЕДШЕСТВУЮЩЕЙ доставки автоматизации: без границы по времени свёртка
переставала быть функцией от префикса журнала (1737 объектов против 1742)
- task verify:archive — сходимость на живом архиве, 99 доставок из 99
- слияние read-modify-write в одной транзакции, ключ точки — интервал,
победа более полной точки, тай-брейк по канонической форме
- хеш объекта как детектор изменений: совпал — записи нет
- _txlock=immediate и повтор всей тройки при SQLITE_BUSY/BUSY_SNAPSHOT
- миграции через goose.Provider: пакетные SetBaseFS/SetDialect писали глобалы
и давали гонку при двух Open
- tmp/research/fixtures.py собирает фикстуры из архива, вычищая измерения и
сохраняя порядок ключей, форму литералов, выравнивание меток и невидимые
символы; шесть фикстур в internal/hae/testdata
- internal/canon — общий дом канонической формы, полноты и хеша: числа читаются
литералом через json.Number, округление до 12 значащих цифр
- internal/hae — разбор секции metrics, вывод слоя по метрике, разделение схем
сна, координаты интервалом; recover внутри Parse, фаззинг
- миграция bucket и docs/database.md
Перепись по 22 метрикам с интервалами опровергла признак из первой редакции:
start всегда равен date, интервалы несёт не только сон, обе формы точки не
смешиваются внутри метрики одной доставки, а разные интервалы под одной меткой
всегда несут разное содержимое. Значит ключ единый — метрика + слой + начало +
конец, у измерения вырожденный, без ветвления по классу.
- находка 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 и границы содержимого
- калибровка по первому прогону: самые ценные находки дали opus-проходы —
idiom с тремя выполненными экспериментами и specs с 13 находками; разницы в
пользу дорогой модели на опиниативных проходах не обнаружилось
- fable у triage и architecture — их ошибка распространяется дальше собственной
находки: триаж решает, что реализуется молча, архитектура работает до кода
- reimpl намеренно на opus: его счёт определяется объёмом вывода, а ценность —
независимостью взгляда, а не мощностью модели
- proposal и две capability: parsing (вывод слоя, форматы времени, канонизация)
и storage (координатный ключ, слияние по полноте, часовые объекты)
- design фиксирует границы: разбор отдельным пакетом, синхронно после записи в
архив, конкурентная запись в один час — риск с тестом
- вне scope сознательно: тренировки, reindex, словарь кодов, род агрегации
- перенесено из jellybit: язык артефактов, именование capabilities, SHALL/MUST
как требование валидатора, два чекпоинта ревью для нетривиальных задач
- домен переписан под healthlog: вместо archrules и htmx — task gate и
docs/conventions.md, вместо секретов qBittorrent — чувствительность данных
о здоровье; добавлены журнал событий и указание сверяться с local-research
- имена task-pipeline и review-pipeline совпадали со скиллами jellybit, и
вызов подхватывал чужие: там AskUserQuestion вместо блокеров и пути docs/specs
- теперь healthlog-task-pipeline и healthlog-review-pipeline, ссылки обновлены
в агентах и CLAUDE.md
- sonnet на gate/code/ops (вход структурный, критерий записан), opus на
specs/idiom/negative/adversary, fable на rubric/reimpl/architecture/triage —
там, где качество порождённого и есть вся ценность прохода
- haiku не используется нигде: механизируемое вынесено ниже модели в скрипты,
а дешёвый опиниативный проход дороже в триаже, чем экономит
- frontmatter агентов приведён к валидному YAML: описания с двоеточиями
закавычены, иначе строгий парсер молча потеряет агента
- умолчание — делать, а не спрашивать: развилка вынимается пунктом в секцию
`блокеры` беклога, задача переформулируется на остаток и доводится до коммита
- спрашиваем только про необратимое: деплой, выкладку наружу, удаление данных
- находка ревью с пометкой «развилка» больше не останавливает работу, а
уезжает блокером с вариантами и ценой каждого
- экспорт Apple это снапшот всей истории, доставки после его даты — события
поверх; состояние пересобирается как import(экспорт) + replay(доставки)
- отсюда ретеншен архива меняется с произвольных 14 дней на «до следующего
проверенного экспорта» (~2 ГБ за квартал, измерено), а свёртка обязана быть
детерминированной — воспроизведение строго по received_at
- названы границы модели: stateOfMind в экспорт не попадает вовсе, а верхние
слои за периоды с удалёнными доставками не воскресают и досчитываться не
должны — каталог обязан говорить это честно
- девять экспортов, версии 11→13→14; ни один тип записи не исчез, только
добавлялись — разбор можно писать аддитивно
- Correlation (давление) появился только в 2026 и ломает парсер по одним Record;
имя файла локализовано, DTD расходится с данными
- коды HealthKit переименовываются: те же записи сна приезжают как Asleep из
экспорта 2021 и как AsleepUnspecified из свежего
- пути указывают на ./data, добавлено предупреждение про умолчания в коде
- заведена задача на подтягивание умолчаний: запуск без конфига сейчас молча
создаёт пустую базу рядом с настоящей
- 20 задач: 8 высоких, 9 средних, 3 низких; две заведены идеями — сутки при
смене часового пояса и пересекающиеся источники одной метрики
- план сведён к порядку и его обоснованию, единицы работы переехали в беклог
- одиннадцать проходов ревью перенесены из jellybit и переписаны под домен:
приём пакетов, слои, координатная идентичность, чувствительность данных
- скиллы task-pipeline и review-pipeline, контракт находок, журнал промахов
- сервис поднимается через `task up`/`task restart`, данные вынесены в ./data
и переживают пересборку — поток с телефона не прерывается
- `task gate` гоняет build/vet/lint/test/race/покрытие диффа/миграции, плюс два
шага под healthlog: данные о здоровье в индексе и синхронность образцов конфига
- `task review:context` собирает вход для архитектурного прохода ревью
- агрегация появилась в ответе на запрос: род метрики измеряется сверкой
слоёв, нижний слой HAE не суммируется никогда
- переведённые строки хранятся дословно с приписанным кодом HealthKit;
снято правило «настройки данных у всех проходов одинаковы» — слой в ключе
- добавлены устаревание нижнего слоя по проверенному экспорту и MCP поверх
read API; план вырос до 11 шагов
- категориальные значения приходят переведёнными на язык телефона, а родной
экспорт Apple говорит кодами HealthKit — источники несопоставимы без словаря
- sleep_analysis шлёт две несовместимые схемы под одним именем, HRV несёт
внутри точки серию ударов на 93% своего объёма и третий формат времени
- род агрегации из формы точки не выводится, но выводится сверкой слоёв
- POST /api/v1/ingest: токен, лимит тела, gzip, durable-запись тела в сырой
архив, учёт доставки в SQLite
- код ответа отражает доставку, а не разбор: битый JSON — 400, непонятое
содержимое — 200, данные уже сохранены и доразберутся позже
- сохраняется полный набор заголовков запроса с вычисткой секретов по имени
и по совпадению значения с токеном
- README, CLAUDE.md, docs: назначение и границы, архитектура, конвенции, план
- docs/local-research.md — 36 находок по формату Health Auto Export, снятых на
живых данных; документация приложения местами расходится с тем, что оно шлёт
- Taskfile, .golangci.yml, самодокументируемый config.example.toml