tasks: закрыт разбор и хранилище, начат спринт по чтению данных клиентами
- цель parsing-and-storage закрыта по своему критерию; незакрываемый остаток (новые формы от источника, ручные секции задним числом) переехал в тему parsing-completeness - цель mcp поглощена целью read-api, переименованной в «Чтение данных клиентами»: адаптер — последний шаг того же направления, а не своё - read-api-points разложена на конверт с точками, свёртку по сетке и тренировки с записями; спринт 2026-08-04 набран пятью задачами
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
|
||||
- **Секция:** ядро
|
||||
- **Зачем:** Тренировка с меткой в неизвестном формате пропадает целиком, а после включения ретеншена окно становится необратимым
|
||||
- **Теги:** goal:parsing-and-storage
|
||||
- **Теги:** goal:parsing-completeness
|
||||
|
||||
**Решение принято владельцем 2026-08-03: вариант (1) — хранить с NULL-меткой.**
|
||||
`start_utc`/`ts_utc` становятся NULLABLE, содержимое (включая маршрут) хранится,
|
||||
@@ -10,7 +10,7 @@
|
||||
отвергнут при постановке: подстановка метки доставки — выдуманное измерение в
|
||||
колонке, по которой идёт выборка.
|
||||
|
||||
**Берётся после [Read API по точкам и сущностям](read-api-points.md).** Правило
|
||||
**Берётся после [тренировок и записей наружу](read-api-workouts-and-records.md).** Правило
|
||||
чтения — что выборка «за период» делает со строками без метки — обязано
|
||||
проектироваться вместе с читателем, иначе такие строки молча исчезнут из любого
|
||||
ответа. Порядок тот же, что у [journal-order-on-ingest](journal-order-on-ingest.md)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
- **Секция:** ядро
|
||||
- **Зачем:** Агент-медик — первый заказчик проекта, а подключить его сейчас нечем
|
||||
- **Теги:** goal:mcp
|
||||
- **Теги:** goal:read-api
|
||||
|
||||
Агент-медик — первый заказчик проекта. Ему нужна актуальная сводка, а не срез
|
||||
на дату последнего ручного экспорта.
|
||||
@@ -21,4 +21,25 @@ MCP не даёт ничего, чего не даёт HTTP, и права об
|
||||
Готово, когда агент подключается по URL и отвечает на «как я спал на прошлой
|
||||
неделе» без промежуточного кода.
|
||||
|
||||
Связано: `docs/architecture.md` → «MCP», план → шаг «MCP».
|
||||
## Критерии приёмки
|
||||
|
||||
- живой агент подключается по URL и отвечает на «как я спал на прошлой неделе»
|
||||
без промежуточного кода — оракул: подключение реального MCP-клиента к
|
||||
поднятому сервису
|
||||
- вызов инструмента и соответствующий HTTP-запрос дают одни и те же данные —
|
||||
оракул: тест, сравнивающий выход инструмента с ответом маршрута на тех же
|
||||
параметрах
|
||||
- запрос без токена чтения отклоняется обоими транспортами одинаково — оракул:
|
||||
тест на паре «MCP без токена / HTTP без токена»
|
||||
- правило размера ответа действует и в MCP: слишком широкий запрос получает
|
||||
названную сетку или ошибку со списком, а не обрезанный ответ — оракул: тест на
|
||||
запросе за пределом
|
||||
|
||||
## Рамки
|
||||
|
||||
Схема не трогается, данные только читаются, сервис перезапускается. Собственной
|
||||
логики адаптер не несёт — новое поведение здесь признак того, что оно должно
|
||||
было появиться в маршруте чтения. Берётся последней в цели: переводить нечего,
|
||||
пока обработчиков нет.
|
||||
|
||||
Связано: `docs/architecture.md` → «MCP».
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
# [goal] MCP
|
||||
|
||||
- **Секция:** порядок
|
||||
- **Зачем:** Агент-медик — первый заказчик проекта, а подключить его сейчас нечем
|
||||
- **Теги:** decomposed
|
||||
|
||||
Агент-медик — первый заказчик проекта — подключается к хранилищу.
|
||||
|
||||
Выведена из шага 7 плана. Идёт после Read API намеренно: адаптер собственной
|
||||
логики не несёт, он переводит вызовы в те же обработчики, и переводить пока
|
||||
нечего.
|
||||
|
||||
Завершена, когда агент читает данные через MCP тем же токеном чтения.
|
||||
|
||||
## Завершение
|
||||
|
||||
Агент читает данные через MCP тем же токеном чтения, и собственной логики
|
||||
адаптер не несёт.
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
- **Секция:** ядро
|
||||
- **Зачем:** Симптомы и лекарства заводятся задним числом на недели — недельного глубокого прохода им не хватит
|
||||
- **Теги:** goal:parsing-and-storage
|
||||
- **Теги:** goal:parsing-completeness
|
||||
|
||||
Окно досчёта не единое, и это измеренное различие, а не предположение.
|
||||
Количественные метрики (пульс, шаги, энергия) человек руками не правит — они
|
||||
|
||||
@@ -19,4 +19,4 @@ Read API отдаёт ответ одним JSON. Для выборок нижн
|
||||
последовательно или с возвратами.
|
||||
|
||||
Связано: `docs/architecture.md` → «Свёртка и размер ответа», задача
|
||||
`read-api-points`.
|
||||
`read-api-bucketing` (правило размера ответа проектируется там).
|
||||
|
||||
@@ -18,8 +18,30 @@
|
||||
он должен работать в локальной сети без интернета);
|
||||
- проверка актуальности спеки в гейте: контракт разъезжается молча.
|
||||
|
||||
**Решено владельцем 2026-08-04: спека пишется руками и она источник истины.**
|
||||
Для API из шести ручек это честнее вывода из кода: контракт проектируется, а не
|
||||
фотографируется с того, что вышло, — опечатка в имени поля иначе становится
|
||||
частью спеки. Совпадает с тем, как в проекте уже устроен OpenSpec: спека
|
||||
первична к коду. Плата названа: спека расходится с кодом молча, и именно поэтому
|
||||
проверка её актуальности идёт в гейт третьим шагом, а не остаётся регламентом.
|
||||
|
||||
Готово, когда по спеке можно сгенерировать клиент, а Swagger UI открывается
|
||||
локально и выполняет запрос к живому сервису.
|
||||
|
||||
Развилка на решение: спека пишется руками как источник истины или выводится из
|
||||
кода. Для маленького API рукописная спека честнее — но это стоит обсудить.
|
||||
## Критерии приёмки
|
||||
|
||||
- по спеке генерируется клиент, и сгенерированный клиент выполняет запрос к
|
||||
живому сервису — оракул: прогон генератора плюс запрос сгенерированным
|
||||
клиентом
|
||||
- Swagger UI открывается и выполняет запрос **без внешней сети** — оракул:
|
||||
запуск с отключённым интернетом
|
||||
- маршрут, разошедшийся со спекой, красит гейт — оракул: намеренно
|
||||
рассогласованный маршрут в прогоне гейта
|
||||
- спека покрывает приём, каталог, точки, тренировки, записи и `/stats` —
|
||||
оракул: сверка перечня путей спеки с таблицей маршрутов в `architecture.md`
|
||||
|
||||
## Рамки
|
||||
|
||||
Схема не трогается, данные только читаются, сервис перезапускается. Берётся
|
||||
после того, как маршруты чтения существуют: спека рукописная, но описывать
|
||||
нечего, пока конверт не задан.
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
# [goal] Разбор и хранилище
|
||||
|
||||
- **Секция:** порядок
|
||||
- **Зачем:** Секции живого потока разобраны все, кроме словаря категориальных значений и пяти невиденных
|
||||
- **Теги:** decomposed
|
||||
|
||||
Метрики, тренировки и записи со своими `id` разбираются и ложатся в часовые
|
||||
объекты; тела перестали быть недифференцированной кучей.
|
||||
|
||||
Выведена из шага 3 плана. Сделано: разбор метрик в объекты, тренировки и
|
||||
записи, `reindex`. Осталось: словарь категориальных значений и секции, которых
|
||||
поток ещё не приносил.
|
||||
|
||||
Завершена, когда ни одна секция живого потока не числится неразобранной, а
|
||||
категориальные значения имеют стабильный код рядом с переведённой строкой.
|
||||
|
||||
## Завершение
|
||||
|
||||
Ни одна секция живого потока не числится неразобранной, а категориальные
|
||||
значения несут стабильный код рядом с переведённой строкой.
|
||||
@@ -0,0 +1,22 @@
|
||||
# [goal] Полнота разбора потока
|
||||
|
||||
- **Секция:** темы
|
||||
- **Зачем:** Пять секций HAE поток ещё не приносил, а ручные секции заводятся задним числом — разбор полон ровно до следующей новой формы
|
||||
|
||||
Тема: всё, что приезжает от источника, разобрано и доехало до витрины — не
|
||||
только сегодня, но и после того, как источник изменится.
|
||||
|
||||
Выделена из цели «Разбор и хранилище», когда та достигла своего критерия
|
||||
завершения: секции живого потока разобраны, категориальные значения несут
|
||||
стабильный код. Осталось то, что заканчиваться не умеет по природе — источник
|
||||
вправе прислать форму, которой раньше не было, а часть секций заводится
|
||||
человеком задним числом.
|
||||
|
||||
В порядок не встаёт: работа приходит от потока, а не от плана. Первая встреча
|
||||
новой секции наблюдаема (`healthlog uncovered` и `WARN` на свёртке) — тема
|
||||
кормится этими событиями.
|
||||
|
||||
## Завершение
|
||||
|
||||
Завершена не бывает — это тема. Закрывается по мере того, как каждая приезжающая
|
||||
форма доезжает до витрины, а не теряется между «принято» и «разобрано».
|
||||
@@ -0,0 +1,59 @@
|
||||
# Свёртка по сетке и предел размера ответа
|
||||
|
||||
- **Секция:** ядро
|
||||
- **Зачем:** Враждебный запрос к каталогу стоил 693 мс и +153 МиБ кучи, а у точек множители те же и потолка нет ни у одного
|
||||
- **Теги:** goal:read-api
|
||||
|
||||
Потребитель просит разбивку (`?from&to&bucket`) и получает свёртку по сетке —
|
||||
либо честную ошибку вместо тихо подменённой сетки, — а сервис получает
|
||||
названный предел на то, сколько он готов отдать за один запрос.
|
||||
|
||||
Вторая из трёх частей `read-api-points`. Берётся после
|
||||
[конверта ответа](read-api-envelope-and-points.md): сетка — параметр того же
|
||||
маршрута.
|
||||
|
||||
**Решение по размеру ответа принято, вариант «б».** Разбивка не задана и ответ
|
||||
не влезает — сервер сам берёт сетку погрубее и **называет её в ответе**;
|
||||
разбивка задана явно и не влезает — ошибка со списком доступных сеток, а не
|
||||
тихая подмена. Различие существенно: иначе агент, попросивший минутную сетку,
|
||||
получит суточные суммы и не узнает об этом.
|
||||
|
||||
**Порог неполного ведра решается здесь, и вместе с ним — его полярность.**
|
||||
Измерению рода агрегации порог не понадобился, свёртке в ответе он нужен, а
|
||||
готовые решения задают его **противоположно**: Graphite `xFilesFactor` — доля
|
||||
обязательно известных точек (умолчание 0.5 при роллапе и 0 при рендере, один
|
||||
параметр с двумя умолчаниями), RRDtool `xff` — доля допустимо неизвестных. Обе
|
||||
величины выглядят как «0.5», означая разное; полярность придётся назвать вслух в
|
||||
`architecture.md`, иначе через полгода два места кода поймут поле по-разному.
|
||||
|
||||
**Цена измерена, и она унаследована.** На каталоге враждебный запрос
|
||||
(20 метрик × 8 часов × 5000 точек) дал 693 мс и +153 МиБ живой кучи, при том что
|
||||
приём в том же процессе уже даёт пик 768 МиБ на теле 40 МиБ. Условный запрос
|
||||
снял повтор, но первый запрос стоит столько же, а множители «метрики × окно ×
|
||||
точки × одновременные запросы» по-прежнему без потолка. Сюда же уезжают
|
||||
отложенные варианты задачи «цена читающего маршрута»: собственный дедлайн
|
||||
маршрута и потоковое измерение по метрике — второе только если счётчик
|
||||
заговорит.
|
||||
|
||||
Инвариант, который здесь легче всего нарушить: **нижний слой HAE не
|
||||
суммируется** ни при какой сетке — это интерполяция, а не сэмплы.
|
||||
|
||||
## Критерии приёмки
|
||||
|
||||
- «шаги за неделю по дням» отвечаются одним запросом, и в ответе названа
|
||||
фактическая сетка — оракул: запрос к поднятому сервису на живом архиве
|
||||
- явно заданная сетка, которая не влезает в предел, даёт ошибку со списком
|
||||
доступных сеток, а не подменённый ответ — оракул: тест
|
||||
- неполное ведро обрабатывается объявленным порогом, и полярность порога
|
||||
названа в `docs/architecture.md` вслух — оракул: тест на границе плюс глазами
|
||||
по разделу
|
||||
- нижний слой HAE не суммируется ни при какой сетке — оракул: тест на
|
||||
накопительной метрике, у которой есть и нижний, и часовой слой
|
||||
|
||||
## Рамки
|
||||
|
||||
Схема не трогается, данные только читаются, сервис перезапускается. Берётся
|
||||
после конверта ответа. Против `./data` — только `task up` / `task run`.
|
||||
|
||||
Связано: `docs/architecture.md` → «Свёртка и размер ответа»; идея
|
||||
[NDJSON-потока](ndjson-stream.md) закрывает другую сторону того же предела.
|
||||
@@ -0,0 +1,56 @@
|
||||
# Конверт ответа и точки за период
|
||||
|
||||
- **Секция:** ядро
|
||||
- **Зачем:** Точки лежат в витрине и наружу не отдаются: ни один потребитель не может спросить «вес за год» иначе как sqlite3 на хосте
|
||||
- **Теги:** goal:read-api
|
||||
|
||||
Потребитель получает значения метрики за период одним запросом
|
||||
`?from&to`, и из ответа видно, что именно ему отдали: слой, род свёртки и
|
||||
границу окна, в котором род измерен.
|
||||
|
||||
Первая из трёх частей, на которые разложена `read-api-points`. Здесь решается
|
||||
**конверт** — форма, которую унаследуют все остальные маршруты чтения и оба
|
||||
транспорта.
|
||||
|
||||
**Форма провода решается здесь, один раз.** Сегодня типы `internal/catalog`
|
||||
сами несут json-теги, а транспорт владеет только обёрткой: переименование поля
|
||||
в домене меняет публичный контракт без касания `httpapi`, и держит это один
|
||||
байтовый тест непустого ответа. Либо объявить в `architecture.md`, что типы
|
||||
чтения и есть форма провода для всех транспортов (HTTP и MCP отдают её байт в
|
||||
байт), либо завести DTO в транспорте. Выбрать надо **до** того, как образец
|
||||
скопируют соседние задачи.
|
||||
|
||||
**Машинерия условного запроса готова — её берут, а не пишут заново.**
|
||||
`store.VersionedRead` держит правило «версией, снятой после чтения, не
|
||||
подписывать»; `httpapi` умеет `If-None-Match` и `304`. Новое здесь одно: метка
|
||||
обязана нести **область действия** — у точек ответ есть функция параметров
|
||||
запроса, поэтому `etag(scope, version)` требует их канонизированной формы, иначе
|
||||
`304` ответит на другой набор данных. Детали — `docs/architecture.md`,
|
||||
«Условный запрос».
|
||||
|
||||
**Клиент обязан видеть границы окна измерения.** Род метрики измерен по 48 самым
|
||||
свежим **общим** часам, а не по последним 48 часам календаря: выключенная
|
||||
минутная автоматизация HAE останавливает пополнение множества общих часов, и
|
||||
окно замирает, продолжая объявлять род. Единственный след — `last_hour` в
|
||||
ответе; конверт обязан его нести.
|
||||
|
||||
## Критерии приёмки
|
||||
|
||||
- «вес за год» отвечается одним запросом без доступа к файлу базы — оракул:
|
||||
запрос к поднятому сервису на живом архиве
|
||||
- в ответе всегда видны `layer`, `aggregation` и `last_hour` — оракул: тест на
|
||||
форме ответа
|
||||
- повторный запрос с `If-None-Match` даёт `304`, а тот же запрос с другими
|
||||
параметрами — `200` с другим телом — оракул: тест на паре запросов с разной
|
||||
областью действия метки
|
||||
- форма провода объявлена в `docs/architecture.md` — доменные типы или DTO, —
|
||||
и соседние маршруты ссылаются на это решение, а не повторяют выбор — оракул:
|
||||
глазами по разделу
|
||||
|
||||
## Рамки
|
||||
|
||||
Схема не трогается, данные только читаются, сервис перезапускается. Против
|
||||
`./data` — только `task up` / `task run`.
|
||||
|
||||
Связано: `docs/architecture.md` → «Read API», «Условный запрос», «Измерение рода
|
||||
агрегации».
|
||||
@@ -1,78 +0,0 @@
|
||||
# Read API: точки, выбор слоя, свёртка по сетке
|
||||
|
||||
- **Секция:** ядро
|
||||
- **Зачем:** Данные видны только через sqlite на хосте — ни один из трёх потребителей ничего прочитать не может
|
||||
- **Теги:** goal:read-api
|
||||
|
||||
Сейчас данные достаются только `sqlite3` на хосте. Все три сценария —
|
||||
агент-медик, трекер тренировок, фитнес-игра — упираются в отсутствие чтения.
|
||||
|
||||
Формы запроса ровно две, и это один запрос с необязательным параметром:
|
||||
`?from&to` — все значения за период (вес, лекарства, симптомы), `?from&to&bucket`
|
||||
— с разбивкой (шаги, энергия).
|
||||
|
||||
Решение по размеру ответа (вариант «б»): разбивка не задана и ответ не влезает —
|
||||
сервер сам берёт сетку погрубее и **называет её в ответе**; разбивка задана явно
|
||||
и не влезает — ошибка со списком доступных сеток, а не тихая подмена. Различие
|
||||
существенно: иначе агент, попросивший минутную сетку, получит суточные суммы.
|
||||
|
||||
**Отдача тренировок и записей входит сюда же.** Разбор и хранение сущностей с
|
||||
собственным `id` сделаны (change `2026-08-02-trenirovki-i-zapisi`), а эндпоинтов
|
||||
нет: тренировка с маршрутом и записи `stateOfMind` лежат в витрине и наружу не
|
||||
отдаются. Вводить их раньше конверта ответа значило бы задать контракт
|
||||
мимоходом, поэтому `GET /workouts`, `GET /workouts/{id}` и
|
||||
`GET /records/{kind}` закрываются этой задачей — вместе с формой конверта и
|
||||
правилом размера ответа. Второй сценарий паспорта (трекер) до тех пор не закрыт.
|
||||
|
||||
Готово, когда «шаги за неделю по дням» и «вес за год» отвечаются одним запросом
|
||||
каждый, тренировка отдаётся одним пакетом вместе с маршрутом, а в ответе всегда
|
||||
видно `layer`, `bucket` и `aggregation`.
|
||||
|
||||
**Порог неполного ведра решается здесь, и вместе с ним — его полярность.**
|
||||
Каталог и род агрегации сделаны (change `2026-08-02-katalog-i-rod-agregacii`), и
|
||||
измерению порог заполненности не понадобился: у него две конкурирующие гипотезы,
|
||||
и неполный час не сходится ни с одной сам собой. Свёртке в ответе он нужен, а
|
||||
готовые решения задают его **противоположно**: Graphite `xFilesFactor` — доля
|
||||
обязательно известных точек (умолчание 0.5 при роллапе и 0 при рендере, один
|
||||
параметр с двумя умолчаниями), RRDtool `xff` — доля допустимо неизвестных. Обе
|
||||
величины выглядят как «0.5», означая разное; полярность придётся назвать вслух в
|
||||
`architecture.md`, иначе через полгода два места кода поймут поле по-разному.
|
||||
|
||||
**Предел размера ответа тоже здесь, и он унаследовал измеренную цену.** У
|
||||
каталога предела нет намеренно: правило размера — общее для маршрутов чтения, и
|
||||
задавать его мимоходом на первой ручке значило бы решить контракт до того, как
|
||||
известна форма тяжёлого ответа. Каталог станет первым его потребителем.
|
||||
|
||||
Цена измерена на каталоге (задача «цена читающего маршрута», закрыта чекпойнтом
|
||||
WAL и условным запросом): 693 мс и +153 МиБ живой кучи на враждебном запросе
|
||||
(20 метрик × 8 часов × 5000 точек), при том что приём в том же процессе уже даёт
|
||||
пик 768 МиБ на теле 40 МиБ. Условный запрос снял повтор, но первый запрос стоит
|
||||
столько же, а множители «метрики × окно × точки × одновременные запросы»
|
||||
по-прежнему без потолка. Сюда же уезжают отложенные варианты той задачи:
|
||||
собственный дедлайн маршрута и потоковое измерение по метрике (второе — только
|
||||
если счётчик заговорит).
|
||||
|
||||
**Машинерия условного запроса готова, и её надо взять, а не написать заново.**
|
||||
`store.VersionedRead` держит правило «версией, снятой после чтения, не
|
||||
подписывать»; `httpapi` — разбор `If-None-Match` и `304`. Метка обязана нести
|
||||
**область действия**: у точек ответ есть функция параметров запроса, и
|
||||
`etag(scope, version)` требует их канонизированную форму — иначе `304` ответит
|
||||
на другой набор данных. Детали — `docs/architecture.md`, «Условный запрос».
|
||||
|
||||
**Форма провода наследуется от каталога, и это надо решить один раз.** Сегодня
|
||||
типы `internal/catalog` сами несут json-теги, а транспорт владеет только
|
||||
обёрткой: переименование поля в домене меняет публичный контракт без касания
|
||||
`httpapi`. Держит это один байтовый тест непустого ответа. Либо объявить в
|
||||
`architecture.md`, что типы чтения и есть форма провода для всех транспортов
|
||||
(HTTP и MCP отдают её байт в байт), либо завести DTO в транспорте — но выбрать до
|
||||
того, как образец скопирует эта задача.
|
||||
|
||||
**Клиент обязан смотреть на границы окна измерения.** Род метрики измерен по
|
||||
48 самым свежим ОБЩИМ часам, а не по последним 48 часам календаря: если минутная
|
||||
автоматизация HAE выключена, множество общих часов не пополняется и окно
|
||||
замирает. Род при этом продолжает объявляться, и единственный след — `last_hour`
|
||||
в ответе. Правило выбора свёртки в Read API обязано это учитывать (или явно
|
||||
объявить, что не учитывает).
|
||||
|
||||
Связано: `docs/architecture.md` → «Read API», «Измерение рода агрегации»,
|
||||
план → шаг «Read API».
|
||||
@@ -0,0 +1,46 @@
|
||||
# Тренировки и записи наружу
|
||||
|
||||
- **Секция:** ядро
|
||||
- **Зачем:** Тренировки с маршрутами и stateOfMind разобраны и лежат в витрине, а эндпоинтов нет — второй сценарий паспорта не закрыт
|
||||
- **Теги:** goal:read-api
|
||||
|
||||
Трекер тренировок забирает тренировку одним пакетом вместе с маршрутом, а
|
||||
записи `stateOfMind` достаются за период — без доступа к файлу базы.
|
||||
|
||||
Третья из трёх частей `read-api-points`. Разбор и хранение сущностей с
|
||||
собственным `id` сделаны (change `2026-08-02-trenirovki-i-zapisi`), эндпоинтов
|
||||
нет: данные лежат в витрине и наружу не отдаются. Второй сценарий паспорта до
|
||||
тех пор не закрыт.
|
||||
|
||||
Маршруты: `GET /workouts`, `GET /workouts/{id}`, `GET /records/{kind}`.
|
||||
|
||||
**Конверт не изобретается заново** — наследуется от
|
||||
[конверта ответа](read-api-envelope-and-points.md) вместе с формой провода и
|
||||
условным запросом. Вводить эти маршруты раньше конверта значило бы задать
|
||||
контракт мимоходом; именно поэтому они здесь, а не в задаче о разборе.
|
||||
|
||||
**Маршрут — первый по-настоящему тяжёлый ответ проекта.** Он лежит блобом внутри
|
||||
тренировки, и правило размера ответа
|
||||
([свёртка по сетке](read-api-bucketing.md)) обязано распространяться и на него —
|
||||
иначе одна тренировка с длинным треком проходит мимо предела, названного для
|
||||
точек. Разворачивание маршрута в отдельную таблицу остаётся идеей и этой задачей
|
||||
не решается.
|
||||
|
||||
## Критерии приёмки
|
||||
|
||||
- тренировка отдаётся одним пакетом вместе с маршрутом — оракул: запрос к
|
||||
поднятому сервису на живом архиве
|
||||
- записи `stateOfMind` за период достаются `GET /records/{kind}` — оракул: тест
|
||||
на пакетах `internal/hae/testdata`
|
||||
- конверт, форма провода и условный запрос те же, что у точек, а не собственные
|
||||
— оракул: тест, сравнивающий форму ответа обоих маршрутов
|
||||
- тренировка с длинным маршрутом подчиняется тому же пределу размера ответа,
|
||||
что и точки — оракул: тест на синтетическом треке за пределом
|
||||
|
||||
## Рамки
|
||||
|
||||
Схема не трогается, данные только читаются, сервис перезапускается. Берётся
|
||||
после конверта ответа. Против `./data` — только `task up` / `task run`.
|
||||
|
||||
Связано: `docs/architecture.md` → «Read API»; идея
|
||||
[разворачивания маршрутов в таблицу](workout-routes-table.md).
|
||||
@@ -1,19 +1,26 @@
|
||||
# [goal] Read API
|
||||
# [goal] Чтение данных клиентами
|
||||
|
||||
- **Секция:** порядок
|
||||
- **Зачем:** Данные видны только через sqlite на хосте — ни один из трёх потребителей ничего прочитать не может
|
||||
- **Зачем:** Данные видны только через sqlite на хосте — ни один из трёх потребителей, включая агента-медика, ничего прочитать не может
|
||||
- **Теги:** decomposed
|
||||
|
||||
Потребители читают точки: выбор слоя, свёртка по сетке, предел размера ответа.
|
||||
Потребители читают данные: точки с выбором слоя и свёрткой по сетке, тренировки
|
||||
и записи, машиночитаемый контракт — и всё то же самое через MCP.
|
||||
|
||||
Выведена из шага 5 плана. Идёт после каталога и рода агрегации намеренно: без
|
||||
измеренного рода свёртка в ответе неотличима от угадывания, а ошибиться здесь
|
||||
дорого — просуммировать нижний слой значит завысить втрое.
|
||||
Выведена из шагов 5 и 7 плана. Идёт после каталога и рода агрегации намеренно:
|
||||
без измеренного рода свёртка в ответе неотличима от угадывания, а ошибиться
|
||||
здесь дорого — просуммировать нижний слой значит завысить втрое.
|
||||
|
||||
Завершена, когда любой из трёх потребителей получает точки за период без
|
||||
доступа к файлу базы.
|
||||
**MCP входит в эту цель, а не идёт отдельной.** Прежде их было две, и разделяла
|
||||
их очередь: адаптер собственной логики не несёт, он переводит вызовы в те же
|
||||
обработчики, и переводить было нечего. Очередь никуда не делась — она стала
|
||||
порядком задач внутри цели, — а вот отдельная цель под адаптер описывала не
|
||||
направление, а последний шаг этого же направления. Заказчик у обоих транспортов
|
||||
один: три потребителя, из которых первый — агент.
|
||||
|
||||
## Завершение
|
||||
|
||||
Любой из трёх потребителей получает точки за период без доступа к файлу базы,
|
||||
и предел размера ответа объявлен, а не подразумевается.
|
||||
Любой из трёх потребителей получает точки, тренировки и записи за период без
|
||||
доступа к файлу базы; предел размера ответа объявлен, а не подразумевается;
|
||||
агент-медик читает то же самое через MCP тем же токеном чтения, и собственной
|
||||
логики адаптер не несёт.
|
||||
|
||||
Reference in New Issue
Block a user