docs(specs): archive api contract resilience remediation

This commit is contained in:
2026-08-24 00:51:06 +03:00
parent 6e20cdffaf
commit 800450d075
8 changed files with 386 additions and 0 deletions
@@ -0,0 +1,120 @@
## Контекст
Мотивация изложена в `proposal.md`, а поведенческий contract — в delta specs.
Backend документирует `200` через OpenAPI decorators, но Nest использует
стандартный `201` для трёх auth `POST` handlers, поскольку их runtime status
не задан явно. Runtime OpenAPI verifier сравнивает два документа, поэтому не
может обнаружить это расхождение HTTP responses.
Во frontend Accounts и Account view models выбирают блокирующий loading/error
state до проверки cached query data. У auth provider два пути refresh:
cold start напрямую вызывает API без `catch`, а общий refresh может отклонить
Promise при повторе защищённого запроса.
## Цели и исключения
**Цели:**
- Согласовать фактические auth success responses, OpenAPI, snapshot и
generated types на `200` с детерминированным regression gate без Prisma или
базы данных.
- Отображать существующие данные Accounts и Portfolio во время refetch и после
его ошибки с доступным локализованным warning и retry action.
- Дать cold start и повтору защищённого запроса единый non-throwing результат
refresh, сохранив текущие serialization и защиту routes.
- Фиксировать active remediation и обновлять feature/development документацию
только фактически поставленным поведением.
**Исключения:**
- Исправлять Prisma migration/e2e infrastructure или создавать workspace-wide
verifier, отложенный как WL-P2-05 и WL-P2-13.
- Менять refresh-cookie attributes, token lifetime, session rotation,
unauthorised-error codes, auth routes, cache TTL, refetch triggers или
backend Portfolio fallback policy.
- Распространять Client Cached Snapshot на постраничные operations/events,
чьи cursor и filters могут определять иной result set.
## Решения
### Явно установить `200` для трёх auth handlers успешного ответа
Handlers register, login и refresh получают явный HTTP `200`, сохраняя
существующие `ApiOkResponse` и payload/cookie code. Frontend artifacts остаются
сгенерированными из backend document: запускается штатный sync workflow, а
artifact коммитится только если этот workflow создаёт изменение; ручное
редактирование generated schema запрещено.
Небольшой независимый от БД backend test проверяет status metadata каждого
handler. Он защищает decorator, управляющий реальным Nest output, пока
Prisma-backed e2e gate остаётся deferred. Существующие e2e assertions меняются
с `201` на `200`, но не являются terminal evidence до восстановления Prisma
bootstrap.
Отклонённые альтернативы: неявный `201` потребовал бы большего перехода
snapshot/types и противоречил бы согласованному единообразному contract;
опора только на runtime OpenAPI comparison повторила бы исходную blind spot.
### Определять представление refetch по наличию данных, а не `isFetching` или `isError`
Accounts и Account view models сначала определяют наличие Client Cached
Snapshot. Без данных они сохраняют текущие loading, error и empty views. При
наличии данных они возвращают loaded/empty presentation с опциональной
информацией об обновлении и его ошибке. Page components сначала отображают
данные, добавляют неблокирующий progress indicator во время fetch и передают
локализованное warning/retry action в loaded presentation после ошибки.
Account view сохраняет backend warning `Portfolio.isStale` отдельно от client
warning об ошибке refresh. Оба могут отображаться, когда независимо истинны.
Новая терминология документируется в `CONTEXT.md`; это не новое API field и
не интерпретация server freshness.
Отклонённые альтернативы: полный error/loading view после refetch скрывает уже
полезные финансовые данные; применение правила к operations/events могло бы
показать прошлую cursor/filter page как запрошенный результат.
### Сделать общий refresh возвращающим `false` при любом неуспешном исходе
Auth provider централизует успешное применение session и локальную очистку
session. Single-flight refresh function перехватывает transport rejection API,
а также no-data/HTTP failures, очищает token, profile и React Query cache и
возвращает `false`; она всегда освобождает in-flight Promise. Cold start
использует эту общую function и устанавливает `ready` в `finally`.
`authenticatedFetch` сохраняет existing one-retry behavior: при `false` он
возвращает исходный `401` после очистки, позволяя protected-route state
перенаправить на login.
Отклонённые альтернативы: перехват только mount effect оставляет 401 retry
path способным отклонить Promise; добавление третьего startup error state
меняет согласованный anonymous/login recovery flow.
## Риски и компромиссы
- [Metadata test не является HTTP e2e test] → WL-P2-05 остаётся deferred,
существующие e2e expectations обновляются, а реальное HTTP coverage станет
обязательным после выполнения Prisma trigger.
- [Ошибка refresh утрачивает in-memory session] → после cold start браузер не
имеет действительного access token; очистка state предотвращает неоднозначный
protected UI и следует существующему 401 recovery behavior.
- [Client warning и backend stale warning могут сосуществовать] → каждому
назначается отдельное localized message и проверяется их независимый render.
- [Generated sync может затронуть frontend artifacts] → он запускается явно
только после backend contract change, а итоговый diff проверяется до принятия.
## План изменений
1. Сначала добавить regression tests, затем сделать backend status явным и
обновить существующие e2e assertions, не заявляя, что deferred e2e gate
прошёл.
2. Обновить OpenAPI artifacts через штатную sync command, проверить
согласованность runtime document и generated client и оставить только
намеренный итоговый diff.
3. Добавить observable frontend tests для состояний refetch cached data и
ошибок refresh, затем отрефакторить view models, pages, localized copy и
auth provider до их прохождения.
4. Обновить feature docs и remediation registry, выполнить применимые
non-mutating gates и OpenSpec verification до archive.
Rollback затрагивает только код: отмена затронутых commits восстанавливает
предыдущий status/UX; data migration, dependency change и очистка persisted
state не нужны.