# remove-frontend-hook-token-params — Design ## Context Защищённые frontend hooks для investment-данных сейчас выставляют session `accessToken` как часть caller-facing interface только для вычисления React Query `enabled`. Сам `Authorization` header уже принадлежит `frontend/src/shared/api/client.ts`: `AuthProvider` конфигурирует shared client token/refresh/clear handlers, `authenticatedFetch` добавляет bearer headers для non-auth endpoints и выполняет существующий single retry после `UNAUTHORIZED`. Повторяющееся token plumbing видно в entity hooks для Brokerage Accounts, Portfolio, Account Ledger и Future Cash Events, а также в их callers: accounts/account page ViewModels, account operations/events pages и breadcrumbs. Из-за этого page-level modules знают transport credential, который больше нигде не используют. FSD-направление импортов важно для выбора seam. Entity hooks могут зависеть от `shared/api`, но не должны импортировать `features/auth`. Поэтому auth-aware query interface нельзя реализовать через прямой `useAuth()` внутри entity hooks. Design должен сохранить поток auth configuration из `features/auth` в `shared/api`, а entity hooks должны потреблять только `shared/api`. Frontend-ограничения: React/Vite, TanStack Query, openapi-react-query, MSW-backed Vitest tests, strict TypeScript, dependency-cruiser FSD boundaries, canonical gate `npm ci && npm run verify` под Node 22. Backend routes, generated OpenAPI snapshot и user-visible behavior не меняются. ## Goals / Non-Goals **Goals:** - Убрать session token parameters из interfaces защищённых investment entity hooks. - Централизовать authenticated query enablement в `shared/api`, рядом с существующим authenticated transport seam. - Сохранить bearer injection, refresh/retry behavior, cache keys, stale policies, retry policies и page state behavior. - Сохранить валидное FSD-направление зависимостей: entity hooks зависят от shared API/auth query utilities, а не от auth feature. - Добавить focused regression proof, что protected queries остаются disabled без active session и загружаются нормально, когда session появляется. **Non-Goals:** - Менять backend auth semantics, HTTP routes, DTOs, generated OpenAPI files или T-Bank behavior. - Переносить auth feature целиком в shared или менять login/register/logout UX. - Refactor `useTbankConnectionViewModel`; там есть похожий локальный `enabled: Boolean(accessToken)` pattern, но он находится вне четырёх entity hook interfaces, названных этим review item. - Перестраивать account page VM/view, account operations/events pages или breadcrumbs за пределами удаления token plumbing, нужного из-за изменения hook interfaces. - Консолидировать дублированные frontend error-code helpers. ## Decisions **D1. Ввести shared authenticated-query module.** `shared/api` получает небольшой authenticated-query interface, который оборачивает TanStack `useQuery` и совмещает обычные query options вызывающего кода с session-aware enablement. Protected entity hooks вызывают этот interface вместо того, чтобы принимать `accessToken` и самостоятельно выставлять `enabled`. Endpoint-specific hooks по-прежнему владеют path/query parameters, cache keys, stale times и retry policy. *Альтернатива:* оставить `accessToken` hook parameter и почистить только callers. Отклонено: shallow interface остаётся видимым, поэтому новые callers продолжат изучать token plumbing как обязательный pattern. **D2. Auth state передаётся в shared API через внутренний snapshot, а не через импорт `useAuth`.** `features/auth` продолжает конфигурировать `shared/api` current token и refresh handlers. Shared API layer дополнительно публикует минимальный authenticated-session snapshot/subscription, который authenticated queries используют для вычисления `enabled`. Это сохраняет направление зависимостей `features -> shared` и `entities -> shared`, без импортов `entities -> features` или `shared -> features`. *Альтернатива:* разрешить entity hooks вызывать `useAuth()`. Отклонено: entities начали бы зависеть вверх от feature slice и нарушили бы documented FSD direction. **D3. Compose, а не replace, caller enablement.** Authenticated query enablement вычисляется как `hasSession && callerEnabled`, где `callerEnabled` по умолчанию равен `true`. Account-scoped hooks сохраняют domain precondition, передавая `Boolean(accountId)` как caller enablement. Это сохраняет текущее поведение: undefined account id не создаёт request, а unauthenticated state подавляет все protected investment requests. *Альтернатива:* проверять в authenticated query только session и полагаться на empty path params при отсутствующем account id. Отклонено: это превратило бы текущую disabled-query ветку в невалидный protected request. **D4. Transport retry остаётся независимым от query gating.** Authenticated query module только решает, может ли protected query стартовать. Он не строит headers, не запускает refresh напрямую и не интерпретирует `401` responses. Существующий `authenticatedFetch` остаётся единственным owner bearer injection и refresh/retry path для requests, которые уже стартовали. **D5. Protected entity hook interfaces становятся domain-shaped.** Caller-facing interfaces после изменения описываются потребностями данных: accounts list не имеет arguments, portfolio/events принимают optional account id, operations принимает optional account id и query object. Page/ViewModel modules перестают читать `accessToken`, если он был нужен только для передачи в hook. **D6. Tests покрывают seam, а не token plumbing.** Regression tests должны доказывать observable behavior через shared authenticated-query seam: protected query не попадает в MSW без configured session, и тот же hook загружается, когда session exists. Existing endpoint tests обновляются под новые interfaces и продолжают проверять query parameter normalization и successful endpoint paths. Tests не должны grep-ить source, чтобы доказать исчезновение token argument; TypeScript и компиляция call sites покрывают interface change. ## Delivery Map | Area | Status | Architectural outcome or N/A reason | | --- | --- | --- | | frontend | `affected` | `shared/api` владеет authenticated query enablement; четыре protected investment entity hooks убирают `accessToken` parameters; callers обновлены на domain-shaped hook interfaces; focused tests покрывают no-session gating и session-enabled loading. | | backend | `N/A` | Backend auth, routes, DTOs, T-Bank integration и runtime OpenAPI не меняются. | | specs | `affected` | Этот SDD change хранит approved Proposal, skipped Delta Specs, Design и Tasks для behavior-preserving frontend architecture cleanup. | | docs | `N/A` | As-built product docs не меняются, потому что user-visible behavior и system contract остаются прежними; frontend-local guidance уже фиксирует, что bearer headers принадлежат shared authenticated fetch. | ### Contract Gate Decision | Gate | Status | Reason | | --- | --- | --- | | runtime-openapi | `N/A` | Change не затрагивает backend или frontend/backend API contract, generated client или OpenAPI snapshot. | ### Solution Assessment Формальная build-versus-buy assessment не triggered. Change не добавляет и не заменяет authentication, security, storage, external integration, observability, caching, concurrency control или infrastructure. Он сохраняет существующий auth mechanism и TanStack Query/openapi-react-query stack, перенося project-owned query enablement за меньший in-process interface. ## Risks / Trade-offs - **Session reactivity regression.** Если shared auth snapshot не уведомляет authenticated queries при изменении token state, queries могут остаться disabled после login/refresh или enabled после logout. Митигация: реализовать snapshot как subscribed external store и покрыть disabled/enabled transition behavior tests. - **FSD boundary regression.** Чтение auth state из entity hooks через `features/auth` исправило бы симптом, но нарушило architecture. Митигация: разместить seam в `shared/api` и опираться на существующий dependency-cruiser gate. - **Over-wide shared API interface.** Helper, который зеркалит все TanStack Query options, может стать shallow. Митигация: держать его узко ответственным за composition обычных `useQuery` options с authenticated enablement; endpoint hooks сохраняют endpoint-specific policy. - **Похожий pattern остаётся в T-Bank connection VM.** Change намеренно оставляет один feature-local use of `accessToken` gating без изменений. Митигация: зафиксировать это как out of scope; отдельный будущий change может решить, должны ли feature-level status queries тоже переехать на shared authenticated-query seam. ## Migration Plan 1. Добавить shared authenticated-query module и tests для no-session disabled behavior и session-enabled loading. 2. Refactor Brokerage Accounts, Portfolio, Account Ledger и Future Cash Events entity hooks на shared authenticated-query interface, сохранив query keys, request parameters, stale times и retry policies. 3. Обновить все callers и tests на новые domain-shaped hook interfaces, убрав `useAuth()` reads, которые существовали только для передачи `accessToken`. 4. Запустить focused frontend tests для изменённых hooks/callers, затем frontend canonical gate `npm run verify`. 5. Запустить `openspec validate remove-frontend-hook-token-params --type change --strict --store wealth-ledger-specs`, выполнить semantic verification против Proposal/Design/Tasks и использовать workspace dispatcher перед archive. Откат — обычный frontend/specs code revert. Data migration, OpenAPI sync, backend migration и feature flag не вводятся. ## Open Questions