docs(specs): archive api contract resilience remediation
This commit is contained in:
+120
@@ -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 не нужны.
|
||||
Reference in New Issue
Block a user