docs: документация приведена к канону av-dev-pm 4

- каждая запись каталога задач получила тип вместо тега kind: и префикса
  заголовка; секция роадмапа «Разработка» стала «Сопровождением», порядок
  секций канонический
- поправлены протухшие факты: нереализованные маршруты Read API, MCP и
  `healthlog import`, словарь слоёв в инварианте, семантика гейта по покрытию
  диффа, периметр перестал дублировать security.md
- замер слияния переведён с находки 49 на находку 54, заполнены Purpose спек
  storage и parsing
This commit is contained in:
av
2026-08-05 19:09:35 +03:00
parent e4f62785d8
commit 3d24248075
66 changed files with 322 additions and 227 deletions
+3 -2
View File
@@ -1,6 +1,7 @@
# Импортировать родной экспорт Apple Health
# Импортировать родной экспорт Apple Health
- **Секция:** Ядро
- **Тип:** feature
- **Категория:** Ядро
- **Зачем:** Слой sample пуст: полная история и точные сэмплы лежат в zip-архиве и никуда не едут
- **Теги:** goal:native-export-import
+3 -2
View File
@@ -1,6 +1,7 @@
# Свести умолчания конфига с рабочей раскладкой данных
# 🐞 Свести умолчания конфига с рабочей раскладкой данных
- **Секция:** Инфра
- **Тип:** fix
- **Категория:** Инфра
- **Зачем:** Запуск без конфига заведёт пустую базу в корне рядом с настоящей — тихая ловушка
- **Теги:** goal:deploy
@@ -1,6 +1,7 @@
# Не отбирать строки в data-миграциях по обрезаемым спискам
# 🐞 Не отбирать строки в data-миграциях по обрезаемым спискам
- **Секция:** Ядро
- **Тип:** fix
- **Категория:** Ядро
- **Зачем:** Миграция 00007 отбирает по uncovered_sections, который обрезается на 32 — следующая покрытая секция унаследует слепую зону
- **Теги:** goal:journal-and-rebuild
+3 -2
View File
@@ -1,6 +1,7 @@
# [idea] Что считать сутками при смене часового пояса
# 🔬 Что считать сутками при смене часового пояса
- **Секция:** Ядро
- **Тип:** research
- **Категория:** Ядро
- **Зачем:** Шаги за день — базовый запрос трекера и игры, но чей это день при перелёте, не решено
- **Теги:** goal:read-api
+3 -2
View File
@@ -1,6 +1,7 @@
# Ограничить размер и число заголовков доставки
# Ограничить размер и число заголовков доставки
- **Секция:** Ядро
- **Тип:** feature
- **Категория:** Ядро
- **Зачем:** MaxHeaderBytes не задан, в базу заголовки пишутся целиком: дефект спит до деплоя, а просыпается вместе с ним
- **Теги:** goal:limits-and-load
@@ -1,6 +1,7 @@
# Класть заголовки доставки в архив рядом с телом
# 🐞 Класть заголовки доставки в архив рядом с телом
- **Секция:** Ядро
- **Тип:** fix
- **Категория:** Ядро
- **Зачем:** Заголовки живут только в базе — потеря базы навсегда ломает вывод слоя при пересборке
- **Теги:** goal:journal-and-rebuild
+3 -2
View File
@@ -1,6 +1,7 @@
# Выложить сервис на rivendell
# Выложить сервис на rivendell
- **Секция:** Инфра
- **Тип:** feature
- **Категория:** Инфра
- **Зачем:** Сервис живёт на рабочей машине — телефон достаёт до него только дома
- **Теги:** goal:deploy
+3 -2
View File
@@ -1,6 +1,7 @@
# [goal] Сервис доступен телефону из любой сети
# 🎯 Сервис доступен телефону из любой сети
- **Секция:** Разработка
- **Тип:** goal
- **Секция:** Сопровождение
- **Зачем:** Сервис живёт на рабочей машине — телефон достаёт до него только дома, и оба контура открыты
- **Теги:** decomposed
+3 -2
View File
@@ -1,6 +1,7 @@
# Выводить схемы содержимого из данных
# Выводить схемы содержимого из данных
- **Секция:** Ядро
- **Тип:** feature
- **Категория:** Ядро
- **Зачем:** Метрик больше сотни и формы точек разные — клиент вынужден угадывать структуру по выборке
- **Теги:** goal:self-description
+3 -2
View File
@@ -1,6 +1,7 @@
# [idea] Отказ от heartbeatSeries
# 🔬 Отказ от heartbeatSeries
- **Секция:** Ядро
- **Тип:** research
- **Категория:** Ядро
- **Зачем:** 93% объёма HRV ради данных, которых нет ни в одном планируемом запросе
- **Теги:** goal:lower-layer-cleanup
+3 -2
View File
@@ -1,6 +1,7 @@
# Ограничить размер сущности и считать форму потоково
# Ограничить размер сущности и считать форму потоково
- **Секция:** Ядро
- **Тип:** feature
- **Категория:** Ядро
- **Зачем:** Тело 40 МиБ даёт 768 МиБ пика кучи, 63 МиБ держат блокировку 5.019 с — предела на одну сущность нет вовсе
- **Теги:** goal:limits-and-load
@@ -1,6 +1,7 @@
# Не терять сущность с id и неразобранной меткой
# 🐞 Не терять сущность с id и неразобранной меткой
- **Секция:** Ядро
- **Тип:** fix
- **Категория:** Ядро
- **Зачем:** Тренировка с меткой в неизвестном формате пропадает целиком, а после включения ретеншена окно становится необратимым
- **Теги:** goal:parsing-completeness
+3 -2
View File
@@ -1,6 +1,7 @@
# Проверять целостность собранной витрины до подмены
# Проверять целостность собранной витрины до подмены
- **Секция:** Ядро
- **Тип:** feature
- **Категория:** Ядро
- **Зачем:** Подмена файла необратима, а годность выхода подтверждена отпечатком на ещё открытом дескрипторе
- **Теги:** goal:journal-and-rebuild
+2 -1
View File
@@ -1,5 +1,6 @@
# [goal] Расхождение витрины с журналом не молчит
# 🎯 Расхождение витрины с журналом не молчит
- **Тип:** goal
- **Секция:** Направления
- **Зачем:** Расхождение витрины с журналом сейчас молчит: reindex печатает оба отпечатка, а сравнивать их некому
- **Теги:** decomposed
+3 -2
View File
@@ -1,6 +1,7 @@
# Держать порядок журнала при конкурентных приёмах
# 🐞 Держать порядок журнала при конкурентных приёмах
- **Секция:** Ядро
- **Тип:** fix
- **Категория:** Ядро
- **Зачем:** Доставка, свёрнутая раньше своей предшественницы, уходит в failed навсегда, и живая витрина молча расходится с пересборкой
- **Теги:** goal:journal-and-rebuild, question
@@ -1,6 +1,7 @@
# Измерить, нужно ли правило полноты рядом с LWW
# 🔬 Измерить, нужно ли правило полноты рядом с LWW
- **Секция:** Ядро
- **Тип:** research
- **Категория:** Ядро
- **Зачем:** Полнота решает 1,2% спорных координат, и неизвестно, была ли более полная точка более поздней — от этого зависит, нужна ли она вообще
- **Теги:** goal:merge-robustness, sprint:2026-08-03
+2 -1
View File
@@ -1,5 +1,6 @@
# [goal] У каждого входа есть названный предел
# 🎯 У каждого входа есть названный предел
- **Тип:** goal
- **Секция:** Направления
- **Зачем:** Предела на одну сущность нет вовсе: 63 МиБ держат блокировку 5.019 с, соседние доставки уходят в failed
- **Теги:** decomposed
+2 -1
View File
@@ -1,5 +1,6 @@
# [goal] Нижний слой чистится после проверенного экспорта
# 🎯 Нижний слой чистится после проверенного экспорта
- **Тип:** goal
- **Секция:** Запланировано
- **Зачем:** Нижний слой растёт на ~100 тысяч координат в сутки, а после экспорта Apple он избыточен
- **Теги:** decomposed
+3 -2
View File
@@ -1,6 +1,7 @@
# Помечать нижний слой устаревшим после экспорта
# Помечать нижний слой устаревшим после экспорта
- **Секция:** Ядро
- **Тип:** feature
- **Категория:** Ядро
- **Зачем:** Нижний слой растёт на ~100 тысяч координат в сутки, а после экспорта Apple он избыточен
- **Теги:** goal:lower-layer-cleanup
+3 -2
View File
@@ -1,6 +1,7 @@
# Поднять MCP-сервер поверх Read API
# Поднять MCP-сервер поверх Read API
- **Секция:** Ядро — набор ограничен HTTP-слоем чтения после дробления; адаптер берётся следующим спринтом по той же цели
- **Тип:** feature
- **Категория:** Ядро — набор ограничен HTTP-слоем чтения после дробления; адаптер берётся следующим спринтом по той же цели
- **Зачем:** Агент-медик — первый заказчик проекта, а подключить его сейчас нечем
- **Теги:** goal:read-api
+3 -2
View File
@@ -1,6 +1,7 @@
# Снизить цену слияния на широкой доставке
# 🐞 Снизить цену слияния на широкой доставке
- **Секция:** Ядро
- **Тип:** fix
- **Категория:** Ядро
- **Зачем:** 63 МБ на одной координате держат транзакцию 5.15 с при busy_timeout 5 с — соседние доставки уходят в failed
- **Теги:** goal:limits-and-load
+3 -2
View File
@@ -1,6 +1,7 @@
# Хранить счётчики слияния вне логов
# Хранить счётчики слияния вне логов
- **Секция:** Инфра
- **Тип:** feature
- **Категория:** Инфра
- **Зачем:** Единственный след несравнимых наборов — строка WARN в docker-логе с ротацией 3×10 МБ: событие может произойти и не оставить ничего
- **Теги:** goal:observability
+2 -1
View File
@@ -1,5 +1,6 @@
# [goal] Исход слияния не зависит от порядка элементов на проводе
# 🎯 Исход слияния не зависит от порядка элементов на проводе
- **Тип:** goal
- **Секция:** Направления
- **Зачем:** Правила слияния держатся на замерах, и каждый новый замер находит зависимость от порядка на проводе
- **Теги:** decomposed
@@ -1,6 +1,7 @@
# [idea] Месячный проход по ручным секциям
# 🔬 Месячный проход по ручным секциям
- **Секция:** Ядро
- **Тип:** research
- **Категория:** Ядро
- **Зачем:** Симптомы и лекарства заводятся задним числом на недели — недельного глубокого прохода им не хватит
- **Теги:** goal:parsing-completeness
+2 -1
View File
@@ -1,5 +1,6 @@
# [goal] История из родного экспорта Apple лежит в хранилище
# 🎯 История из родного экспорта Apple лежит в хранилище
- **Тип:** goal
- **Секция:** Запланировано
- **Зачем:** Слой sample пуст: полная история и точные сэмплы лежат в zip-архиве и никуда не едут
- **Теги:** decomposed
+3 -2
View File
@@ -1,6 +1,7 @@
# [idea] NDJSON-поток для больших выборок Read API
# 🔬 NDJSON-поток для больших выборок Read API
- **Секция:** Ядро
- **Тип:** research
- **Категория:** Ядро
- **Зачем:** Выборка нижнего слоя за месяц не влезает в один JSON-ответ — либо поток, либо пагинация
- **Теги:** goal:read-api
+2 -1
View File
@@ -1,5 +1,6 @@
# [goal] Приложение сообщает о своём состоянии
# 🎯 Приложение сообщает о своём состоянии
- **Тип:** goal
- **Секция:** Запланировано
- **Зачем:** Тихо сломавшаяся автоматизация — главный эксплуатационный риск, а сейчас факт виден только в логах
- **Теги:** decomposed
+3 -2
View File
@@ -1,6 +1,7 @@
# Ловить гейтом расхождение спеки с маршрутами
# 🧹 Ловить гейтом расхождение спеки с маршрутами
- **Секция:** Ядро
- **Тип:** chore
- **Категория:** Ядро
- **Зачем:** Рукописная спека — источник истины, а расходится она с кодом молча: без гейта решение о рукописной спеке не держится
- **Теги:** goal:read-api, sprint:2026-08-04
+3 -2
View File
@@ -1,6 +1,7 @@
# Написать OpenAPI-спеку руками
# Написать OpenAPI-спеку руками
- **Секция:** Ядро
- **Тип:** feature
- **Категория:** Ядро
- **Зачем:** Потребителей три и один из них агент — контракт должен читаться машиной, а не пересказываться в чате
- **Теги:** goal:read-api, sprint:2026-08-04
+3 -2
View File
@@ -1,6 +1,7 @@
# [idea] Пересекающиеся источники одной метрики
# 🔬 Пересекающиеся источники одной метрики
- **Секция:** Ядро
- **Тип:** research
- **Категория:** Ядро
- **Зачем:** Сон пишут и часы, и стороннее приложение — сумма по обоим задвоит ночь
- **Теги:** goal:read-api
+3 -2
View File
@@ -1,6 +1,7 @@
# [idea] Выгрузка в parquet отдельной командой
# 🔬 Выгрузка в parquet отдельной командой
- **Секция:** Ядро
- **Тип:** research
- **Категория:** Ядро
- **Зачем:** Дверь для тяжёлой аналитики снаружи: DuckDB читает и parquet, и файл SQLite — спешить некуда, но и закрывать не нужно
- **Теги:** goal:read-api
+2 -1
View File
@@ -1,5 +1,6 @@
# [goal] Новая форма от источника не теряется молча
# 🎯 Новая форма от источника не теряется молча
- **Тип:** goal
- **Секция:** Направления
- **Зачем:** Пять секций HAE поток ещё не приносил, а ручные секции заводятся задним числом — разбор полон ровно до следующей новой формы
- **Теги:** decomposed
+3 -2
View File
@@ -1,6 +1,7 @@
# Подчищать сырой архив до последнего проверенного экспорта
# Подчищать сырой архив до последнего проверенного экспорта
- **Секция:** Инфра
- **Тип:** feature
- **Категория:** Инфра
- **Зачем:** Архив не подчищается вовсе, а резать его раньше даты проверенного экспорта нельзя — в журнале останется дыра, которую нечем пересобрать
- **Теги:** goal:journal-and-rebuild
+4 -3
View File
@@ -1,8 +1,9 @@
# Отличать неполное ведро от полного
# Отличать неполное ведро от полного
- **Секция:** Ядро
- **Тип:** feature
- **Категория:** Ядро
- **Зачем:** Текущий час неполон всегда, и без порога свёртка отдаёт его наравне с полными — клиент видит провал вместо неизвестности
- **Теги:** goal:read-api, sprint:2026-08-04, kind:feature
- **Теги:** goal:read-api, sprint:2026-08-04
Ведро, в котором известна не вся сетка, отличимо от полного — а полярность
порога названа вслух, а не выводится читателем из умолчания.
+4 -3
View File
@@ -1,8 +1,9 @@
# Сворачивать точки по заданной сетке
# Сворачивать точки по заданной сетке
- **Секция:** Ядро
- **Тип:** feature
- **Категория:** Ядро
- **Зачем:** «Шаги за неделю по дням» — базовый запрос трекера и игры, и сегодня его нечем задать
- **Теги:** goal:read-api, sprint:2026-08-04, kind:feature
- **Теги:** goal:read-api, sprint:2026-08-04
«Шаги за неделю по дням» отвечаются одним запросом `?from&to&bucket`, и род
свёртки берётся измеренным, а не угаданным.
@@ -1,8 +1,9 @@
# Отвечать 304 на повторный запрос точек
# Отвечать 304 на повторный запрос точек
- **Секция:** Ядро
- **Тип:** feature
- **Категория:** Ядро
- **Зачем:** Агент опрашивает по расписанию, а каждый повтор стоит полного чтения: на каталоге это 693 мс и +153 МиБ
- **Теги:** goal:read-api, sprint:2026-08-04, kind:feature
- **Теги:** goal:read-api, sprint:2026-08-04
Повторный опрос точек с той же меткой стоит `304` вместо полного чтения, и метка
не может ответить на другой набор данных.
+4 -3
View File
@@ -1,8 +1,9 @@
# Отдавать записи со своим id за период
# Отдавать записи со своим id за период
- **Секция:** Ядро
- **Тип:** feature
- **Категория:** Ядро
- **Зачем:** stateOfMind разобран и хранится, но наружу не отдаётся — а восстановить его нечем: в экспорте Apple его нет
- **Теги:** goal:read-api, sprint:2026-08-04, kind:feature
- **Теги:** goal:read-api, sprint:2026-08-04
Записи со своим `id` — сегодня это `stateOfMind` — достаются за период через
`GET /records/{kind}`.
+4 -3
View File
@@ -1,8 +1,9 @@
# Ограничить размер ответа маршрутов чтения
# Ограничить размер ответа маршрутов чтения
- **Секция:** Ядро
- **Тип:** feature
- **Категория:** Ядро
- **Зачем:** У маршрутов чтения нет ни одного потолка: множители «метрики × окно × точки × одновременные запросы» ничем не ограничены
- **Теги:** goal:read-api, sprint:2026-08-04, kind:feature
- **Теги:** goal:read-api, sprint:2026-08-04
У маршрутов чтения появляется названный потолок: сетка не задана и ответ не
влезает — сервер огрубляет её и **называет** в ответе; сетка задана явно и не
+4 -3
View File
@@ -1,8 +1,9 @@
# Отдавать тренировки вместе с маршрутом
# Отдавать тренировки вместе с маршрутом
- **Секция:** Ядро
- **Тип:** feature
- **Категория:** Ядро
- **Зачем:** Тренировки с маршрутами разобраны и лежат в витрине, а маршрутов чтения нет — сценарий трекера не закрыт
- **Теги:** goal:read-api, sprint:2026-08-04, kind:feature
- **Теги:** goal:read-api, sprint:2026-08-04
Трекер забирает тренировку одним пакетом вместе с маршрутом: `GET /workouts` за
период и `GET /workouts/{id}` поштучно.
+2 -1
View File
@@ -1,5 +1,6 @@
# [goal] Клиенты читают данные через HTTP и MCP
# 🎯 Клиенты читают данные через HTTP и MCP
- **Тип:** goal
- **Секция:** Запланировано
- **Зачем:** Данные видны только через sqlite на хосте — ни один из трёх потребителей, включая агента-медика, ничего прочитать не может
- **Теги:** decomposed
+3 -2
View File
@@ -1,6 +1,7 @@
# Сверять живую витрину с пересборкой
# Сверять живую витрину с пересборкой
- **Секция:** Ядро
- **Тип:** feature
- **Категория:** Ядро
- **Зачем:** reindex печатает оба отпечатка, но сравнивать их некому — расхождение с журналом молчит
- **Теги:** goal:journal-and-rebuild
+3 -2
View File
@@ -1,6 +1,7 @@
# Не держать весь журнал в памяти при пересборке
# 🧹 Не держать весь журнал в памяти при пересборке
- **Секция:** Ядро
- **Тип:** chore
- **Категория:** Ядро
- **Зачем:** Учёт доставок и список путей архива материализуются целиком: расход растёт вместе с журналом, а у журнала конца нет
- **Теги:** goal:journal-and-rebuild
@@ -1,6 +1,7 @@
# Назвать механизм отката релиза после наката миграции
# Назвать механизм отката релиза после наката миграции
- **Секция:** Инфра
- **Тип:** feature
- **Категория:** Инфра
- **Зачем:** Страж версии схемы делает возврат старого бинаря отказом, а понизить схему нечем — аварийный путь пришлось бы изобретать в аварии
- **Теги:** goal:deploy
+3 -2
View File
@@ -1,6 +1,7 @@
# [idea] Человеческие аннотации поверх выведенных схем
# 🔬 Человеческие аннотации поверх выведенных схем
- **Секция:** Ядро
- **Тип:** research
- **Категория:** Ядро
- **Зачем:** Выведенная схема говорит форму, но не смысл метрики — нужно ли описание сверху, зависит от стабильности формата
- **Теги:** goal:self-description
+3 -2
View File
@@ -1,6 +1,7 @@
# [idea] Порог sealed: с какого возраста час считается запечатанным
# 🔬 Порог sealed: с какого возраста час считается запечатанным
- **Секция:** Ядро
- **Тип:** research
- **Категория:** Ядро
- **Зачем:** WARN на изменение старого часа уже пишется, но порог не выбран — ставим по факту, когда накопится статистика досчёта
- **Теги:** goal:merge-robustness
+2 -1
View File
@@ -1,5 +1,6 @@
# [goal] Клиент узнаёт форму данных из ответа
# 🎯 Клиент узнаёт форму данных из ответа
- **Тип:** goal
- **Секция:** Запланировано
- **Зачем:** Метрик больше сотни и формы точек разные — клиент вынужден угадывать структуру по выборке
- **Теги:** decomposed
@@ -1,6 +1,7 @@
# Развести бюджеты остановки и оставить следы миграции в логе
# 🐞 Развести бюджеты остановки и оставить следы миграции в логе
- **Секция:** Инфра
- **Тип:** fix
- **Категория:** Инфра
- **Зачем:** Долгий запрос чтения съедает бюджет остановки, и WARN обвиняет воркер свёртки; миграция молчит и не прерывается SIGTERM
- **Теги:** goal:deploy
+3 -2
View File
@@ -1,6 +1,7 @@
# Отдавать состояние сервиса маршрутом /stats
# Отдавать состояние сервиса маршрутом /stats
- **Секция:** Инфра
- **Тип:** feature
- **Категория:** Инфра
- **Зачем:** Тихо сломавшаяся автоматизация — главный эксплуатационный риск, а сейчас факт виден только в логах
- **Теги:** goal:observability
+3 -2
View File
@@ -1,6 +1,7 @@
# Слать уведомление, когда данных нет N часов
# Слать уведомление, когда данных нет N часов
- **Секция:** Инфра
- **Тип:** feature
- **Категория:** Инфра
- **Зачем:** Пропажу потока сейчас замечает человек, а не сервис
- **Теги:** goal:observability
+3 -2
View File
@@ -1,6 +1,7 @@
# Поднять Swagger UI без внешней сети
# Поднять Swagger UI без внешней сети
- **Секция:** Ядро
- **Тип:** feature
- **Категория:** Ядро
- **Зачем:** Контракт читается машиной, но человеку нечем выполнить запрос к живому сервису из браузера, а внешних CDN в локальной сети нет
- **Теги:** goal:read-api, sprint:2026-08-04
@@ -1,6 +1,7 @@
# Развести токены контуров и убрать секреты из репозитория
# Развести токены контуров и убрать секреты из репозитория
- **Секция:** Инфра
- **Тип:** feature
- **Категория:** Инфра
- **Зачем:** Проверка токенов выключена, config.docker.toml коммитится — так нельзя выезжать наружу
- **Теги:** goal:deploy
@@ -1,6 +1,7 @@
# Не задваивать тренировки при импорте родного экспорта
# Не задваивать тренировки при импорте родного экспорта
- **Секция:** Ядро
- **Тип:** feature
- **Категория:** Ядро
- **Зачем:** В export.xml у тренировки нет id — импорт снапшота задвоит тренировки, приехавшие от HAE
- **Теги:** goal:native-export-import
+3 -2
View File
@@ -1,6 +1,7 @@
# [idea] Разворачивание маршрутов тренировок в отдельную таблицу
# 🔬 Разворачивание маршрутов тренировок в отдельную таблицу
- **Секция:** Ядро
- **Тип:** research
- **Категория:** Ядро
- **Зачем:** Маршрут лежит блобом внутри тренировки — понадобится, только если появится клиент, которому мало отдачи одним пакетом
- **Теги:** goal:read-api