- беклог и план переехали в docs/tasks (38 задач, 11 целей), слаги переименованы с транслита на английские, 85 ссылок поправлены - conventions.md разобран в docs/conventions/, local-research.md — в docs/research/, review-journal.md — в docs/review.md с разделом настройки конвейера; заведены security.md, adr/ и .pm.json - шаг docs.py check добавлен в task gate; поведение в architecture.md помечено девятью маркерами долга, database.md получил настройки с числовым значением
11 KiB
11 KiB
1. Фикстуры на реальных пакетах
- 1.1 Расширить
tmp/research/fixtures.py: вычистка UUID (id), меток RFC 3339,route[].timestampи словарных значенийstateOfMind(kind,valenceClassification,labels,associations) при сохранении формы литерала, структуры и порядка ключей - 1.2 Собрать
internal/hae/testdata/workout_route.json— уличная тренировка с маршрутом (проверяет дословность маршрута и внутренних рядов) - 1.3 Собрать
internal/hae/testdata/workout_indoor.json— тренировка без маршрута, с полями, которых нет у уличной (temperature,humidity,intensity) - 1.4 Собрать
internal/hae/testdata/state_of_mind.json— состояние разума (RFC 3339, отсутствиеsource) - 1.5 Дописать в
handmade_edge.jsonслучаи, которых поток не даёт: сущность безid, с пустым и со слишком длиннымid, с неразбираемой меткой, с неразбираемымend, с нечисловой длительностью, два элемента с однимidв одном теле - 1.6 Обновить
internal/hae/testdata/README.md: новые файлы и что именно вычищено
2. Разбор (internal/hae)
- 2.1 Множество покрытых секций:
metrics,workouts,stateOfMind— одно объявление на разбор и на перечисление непокрытых - 2.2 Типы
WorkoutиRecordвResult; счётчикиSkippedNoID,SkippedNoTimeдля сущностей - 2.3
parseEntityTime: формат HAE, затем RFC 3339; офсет из разобранной зоны. Парсер точек остаётся строгим — причина записана в спеке - 2.4 Разбор
workouts: заголовок (id,name,start,end,duration), содержимое — исходные байты элемента;endнечитаем → равен началу; длительность отсутствует → не заполнена (не ноль) - 2.5 Разбор
stateOfMindв записи родаstateOfMind(имя секции дословно) - 2.6
Parseотдаёт сущности и при отсутствии секцииmetrics; при ошибке (обрыв тела, невыводимый слой) не отдаёт ни точек, ни сущностей - 2.7 Повтор ключа покрытой секции даёт объединение; повтор члена
dataнакапливает результаты, а не присваивает последний (существующий дефектdecodeEnvelope) - 2.8 Предел длины
id: сущность сверх него пропускается тем же счётчиком - 2.9 Тесты на фикстурах: маршрут дословно, пульс тренировки не стал метрикой, оба формата времени, пропуски со счётчиками
- 2.10 Тест: доставка из одних тренировок и из одного
stateOfMindнепокрытых ключей не даёт; из одногоecg— даёт
3. Схема (internal/store/migrations)
- 3.1 Миграция
00007_workout_record.sql: таблицыworkoutиrecord(провенансdelivery_id+delivery_received_at,content_hash, nullableduration_sec), индексы по времени, переводpartial-доставок с ключамиworkoutsиstateOfMindвpending - 3.2 Проверить, что перевод в
pendingотбирает строки точно (по элементу JSON-массива, а не по подстроке тела) - 3.3 Обновить
docs/database.md: обе таблицы, смысл колонок, ключрод + id, провенанс,NULLу длительности, офсет0уstateOfMind
4. Хранение (internal/store, internal/canon)
- 4.1
canon: сравнение множеств содержательных ключей без условия о совпадении значений (поверх существующегоrelateKeys) плюс сравнение длин верхнеуровневых массивов - 4.2
Incoming{Points, Workouts, Records}иMergeвместоMergePoints: одна транзакция на доставку - 4.3 Правило замены версии: хеш-детектор без чтения блоба, условие «не
теряет содержания», тай-брейк по позиции журнала
(received_at, id), внутридоставочный тай-брейк по канонической форме, счётчик и координаты дляWARN - 4.4 Чтение и запись тренировки и записи; содержимое — сжатый блоб; хеш приехавшей считается один раз на доставку, до входа в транзакцию
- 4.5
Fingerprintпокрывает тренировки и записи, читает одной read-only транзакцией, строки разделов различаются константным признаком - 4.6 Тесты: замещение маршрутом; досчёт при том же наборе полей; обеднённая
версия с изменившимися значениями не затирает; усечённый маршрут не
затирает; несравнимые наборы; повтор не пишет; две версии в одном теле;
перестановка трёх версий в двух порядках подачи (конвенция
docs/conventions.md) - 4.7 Тест: отпечаток расходится при расхождении одной тренировки
5. Свёртка и пересборка (internal/fold, internal/replay, cmd)
- 5.1
Statsнесёт счётчики сущностей; свёртка зовётMergeодин раз; счётчики доезжают до лога без ручного копирования (или это покрыто тестом) - 5.2 Единственный логирующий чекпоинт: атрибуты сущностей, ветка
WARNдля удержанной обеднённой версии — координатами, без содержимого - 5.3 Отчёт
reindex: «до и после» по тренировкам и записям, «покрыта новая секция» в перечне ожидаемых классов расхождения - 5.4 Тест: содержимое сущности в лог не попадает
6. Сходимость и проверка на живом архиве
- 6.1
task gate— зелёный - 6.2
task verify:archive— прогон всего./data/raw, повтор даёт то же состояние; он же оракул того, что живой приём и пересборка применяют к сущностям один порядок - 6.3 Проверка на копии рабочей базы в отдельном каталоге данных: миграция
накатывается,
partial-доставки пересворачиваются, тренировки и записи появляются
7. Документация и беклог
- 7.1
docs/architecture.md— раздел «Тренировки и прочие секции»: правило замены версии с обоснованием, отвергнутые варианты, пределimport; плюс блок схемы БД (record(kind, id), сжатый блоб,content_hash, провенанс) - 7.2
docs/conventions.md— строка про ключrecordустарела, поправить - 7.3
docs/local-research.md— находка о поведении тренировки при переприсылке (числа замера) и пересчёт находки 50 на 118 доставок - 7.4 Беклог: задача про идентичность тренировок при импорте родного
экспорта (в
export.xmlidнет —dogsheepсчитает hash_id);raw-archive-retention— предусловие проstateOfMindснова открыто; отметить вread-api-points, что отдача тренировок и записей входит в неё; уточнитьunseen-sections-check— модель заложена, остались пять секций - 7.5 Удалить
docs/tasks/items/trenirovki-i-zapisi.mdи строку индекса
8. Приёмочные критерии (рубрика ревью)
- 8.1 Тренировка с маршрутом переживает круг «разбор → хранение → чтение» побайтово
- 8.2 Повторная свёртка того же журнала не меняет отпечатка витрины
- 8.3 Ни один путь не пишет содержимое сущности в лог выше
DEBUG - 8.4 Доставка из одних тренировок получает
parsed, а неpartial - 8.5 Доставка с непокрытой секцией по-прежнему
partial, и её тело ретеншену трогать нельзя - 8.6 Исход правила замены не зависит от порядка свёртки в пунктах 2–4 правила; зависимость в пункте 5 наблюдаема счётчиком
- 8.7 У каждого класса пропуска при разборе сущности — свой счётчик, и пропуск одного элемента не уносит соседей