Нарезка задач под целевое состояние: PWA, вход, хранилище

Роадмап: web-access переименована под приложение, которое ставится на
телефон; заведена цель ready-notification — уведомление о готовности
без открытого приложения.

Беклог: десять задач. Многопользовательская цепочка (oidc-login,
record-ownership, telegram-account-link), веб (json-api-for-spa,
spa-skeleton, upload-and-status-screen, records-list-screen,
installable-pwa), уведомления через apprise и ntfy, разведка выбора
фреймворка. Очередь: долги, хранилище, вход, приложение.

Решение сменилось с htmx на SPA, поэтому conventions/web-ui.md снята
целиком и оставлена честной строкой до итога разведки. Открытые вопросы
архитектуры, границы паспорта и триггеры метки ревью приведены в
соответствие.
This commit is contained in:
av
2026-08-10 21:36:54 +03:00
parent 4d1c2bf44c
commit 115b3796e8
19 changed files with 536 additions and 160 deletions
+44
View File
@@ -0,0 +1,44 @@
# ✨ Свести приём и чтение записей к одному контракту для приложения
- **Тип:** feature
- **Категория:** Ядро
- **Зачем:** Сегодняшний API отвечает 404 на любую ошибку чтения и 500 на любую ошибку приёма: строить на нём экраны нечем.
- **Теги:** goal:web-access
Двигает пункты 1, 2 и 4 «Завершения» цели: экраны заводят задачу, видят её
состояние и листают список — всё через один контракт.
Обработчик `GET /api/status/:id` сегодня отвечает `404` на **любую** ошибку
чтения, включая сбой базы, а `POST /api/audio``500` на любую ошибку заведения,
включая негодный файл. Экран, построенный на таком контракте, показывает «не
найдено» при упавшей базе.
## Затрагивает
- `POST /api/audio` и `GET /api/status/:id` — коды ответа и форма ошибки;
- новый эндпоинт списка своих записей с постраничным чтением;
- единая точка отображения доменной ошибки в код ответа — её сегодня нет
([conventions/errors.md](../../docs/conventions/errors.md));
- `internal/contract` — доменные ошибки под отображение;
- `internal/controller/http` целиком;
- `docs/architecture.md`, раздел «Единые точки проекта».
## Критерии приёмки
- Сбой базы при чтении задачи даёт `500`, а не `404`. Оракул — тест с
репозиторием, возвращающим ошибку драйвера.
- Негодный файл даёт `400` с человекочитаемым текстом, а не `500`. Оракул —
тест: файл, который отвергает разбор метаданных.
- Тело ошибки одной формы на всех эндпоинтах и не содержит сырого `err.Error()`.
Оракул — тест на четырёх ветвях отказа: форма совпадает, текста внутренней
ошибки в теле нет.
- Список записей отдаётся страницами и упорядочен по времени создания. Оракул —
тест на выборке больше страницы.
- Отображение ошибки живёт в одной функции, и она названа в
`docs/architecture.md`. Оракул — `task gate`, шаг `docs.py check`.
## Рамки
Аутентификацию и владельца не заводим — это `oidc-login` и `record-ownership`;
задача про форму контракта. Публичный контракт после мерджа обратной правкой не
откатывается.