docs: документация приведена к канону av-dev-pm 4
- каждая запись каталога задач получила тип вместо тега kind: и префикса заголовка; секция роадмапа «Разработка» стала «Сопровождением», порядок секций канонический - поправлены протухшие факты: нереализованные маршруты Read API, MCP и `healthlog import`, словарь слоёв в инварианте, семантика гейта по покрытию диффа, периметр перестал дублировать security.md - замер слияния переведён с находки 49 на находку 54, заполнены Purpose спек storage и parsing
This commit is contained in:
@@ -13,7 +13,7 @@
|
|||||||
|
|
||||||
Коллектор данных Apple Health. Принимает выгрузки из Health Auto Export и
|
Коллектор данных Apple Health. Принимает выгрузки из Health Auto Export и
|
||||||
родного экспорта Apple, хранит их и отдаёт другим моим проектам — через HTTP
|
родного экспорта Apple, хранит их и отдаёт другим моим проектам — через HTTP
|
||||||
API и через MCP. Это **хранилище, а не аналитика**: принять, дедуплицировать,
|
API и, в планах, через MCP. Это **хранилище, а не аналитика**: принять, дедуплицировать,
|
||||||
сохранить, отдать. Не переименовывать поля Apple, не интерпретировать
|
сохранить, отдать. Не переименовывать поля Apple, не интерпретировать
|
||||||
значения. Агрегат считается только в ответе на запрос и только там, где род
|
значения. Агрегат считается только в ответе на запрос и только там, где род
|
||||||
метрики измерен.
|
метрики измерен.
|
||||||
@@ -22,7 +22,7 @@ API и через MCP. Это **хранилище, а не аналитика**
|
|||||||
|
|
||||||
Go, один статический бинарь (`CGO_ENABLED=0`). SQLite (`modernc.org/sqlite`,
|
Go, один статический бинарь (`CGO_ENABLED=0`). SQLite (`modernc.org/sqlite`,
|
||||||
чистый Go), `chi`, `sqlx`, `goose` (миграции), `pelletier/go-toml/v2`,
|
чистый Go), `chi`, `sqlx`, `goose` (миграции), `pelletier/go-toml/v2`,
|
||||||
`log/slog`, ULID через `internal/ident`.
|
`log/slog`, ULID (`github.com/oklog/ulid/v2`) через `internal/ident`.
|
||||||
|
|
||||||
Module path — `git.vakhrushev.me/av/healthlog`.
|
Module path — `git.vakhrushev.me/av/healthlog`.
|
||||||
|
|
||||||
@@ -65,8 +65,10 @@ Module path — `git.vakhrushev.me/av/healthlog`.
|
|||||||
образ жизни» на языке телефона, а родной экспорт — коды HealthKit, и без
|
образ жизни» на языке телефона, а родной экспорт — коды HealthKit, и без
|
||||||
словаря эти два источника не сойтись.
|
словаря эти два источника не сойтись.
|
||||||
- **Своей агрегации в хранении нет — есть слои.** `critical`, обратимо
|
- **Своей агрегации в хранении нет — есть слои.** `critical`, обратимо
|
||||||
пересборкой. Метрика лежит в той подробности, в какой пришла (`sample`/`raw`/`minute`/`hour`); слой выводится
|
пересборкой. Метрика лежит в той подробности, в какой пришла
|
||||||
из выравнивания меток, а не из заголовка HAE — тот врёт.
|
(`sample`/`raw`/`minute`/`hour`/`day`); слой выводится
|
||||||
|
из выравнивания меток, а не из заголовка HAE — тот врёт. Перечень слоёв один и
|
||||||
|
лежит в [docs/database.md](docs/database.md), таблица `bucket`.
|
||||||
- **Агрегация в ответе — только измеренная.** `critical`, обратимо: ответ не
|
- **Агрегация в ответе — только измеренная.** `critical`, обратимо: ответ не
|
||||||
хранится, но потребитель уже принял по нему решение. Род свёртки выводится
|
хранится, но потребитель уже принял по нему решение. Род свёртки выводится
|
||||||
сверкой слоёв между собой (часовое = сумма минутных → накопительная, = среднее →
|
сверкой слоёв между собой (часовое = сумма минутных → накопительная, = среднее →
|
||||||
@@ -110,9 +112,12 @@ Module path — `git.vakhrushev.me/av/healthlog`.
|
|||||||
- **Исходы:** 0 — зелёный; ненулевой — красный, и до его починки опиниативные
|
- **Исходы:** 0 — зелёный; ненулевой — красный, и до его починки опиниативные
|
||||||
проходы ревью **не запускаются**.
|
проходы ревью **не запускаются**.
|
||||||
- **Что красит безусловно:** любой файл из `./data` в индексе, любой токен в
|
- **Что красит безусловно:** любой файл из `./data` в индексе, любой токен в
|
||||||
индексе, непокрытая изменённая строка, миграция без правки `docs/database.md`.
|
индексе, миграция без правки `docs/database.md`. Причина одна на все: это
|
||||||
Причина одна на все: это ровно те отказы, которые не видны глазами и стоят
|
ровно те отказы, которые не видны глазами и стоят необратимо. Покрытие
|
||||||
необратимо.
|
изменённых строк задумано тем же классом, но сегодня гейт от него **не
|
||||||
|
краснеет**: `scripts/diff-coverage.py` всегда возвращает `0`, и шаг печатает
|
||||||
|
`OK` при любом покрытии — разбор непокрытых строк остаётся человеку или
|
||||||
|
проходу ревью. Запись 2026-08-04 в [docs/review.md](docs/review.md).
|
||||||
- **Чего в гейте намеренно нет и кто обязан это гонять:**
|
- **Чего в гейте намеренно нет и кто обязан это гонять:**
|
||||||
`task verify:archive` (минута прогона, данные есть только на этой машине) и
|
`task verify:archive` (минута прогона, данные есть только на этой машине) и
|
||||||
`task verify:busy` (около 50 секунд). Гоняет их **человек или оркестратор задачи**
|
`task verify:busy` (около 50 секунд). Гоняет их **человек или оркестратор задачи**
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
{
|
{
|
||||||
"canon": 3,
|
"canon": 4,
|
||||||
"migrations": "internal/store/migrations"
|
"migrations": "internal/store/migrations"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# Код HealthKit кладётся реестром рядом, а не полем внутри точки
|
# Код HealthKit кладётся реестром рядом, а не полем внутри точки
|
||||||
|
|
||||||
- Дата: 2026-08-03
|
- **Дата:** 2026-08-03
|
||||||
- Источник: openspec/changes/archive/2026-08-03-slovar-kategorialnyh-znachenij/design.md
|
- **Источник:** openspec/changes/archive/2026-08-03-slovar-kategorialnyh-znachenij/design.md
|
||||||
|
|
||||||
## Решение
|
## Решение
|
||||||
|
|
||||||
|
|||||||
+1
-4
@@ -28,10 +28,7 @@
|
|||||||
|
|
||||||
## Записи
|
## Записи
|
||||||
|
|
||||||
Новые сверху.
|
Новые сверху. Все шесть активны — статуса поэтому ни у одной нет.
|
||||||
|
|
||||||
| Дата | Запись | Статус |
|
|
||||||
| --- | --- | --- |
|
|
||||||
|
|
||||||
- [ADR-2026-08-04-otvet-tochek-nesyot-rod-i-ego-primenimost](ADR-2026-08-04-otvet-tochek-nesyot-rod-i-ego-primenimost.md)
|
- [ADR-2026-08-04-otvet-tochek-nesyot-rod-i-ego-primenimost](ADR-2026-08-04-otvet-tochek-nesyot-rod-i-ego-primenimost.md)
|
||||||
— конверт точек несёт измеренный род, его **применимость к отданному ряду** и
|
— конверт точек несёт измеренный род, его **применимость к отданному ряду** и
|
||||||
|
|||||||
+54
-32
@@ -46,12 +46,14 @@ healthlog принимает выгрузки Apple Health из приложен
|
|||||||
с переведённой строкой (см. «Категориальные значения»): он приписывается, а
|
с переведённой строкой (см. «Категориальные значения»): он приписывается, а
|
||||||
не подменяет.
|
не подменяет.
|
||||||
- **Своей агрегации в хранении нет — есть слои.** Метрика лежит в тех
|
- **Своей агрегации в хранении нет — есть слои.** Метрика лежит в тех
|
||||||
разрезах подробности, в которых пришла (`sample`/`raw`/`minute`/`hour`);
|
разрезах подробности, в которых пришла (перечень слоёв —
|
||||||
переагрегирования при записи не происходит никогда.
|
[database.md](database.md), таблица `bucket`); переагрегирования при записи
|
||||||
- **Агрегация в ответе — только измеренная.** Read API умеет свести метрику к
|
не происходит никогда.
|
||||||
запрошенной сетке, но род свёртки (сумма или среднее) выведен сверкой слоёв
|
- **Агрегация в ответе — только измеренная.** Род свёртки (сумма или среднее)
|
||||||
между собой, а не проставлен вручную. Где род неизвестен, агрегация не
|
выведен сверкой слоёв между собой, а не проставлен вручную. Где род
|
||||||
предлагается: отдаются значения как есть.
|
неизвестен, агрегация не предлагается: отдаются значения как есть. Свёртка к
|
||||||
|
запрошенной сетке объявлена контрактом и **ещё не реализована** — параметр
|
||||||
|
`bucket` отвергается `400` (задача `read-api-points-bucket`).
|
||||||
- **Минимум компонентов** — один процесс, SQLite, файлы. Без очередей и
|
- **Минимум компонентов** — один процесс, SQLite, файлы. Без очередей и
|
||||||
внешних зависимостей.
|
внешних зависимостей.
|
||||||
|
|
||||||
@@ -170,7 +172,7 @@ HRV); у накопительных — только `date`. Поэтому то
|
|||||||
```
|
```
|
||||||
дыра моложе суток → закроется в течение часа
|
дыра моложе суток → закроется в течение часа
|
||||||
дыра моложе недели → закроется в течение суток
|
дыра моложе недели → закроется в течение суток
|
||||||
дыра старше недели → не закроется; лечится `healthlog import`
|
дыра старше недели → не закроется; лечится только `healthlog import` (ещё не написан)
|
||||||
```
|
```
|
||||||
|
|
||||||
Широкие проходы почти бесплатны именно из-за часовых объектов: глубокий проход
|
Широкие проходы почти бесплатны именно из-за часовых объектов: глубокий проход
|
||||||
@@ -225,7 +227,7 @@ capability**, и здесь стоит ссылка, а не пересказ т
|
|||||||
| `ident` | генерация и разбор ULID | — |
|
| `ident` | генерация и разбор ULID | — |
|
||||||
| `archive` | сырой архив: запись тела, чтение для reindex, ретеншен | [`storage`](../openspec/specs/storage/spec.md) |
|
| `archive` | сырой архив: запись тела, чтение для reindex, ретеншен | [`storage`](../openspec/specs/storage/spec.md) |
|
||||||
| `hae` | разбор формата HAE, канонизация, хеш содержимого | [`parsing`](../openspec/specs/parsing/spec.md) |
|
| `hae` | разбор формата HAE, канонизация, хеш содержимого | [`parsing`](../openspec/specs/parsing/spec.md) |
|
||||||
| `ingest` | use-case приёма, общий для HTTP и CLI `import` | [`ingest`](../openspec/specs/ingest/spec.md) |
|
| `ingest` | use-case приёма, общий для HTTP и будущего CLI `import` | [`ingest`](../openspec/specs/ingest/spec.md) |
|
||||||
| `fold` | свёртка одной доставки в часовые объекты | [`storage`](../openspec/specs/storage/spec.md), [`uncovered-sections`](../openspec/specs/uncovered-sections/spec.md) |
|
| `fold` | свёртка одной доставки в часовые объекты | [`storage`](../openspec/specs/storage/spec.md), [`uncovered-sections`](../openspec/specs/uncovered-sections/spec.md) |
|
||||||
| `replay` | проигрывание журнала в витрину: состав, порядок, отчёт | [`reindex`](../openspec/specs/reindex/spec.md) |
|
| `replay` | проигрывание журнала в витрину: состав, порядок, отчёт | [`reindex`](../openspec/specs/reindex/spec.md) |
|
||||||
| `catalog` | каталог разрезов и измерение рода агрегации | [`catalog`](../openspec/specs/catalog/spec.md) |
|
| `catalog` | каталог разрезов и измерение рода агрегации | [`catalog`](../openspec/specs/catalog/spec.md) |
|
||||||
@@ -235,6 +237,8 @@ capability**, и здесь стоит ссылка, а не пересказ т
|
|||||||
|
|
||||||
## Приём
|
## Приём
|
||||||
|
|
||||||
|
<!-- канон: поведение → openspec/specs/ingest -->
|
||||||
|
|
||||||
```
|
```
|
||||||
запрос → токен → лимит тела, gzip → проверка формы JSON
|
запрос → токен → лимит тела, gzip → проверка формы JSON
|
||||||
→ запись тела в архив → строка в delivery → 200
|
→ запись тела в архив → строка в delivery → 200
|
||||||
@@ -264,7 +268,8 @@ capability**, и здесь стоит ссылка, а не пересказ т
|
|||||||
- **200** — тело сохранено в архив. Дальше даже полный провал разбора
|
- **200** — тело сохранено в архив. Дальше даже полный провал разбора
|
||||||
(незнакомая метрика, новая форма точки) не меняет ответ: данные уже в
|
(незнакомая метрика, новая форма точки) не меняет ответ: данные уже в
|
||||||
безопасности, исход разбора виден в логе, в `delivery.parse_status` и в
|
безопасности, исход разбора виден в логе, в `delivery.parse_status` и в
|
||||||
`/stats`, а доразобрать их можно командой `reindex`.
|
`/stats` (маршрут — задача `stats-endpoint`), а доразобрать их можно командой
|
||||||
|
`reindex`.
|
||||||
|
|
||||||
#### Очередь свёртки — таблица, а не структура в памяти
|
#### Очередь свёртки — таблица, а не структура в памяти
|
||||||
|
|
||||||
@@ -942,8 +947,11 @@ hour метки выровнены на час heart_rate 00:00:00
|
|||||||
|
|
||||||
#### Разрешение столкновений
|
#### Разрешение столкновений
|
||||||
|
|
||||||
По одним координатам приезжают разные содержимые: 2 897 случаев из 444 256
|
По одним координатам приезжают разные содержимые: спорных координат 80 129 из
|
||||||
координат, 0.65% (находка 49). Выигрывает **более полная** точка, и полнота —
|
460 995 (17,4%), и полнота отбрасывает кого-то лишь в 981 из них (1,2%) —
|
||||||
|
остальное решает тай-брейк ([research/apple-health.md](research/apple-health.md),
|
||||||
|
находка 54; прежняя оценка «0,65%» из находки 49 считала ключ без слоя).
|
||||||
|
Выигрывает **более полная** точка, и полнота —
|
||||||
это сравнение **множеств** ключей с непустым значением, а не их числа.
|
это сравнение **множеств** ключей с непустым значением, а не их числа.
|
||||||
|
|
||||||
Число сравнимо всегда и потому отвечает там, где ответа нет: точка
|
Число сравнимо всегда и потому отвечает там, где ответа нет: точка
|
||||||
@@ -981,9 +989,9 @@ hour метки выровнены на час heart_rate 00:00:00
|
|||||||
отдельно (см. ниже).
|
отдельно (см. ниже).
|
||||||
|
|
||||||
**Несравнимые множества не сливаются, а считаются.** Объединение полей — самая
|
**Несравнимые множества не сливаются, а считаются.** Объединение полей — самая
|
||||||
дорогая часть правила — на живом потоке не потребовалось ни разу (0 из 2 897),
|
дорогая часть правила — на живом корпусе наступило дважды на 155 доставок
|
||||||
поэтому вместо реализации стоит счётчик и `WARN` с координатами объекта. Если
|
(находка 54), поэтому вместо реализации стоит счётчик и `WARN` с координатами
|
||||||
событие наступит, оно будет видно, а не додумано заранее.
|
объекта. Событие видно, а не додумано заранее.
|
||||||
|
|
||||||
**Тай-брейк при равной полноте — пришедшая доставка.** Порядок канонических
|
**Тай-брейк при равной полноте — пришедшая доставка.** Порядок канонических
|
||||||
форм отвергнут замером: он берёт меньшее значение в 96% случаев (находка 49) и
|
форм отвергнут замером: он берёт меньшее значение в 96% случаев (находка 49) и
|
||||||
@@ -1445,18 +1453,26 @@ MongoDB, и так просилось из слова «перезаписыва
|
|||||||
|
|
||||||
## Read API
|
## Read API
|
||||||
|
|
||||||
|
<!-- канон: поведение → openspec/specs/read-api -->
|
||||||
|
|
||||||
```
|
```
|
||||||
GET /api/v1/metrics каталог: имя, units, род, слои с диапазонами
|
GET /api/v1/metrics каталог: имя, units, род, слои с диапазонами
|
||||||
GET /api/v1/metrics/{name}?from&to&layer точки метрики за период (bucket — соседняя задача, пока 400)
|
GET /api/v1/metrics/{name}?from&to&layer точки метрики за период (bucket — соседняя задача, пока 400)
|
||||||
GET /api/v1/workouts?from&to заголовки тренировок
|
|
||||||
GET /api/v1/workouts/{id} тренировка целиком, с маршрутом
|
|
||||||
GET /api/v1/records/{kind}?from&to прочие секции
|
|
||||||
GET /api/v1/schema схемы всего, что есть в хранилище
|
|
||||||
GET /api/v1/metrics/{name}/schema схема и статистика одной метрики
|
|
||||||
GET /stats последняя доставка, счётчики, тишина по потоку
|
|
||||||
GET /healthz
|
GET /healthz
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**Целевая поверхность шире реализованной.** Маршрутов ниже в роутере ещё нет,
|
||||||
|
и запрос к ним получает `404`:
|
||||||
|
|
||||||
|
```
|
||||||
|
GET /api/v1/workouts?from&to заголовки тренировок → read-api-workouts
|
||||||
|
GET /api/v1/workouts/{id} тренировка целиком, с маршрутом → read-api-workouts
|
||||||
|
GET /api/v1/records/{kind}?from&to прочие секции → read-api-records
|
||||||
|
GET /api/v1/schema схемы всего, что есть в хранилище → цель self-description
|
||||||
|
GET /api/v1/metrics/{name}/schema схема и статистика одной метрики → цель self-description
|
||||||
|
GET /stats последняя доставка, счётчики, тишина → stats-endpoint
|
||||||
|
```
|
||||||
|
|
||||||
Хранение пачками на контракт не влияет: `GET /metrics/{name}` собирает ответ
|
Хранение пачками на контракт не влияет: `GET /metrics/{name}` собирает ответ
|
||||||
из часовых объектов, попавших в диапазон, и отдаёт точки. Клиент про объекты
|
из часовых объектов, попавших в диапазон, и отдаёт точки. Клиент про объекты
|
||||||
не знает — это деталь хранения, а не API.
|
не знает — это деталь хранения, а не API.
|
||||||
@@ -1687,8 +1703,9 @@ Docker и go-kit; версионирование с конверсией у Kube
|
|||||||
|
|
||||||
### MCP
|
### MCP
|
||||||
|
|
||||||
Поверх Read API — адаптер MCP, чтобы агент подключался без промежуточного
|
Поверх Read API **встанет** адаптер MCP, чтобы агент подключался без
|
||||||
кода. Инструментов ровно два, по числу форм запроса выше, плюс каталог.
|
промежуточного кода — кода адаптера сегодня нет, это задача `mcp-server` цели
|
||||||
|
`read-api`. Инструментов ровно два, по числу форм запроса выше, плюс каталог.
|
||||||
Собственной логики в адаптере нет: он переводит вызовы в те же обработчики.
|
Собственной логики в адаптере нет: он переводит вызовы в те же обработчики.
|
||||||
|
|
||||||
**Транспорт — HTTP** (Streamable HTTP), не stdio: сервис живёт на VPS, и агент
|
**Транспорт — HTTP** (Streamable HTTP), не stdio: сервис живёт на VPS, и агент
|
||||||
@@ -1741,18 +1758,18 @@ Docker и go-kit; версионирование с конверсией у Kube
|
|||||||
|
|
||||||
## Аутентификация
|
## Аутентификация
|
||||||
|
|
||||||
Статический токен в заголовке `Authorization: Bearer …`; список допустимых
|
Периметр, модель угроз и разграничение контуров — [security.md](security.md),
|
||||||
токенов — в конфиге. HAE умеет слать произвольные заголовки, этого достаточно.
|
разделы «Периметр» и «Что разграничивает доступ»; сегодняшний контур отличается
|
||||||
|
от целевого, и сказано это там. Здесь важно одно следствие для компоновки: MCP —
|
||||||
Токены **раздельные**: на запись (приём) и на чтение. Клиент, читающий
|
эндпоинт того же процесса и того же контура чтения, отдельного контура доступа у
|
||||||
данные, не может писать. MCP пользуется токеном чтения — отдельного контура
|
него нет (см. «MCP»).
|
||||||
у него нет, см. «MCP».
|
|
||||||
|
|
||||||
Наружу открыты два контура: приём (телефон) и чтение вместе с MCP (агенты и
|
|
||||||
приложения). Оба через Caddy с TLS, оба с разными токенами.
|
|
||||||
|
|
||||||
## Деплой
|
## Деплой
|
||||||
|
|
||||||
|
**Целевая** раскладка; сегодняшний контур — [security.md](security.md),
|
||||||
|
«Периметр», статус работ — [tasks/ROADMAP.md](tasks/ROADMAP.md),
|
||||||
|
«Сопровождение».
|
||||||
|
|
||||||
VPS **rivendell** (Timeweb), доступен всегда. Перед сервисом — **Caddy**, он
|
VPS **rivendell** (Timeweb), доступен всегда. Перед сервисом — **Caddy**, он
|
||||||
терминирует TLS; сам сервис слушает plain HTTP. Приём открыт наружу на
|
терминирует TLS; сам сервис слушает plain HTTP. Приём открыт наружу на
|
||||||
отдельном поддомене — телефон должен доставать до него из любой сети, иначе
|
отдельном поддомене — телефон должен доставать до него из любой сети, иначе
|
||||||
@@ -1764,7 +1781,12 @@ VPS **rivendell** (Timeweb), доступен всегда. Перед серв
|
|||||||
Тома: каталог сырого архива и файл SQLite — на постоянном хранении, конфиг
|
Тома: каталог сырого архива и файл SQLite — на постоянном хранении, конфиг
|
||||||
(с токенами) — отдельно, `0600`.
|
(с токенами) — отдельно, `0600`.
|
||||||
|
|
||||||
**Откат бинаря поверх новой схемы отказывает на старте.** Версия схемы базы выше
|
<!-- канон: поведение → openspec/specs/storage -->
|
||||||
|
|
||||||
|
**Откат бинаря поверх новой схемы отказывает на старте** — правило нормировано в
|
||||||
|
[`storage`](../openspec/specs/storage/spec.md), требование «Открытие базы
|
||||||
|
отказывает при схеме из будущего»; здесь только следствия для деплоя. Версия
|
||||||
|
схемы базы выше
|
||||||
версии, вшитой в бинарь, — отказ, а не повод мигрировать; в контейнере это
|
версии, вшитой в бинарь, — отказ, а не повод мигрировать; в контейнере это
|
||||||
выглядит циклом перезапуска, и лечится возвратом бинаря вперёд. Версию читает
|
выглядит циклом перезапуска, и лечится возвратом бинаря вперёд. Версию читает
|
||||||
сам goose (`Provider.GetVersions`), а не собственный запрос: имя таблицы учёта и
|
сам goose (`Provider.GetVersions`), а не собственный запрос: имя таблицы учёта и
|
||||||
|
|||||||
@@ -240,5 +240,6 @@ Data-миграции у таблицы нет и быть не может: ко
|
|||||||
| таймаут чтения запроса | 5 мин (`server.read_timeout`) | конфиг; щедро: экспорт истории по мобильной сети |
|
| таймаут чтения запроса | 5 мин (`server.read_timeout`) | конфиг; щедро: экспорт истории по мобильной сети |
|
||||||
| таймаут отправки ответа | 30 с (`server.write_timeout`) | конфиг; маршрут приёма держит собственный бюджет |
|
| таймаут отправки ответа | 30 с (`server.write_timeout`) | конфиг; маршрут приёма держит собственный бюджет |
|
||||||
| бюджет остановки | 30 с | `cmd/healthlog/serve.go`, `shutdownTimeout` |
|
| бюджет остановки | 30 с | `cmd/healthlog/serve.go`, `shutdownTimeout` |
|
||||||
|
| дедлайн свёртки одной доставки | 2 мин | `internal/replay/worker.go`, `foldTimeout`; обстоятельством не считается — не уложившаяся доставка уходит в `failed` |
|
||||||
| ретеншен сырого архива | до следующего проверенного экспорта (~2 ГБ за квартал) | правило, а не число; не реализован — задача `raw-archive-retention` |
|
| ретеншен сырого архива | до следующего проверенного экспорта (~2 ГБ за квартал) | правило, а не число; не реализован — задача `raw-archive-retention` |
|
||||||
| предела на одну сущность | **нет** | задача `entity-size-limits` |
|
| предела на одну сущность | **нет** | задача `entity-size-limits` |
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ python3 tmp/research/hl.py workouts тренировки, ряд
|
|||||||
|
|
||||||
## Записи
|
## Записи
|
||||||
|
|
||||||
- [apple-health.md](apple-health.md) — 53 находки на живом потоке Health Auto
|
- [apple-health.md](apple-health.md) — 54 находки на живом потоке Health Auto
|
||||||
Export и на родном экспорте Apple.
|
Export и на родном экспорте Apple.
|
||||||
|
|
||||||
Записи нумерованы сквозным номером внутри файла, и **на номер ссылаются
|
Записи нумерованы сквозным номером внутри файла, и **на номер ссылаются
|
||||||
@@ -64,7 +64,7 @@ python3 tmp/research/hl.py workouts тренировки, ряд
|
|||||||
| --- | --- |
|
| --- | --- |
|
||||||
| Форма точки, схемы, типы значений | 4, 21, 38, 39, 44 |
|
| Форма точки, схемы, типы значений | 4, 21, 38, 39, 44 |
|
||||||
| Слой и гранулярность, режимы автоматизации | 5, 6, 13, 19, 20, 23, 33, 41 |
|
| Слой и гранулярность, режимы автоматизации | 5, 6, 13, 19, 20, 23, 33, 41 |
|
||||||
| Идентичность, столкновения, слияние, полнота | 11, 14, 36, 47, 49 |
|
| Идентичность, столкновения, слияние, полнота | 11, 14, 36, 47, 49, 54 |
|
||||||
| Досчёт задним числом и стабильность значений | 3, 10, 30, 48, 51 |
|
| Досчёт задним числом и стабильность значений | 3, 10, 30, 48, 51 |
|
||||||
| Локализация и категориальные значения | 8, 24, 37, 43 |
|
| Локализация и категориальные значения | 8, 24, 37, 43 |
|
||||||
| Секции потока и их состав | 9, 15, 16, 17, 22, 34, 50, 52 |
|
| Секции потока и их состав | 9, 15, 16, 17, 22, 34, 50, 52 |
|
||||||
|
|||||||
+37
-37
@@ -11,43 +11,43 @@
|
|||||||
|
|
||||||
## Ядро
|
## Ядро
|
||||||
|
|
||||||
- [[idea] Человеческие аннотации поверх выведенных схем](items/schema-annotations.md) — Выведенная схема говорит форму, но не смысл метрики — нужно ли описание сверху, зависит от стабильности формата
|
- [✨ Проверять целостность собранной витрины до подмены](items/integrity-before-swap.md) — Подмена файла необратима, а годность выхода подтверждена отпечатком на ещё открытом дескрипторе
|
||||||
- [Проверять целостность собранной витрины до подмены](items/integrity-before-swap.md) — Подмена файла необратима, а годность выхода подтверждена отпечатком на ещё открытом дескрипторе
|
- [🐞 Снизить цену слияния на широкой доставке](items/merge-cost-wide-delivery.md) — 63 МБ на одной координате держат транзакцию 5.15 с при busy_timeout 5 с — соседние доставки уходят в failed
|
||||||
- [Снизить цену слияния на широкой доставке](items/merge-cost-wide-delivery.md) — 63 МБ на одной координате держат транзакцию 5.15 с при busy_timeout 5 с — соседние доставки уходят в failed
|
- [🐞 Не терять сущность с id и неразобранной меткой](items/entity-without-parsed-label.md) — Тренировка с меткой в неизвестном формате пропадает целиком, а после включения ретеншена окно становится необратимым
|
||||||
- [Не терять сущность с id и неразобранной меткой](items/entity-without-parsed-label.md) — Тренировка с меткой в неизвестном формате пропадает целиком, а после включения ретеншена окно становится необратимым
|
- [✨ Не задваивать тренировки при импорте родного экспорта](items/workout-identity-on-import.md) — В export.xml у тренировки нет id — импорт снапшота задвоит тренировки, приехавшие от HAE
|
||||||
- [Не задваивать тренировки при импорте родного экспорта](items/workout-identity-on-import.md) — В export.xml у тренировки нет id — импорт снапшота задвоит тренировки, приехавшие от HAE
|
- [✨ Импортировать родной экспорт Apple Health](items/apple-export-import.md) — Слой sample пуст: полная история и точные сэмплы лежат в zip-архиве и никуда не едут
|
||||||
- [Импортировать родной экспорт Apple Health](items/apple-export-import.md) — Слой sample пуст: полная история и точные сэмплы лежат в zip-архиве и никуда не едут
|
- [🐞 Не отбирать строки в data-миграциях по обрезаемым спискам](items/data-migration-row-selection.md) — Миграция 00007 отбирает по uncovered_sections, который обрезается на 32 — следующая покрытая секция унаследует слепую зону
|
||||||
- [[idea] Месячный проход по ручным секциям](items/monthly-manual-sections-pass.md) — Симптомы и лекарства заводятся задним числом на недели — недельного глубокого прохода им не хватит
|
- [🧹 Не держать весь журнал в памяти при пересборке](items/rebuild-memory-footprint.md) — Учёт доставок и список путей архива материализуются целиком: расход растёт вместе с журналом, а у журнала конца нет
|
||||||
- [[idea] NDJSON-поток для больших выборок Read API](items/ndjson-stream.md) — Выборка нижнего слоя за месяц не влезает в один JSON-ответ — либо поток, либо пагинация
|
- [🐞 Держать порядок журнала при конкурентных приёмах](items/journal-order-on-ingest.md) — Доставка, свёрнутая раньше своей предшественницы, уходит в failed навсегда, и живая витрина молча расходится с пересборкой
|
||||||
- [Не отбирать строки в data-миграциях по обрезаемым спискам](items/data-migration-row-selection.md) — Миграция 00007 отбирает по uncovered_sections, который обрезается на 32 — следующая покрытая секция унаследует слепую зону
|
- [✨ Ограничить размер и число заголовков доставки](items/delivery-header-limits.md) — MaxHeaderBytes не задан, в базу заголовки пишутся целиком: дефект спит до деплоя, а просыпается вместе с ним
|
||||||
- [[idea] Отказ от heartbeatSeries](items/drop-heartbeat-series.md) — 93% объёма HRV ради данных, которых нет ни в одном планируемом запросе
|
- [✨ Ограничить размер сущности и считать форму потоково](items/entity-size-limits.md) — Тело 40 МиБ даёт 768 МиБ пика кучи, 63 МиБ держат блокировку 5.019 с — предела на одну сущность нет вовсе
|
||||||
- [Не держать весь журнал в памяти при пересборке](items/rebuild-memory-footprint.md) — Учёт доставок и список путей архива материализуются целиком: расход растёт вместе с журналом, а у журнала конца нет
|
- [✨ Выводить схемы содержимого из данных](items/derived-content-schemas.md) — Метрик больше сотни и формы точек разные — клиент вынужден угадывать структуру по выборке
|
||||||
- [[idea] Пересекающиеся источники одной метрики](items/overlapping-sources.md) — Сон пишут и часы, и стороннее приложение — сумма по обоим задвоит ночь
|
- [✨ Сверять живую витрину с пересборкой](items/rebuild-comparison-check.md) — reindex печатает оба отпечатка, но сравнивать их некому — расхождение с журналом молчит
|
||||||
- [[idea] Порог sealed: с какого возраста час считается запечатанным](items/sealed-threshold.md) — WARN на изменение старого часа уже пишется, но порог не выбран — ставим по факту, когда накопится статистика досчёта
|
- [✨ Помечать нижний слой устаревшим после экспорта](items/lower-layer-expiry.md) — Нижний слой растёт на ~100 тысяч координат в сутки, а после экспорта Apple он избыточен
|
||||||
- [Держать порядок журнала при конкурентных приёмах](items/journal-order-on-ingest.md) — Доставка, свёрнутая раньше своей предшественницы, уходит в failed навсегда, и живая витрина молча расходится с пересборкой
|
- [🐞 Класть заголовки доставки в архив рядом с телом](items/delivery-headers-in-archive.md) — Заголовки живут только в базе — потеря базы навсегда ломает вывод слоя при пересборке
|
||||||
- [Ограничить размер и число заголовков доставки](items/delivery-header-limits.md) — MaxHeaderBytes не задан, в базу заголовки пишутся целиком: дефект спит до деплоя, а просыпается вместе с ним
|
- [✨ Поднять MCP-сервер поверх Read API](items/mcp-server.md) — Агент-медик — первый заказчик проекта, а подключить его сейчас нечем
|
||||||
- [Ограничить размер сущности и считать форму потоково](items/entity-size-limits.md) — Тело 40 МиБ даёт 768 МиБ пика кучи, 63 МиБ держат блокировку 5.019 с — предела на одну сущность нет вовсе
|
- [✨ Написать OpenAPI-спеку руками](items/openapi-spec.md) — Потребителей три и один из них агент — контракт должен читаться машиной, а не пересказываться в чате
|
||||||
- [[idea] Разворачивание маршрутов тренировок в отдельную таблицу](items/workout-routes-table.md) — Маршрут лежит блобом внутри тренировки — понадобится, только если появится клиент, которому мало отдачи одним пакетом
|
- [🧹 Ловить гейтом расхождение спеки с маршрутами](items/openapi-gate-check.md) — Рукописная спека — источник истины, а расходится она с кодом молча: без гейта решение о рукописной спеке не держится
|
||||||
- [Выводить схемы содержимого из данных](items/derived-content-schemas.md) — Метрик больше сотни и формы точек разные — клиент вынужден угадывать структуру по выборке
|
- [✨ Поднять Swagger UI без внешней сети](items/swagger-ui.md) — Контракт читается машиной, но человеку нечем выполнить запрос к живому сервису из браузера, а внешних CDN в локальной сети нет
|
||||||
- [[idea] Что считать сутками при смене часового пояса](items/day-boundary-timezone.md) — Шаги за день — базовый запрос трекера и игры, но чей это день при перелёте, не решено
|
- [🔬 Измерить, нужно ли правило полноты рядом с LWW](items/last-wins-over-completeness.md) — Полнота решает 1,2% спорных координат, и неизвестно, была ли более полная точка более поздней — от этого зависит, нужна ли она вообще
|
||||||
- [Сверять живую витрину с пересборкой](items/rebuild-comparison-check.md) — reindex печатает оба отпечатка, но сравнивать их некому — расхождение с журналом молчит
|
- [🔬 Человеческие аннотации поверх выведенных схем](items/schema-annotations.md) — Выведенная схема говорит форму, но не смысл метрики — нужно ли описание сверху, зависит от стабильности формата
|
||||||
- [Помечать нижний слой устаревшим после экспорта](items/lower-layer-expiry.md) — Нижний слой растёт на ~100 тысяч координат в сутки, а после экспорта Apple он избыточен
|
- [🔬 Месячный проход по ручным секциям](items/monthly-manual-sections-pass.md) — Симптомы и лекарства заводятся задним числом на недели — недельного глубокого прохода им не хватит
|
||||||
- [[idea] Выгрузка в parquet отдельной командой](items/parquet-export.md) — Дверь для тяжёлой аналитики снаружи: DuckDB читает и parquet, и файл SQLite — спешить некуда, но и закрывать не нужно
|
- [🔬 NDJSON-поток для больших выборок Read API](items/ndjson-stream.md) — Выборка нижнего слоя за месяц не влезает в один JSON-ответ — либо поток, либо пагинация
|
||||||
- [Класть заголовки доставки в архив рядом с телом](items/delivery-headers-in-archive.md) — Заголовки живут только в базе — потеря базы навсегда ломает вывод слоя при пересборке
|
- [🔬 Отказ от heartbeatSeries](items/drop-heartbeat-series.md) — 93% объёма HRV ради данных, которых нет ни в одном планируемом запросе
|
||||||
- [Измерить, нужно ли правило полноты рядом с LWW](items/last-wins-over-completeness.md) — Полнота решает 1,2% спорных координат, и неизвестно, была ли более полная точка более поздней — от этого зависит, нужна ли она вообще
|
- [🔬 Пересекающиеся источники одной метрики](items/overlapping-sources.md) — Сон пишут и часы, и стороннее приложение — сумма по обоим задвоит ночь
|
||||||
- [Поднять MCP-сервер поверх Read API](items/mcp-server.md) — Агент-медик — первый заказчик проекта, а подключить его сейчас нечем
|
- [🔬 Порог sealed: с какого возраста час считается запечатанным](items/sealed-threshold.md) — WARN на изменение старого часа уже пишется, но порог не выбран — ставим по факту, когда накопится статистика досчёта
|
||||||
- [Написать OpenAPI-спеку руками](items/openapi-spec.md) — Потребителей три и один из них агент — контракт должен читаться машиной, а не пересказываться в чате
|
- [🔬 Разворачивание маршрутов тренировок в отдельную таблицу](items/workout-routes-table.md) — Маршрут лежит блобом внутри тренировки — понадобится, только если появится клиент, которому мало отдачи одним пакетом
|
||||||
- [Ловить гейтом расхождение спеки с маршрутами](items/openapi-gate-check.md) — Рукописная спека — источник истины, а расходится она с кодом молча: без гейта решение о рукописной спеке не держится
|
- [🔬 Что считать сутками при смене часового пояса](items/day-boundary-timezone.md) — Шаги за день — базовый запрос трекера и игры, но чей это день при перелёте, не решено
|
||||||
- [Поднять Swagger UI без внешней сети](items/swagger-ui.md) — Контракт читается машиной, но человеку нечем выполнить запрос к живому сервису из браузера, а внешних CDN в локальной сети нет
|
- [🔬 Выгрузка в parquet отдельной командой](items/parquet-export.md) — Дверь для тяжёлой аналитики снаружи: DuckDB читает и parquet, и файл SQLite — спешить некуда, но и закрывать не нужно
|
||||||
|
|
||||||
## Инфра
|
## Инфра
|
||||||
|
|
||||||
- [Слать уведомление, когда данных нет N часов](items/stream-silence-alert.md) — Пропажу потока сейчас замечает человек, а не сервис
|
- [✨ Слать уведомление, когда данных нет N часов](items/stream-silence-alert.md) — Пропажу потока сейчас замечает человек, а не сервис
|
||||||
- [Выложить сервис на rivendell](items/deploy-rivendell.md) — Сервис живёт на рабочей машине — телефон достаёт до него только дома
|
- [✨ Выложить сервис на rivendell](items/deploy-rivendell.md) — Сервис живёт на рабочей машине — телефон достаёт до него только дома
|
||||||
- [Хранить счётчики слияния вне логов](items/merge-counters-in-db.md) — Единственный след несравнимых наборов — строка WARN в docker-логе с ротацией 3×10 МБ: событие может произойти и не оставить ничего
|
- [✨ Хранить счётчики слияния вне логов](items/merge-counters-in-db.md) — Единственный след несравнимых наборов — строка WARN в docker-логе с ротацией 3×10 МБ: событие может произойти и не оставить ничего
|
||||||
- [Развести бюджеты остановки и оставить следы миграции в логе](items/shutdown-and-migration-traces.md) — Долгий запрос чтения съедает бюджет остановки, и WARN обвиняет воркер свёртки; миграция молчит и не прерывается SIGTERM
|
- [🐞 Развести бюджеты остановки и оставить следы миграции в логе](items/shutdown-and-migration-traces.md) — Долгий запрос чтения съедает бюджет остановки, и WARN обвиняет воркер свёртки; миграция молчит и не прерывается SIGTERM
|
||||||
- [Назвать механизм отката релиза после наката миграции](items/release-rollback-after-migration.md) — Страж версии схемы делает возврат старого бинаря отказом, а понизить схему нечем — аварийный путь пришлось бы изобретать в аварии
|
- [✨ Назвать механизм отката релиза после наката миграции](items/release-rollback-after-migration.md) — Страж версии схемы делает возврат старого бинаря отказом, а понизить схему нечем — аварийный путь пришлось бы изобретать в аварии
|
||||||
- [Подчищать сырой архив до последнего проверенного экспорта](items/raw-archive-retention.md) — Архив не подчищается вовсе, а резать его раньше даты проверенного экспорта нельзя — в журнале останется дыра, которую нечем пересобрать
|
- [✨ Подчищать сырой архив до последнего проверенного экспорта](items/raw-archive-retention.md) — Архив не подчищается вовсе, а резать его раньше даты проверенного экспорта нельзя — в журнале останется дыра, которую нечем пересобрать
|
||||||
- [Отдавать состояние сервиса маршрутом /stats](items/stats-endpoint.md) — Тихо сломавшаяся автоматизация — главный эксплуатационный риск, а сейчас факт виден только в логах
|
- [✨ Отдавать состояние сервиса маршрутом /stats](items/stats-endpoint.md) — Тихо сломавшаяся автоматизация — главный эксплуатационный риск, а сейчас факт виден только в логах
|
||||||
- [Свести умолчания конфига с рабочей раскладкой данных](items/config-defaults-data-dir.md) — Запуск без конфига заведёт пустую базу в корне рядом с настоящей — тихая ловушка
|
- [🐞 Свести умолчания конфига с рабочей раскладкой данных](items/config-defaults-data-dir.md) — Запуск без конфига заведёт пустую базу в корне рядом с настоящей — тихая ловушка
|
||||||
- [Развести токены контуров и убрать секреты из репозитория](items/token-and-secret-management.md) — Проверка токенов выключена, config.docker.toml коммитится — так нельзя выезжать наружу
|
- [✨ Развести токены контуров и убрать секреты из репозитория](items/token-and-secret-management.md) — Проверка токенов выключена, config.docker.toml коммитится — так нельзя выезжать наружу
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
- 2026-08-01 `bekap-dannyh` — Резервное копирование ./data. Причина: бекап обеспечивает готовый механизм на сервере пет-проектов — своего заводить не нужно, задача снимается деплоем. Была секция: высокий.
|
- 2026-08-01 `bekap-dannyh` — Резервное копирование ./data. Причина: бекап обеспечивает готовый механизм на сервере пет-проектов — своего заводить не нужно, задача снимается деплоем. Была секция: высокий.
|
||||||
- 2026-08-01 `identichnost-epizodnyh-metrik` — Идентичность эпизодных метрик. Причина: решён измерением и prior art: ключ эпизода — метрика+слой+start+end (находка 47), вариант А; вернулся в scope razbor-metrik-v-obekty. Была секция: блокеры.
|
- 2026-08-01 `identichnost-epizodnyh-metrik` — Идентичность эпизодных метрик. Причина: решён измерением и prior art: ключ эпизода — метрика+слой+start+end (находка 47), вариант А; вернулся в scope razbor-metrik-v-obekty. Была секция: блокеры.
|
||||||
- 2026-08-01 `edinicy-metriki-v-razreze` — Единицы метрики: часть координаты или свойство объекта. Причина: измерено: на 99 доставках единицы не менялись ни у одной из 30 метрик (находка 49 → 48); реализованное правило «сохранённое побеждает + WARN + счётчик» делает событие наблюдаемым. Была секция: блокеры.
|
- 2026-08-01 `edinicy-metriki-v-razreze` — Единицы метрики: часть координаты или свойство объекта. Причина: измерено: на 99 доставках единицы не менялись ни у одной из 30 метрик (находка 49 → 48); реализованное правило «сохранённое побеждает + WARN + счётчик» делает событие наблюдаемым. Была секция: блокеры.
|
||||||
- 2026-08-04 `mcp` — [goal] MCP. Причина: поглощена целью read-api («Чтение данных клиентами»): MCP — не направление, а последний шаг того же направления; адаптер переводит вызовы в те же обработчики и собственной логики не несёт. Очередь «Read API перед MCP» стала порядком задач внутри цели. Задача mcp-server жива и перевешена на read-api. Была секция: порядок.
|
- 2026-08-04 `mcp` — 🎯 MCP. Причина: поглощена целью read-api («Чтение данных клиентами»): MCP — не направление, а последний шаг того же направления; адаптер переводит вызовы в те же обработчики и собственной логики не несёт. Очередь «Read API перед MCP» стала порядком задач внутри цели. Задача mcp-server жива и перевешена на read-api. Была секция: порядок.
|
||||||
- 2026-08-04 `read-api-points` — Read API: точки, выбор слоя, свёртка по сетке. Причина: разложена на read-api-envelope-and-points (конверт, точки за период, форма провода, условный запрос), read-api-bucketing (свёртка по сетке, предел размера ответа, порог неполного ведра) и read-api-workouts-and-records (тренировки и записи наружу). Одним заходом не мерджилась: десяток критериев приёмки и три развилки в одном файле. Была секция: ядро.
|
- 2026-08-04 `read-api-points` — Read API: точки, выбор слоя, свёртка по сетке. Причина: разложена на read-api-envelope-and-points (конверт, точки за период, форма провода, условный запрос), read-api-bucketing (свёртка по сетке, предел размера ответа, порог неполного ведра) и read-api-workouts-and-records (тренировки и записи наружу). Одним заходом не мерджилась: десяток критериев приёмки и три развилки в одном файле. Была секция: ядро.
|
||||||
- 2026-08-04 `read-api-envelope-and-points` — Конверт ответа и точки за период. Причина: разложена на read-api-wire-format (форма провода, мерджится первой и трогает только живой каталог), read-api-points-period (точки за период с конвертом) и read-api-points-conditional (условный запрос со scope-etag). Была секция: ядро.
|
- 2026-08-04 `read-api-envelope-and-points` — Конверт ответа и точки за период. Причина: разложена на read-api-wire-format (форма провода, мерджится первой и трогает только живой каталог), read-api-points-period (точки за период с конвертом) и read-api-points-conditional (условный запрос со scope-etag). Была секция: ядро.
|
||||||
- 2026-08-04 `read-api-bucketing` — Свёртка по сетке и предел размера ответа. Причина: разложена на read-api-points-bucket (свёртка по сетке), read-api-partial-bucket (порог неполного ведра и его полярность) и read-api-response-limit (предел размера ответа, общий для всех маршрутов чтения). Была секция: ядро.
|
- 2026-08-04 `read-api-bucketing` — Свёртка по сетке и предел размера ответа. Причина: разложена на read-api-points-bucket (свёртка по сетке), read-api-partial-bucket (порог неполного ведра и его полярность) и read-api-response-limit (предел размера ответа, общий для всех маршрутов чтения). Была секция: ядро.
|
||||||
|
|||||||
+30
-27
@@ -1,9 +1,37 @@
|
|||||||
# Роадмап
|
# Роадмап
|
||||||
|
|
||||||
Что приложение уже умеет и чего ещё не умеет. Цель — возможность приложения,
|
Что приложение уже умеет и чего ещё не умеет. Цель — возможность приложения,
|
||||||
файл `[goal]` в `items/`; её задачи здесь **не перечисляются** — перечень даёт
|
файл типа `goal` в `items/`; её задачи здесь **не перечисляются** — перечень даёт
|
||||||
`tasks.py list --goal <слаг>`. Очередь значима только в «Запланировано» и
|
`tasks.py list --goal <слаг>`. Очередь значима только в «Запланировано» и
|
||||||
обосновывается прозой рядом.
|
обосновывается прозой рядом. В «Сопровождении» лежит то, чем держат проект —
|
||||||
|
выкладка, инструмент, эксплуатация; граница проходит по тому, кто наблюдает:
|
||||||
|
сообщает ли о состоянии приложение своему пользователю или дежурный смотрит на
|
||||||
|
сервис снаружи.
|
||||||
|
|
||||||
|
## Запланировано
|
||||||
|
|
||||||
|
Очередь держится на двух зависимостях. **`healthlog import` идёт перед чисткой
|
||||||
|
нижнего слоя:** пока импорт экспорта не написан, помечать что-либо устаревшим не
|
||||||
|
на основании чего. **MCP входит в чтение, а не идёт отдельной целью:** адаптер
|
||||||
|
собственной логики не несёт, он переводит вызовы в те же обработчики, и очередь
|
||||||
|
осталась порядком задач внутри цели.
|
||||||
|
|
||||||
|
- [🎯 Клиенты читают данные через HTTP и MCP](items/read-api.md) — Данные видны только через sqlite на хосте — ни один из трёх потребителей, включая агента-медика, ничего прочитать не может
|
||||||
|
- [🎯 Клиент узнаёт форму данных из ответа](items/self-description.md) — Метрик больше сотни и формы точек разные — клиент вынужден угадывать структуру по выборке
|
||||||
|
- [🎯 История из родного экспорта Apple лежит в хранилище](items/native-export-import.md) — Слой sample пуст: полная история и точные сэмплы лежат в zip-архиве и никуда не едут
|
||||||
|
- [🎯 Нижний слой чистится после проверенного экспорта](items/lower-layer-cleanup.md) — Нижний слой растёт на ~100 тысяч координат в сутки, а после экспорта Apple он избыточен
|
||||||
|
- [🎯 Приложение сообщает о своём состоянии](items/observability.md) — Тихо сломавшаяся автоматизация — главный эксплуатационный риск, а сейчас факт виден только в логах
|
||||||
|
|
||||||
|
## Направления
|
||||||
|
|
||||||
|
- [🎯 Исход слияния не зависит от порядка элементов на проводе](items/merge-robustness.md) — Правила слияния держатся на замерах, и каждый новый замер находит зависимость от порядка на проводе
|
||||||
|
- [🎯 Расхождение витрины с журналом не молчит](items/journal-and-rebuild.md) — Расхождение витрины с журналом сейчас молчит: reindex печатает оба отпечатка, а сравнивать их некому
|
||||||
|
- [🎯 У каждого входа есть названный предел](items/limits-and-load.md) — Предела на одну сущность нет вовсе: 63 МиБ держат блокировку 5.019 с, соседние доставки уходят в failed
|
||||||
|
- [🎯 Новая форма от источника не теряется молча](items/parsing-completeness.md) — Пять секций HAE поток ещё не приносил, а ручные секции заводятся задним числом — разбор полон ровно до следующей новой формы
|
||||||
|
|
||||||
|
## Сопровождение
|
||||||
|
|
||||||
|
- [🎯 Сервис доступен телефону из любой сети](items/deploy.md) — Сервис живёт на рабочей машине — телефон достаёт до него только дома, и оба контура открыты
|
||||||
|
|
||||||
## Готово
|
## Готово
|
||||||
|
|
||||||
@@ -24,28 +52,3 @@
|
|||||||
[research/apple-health.md](../research/apple-health.md): правило вывода слоя,
|
[research/apple-health.md](../research/apple-health.md): правило вывода слоя,
|
||||||
модель идентичности и формы точки проверены на живом потоке. Возможностью
|
модель идентичности и формы точки проверены на живом потоке. Возможностью
|
||||||
приложения она не была, поэтому строки среди достигнутых целей не занимает.
|
приложения она не была, поэтому строки среди достигнутых целей не занимает.
|
||||||
|
|
||||||
## Запланировано
|
|
||||||
|
|
||||||
Очередь держится на двух зависимостях. **`healthlog import` идёт перед чисткой
|
|
||||||
нижнего слоя:** пока импорт экспорта не написан, помечать что-либо устаревшим не
|
|
||||||
на основании чего. **MCP входит в чтение, а не идёт отдельной целью:** адаптер
|
|
||||||
собственной логики не несёт, он переводит вызовы в те же обработчики, и очередь
|
|
||||||
осталась порядком задач внутри цели.
|
|
||||||
|
|
||||||
- [[goal] Клиенты читают данные через HTTP и MCP](items/read-api.md) — Данные видны только через sqlite на хосте — ни один из трёх потребителей, включая агента-медика, ничего прочитать не может
|
|
||||||
- [[goal] Клиент узнаёт форму данных из ответа](items/self-description.md) — Метрик больше сотни и формы точек разные — клиент вынужден угадывать структуру по выборке
|
|
||||||
- [[goal] История из родного экспорта Apple лежит в хранилище](items/native-export-import.md) — Слой sample пуст: полная история и точные сэмплы лежат в zip-архиве и никуда не едут
|
|
||||||
- [[goal] Нижний слой чистится после проверенного экспорта](items/lower-layer-cleanup.md) — Нижний слой растёт на ~100 тысяч координат в сутки, а после экспорта Apple он избыточен
|
|
||||||
- [[goal] Приложение сообщает о своём состоянии](items/observability.md) — Тихо сломавшаяся автоматизация — главный эксплуатационный риск, а сейчас факт виден только в логах
|
|
||||||
|
|
||||||
## Направления
|
|
||||||
|
|
||||||
- [[goal] Исход слияния не зависит от порядка элементов на проводе](items/merge-robustness.md) — Правила слияния держатся на замерах, и каждый новый замер находит зависимость от порядка на проводе
|
|
||||||
- [[goal] Расхождение витрины с журналом не молчит](items/journal-and-rebuild.md) — Расхождение витрины с журналом сейчас молчит: reindex печатает оба отпечатка, а сравнивать их некому
|
|
||||||
- [[goal] У каждого входа есть названный предел](items/limits-and-load.md) — Предела на одну сущность нет вовсе: 63 МиБ держат блокировку 5.019 с, соседние доставки уходят в failed
|
|
||||||
- [[goal] Новая форма от источника не теряется молча](items/parsing-completeness.md) — Пять секций HAE поток ещё не приносил, а ручные секции заводятся задним числом — разбор полон ровно до следующей новой формы
|
|
||||||
|
|
||||||
## Разработка
|
|
||||||
|
|
||||||
- [[goal] Сервис доступен телефону из любой сети](items/deploy.md) — Сервис живёт на рабочей машине — телефон достаёт до него только дома, и оба контура открыты
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Спринт
|
# Спринт
|
||||||
|
|
||||||
- **Цель:** [[goal] Клиенты читают данные через HTTP и MCP](items/read-api.md)
|
- **Цель:** [🎯 Клиенты читают данные через HTTP и MCP](items/read-api.md)
|
||||||
- **Начат:** 2026-08-04
|
- **Начат:** 2026-08-04
|
||||||
- **Спринт:** `2026-08-04`
|
- **Спринт:** `2026-08-04`
|
||||||
|
|
||||||
@@ -8,9 +8,9 @@
|
|||||||
|
|
||||||
## Набор
|
## Набор
|
||||||
|
|
||||||
- [Отвечать 304 на повторный запрос точек](items/read-api-points-conditional.md) — Агент опрашивает по расписанию, а каждый повтор стоит полного чтения: на каталоге это 693 мс и +153 МиБ
|
- [✨ Отвечать 304 на повторный запрос точек](items/read-api-points-conditional.md) — Агент опрашивает по расписанию, а каждый повтор стоит полного чтения: на каталоге это 693 мс и +153 МиБ
|
||||||
- [Сворачивать точки по заданной сетке](items/read-api-points-bucket.md) — «Шаги за неделю по дням» — базовый запрос трекера и игры, и сегодня его нечем задать
|
- [✨ Сворачивать точки по заданной сетке](items/read-api-points-bucket.md) — «Шаги за неделю по дням» — базовый запрос трекера и игры, и сегодня его нечем задать
|
||||||
- [Отличать неполное ведро от полного](items/read-api-partial-bucket.md) — Текущий час неполон всегда, и без порога свёртка отдаёт его наравне с полными — клиент видит провал вместо неизвестности
|
- [✨ Отличать неполное ведро от полного](items/read-api-partial-bucket.md) — Текущий час неполон всегда, и без порога свёртка отдаёт его наравне с полными — клиент видит провал вместо неизвестности
|
||||||
- [Ограничить размер ответа маршрутов чтения](items/read-api-response-limit.md) — У маршрутов чтения нет ни одного потолка: множители «метрики × окно × точки × одновременные запросы» ничем не ограничены
|
- [✨ Ограничить размер ответа маршрутов чтения](items/read-api-response-limit.md) — У маршрутов чтения нет ни одного потолка: множители «метрики × окно × точки × одновременные запросы» ничем не ограничены
|
||||||
- [Отдавать тренировки вместе с маршрутом](items/read-api-workouts.md) — Тренировки с маршрутами разобраны и лежат в витрине, а маршрутов чтения нет — сценарий трекера не закрыт
|
- [✨ Отдавать тренировки вместе с маршрутом](items/read-api-workouts.md) — Тренировки с маршрутами разобраны и лежат в витрине, а маршрутов чтения нет — сценарий трекера не закрыт
|
||||||
- [Отдавать записи со своим id за период](items/read-api-records.md) — stateOfMind разобран и хранится, но наружу не отдаётся — а восстановить его нечем: в экспорте Apple его нет
|
- [✨ Отдавать записи со своим id за период](items/read-api-records.md) — stateOfMind разобран и хранится, но наружу не отдаётся — а восстановить его нечем: в экспорте Apple его нет
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# Импортировать родной экспорт Apple Health
|
# ✨ Импортировать родной экспорт Apple Health
|
||||||
|
|
||||||
- **Секция:** Ядро
|
- **Тип:** feature
|
||||||
|
- **Категория:** Ядро
|
||||||
- **Зачем:** Слой sample пуст: полная история и точные сэмплы лежат в zip-архиве и никуда не едут
|
- **Зачем:** Слой sample пуст: полная история и точные сэмплы лежат в zip-архиве и никуда не едут
|
||||||
- **Теги:** goal:native-export-import
|
- **Теги:** goal:native-export-import
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# Свести умолчания конфига с рабочей раскладкой данных
|
# 🐞 Свести умолчания конфига с рабочей раскладкой данных
|
||||||
|
|
||||||
- **Секция:** Инфра
|
- **Тип:** fix
|
||||||
|
- **Категория:** Инфра
|
||||||
- **Зачем:** Запуск без конфига заведёт пустую базу в корне рядом с настоящей — тихая ловушка
|
- **Зачем:** Запуск без конфига заведёт пустую базу в корне рядом с настоящей — тихая ловушка
|
||||||
- **Теги:** goal:deploy
|
- **Теги:** goal:deploy
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# Не отбирать строки в data-миграциях по обрезаемым спискам
|
# 🐞 Не отбирать строки в data-миграциях по обрезаемым спискам
|
||||||
|
|
||||||
- **Секция:** Ядро
|
- **Тип:** fix
|
||||||
|
- **Категория:** Ядро
|
||||||
- **Зачем:** Миграция 00007 отбирает по uncovered_sections, который обрезается на 32 — следующая покрытая секция унаследует слепую зону
|
- **Зачем:** Миграция 00007 отбирает по uncovered_sections, который обрезается на 32 — следующая покрытая секция унаследует слепую зону
|
||||||
- **Теги:** goal:journal-and-rebuild
|
- **Теги:** goal:journal-and-rebuild
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# [idea] Что считать сутками при смене часового пояса
|
# 🔬 Что считать сутками при смене часового пояса
|
||||||
|
|
||||||
- **Секция:** Ядро
|
- **Тип:** research
|
||||||
|
- **Категория:** Ядро
|
||||||
- **Зачем:** Шаги за день — базовый запрос трекера и игры, но чей это день при перелёте, не решено
|
- **Зачем:** Шаги за день — базовый запрос трекера и игры, но чей это день при перелёте, не решено
|
||||||
- **Теги:** goal:read-api
|
- **Теги:** goal:read-api
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# Ограничить размер и число заголовков доставки
|
# ✨ Ограничить размер и число заголовков доставки
|
||||||
|
|
||||||
- **Секция:** Ядро
|
- **Тип:** feature
|
||||||
|
- **Категория:** Ядро
|
||||||
- **Зачем:** MaxHeaderBytes не задан, в базу заголовки пишутся целиком: дефект спит до деплоя, а просыпается вместе с ним
|
- **Зачем:** MaxHeaderBytes не задан, в базу заголовки пишутся целиком: дефект спит до деплоя, а просыпается вместе с ним
|
||||||
- **Теги:** goal:limits-and-load
|
- **Теги:** goal:limits-and-load
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# Класть заголовки доставки в архив рядом с телом
|
# 🐞 Класть заголовки доставки в архив рядом с телом
|
||||||
|
|
||||||
- **Секция:** Ядро
|
- **Тип:** fix
|
||||||
|
- **Категория:** Ядро
|
||||||
- **Зачем:** Заголовки живут только в базе — потеря базы навсегда ломает вывод слоя при пересборке
|
- **Зачем:** Заголовки живут только в базе — потеря базы навсегда ломает вывод слоя при пересборке
|
||||||
- **Теги:** goal:journal-and-rebuild
|
- **Теги:** goal:journal-and-rebuild
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# Выложить сервис на rivendell
|
# ✨ Выложить сервис на rivendell
|
||||||
|
|
||||||
- **Секция:** Инфра
|
- **Тип:** feature
|
||||||
|
- **Категория:** Инфра
|
||||||
- **Зачем:** Сервис живёт на рабочей машине — телефон достаёт до него только дома
|
- **Зачем:** Сервис живёт на рабочей машине — телефон достаёт до него только дома
|
||||||
- **Теги:** goal:deploy
|
- **Теги:** goal:deploy
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# [goal] Сервис доступен телефону из любой сети
|
# 🎯 Сервис доступен телефону из любой сети
|
||||||
|
|
||||||
- **Секция:** Разработка
|
- **Тип:** goal
|
||||||
|
- **Секция:** Сопровождение
|
||||||
- **Зачем:** Сервис живёт на рабочей машине — телефон достаёт до него только дома, и оба контура открыты
|
- **Зачем:** Сервис живёт на рабочей машине — телефон достаёт до него только дома, и оба контура открыты
|
||||||
- **Теги:** decomposed
|
- **Теги:** decomposed
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# Выводить схемы содержимого из данных
|
# ✨ Выводить схемы содержимого из данных
|
||||||
|
|
||||||
- **Секция:** Ядро
|
- **Тип:** feature
|
||||||
|
- **Категория:** Ядро
|
||||||
- **Зачем:** Метрик больше сотни и формы точек разные — клиент вынужден угадывать структуру по выборке
|
- **Зачем:** Метрик больше сотни и формы точек разные — клиент вынужден угадывать структуру по выборке
|
||||||
- **Теги:** goal:self-description
|
- **Теги:** goal:self-description
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# [idea] Отказ от heartbeatSeries
|
# 🔬 Отказ от heartbeatSeries
|
||||||
|
|
||||||
- **Секция:** Ядро
|
- **Тип:** research
|
||||||
|
- **Категория:** Ядро
|
||||||
- **Зачем:** 93% объёма HRV ради данных, которых нет ни в одном планируемом запросе
|
- **Зачем:** 93% объёма HRV ради данных, которых нет ни в одном планируемом запросе
|
||||||
- **Теги:** goal:lower-layer-cleanup
|
- **Теги:** goal:lower-layer-cleanup
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# Ограничить размер сущности и считать форму потоково
|
# ✨ Ограничить размер сущности и считать форму потоково
|
||||||
|
|
||||||
- **Секция:** Ядро
|
- **Тип:** feature
|
||||||
|
- **Категория:** Ядро
|
||||||
- **Зачем:** Тело 40 МиБ даёт 768 МиБ пика кучи, 63 МиБ держат блокировку 5.019 с — предела на одну сущность нет вовсе
|
- **Зачем:** Тело 40 МиБ даёт 768 МиБ пика кучи, 63 МиБ держат блокировку 5.019 с — предела на одну сущность нет вовсе
|
||||||
- **Теги:** goal:limits-and-load
|
- **Теги:** goal:limits-and-load
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# Не терять сущность с id и неразобранной меткой
|
# 🐞 Не терять сущность с id и неразобранной меткой
|
||||||
|
|
||||||
- **Секция:** Ядро
|
- **Тип:** fix
|
||||||
|
- **Категория:** Ядро
|
||||||
- **Зачем:** Тренировка с меткой в неизвестном формате пропадает целиком, а после включения ретеншена окно становится необратимым
|
- **Зачем:** Тренировка с меткой в неизвестном формате пропадает целиком, а после включения ретеншена окно становится необратимым
|
||||||
- **Теги:** goal:parsing-completeness
|
- **Теги:** goal:parsing-completeness
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# Проверять целостность собранной витрины до подмены
|
# ✨ Проверять целостность собранной витрины до подмены
|
||||||
|
|
||||||
- **Секция:** Ядро
|
- **Тип:** feature
|
||||||
|
- **Категория:** Ядро
|
||||||
- **Зачем:** Подмена файла необратима, а годность выхода подтверждена отпечатком на ещё открытом дескрипторе
|
- **Зачем:** Подмена файла необратима, а годность выхода подтверждена отпечатком на ещё открытом дескрипторе
|
||||||
- **Теги:** goal:journal-and-rebuild
|
- **Теги:** goal:journal-and-rebuild
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
# [goal] Расхождение витрины с журналом не молчит
|
# 🎯 Расхождение витрины с журналом не молчит
|
||||||
|
|
||||||
|
- **Тип:** goal
|
||||||
- **Секция:** Направления
|
- **Секция:** Направления
|
||||||
- **Зачем:** Расхождение витрины с журналом сейчас молчит: reindex печатает оба отпечатка, а сравнивать их некому
|
- **Зачем:** Расхождение витрины с журналом сейчас молчит: reindex печатает оба отпечатка, а сравнивать их некому
|
||||||
- **Теги:** decomposed
|
- **Теги:** decomposed
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# Держать порядок журнала при конкурентных приёмах
|
# 🐞 Держать порядок журнала при конкурентных приёмах
|
||||||
|
|
||||||
- **Секция:** Ядро
|
- **Тип:** fix
|
||||||
|
- **Категория:** Ядро
|
||||||
- **Зачем:** Доставка, свёрнутая раньше своей предшественницы, уходит в failed навсегда, и живая витрина молча расходится с пересборкой
|
- **Зачем:** Доставка, свёрнутая раньше своей предшественницы, уходит в failed навсегда, и живая витрина молча расходится с пересборкой
|
||||||
- **Теги:** goal:journal-and-rebuild, question
|
- **Теги:** goal:journal-and-rebuild, question
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# Измерить, нужно ли правило полноты рядом с LWW
|
# 🔬 Измерить, нужно ли правило полноты рядом с LWW
|
||||||
|
|
||||||
- **Секция:** Ядро
|
- **Тип:** research
|
||||||
|
- **Категория:** Ядро
|
||||||
- **Зачем:** Полнота решает 1,2% спорных координат, и неизвестно, была ли более полная точка более поздней — от этого зависит, нужна ли она вообще
|
- **Зачем:** Полнота решает 1,2% спорных координат, и неизвестно, была ли более полная точка более поздней — от этого зависит, нужна ли она вообще
|
||||||
- **Теги:** goal:merge-robustness, sprint:2026-08-03
|
- **Теги:** goal:merge-robustness, sprint:2026-08-03
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
# [goal] У каждого входа есть названный предел
|
# 🎯 У каждого входа есть названный предел
|
||||||
|
|
||||||
|
- **Тип:** goal
|
||||||
- **Секция:** Направления
|
- **Секция:** Направления
|
||||||
- **Зачем:** Предела на одну сущность нет вовсе: 63 МиБ держат блокировку 5.019 с, соседние доставки уходят в failed
|
- **Зачем:** Предела на одну сущность нет вовсе: 63 МиБ держат блокировку 5.019 с, соседние доставки уходят в failed
|
||||||
- **Теги:** decomposed
|
- **Теги:** decomposed
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
# [goal] Нижний слой чистится после проверенного экспорта
|
# 🎯 Нижний слой чистится после проверенного экспорта
|
||||||
|
|
||||||
|
- **Тип:** goal
|
||||||
- **Секция:** Запланировано
|
- **Секция:** Запланировано
|
||||||
- **Зачем:** Нижний слой растёт на ~100 тысяч координат в сутки, а после экспорта Apple он избыточен
|
- **Зачем:** Нижний слой растёт на ~100 тысяч координат в сутки, а после экспорта Apple он избыточен
|
||||||
- **Теги:** decomposed
|
- **Теги:** decomposed
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# Помечать нижний слой устаревшим после экспорта
|
# ✨ Помечать нижний слой устаревшим после экспорта
|
||||||
|
|
||||||
- **Секция:** Ядро
|
- **Тип:** feature
|
||||||
|
- **Категория:** Ядро
|
||||||
- **Зачем:** Нижний слой растёт на ~100 тысяч координат в сутки, а после экспорта Apple он избыточен
|
- **Зачем:** Нижний слой растёт на ~100 тысяч координат в сутки, а после экспорта Apple он избыточен
|
||||||
- **Теги:** goal:lower-layer-cleanup
|
- **Теги:** goal:lower-layer-cleanup
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# Поднять MCP-сервер поверх Read API
|
# ✨ Поднять MCP-сервер поверх Read API
|
||||||
|
|
||||||
- **Секция:** Ядро — набор ограничен HTTP-слоем чтения после дробления; адаптер берётся следующим спринтом по той же цели
|
- **Тип:** feature
|
||||||
|
- **Категория:** Ядро — набор ограничен HTTP-слоем чтения после дробления; адаптер берётся следующим спринтом по той же цели
|
||||||
- **Зачем:** Агент-медик — первый заказчик проекта, а подключить его сейчас нечем
|
- **Зачем:** Агент-медик — первый заказчик проекта, а подключить его сейчас нечем
|
||||||
- **Теги:** goal:read-api
|
- **Теги:** goal:read-api
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# Снизить цену слияния на широкой доставке
|
# 🐞 Снизить цену слияния на широкой доставке
|
||||||
|
|
||||||
- **Секция:** Ядро
|
- **Тип:** fix
|
||||||
|
- **Категория:** Ядро
|
||||||
- **Зачем:** 63 МБ на одной координате держат транзакцию 5.15 с при busy_timeout 5 с — соседние доставки уходят в failed
|
- **Зачем:** 63 МБ на одной координате держат транзакцию 5.15 с при busy_timeout 5 с — соседние доставки уходят в failed
|
||||||
- **Теги:** goal:limits-and-load
|
- **Теги:** goal:limits-and-load
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# Хранить счётчики слияния вне логов
|
# ✨ Хранить счётчики слияния вне логов
|
||||||
|
|
||||||
- **Секция:** Инфра
|
- **Тип:** feature
|
||||||
|
- **Категория:** Инфра
|
||||||
- **Зачем:** Единственный след несравнимых наборов — строка WARN в docker-логе с ротацией 3×10 МБ: событие может произойти и не оставить ничего
|
- **Зачем:** Единственный след несравнимых наборов — строка WARN в docker-логе с ротацией 3×10 МБ: событие может произойти и не оставить ничего
|
||||||
- **Теги:** goal:observability
|
- **Теги:** goal:observability
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
# [goal] Исход слияния не зависит от порядка элементов на проводе
|
# 🎯 Исход слияния не зависит от порядка элементов на проводе
|
||||||
|
|
||||||
|
- **Тип:** goal
|
||||||
- **Секция:** Направления
|
- **Секция:** Направления
|
||||||
- **Зачем:** Правила слияния держатся на замерах, и каждый новый замер находит зависимость от порядка на проводе
|
- **Зачем:** Правила слияния держатся на замерах, и каждый новый замер находит зависимость от порядка на проводе
|
||||||
- **Теги:** decomposed
|
- **Теги:** decomposed
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# [idea] Месячный проход по ручным секциям
|
# 🔬 Месячный проход по ручным секциям
|
||||||
|
|
||||||
- **Секция:** Ядро
|
- **Тип:** research
|
||||||
|
- **Категория:** Ядро
|
||||||
- **Зачем:** Симптомы и лекарства заводятся задним числом на недели — недельного глубокого прохода им не хватит
|
- **Зачем:** Симптомы и лекарства заводятся задним числом на недели — недельного глубокого прохода им не хватит
|
||||||
- **Теги:** goal:parsing-completeness
|
- **Теги:** goal:parsing-completeness
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
# [goal] История из родного экспорта Apple лежит в хранилище
|
# 🎯 История из родного экспорта Apple лежит в хранилище
|
||||||
|
|
||||||
|
- **Тип:** goal
|
||||||
- **Секция:** Запланировано
|
- **Секция:** Запланировано
|
||||||
- **Зачем:** Слой sample пуст: полная история и точные сэмплы лежат в zip-архиве и никуда не едут
|
- **Зачем:** Слой sample пуст: полная история и точные сэмплы лежат в zip-архиве и никуда не едут
|
||||||
- **Теги:** decomposed
|
- **Теги:** decomposed
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# [idea] NDJSON-поток для больших выборок Read API
|
# 🔬 NDJSON-поток для больших выборок Read API
|
||||||
|
|
||||||
- **Секция:** Ядро
|
- **Тип:** research
|
||||||
|
- **Категория:** Ядро
|
||||||
- **Зачем:** Выборка нижнего слоя за месяц не влезает в один JSON-ответ — либо поток, либо пагинация
|
- **Зачем:** Выборка нижнего слоя за месяц не влезает в один JSON-ответ — либо поток, либо пагинация
|
||||||
- **Теги:** goal:read-api
|
- **Теги:** goal:read-api
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
# [goal] Приложение сообщает о своём состоянии
|
# 🎯 Приложение сообщает о своём состоянии
|
||||||
|
|
||||||
|
- **Тип:** goal
|
||||||
- **Секция:** Запланировано
|
- **Секция:** Запланировано
|
||||||
- **Зачем:** Тихо сломавшаяся автоматизация — главный эксплуатационный риск, а сейчас факт виден только в логах
|
- **Зачем:** Тихо сломавшаяся автоматизация — главный эксплуатационный риск, а сейчас факт виден только в логах
|
||||||
- **Теги:** decomposed
|
- **Теги:** decomposed
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# Ловить гейтом расхождение спеки с маршрутами
|
# 🧹 Ловить гейтом расхождение спеки с маршрутами
|
||||||
|
|
||||||
- **Секция:** Ядро
|
- **Тип:** chore
|
||||||
|
- **Категория:** Ядро
|
||||||
- **Зачем:** Рукописная спека — источник истины, а расходится она с кодом молча: без гейта решение о рукописной спеке не держится
|
- **Зачем:** Рукописная спека — источник истины, а расходится она с кодом молча: без гейта решение о рукописной спеке не держится
|
||||||
- **Теги:** goal:read-api, sprint:2026-08-04
|
- **Теги:** goal:read-api, sprint:2026-08-04
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# Написать OpenAPI-спеку руками
|
# ✨ Написать OpenAPI-спеку руками
|
||||||
|
|
||||||
- **Секция:** Ядро
|
- **Тип:** feature
|
||||||
|
- **Категория:** Ядро
|
||||||
- **Зачем:** Потребителей три и один из них агент — контракт должен читаться машиной, а не пересказываться в чате
|
- **Зачем:** Потребителей три и один из них агент — контракт должен читаться машиной, а не пересказываться в чате
|
||||||
- **Теги:** goal:read-api, sprint:2026-08-04
|
- **Теги:** goal:read-api, sprint:2026-08-04
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# [idea] Пересекающиеся источники одной метрики
|
# 🔬 Пересекающиеся источники одной метрики
|
||||||
|
|
||||||
- **Секция:** Ядро
|
- **Тип:** research
|
||||||
|
- **Категория:** Ядро
|
||||||
- **Зачем:** Сон пишут и часы, и стороннее приложение — сумма по обоим задвоит ночь
|
- **Зачем:** Сон пишут и часы, и стороннее приложение — сумма по обоим задвоит ночь
|
||||||
- **Теги:** goal:read-api
|
- **Теги:** goal:read-api
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# [idea] Выгрузка в parquet отдельной командой
|
# 🔬 Выгрузка в parquet отдельной командой
|
||||||
|
|
||||||
- **Секция:** Ядро
|
- **Тип:** research
|
||||||
|
- **Категория:** Ядро
|
||||||
- **Зачем:** Дверь для тяжёлой аналитики снаружи: DuckDB читает и parquet, и файл SQLite — спешить некуда, но и закрывать не нужно
|
- **Зачем:** Дверь для тяжёлой аналитики снаружи: DuckDB читает и parquet, и файл SQLite — спешить некуда, но и закрывать не нужно
|
||||||
- **Теги:** goal:read-api
|
- **Теги:** goal:read-api
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
# [goal] Новая форма от источника не теряется молча
|
# 🎯 Новая форма от источника не теряется молча
|
||||||
|
|
||||||
|
- **Тип:** goal
|
||||||
- **Секция:** Направления
|
- **Секция:** Направления
|
||||||
- **Зачем:** Пять секций HAE поток ещё не приносил, а ручные секции заводятся задним числом — разбор полон ровно до следующей новой формы
|
- **Зачем:** Пять секций HAE поток ещё не приносил, а ручные секции заводятся задним числом — разбор полон ровно до следующей новой формы
|
||||||
- **Теги:** decomposed
|
- **Теги:** decomposed
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# Подчищать сырой архив до последнего проверенного экспорта
|
# ✨ Подчищать сырой архив до последнего проверенного экспорта
|
||||||
|
|
||||||
- **Секция:** Инфра
|
- **Тип:** feature
|
||||||
|
- **Категория:** Инфра
|
||||||
- **Зачем:** Архив не подчищается вовсе, а резать его раньше даты проверенного экспорта нельзя — в журнале останется дыра, которую нечем пересобрать
|
- **Зачем:** Архив не подчищается вовсе, а резать его раньше даты проверенного экспорта нельзя — в журнале останется дыра, которую нечем пересобрать
|
||||||
- **Теги:** goal:journal-and-rebuild
|
- **Теги:** goal:journal-and-rebuild
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
# Отличать неполное ведро от полного
|
# ✨ Отличать неполное ведро от полного
|
||||||
|
|
||||||
- **Секция:** Ядро
|
- **Тип:** feature
|
||||||
|
- **Категория:** Ядро
|
||||||
- **Зачем:** Текущий час неполон всегда, и без порога свёртка отдаёт его наравне с полными — клиент видит провал вместо неизвестности
|
- **Зачем:** Текущий час неполон всегда, и без порога свёртка отдаёт его наравне с полными — клиент видит провал вместо неизвестности
|
||||||
- **Теги:** goal:read-api, sprint:2026-08-04, kind:feature
|
- **Теги:** goal:read-api, sprint:2026-08-04
|
||||||
|
|
||||||
Ведро, в котором известна не вся сетка, отличимо от полного — а полярность
|
Ведро, в котором известна не вся сетка, отличимо от полного — а полярность
|
||||||
порога названа вслух, а не выводится читателем из умолчания.
|
порога названа вслух, а не выводится читателем из умолчания.
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
# Сворачивать точки по заданной сетке
|
# ✨ Сворачивать точки по заданной сетке
|
||||||
|
|
||||||
- **Секция:** Ядро
|
- **Тип:** feature
|
||||||
|
- **Категория:** Ядро
|
||||||
- **Зачем:** «Шаги за неделю по дням» — базовый запрос трекера и игры, и сегодня его нечем задать
|
- **Зачем:** «Шаги за неделю по дням» — базовый запрос трекера и игры, и сегодня его нечем задать
|
||||||
- **Теги:** goal:read-api, sprint:2026-08-04, kind:feature
|
- **Теги:** goal:read-api, sprint:2026-08-04
|
||||||
|
|
||||||
«Шаги за неделю по дням» отвечаются одним запросом `?from&to&bucket`, и род
|
«Шаги за неделю по дням» отвечаются одним запросом `?from&to&bucket`, и род
|
||||||
свёртки берётся измеренным, а не угаданным.
|
свёртки берётся измеренным, а не угаданным.
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
# Отвечать 304 на повторный запрос точек
|
# ✨ Отвечать 304 на повторный запрос точек
|
||||||
|
|
||||||
- **Секция:** Ядро
|
- **Тип:** feature
|
||||||
|
- **Категория:** Ядро
|
||||||
- **Зачем:** Агент опрашивает по расписанию, а каждый повтор стоит полного чтения: на каталоге это 693 мс и +153 МиБ
|
- **Зачем:** Агент опрашивает по расписанию, а каждый повтор стоит полного чтения: на каталоге это 693 мс и +153 МиБ
|
||||||
- **Теги:** goal:read-api, sprint:2026-08-04, kind:feature
|
- **Теги:** goal:read-api, sprint:2026-08-04
|
||||||
|
|
||||||
Повторный опрос точек с той же меткой стоит `304` вместо полного чтения, и метка
|
Повторный опрос точек с той же меткой стоит `304` вместо полного чтения, и метка
|
||||||
не может ответить на другой набор данных.
|
не может ответить на другой набор данных.
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
# Отдавать записи со своим id за период
|
# ✨ Отдавать записи со своим id за период
|
||||||
|
|
||||||
- **Секция:** Ядро
|
- **Тип:** feature
|
||||||
|
- **Категория:** Ядро
|
||||||
- **Зачем:** stateOfMind разобран и хранится, но наружу не отдаётся — а восстановить его нечем: в экспорте Apple его нет
|
- **Зачем:** stateOfMind разобран и хранится, но наружу не отдаётся — а восстановить его нечем: в экспорте Apple его нет
|
||||||
- **Теги:** goal:read-api, sprint:2026-08-04, kind:feature
|
- **Теги:** goal:read-api, sprint:2026-08-04
|
||||||
|
|
||||||
Записи со своим `id` — сегодня это `stateOfMind` — достаются за период через
|
Записи со своим `id` — сегодня это `stateOfMind` — достаются за период через
|
||||||
`GET /records/{kind}`.
|
`GET /records/{kind}`.
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
# Ограничить размер ответа маршрутов чтения
|
# ✨ Ограничить размер ответа маршрутов чтения
|
||||||
|
|
||||||
- **Секция:** Ядро
|
- **Тип:** feature
|
||||||
|
- **Категория:** Ядро
|
||||||
- **Зачем:** У маршрутов чтения нет ни одного потолка: множители «метрики × окно × точки × одновременные запросы» ничем не ограничены
|
- **Зачем:** У маршрутов чтения нет ни одного потолка: множители «метрики × окно × точки × одновременные запросы» ничем не ограничены
|
||||||
- **Теги:** goal:read-api, sprint:2026-08-04, kind:feature
|
- **Теги:** goal:read-api, sprint:2026-08-04
|
||||||
|
|
||||||
У маршрутов чтения появляется названный потолок: сетка не задана и ответ не
|
У маршрутов чтения появляется названный потолок: сетка не задана и ответ не
|
||||||
влезает — сервер огрубляет её и **называет** в ответе; сетка задана явно и не
|
влезает — сервер огрубляет её и **называет** в ответе; сетка задана явно и не
|
||||||
|
|||||||
@@ -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` за
|
||||||
период и `GET /workouts/{id}` поштучно.
|
период и `GET /workouts/{id}` поштучно.
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
# [goal] Клиенты читают данные через HTTP и MCP
|
# 🎯 Клиенты читают данные через HTTP и MCP
|
||||||
|
|
||||||
|
- **Тип:** goal
|
||||||
- **Секция:** Запланировано
|
- **Секция:** Запланировано
|
||||||
- **Зачем:** Данные видны только через sqlite на хосте — ни один из трёх потребителей, включая агента-медика, ничего прочитать не может
|
- **Зачем:** Данные видны только через sqlite на хосте — ни один из трёх потребителей, включая агента-медика, ничего прочитать не может
|
||||||
- **Теги:** decomposed
|
- **Теги:** decomposed
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# Сверять живую витрину с пересборкой
|
# ✨ Сверять живую витрину с пересборкой
|
||||||
|
|
||||||
- **Секция:** Ядро
|
- **Тип:** feature
|
||||||
|
- **Категория:** Ядро
|
||||||
- **Зачем:** reindex печатает оба отпечатка, но сравнивать их некому — расхождение с журналом молчит
|
- **Зачем:** reindex печатает оба отпечатка, но сравнивать их некому — расхождение с журналом молчит
|
||||||
- **Теги:** goal:journal-and-rebuild
|
- **Теги:** goal:journal-and-rebuild
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# Не держать весь журнал в памяти при пересборке
|
# 🧹 Не держать весь журнал в памяти при пересборке
|
||||||
|
|
||||||
- **Секция:** Ядро
|
- **Тип:** chore
|
||||||
|
- **Категория:** Ядро
|
||||||
- **Зачем:** Учёт доставок и список путей архива материализуются целиком: расход растёт вместе с журналом, а у журнала конца нет
|
- **Зачем:** Учёт доставок и список путей архива материализуются целиком: расход растёт вместе с журналом, а у журнала конца нет
|
||||||
- **Теги:** goal:journal-and-rebuild
|
- **Теги:** goal:journal-and-rebuild
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# Назвать механизм отката релиза после наката миграции
|
# ✨ Назвать механизм отката релиза после наката миграции
|
||||||
|
|
||||||
- **Секция:** Инфра
|
- **Тип:** feature
|
||||||
|
- **Категория:** Инфра
|
||||||
- **Зачем:** Страж версии схемы делает возврат старого бинаря отказом, а понизить схему нечем — аварийный путь пришлось бы изобретать в аварии
|
- **Зачем:** Страж версии схемы делает возврат старого бинаря отказом, а понизить схему нечем — аварийный путь пришлось бы изобретать в аварии
|
||||||
- **Теги:** goal:deploy
|
- **Теги:** goal:deploy
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# [idea] Человеческие аннотации поверх выведенных схем
|
# 🔬 Человеческие аннотации поверх выведенных схем
|
||||||
|
|
||||||
- **Секция:** Ядро
|
- **Тип:** research
|
||||||
|
- **Категория:** Ядро
|
||||||
- **Зачем:** Выведенная схема говорит форму, но не смысл метрики — нужно ли описание сверху, зависит от стабильности формата
|
- **Зачем:** Выведенная схема говорит форму, но не смысл метрики — нужно ли описание сверху, зависит от стабильности формата
|
||||||
- **Теги:** goal:self-description
|
- **Теги:** goal:self-description
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# [idea] Порог sealed: с какого возраста час считается запечатанным
|
# 🔬 Порог sealed: с какого возраста час считается запечатанным
|
||||||
|
|
||||||
- **Секция:** Ядро
|
- **Тип:** research
|
||||||
|
- **Категория:** Ядро
|
||||||
- **Зачем:** WARN на изменение старого часа уже пишется, но порог не выбран — ставим по факту, когда накопится статистика досчёта
|
- **Зачем:** WARN на изменение старого часа уже пишется, но порог не выбран — ставим по факту, когда накопится статистика досчёта
|
||||||
- **Теги:** goal:merge-robustness
|
- **Теги:** goal:merge-robustness
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
# [goal] Клиент узнаёт форму данных из ответа
|
# 🎯 Клиент узнаёт форму данных из ответа
|
||||||
|
|
||||||
|
- **Тип:** goal
|
||||||
- **Секция:** Запланировано
|
- **Секция:** Запланировано
|
||||||
- **Зачем:** Метрик больше сотни и формы точек разные — клиент вынужден угадывать структуру по выборке
|
- **Зачем:** Метрик больше сотни и формы точек разные — клиент вынужден угадывать структуру по выборке
|
||||||
- **Теги:** decomposed
|
- **Теги:** decomposed
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# Развести бюджеты остановки и оставить следы миграции в логе
|
# 🐞 Развести бюджеты остановки и оставить следы миграции в логе
|
||||||
|
|
||||||
- **Секция:** Инфра
|
- **Тип:** fix
|
||||||
|
- **Категория:** Инфра
|
||||||
- **Зачем:** Долгий запрос чтения съедает бюджет остановки, и WARN обвиняет воркер свёртки; миграция молчит и не прерывается SIGTERM
|
- **Зачем:** Долгий запрос чтения съедает бюджет остановки, и WARN обвиняет воркер свёртки; миграция молчит и не прерывается SIGTERM
|
||||||
- **Теги:** goal:deploy
|
- **Теги:** goal:deploy
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# Отдавать состояние сервиса маршрутом /stats
|
# ✨ Отдавать состояние сервиса маршрутом /stats
|
||||||
|
|
||||||
- **Секция:** Инфра
|
- **Тип:** feature
|
||||||
|
- **Категория:** Инфра
|
||||||
- **Зачем:** Тихо сломавшаяся автоматизация — главный эксплуатационный риск, а сейчас факт виден только в логах
|
- **Зачем:** Тихо сломавшаяся автоматизация — главный эксплуатационный риск, а сейчас факт виден только в логах
|
||||||
- **Теги:** goal:observability
|
- **Теги:** goal:observability
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# Слать уведомление, когда данных нет N часов
|
# ✨ Слать уведомление, когда данных нет N часов
|
||||||
|
|
||||||
- **Секция:** Инфра
|
- **Тип:** feature
|
||||||
|
- **Категория:** Инфра
|
||||||
- **Зачем:** Пропажу потока сейчас замечает человек, а не сервис
|
- **Зачем:** Пропажу потока сейчас замечает человек, а не сервис
|
||||||
- **Теги:** goal:observability
|
- **Теги:** goal:observability
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# Поднять Swagger UI без внешней сети
|
# ✨ Поднять Swagger UI без внешней сети
|
||||||
|
|
||||||
- **Секция:** Ядро
|
- **Тип:** feature
|
||||||
|
- **Категория:** Ядро
|
||||||
- **Зачем:** Контракт читается машиной, но человеку нечем выполнить запрос к живому сервису из браузера, а внешних CDN в локальной сети нет
|
- **Зачем:** Контракт читается машиной, но человеку нечем выполнить запрос к живому сервису из браузера, а внешних CDN в локальной сети нет
|
||||||
- **Теги:** goal:read-api, sprint:2026-08-04
|
- **Теги:** goal:read-api, sprint:2026-08-04
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# Развести токены контуров и убрать секреты из репозитория
|
# ✨ Развести токены контуров и убрать секреты из репозитория
|
||||||
|
|
||||||
- **Секция:** Инфра
|
- **Тип:** feature
|
||||||
|
- **Категория:** Инфра
|
||||||
- **Зачем:** Проверка токенов выключена, config.docker.toml коммитится — так нельзя выезжать наружу
|
- **Зачем:** Проверка токенов выключена, config.docker.toml коммитится — так нельзя выезжать наружу
|
||||||
- **Теги:** goal:deploy
|
- **Теги:** goal:deploy
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# Не задваивать тренировки при импорте родного экспорта
|
# ✨ Не задваивать тренировки при импорте родного экспорта
|
||||||
|
|
||||||
- **Секция:** Ядро
|
- **Тип:** feature
|
||||||
|
- **Категория:** Ядро
|
||||||
- **Зачем:** В export.xml у тренировки нет id — импорт снапшота задвоит тренировки, приехавшие от HAE
|
- **Зачем:** В export.xml у тренировки нет id — импорт снапшота задвоит тренировки, приехавшие от HAE
|
||||||
- **Теги:** goal:native-export-import
|
- **Теги:** goal:native-export-import
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# [idea] Разворачивание маршрутов тренировок в отдельную таблицу
|
# 🔬 Разворачивание маршрутов тренировок в отдельную таблицу
|
||||||
|
|
||||||
- **Секция:** Ядро
|
- **Тип:** research
|
||||||
|
- **Категория:** Ядро
|
||||||
- **Зачем:** Маршрут лежит блобом внутри тренировки — понадобится, только если появится клиент, которому мало отдачи одним пакетом
|
- **Зачем:** Маршрут лежит блобом внутри тренировки — понадобится, только если появится клиент, которому мало отдачи одним пакетом
|
||||||
- **Теги:** goal:read-api
|
- **Теги:** goal:read-api
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,12 @@
|
|||||||
# parsing Specification
|
# parsing Specification
|
||||||
|
|
||||||
## Purpose
|
## Purpose
|
||||||
TBD - created by archiving change razbor-metrik-v-obekty. Update Purpose after archive.
|
|
||||||
|
Разбор тела Health Auto Export: какие секции покрыты, как читается точка и её
|
||||||
|
метка времени, как выводится слой доставки и что происходит с непонятым.
|
||||||
|
Источник истины о формате — не документация HAE, а живые пакеты
|
||||||
|
(`docs/research/apple-health.md`).
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
### Requirement: Разбор секции метрик
|
### Requirement: Разбор секции метрик
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,13 @@
|
|||||||
# storage Specification
|
# storage Specification
|
||||||
|
|
||||||
## Purpose
|
## Purpose
|
||||||
TBD - created by archiving change razbor-metrik-v-obekty. Update Purpose after archive.
|
|
||||||
|
Витрина: как принятые точки и сущности ложатся, адресуются и выбираются между
|
||||||
|
версиями. Здесь живут идентичность точки по координатам, полнота и тай-брейк,
|
||||||
|
часовой объект, хранение сущностей с собственным `id`, отпечаток витрины и учёт
|
||||||
|
разобранности доставки. Правило одно на всё: содержимое хранится дословно, а
|
||||||
|
состояние остаётся свёрткой журнала в его порядке.
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
### Requirement: Идентичность точки по координатам
|
### Requirement: Идентичность точки по координатам
|
||||||
|
|
||||||
@@ -410,8 +416,11 @@ HTML-экранирования: `&`, `<` и `>` внутри точки обя
|
|||||||
построчный разбор логов. То же относится к идентификатору сущности: он приходит
|
построчный разбор логов. То же относится к идентификатору сущности: он приходит
|
||||||
из чужого тела и ограничен по длине при разборе.
|
из чужого тела и ограничен по длине при разборе.
|
||||||
|
|
||||||
Частичный разбор уровня записи не повышает: `partial` — установившееся состояние
|
Частичный разбор уровня записи не повышает: `partial` — установившееся
|
||||||
половины потока (53 доставки из 118), и постоянный `WARN` обесценил бы уровень.
|
состояние, а не сигнал. До покрытия `workouts` и `stateOfMind` частичной
|
||||||
|
числилась половина потока (53 доставки из 118); после покрытия непокрытая
|
||||||
|
секция живым потоком не приносилась ни разу на том же корпусе. Постоянный
|
||||||
|
`WARN` обесценил бы уровень.
|
||||||
Повышает уровень другое, и оснований два:
|
Повышает уровень другое, и оснований два:
|
||||||
|
|
||||||
- срабатывание границ списка: тело с сотнями секций или с именем длиннее предела
|
- срабатывание границ списка: тело с сотнями секций или с именем длиннее предела
|
||||||
|
|||||||
Reference in New Issue
Block a user