# Реализация ручного учёта банковских карт **Статус:** согласованный объём реализован и проверен 12 сентября 2026 года. Независимое ревью завершено; change остаётся активным, без архивирования и синхронизации в основные specs. Основание — согласованные [Design revision 1](design.md), включая полностью inline UI/UX package revision 1, и [Delta Specs](specs/manual-bank-cards/spec.md). Внешних визуальных материалов или дополнительных пакетов для начала нет. Checkbox закрывается только после выполнения действия и его Verification. Verification ниже задаёт критерии; наблюдённые результаты отмечаются отдельно. Перед Node-командами из affected repo выполняются `source ~/.nvm/nvm.sh && nvm use --silent`; для specs используется frontend `.nvmrc`. Git-команды выполняются из соответствующего независимого репозитория. DB/HTTP-проверки используют уникальную временную SQLite с вымышленными данными и явно заданной `DATABASE_URL`, без доступа к пользовательской базе или T-Bank. Общий `npm run test:e2e` с фиксированной `/tmp/wealth-ledger-e2e.db` для них не используется. Рабочая финансовая база не сбрасывается и не заменяется ради выполнения проверки. Задачи одного раздела выполняются по указанным зависимостям. Задача 1.2 может идти независимо от 1.1; остальные параллельные работы допустимы только при закрытых зависимостях и раздельном владении изменяемыми файлами. ## 1. Основа хранения и точных денег - [x] 1.1 [backend] Подтвердить применимость выбранного Solution Assessment: существующие Prisma/SQLite и migration boundary под Node 22; зафиксировать исходную версию схемы/приложения для проверки перехода и отката. - **Covers:** `N/A` — техническое предусловие нового долговременного хранения из Design, без самостоятельного пользовательского поведения или смены выбранной архитектуры. - **Verification:** `npx prisma validate`; существующие migrations применяются через `npx prisma migrate deploy` с `DATABASE_URL` каждой уникальной временной базы. Для снятия известного blocker нужны три последовательных успешных fresh-DB применения согласно [правилу проверки](../../../../docs/development/verification.md#известное-ограничение-сквозной-проверки). При ошибке сохранить версию и точный failure, не закрывать задачу и не подменять migration proof через `db push`, reset или ручной обход migrations. - **Результат 2026-09-12:** Node `22.21.0`, Prisma/client `7.9.1`, engine `e922089b7d7502aff4249d5da3420f6fa55fc6ad`; `prisma validate` — passed. Исходный backend `8ec172f48eefa2237dc0c16919fbb2e726b39a65`, frontend `427ca23a433a20be25b1f4373f32c9e1f6f8c400`; оба working tree были чистыми. Обычный fresh deploy воспроизвёл `Error: Schema engine error:` без деталей, в том числе вне sandbox. С `RUST_LOG=info` штатный `npx prisma migrate deploy` вне sandbox прошёл три раза подряд: `/tmp/wealthledger-bank-cards-migrations-logging.rFEHKw/fresh-{1,2,3}.db`, логи `migrate-{1,2,3}.log` рядом. В каждом применены обе исходные migrations. Это подтверждает путь с указанной переменной окружения; запуск без неё остаётся ограничением. Версии зависимостей, применённые migrations и пользовательская база не менялись. - [x] 1.2 [backend] Выделить общего владельца decimal parsing/formatting и MoneyDto, сохранить protobuf-преобразование у T-Bank и добавить точное преобразование допустимого RUB-ввода в копейки. - **Covers:** `manual-bank-cards` — `Точные денежные значения и проверка до сохранения`. - **Verification:** существующие decimal и затронутые брокерские calculation tests проходят до и после переноса; targeted Jest проверяет signed-64-bit границы, отрицательный/нулевой начальный остаток, строго положительную сумму операции, запрет лишней точности и точный Money roundtrip. `npm run typecheck:app`; прежний Money wire contract сохраняется. - **Результат 2026-09-12:** общий owner `src/common/decimal.ts`, `money.dto.ts`, `rub-money.ts`; protobuf-преобразование осталось в T-Bank. До переноса `npm test -- --runInBand src/tbank` — 24 suites / 191 tests passed; после `npm test -- --runInBand 'src/tbank|src/common/rub-money'` — 25 suites / 213 tests passed. Targeted ESLint, `npm run typecheck:app`, `git diff --check` — passed. `npm run test:openapi-contract` до добавления банковских endpoints подтвердил неизменность прежнего контракта, но проверка обнаружила безусловную замену внешнего `DATABASE_URL` в `test/e2e-env.cjs` на фиксированную временную базу. Этот запуск не принимается как доказательство требуемой изоляции; после исправления test setup сравнение повторяется на уникальной базе. Пользовательская база не использовалась. Новый banking API требует отдельного финального сравнения. - [x] 1.3 [backend] После 1.1 добавить BankCard, ExpenseCategory и BankCardOperation, additive migration, связи, ограничения уникальности, индексы, версии, tombstones и fingerprints из Design. - **Covers:** `manual-bank-cards` — `Сохранённый ручной учёт переживает повторный вход и перезапуск`; `manual-bank-cards` — `Точные денежные значения и проверка до сохранения`. - **Verification:** `npx prisma validate`, `npx prisma generate`, `npm run typecheck:app`; migration на изолированной базе с representative User/Session/TbankConnection сохраняет их данные. Реальная DB-проверка подтверждает FK/unique constraints и точное хранение копеек больше `2^53` и обеих граничных величин; применённые migrations не переписаны. Если для устранения подтверждённого migration blocker потребуется другой подход или изменение согласованного стека, это возвращает решение в Design. Успех unit/Swagger-проверки не разрешает закрыть 1.1 или 1.3 без DB-доказательства. Результат 1.3: additive migration `20260912090000_add_manual_bank_cards` (существующие migrations неизменны), `prisma validate/generate` и app typecheck — passed. Реальный adapter/Prisma roundtrip подтвердил `9007199254740993n` и обе signed64 границы; configured DB-тесты подтвердили FK/unique. Переход с исходной схемы сохранил representative User/Session/TbankConnection в проверке сохранности 3.4. ## 2. Backend: пользовательский учёт и денежные изменения - [x] 2.1 [backend] После 1.2–1.3 создать BankCardsModule и сервис сценариев: добавление карт с защитой повторов, пользовательские категории, начальный набор и переименование с сохранением идентичности и default role. - **Covers:** `manual-bank-cards` — `Ручное добавление независимых банковских карт`; `manual-bank-cards` — `Общие для пользователя категории расходов`; `manual-bank-cards` — `Изоляция пользовательских финансовых данных`; `manual-bank-cards` — `Согласованные изменения и защита повторной отправки`. - **Verification:** targeted service/DB cases проверяют ограничения сведений карты, собственника, atomic card+seed creation, concurrent first-card creation без дублей категорий, регистронезависимую уникальность, rename default category и same-intent create карты с тем же/другим содержанием. Чтение не создаёт данных и не обращается к T-Bank. - [x] 2.2 [backend] После 1.2 реализовать чистые правила календарных дат и точные расчёты баланса и месячных расходов из первичных записей, сохранив O(N) подход Design. - **Covers:** `manual-bank-cards` — `Даты начала учёта и ручных операций`; `manual-bank-cards` — `Баланс определяется начальным остатком и внесёнными операциями`; `manual-bank-cards` — `Месячный журнал и сводка расходов по категориям`. - **Verification:** targeted Jest проверяет `1 − 0,10 − 0,20 = 0,70`, отрицательный баланс, итог выше предела отдельного ввода, московскую границу дня/месяца, реальные календарные даты, возврат прошлого месяца и исключение переводов/пополнений из расходов. Расчёт не зависит от страницы журнала; комментарий `ponytail:` фиксирует предел O(N) и условие оптимизации. - [x] 2.3 [backend] После 2.1–2.2 реализовать создание расходов и пополнений с проверкой карты/категории/дат, транзакцией и разрешением сохранённого намерения до повторной проверки изменяемых условий. - **Covers:** `manual-bank-cards` — `Ручное внесение расходов и пополнений`; `manual-bank-cards` — `Согласованные изменения и защита повторной отправки`; `manual-bank-cards` — `Изоляция пользовательских финансовых данных`. - **Verification:** service/DB cases проверяют default category, сохранение при отрицательном остатке, отказ чужим ссылкам и недопустимому вводу без частичной записи; same UUID не создаёт дубликат, другой payload даёт конфликт, две независимые успешные траты сохраняются обе. - [x] 2.4 [backend] После 2.3 реализовать перевод одной записью с проверкой обеих карт и дат начала учёта, двумя проекциями истории и общим денежным эффектом. - **Covers:** `manual-bank-cards` — `Перевод между своими картами как одно действие`; `manual-bank-cards` — `Даты начала учёта и ручных операций`; `manual-bank-cards` — `Изоляция пользовательских финансовых данных`. - **Verification:** реальная DB-проверка подтверждает обе стороны перевода, допустимый отрицательный остаток источника и отсутствие частичного результата при ошибке; отклоняются одинаковые, чужие и архивные карты и дата до начала учёта любой стороны. Сумма не включается в расходы одной или всех карт. - [x] 2.5 [backend] После 2.3 реализовать связанные частичные и повторные возвраты, наследование категории покупки, общий лимит и чтение доступных исходных расходов за любой период. - **Covers:** `manual-bank-cards` — `Возврат связан с исходным расходом`; `manual-bank-cards` — `Согласованные изменения и защита повторной отправки`; `manual-bank-cards` — `Изоляция пользовательских финансовых данных`. - **Verification:** DB cases проверяют несколько возвратов, исходный расход прошлого месяца, чужую/удалённую покупку, дату до покупки и два конкурирующих возврата с превышением общего лимита. Повтор успешно сохранённого полного возврата после потерянного ответа возвращает существующий результат без ошибки нового лимита и без дубликата. - [x] 2.6 [backend] После 2.4–2.5 реализовать version-protected исправление и логическое удаление всех видов операций с проверкой прежних и новых связей, сохранением creation fingerprint и запретом изменения вида/исходной покупки возврата. - **Covers:** `manual-bank-cards` — `Исправление и удаление операций с согласованным пересчётом`; `manual-bank-cards` — `Возврат связан с исходным расходом`; `manual-bank-cards` — `Согласованные изменения и защита повторной отправки`. - **Verification:** DB cases покрывают перенос расхода/пополнения между картами, смену получателя и удаление целого перевода, смену месяца/категории, ограничения исходного расхода с возвратами, edit refund с исключением собственной прежней суммы, stale edit/delete и повтор delete. Late create retry после edit/delete не возвращает старые суммы и не воскрешает запись; гонка refund с уменьшением покупки сохраняет общий лимит. - [x] 2.7 [backend] После 2.6 реализовать изменение сведений и начала учёта карты, архив и возврат из архива с проверкой всей затронутой истории и version conflict. - **Covers:** `manual-bank-cards` — `Ручное добавление независимых банковских карт`; `manual-bank-cards` — `Исправление начала ручного учёта`; `manual-bank-cards` — `Архив карт сохраняет историю`; `manual-bank-cards` — `Согласованные изменения и защита повторной отправки`. - **Verification:** DB cases проверяют opening balance без новой операции/изменения месячных трат, недопустимое новое начало с входящим переводом, архив при любом остатке, запрет create/edit/delete по архиву, unarchive и stale card edit. Гонка архива или смены даты с финансовым изменением даёт последовательный допустимый результат; повтор create архивированной карты возвращает ту же архивную карту. Результат раздела 2: `src/bank-cards/bank-card.calculations.spec.ts` — 23 pure tests passed; полный backend gate — 33 suites / 253 tests passed. `test/bank-cards.e2e-spec.ts` — 18 configured HTTP/SQLite tests passed (`/tmp/bank-cards-http.log`): атомарные повторы/переводы, независимые траты, конкурирующие возвраты и уменьшение покупки, смена начала/архив одновременно с операцией, версии, foreign ownership, исправления/удаления, категории и точные денежные границы. Fixtures вымышленные, URL уникальный; setup сохраняет внешний `DATABASE_URL`, банковский suite запрещает shared/user DB. ## 3. Backend: чтение, HTTP и доказательство сохранности - [x] 3.1 [backend] После раздела 2 подключить расчёты к согласованным чтениям списка/деталей карт, месячного журнала, исходных расходов возврата и сводок одной/всех карт. - **Covers:** `manual-bank-cards` — `Баланс определяется начальным остатком и внесёнными операциями`; `manual-bank-cards` — `Месячный журнал и сводка расходов по категориям`; `manual-bank-cards` — `Архив карт сохраняет историю`; `manual-bank-cards` — `Возврат связан с исходным расходом`. - **Verification:** integration cases проверяют пагинацию и стабильный порядок равных дат, весь набор для баланса/сводки, отрицательный net, архив в общей сводке, перенос между месяцами и перегруппировку связанных возвратов после изменения категории покупки. Связанные данные одного ответа согласованы; отсутствуют server cache и materialized balance. - [x] 3.2 [backend] После 3.1 завершить controller/runtime DTO под `/api/v1/bank-cards`, подключить существующие JWT/filter и сформировать полный runtime OpenAPI для frontend. - **Covers:** `manual-bank-cards` — `Отдельный раздел ручного учёта банковских карт`; `manual-bank-cards` — `Точные денежные значения и проверка до сохранения`; `manual-bank-cards` — `Даты начала учёта и ручных операций`; `manual-bank-cards` — `Изоляция пользовательских финансовых данных`. - **Verification:** configured HTTP cases проверяют фактические validation/status/error codes, отсутствие сессии, чужие связи, архив/version/refund conflicts, безопасность ответов и работу без T-Bank. Runtime OpenAPI отражает действующие DTO и Money; сырые Prisma records не выдаются вместо оговорённого контракта, финансовые DTO не попадают в ответы с ошибками. - [x] 3.3 [backend] После 3.2 завершить runnable configured HTTP/DB проверки критических сценариев и конкурентных изменений в отдельной временной базе, дополнив уже созданные cases вместо их дублирования. - **Covers:** `manual-bank-cards` — `Согласованные изменения и защита повторной отправки`; `manual-bank-cards` — `Изоляция пользовательских финансовых данных`; `manual-bank-cards` — `Исправление и удаление операций с согласованным пересчётом`; `manual-bank-cards` — `Доступные формы и различимые состояния без ложного успеха`. - **Verification:** с уникальной `DATABASE_URL` выполнить `NODE_ENV=development NODE_OPTIONS=--experimental-vm-modules npx jest --config ./test/jest-e2e.json --runInBand bank-cards`. Проверяемые исходы: один результат concurrent same-intent create карты/операции, сохранность обеих независимых трат, no partial transfer/refund, stale conflicts, архивные/чужие ссылки, loss-of-response replay и отсутствие ложного успеха. Mock Prisma не заменяет DB cases. - [x] 3.4 [backend] После 3.3 доказать долговременность ручных данных, корректный backup/restore и совместимость отката приложения с расширенной схемой без удаления новой истории. - **Covers:** `manual-bank-cards` — `Сохранённый ручной учёт переживает повторный вход и перезапуск`; `manual-bank-cards` — `Согласованные изменения и защита повторной отправки`. - **Verification:** на disposable базах перезапуск сохраняет карты, операции, связи, переименованные категории, архив, tombstones и fingerprints; повтор после перезапуска не создаёт дубликат. Проверяются согласованный SQLite backup/restore, сохранность User/Session/TbankConnection, запуск исходной версии приложения с additive schema и доступность новых записей после возврата новой версии. Старый backup не восстанавливается поверх новой истории как штатный rollback. Результат раздела 3: те же 18 HTTP/DB tests подтверждают чтения, пагинацию, month/category regrouping, validation/status и безопасные ошибки без T-Bank. Дополнительный процессный сценарий `node /tmp/bank-cards-durability.cjs` — passed: исходный backend собран из Git archive `8ec172f48eefa2237dc0c16919fbb2e726b39a65` в `/tmp/wealthledger-bank-baseline.8hcDz3`; миграция сохранила старые таблицы, перезапуск — категории/архив/возвраты/tombstones/fingerprints, старое приложение запустилось с расширенной схемой, новое вновь прочитало историю, согласованный SQLite backup восстановлен и прочитан приложением. Свидетельство: `/var/folders/j_/h2ft23gd3qq35ps3v5bcftfm0000gn/T/wealthledger-bank-durability-hPvsEO/result.json`. Runnable версия сохранена в `backend/test/bank-cards-durability.cjs`; ей передаётся каталог отдельно собранной исходной версии. Проверки создают свои временные базы; старый backup не записывается поверх новой истории. ## 4. Контракт и слой данных frontend - [x] 4.1 [frontend] После 3.2 обновить committed OpenAPI snapshot из настроенного backend runtime document и сгенерировать frontend types, сохранив backend единственным editable HTTP source. - **Covers:** `N/A` — производные артефакты двустороннего контракта из Design обеспечивают типизированного потребителя, но не вводят отдельного пользовательского поведения. - **Verification:** `npm run openapi:generate`, `npm run openapi:check`, `npm run typecheck`; snapshot получен по [существующему workflow](../../../../docs/development/runtime-openapi-verification.md), generated types не исправлялись вручную, существующие инвестиционные Money-контракты сохранены. - [x] 4.2 [frontend] После 4.1 добавить banking queries/mutations у domain owner: session-aware transport, стабильный create UUID, version inputs, безопасные retries и invalidation всех затронутых банковских GET-представлений. - **Covers:** `manual-bank-cards` — `Согласованные изменения и защита повторной отправки`; `manual-bank-cards` — `Изоляция пользовательских финансовых данных`; `manual-bank-cards` — `Отдельный раздел ручного учёта банковских карт`; `manual-bank-cards` — `Доступные формы и различимые состояния без ложного успеха`. - **Verification:** Vitest/MSW через `npm run test:run -- src/entities/bank-card` проверяют одинаковый UUID при network/auth retry, отсутствие optimistic balance, invalidation обеих карт/всех месяцев/исходных расходов, rename категории, сохранение данных при ошибке, GET после version conflict, logout isolation и независимость от T-Bank connection mutation. Свой refresh loop и второй query cache отсутствуют. Результат 4.2: `src/entities/bank-card/api/banking.test.tsx` — 10 MSW tests passed; typecheck, focused Biome и dependency boundaries — passed. Повторы сохраняют UUID, подтверждённые mutations инвалидируют только все banking GETs, конфликты вызывают перечитывание, ошибки не меняют суммы оптимистически. Дополнены реальные feature tests AuthProvider/TbankConnectionSection: 10 tests passed доказывают очистку banking cache при logout/refresh failure и его сохранение без invalidation при connect/replace/delete T-Bank. Результат 4.1: snapshot экспортирован из configured runtime Nest/Swagger (42 paths) на уникальной временной SQLite, включая банковские 409/410. `npm run openapi:generate`, `npm run openapi:check`, `npm run typecheck` — passed; generated types не редактировались вручную. После финального добавления описаний 409/410 snapshot/types повторно сгенерированы, `openapi:check` passed. ## 5. Экраны и формы из UI/UX package revision 1 Все задачи раздела используют существующие Mantine-тему, i18n, container/VM/view, RHF/Zod и shared UI из Design. Проверки формы и состояний выполняются через видимый результат и сеть MSW; отдельный тест декларативной обёртки не нужен. - [x] 5.1 [frontend] После 4.2 собрать `/cards`: навигацию, список активных карт и архива, месячную сводку всех карт и форму добавления карты с начальным остатком/датой. - **Covers:** `manual-bank-cards` — `Отдельный раздел ручного учёта банковских карт`; `manual-bank-cards` — `Ручное добавление независимых банковских карт`; `manual-bank-cards` — `Месячный журнал и сводка расходов по категориям`; `manual-bank-cards` — `Доступные формы и различимые состояния без ложного успеха`. - **Verification:** `npm run test:run -- src/pages/cards` и targeted route tests проверяют login/returnTo без T-Bank, обязательные/необязательные поля, точный отрицательный остаток, no request при invalid, блокировку pending, сохранение ввода при ошибке и один create UUID при повторе. Loaded/empty/error/skeleton и архивный переключатель соответствуют Design; сводка всех карт включает архив независимо от фильтра списка. - [x] 5.2 [frontend] После 5.1 собрать `/cards/:id`: крошки и title, текущий баланс, общий выбор месяца для журнала/сводки, пагинацию и представления четырёх видов операций. - **Covers:** `manual-bank-cards` — `Баланс определяется начальным остатком и внесёнными операциями`; `manual-bank-cards` — `Месячный журнал и сводка расходов по категориям`; `manual-bank-cards` — `Архив карт сохраняет историю`; `manual-bank-cards` — `Доступные формы и различимые состояния без ложного успеха`. - **Verification:** `npm run test:run -- src/pages/card` проверяет month/URL и страницу, неизменный текущий баланс, расходы/возвраты/net со знаком, понятные стороны перевода, локальную ошибку сводки, cached snapshot при refetch failure и безопасное состояние отсутствующей карты. Frontend форматирует Money без собственной финансовой арифметики. - [x] 5.3 [frontend] После 5.2 добавить формы создания расхода, пополнения и перевода с общими проверками денежного ввода/дат и сохранением одного намерения до определённого результата. - **Covers:** `manual-bank-cards` — `Ручное внесение расходов и пополнений`; `manual-bank-cards` — `Перевод между своими картами как одно действие`; `manual-bank-cards` — `Точные денежные значения и проверка до сохранения`; `manual-bank-cards` — `Даты начала учёта и ручных операций`; `manual-bank-cards` — `Доступные формы и различимые состояния без ложного успеха`. - **Verification:** RTL/MSW cases проверяют default category, положительную сумму/копейки без потери больших значений, даты обеих карт перевода, отрицательный итог без запрета реальной траты, visible labels, pending/ошибки с сохранённым вводом и повтор одного перевода без дубликата. Новое подтверждённое состояние приходит из банковских queries. - [x] 5.4 [frontend] После 5.3 добавить создание и редактирование возврата с выбором исходной покупки за всю историю карты и корректным лимитом текущей записи. - **Covers:** `manual-bank-cards` — `Возврат связан с исходным расходом`; `manual-bank-cards` — `Исправление и удаление операций с согласованным пересчётом`; `manual-bank-cards` — `Месячный журнал и сводка расходов по категориям`; `manual-bank-cards` — `Согласованные изменения и защита повторной отправки`. - **Verification:** RTL/MSW journey выбирает покупку прошлого месяца, записывает частичный возврат и видит его в месяце получения; полный возврат позволяет исправить описание, исключая собственную старую сумму из лимита. Проверяются превышение, stale source, immutable purchase link, архив, network retry полного возврата и сохранение формы без ложного успеха. - [x] 5.5 [frontend] После 5.3–5.4 добавить остальные исправления/удаления операций, изменение сведений и начала учёта карты, архив и возврат из архива с понятными конфликтами. - **Covers:** `manual-bank-cards` — `Исправление и удаление операций с согласованным пересчётом`; `manual-bank-cards` — `Исправление начала ручного учёта`; `manual-bank-cards` — `Архив карт сохраняет историю`; `manual-bank-cards` — `Доступные формы и различимые состояния без ложного успеха`. - **Verification:** RTL/MSW проверяет edit с version и последующим GET при конфликте, сохранение черновика, перенос между картами/месяцами, подтверждение удаления перевода с обеими картами, запреты связанного расхода и архива, opening date error и обратимый архив. После успешного действия все затронутые видимые суммы соответствуют новым серверным данным. - [x] 5.6 [frontend] После 5.3 добавить управление начальными и своими категориями и создание категории из формы расхода без потери введённой операции. - **Covers:** `manual-bank-cards` — `Общие для пользователя категории расходов`; `manual-bank-cards` — `Ручное внесение расходов и пополнений`; `manual-bank-cards` — `Доступные формы и различимые состояния без ложного успеха`. - **Verification:** RTL/MSW проверяет add/rename, уникальность без регистра и длину, переименование default category без утраты роли, обновление названий в истории/сводке и неизменный черновик расхода при добавлении категории. Удаление и вложенные подкатегории отсутствуют согласно scope. Результат 5.1–5.6: реализованы страницы, четыре вида операций, исправления, удаление, архив и категории. RTL/MSW покрывают точный ввод и UUID после ошибки, month/page/returnTo, неизменный текущий баланс, полный возврат с собственным лимитом, stale version с явным принятием новой версии, opening-date conflict, архив, обе стороны удаления перевода и вложенное создание категории. Финальный frontend gate включает 43 test files / 263 passed tests. Браузер обнаружил потерю нового имени категории после reset неконтролируемого поля: поле переведено на RHF Controller, исправление проверено RTL и настоящим PATCH/GET. Независимое ревью обнаружило скрытые ошибки вспомогательных GET карт/категорий: добавлены локальные сообщения и retry, две regression-проверки и browser recovery passed. Существующие authentication/инвестиционные tests прошли; returnTo сохраняет карточку и query выбранного месяца. ## 6. Проверка пользовательского результата и документация - [x] 6.1 [frontend] После 3.3–3.4 и разделов 4–5 проверить UI package в браузере с изолированным backend и подготовить Storybook-примеры новых собственных представлений на вымышленных данных. - **Covers:** `manual-bank-cards` — `Доступные формы и различимые состояния без ложного успеха`; `manual-bank-cards` — `Отдельный раздел ручного учёта банковских карт`; `manual-bank-cards` — `Сохранённый ручной учёт переживает повторный вход и перезапуск`. - **Verification:** реальный UI journey без T-Bank: две карты → расход/пополнение → перевод → возврат прошлой покупки → исправление/удаление → архив/unarchive → reload/login. Проверить широкий экран и 320–375 px, пять пунктов навигации, длинные суммы/названия, keyboard/focus return, reduced motion и loading/empty/error/refetch recovery. Сохранить краткие результаты и локальные визуальные свидетельства; Storybook build не подменяет браузерную проверку. - [x] 6.2 [docs] После подтверждения реализованного поведения обновить документацию: область банковских карт, глоссарий, обзор продукта и тематическую навигацию, хранимые данные, денежные правила и безопасное восстановление. - **Covers:** `N/A` — as-built объяснение поставленного поведения и восстановления из Delivery Map; новая норма не вводится и не копируется параллельно Delta Specs. - **Verification:** пройти путь README → банковские карты → понятия/расчёты/исправления и восстановление; сверить текст с поставленными flows и ограничениями, включая ручной источник баланса, отрицательные итоги, архив, повторы и сохранение новой истории при rollback. Ссылки ведут на существующие targets; исторические материалы не переписываются, обещания неподтверждённой миграции не добавляются. Результат 6.1: реальный браузер работал с отдельным BrowserContext и backend на уникальной временной SQLite, с вымышленным пользователем без T-Bank. Через UI выполнены две карты (1 000 ₽ и 0 ₽), августовский расход 100 ₽, сентябрьские пополнение 200 ₽, перевод 150 ₽ и возврат 60 ₽: баланс A 1 010 ₽, B 150 ₽, сентябрьский net −60 ₽. Возврат исправлен до полного 100 ₽, его описание изменено, затем сумма возвращена к 60 ₽. Исправление покупки 100 → 150 ₽ дало A 960 ₽; удаление перевода с подтверждением обеих карт — A 1 110 ₽ и B 0 ₽. Архив сохранил историю и общий net; unarchive вернул действия. Добавление категории из расхода сохранило сумму 12,34 ₽ и описание черновика; переименование «Другое» сохранило default role. Проверены Escape и возврат фокуса к действию, visible labels, initial loading/empty, ошибки GET и retry, сохранённый net при неудачном refetch, reduced motion. Logout → protected URL → login вернул ту же карту и август; reload сохранил баланс 1 110 ₽ и расход 150 ₽. Экран 1440 px и 320/375 px проверен визуально: пять пунктов навигации, длинное название и точная предельная сумма доступны без горизонтального overflow. Локальные screenshots итогового UI: `/tmp/wealthledger-bank-cards-final-desktop.png`, `/tmp/wealthledger-bank-cards-final-mobile.png`, `/tmp/wealthledger-bank-cards-final-mobile-long.png`, `/tmp/wealthledger-bank-card-final-mobile.png`; состояние cached error — `/tmp/wealthledger-bank-cards-cached-error.png`. Добавлены две изолированные story groups и 9 примеров на вымышленных данных; storybook dependency isolation и build passed. Результат 6.2: обновлены README/overview, glossary, область cards, описание хранения, локальная миграция/восстановление и UI-каталог. Маршруты README → cards → Money/восстановление проверены; объяснения сверены с Delta Specs, реализацией и наблюдёнными flows. Новых Mermaid-диаграмм и изменений исторических ADR нет. ## 7. Финальные gates и состояние SDD-артефактов Backend `npm run verify` — passed (`/tmp/bank-cards-verify.log`): lint без warnings, app/test TypeScript, 33 suites / 253 tests, Prisma generate и build. Последующий итоговый HTTP-прогон и independent review описаны ниже; успех gates не является заменой semantic review. После независимого review исправлен P2 в DTO: явный `null` для Money, архива и non-null полей отклоняется; nullable реквизиты очищаются по-прежнему. Configured HTTP/SQLite — 19/19 passed, включая 400 без изменения данных/версий; OpenAPI parity на уникальной базе — passed, wire shape не изменился. Итоговый `npm run verify` повторно passed (`/tmp/bank-cards-final-backend-verify.log`). Сохранённый runnable durability сценарий повторно passed на итоговом backend: `/var/folders/j_/h2ft23gd3qq35ps3v5bcftfm0000gn/T/wealthledger-bank-durability-dXSTmz/result.json`. Reviewer перепроверил исправление и не нашёл других backend findings. Gate применяется к итоговому состоянию соответствующего repo. Уже прошедшие проверки не повторяются без новых изменений, сбоев или оставшегося риска. Если финальная правка меняет проверенную границу, повторяется её нужный gate. - [x] 7.1 [backend] После разделов 1–3 выполнить canonical backend gate и сохранить результаты DB/HTTP/durability проверок для того же состояния кода. - **Covers:** `N/A` — обязательная проверка backend delivery из Design, дополняющая уже покрытые поведенческие и DB cases без новой возможности. - **Verification:** `npm run verify`; отдельные результаты 3.3–3.4 остаются применимы к итоговому коду. Type/lint/unit/build не объявляются доказательством транзакций или миграции, если DB-проверка не запускалась успешно. - [x] 7.2 [frontend] После 6.1 выполнить canonical frontend gate для итоговых API types, UI, i18n, stories и dependency boundaries. - **Covers:** `N/A` — обязательный frontend delivery gate из Design, без добавления самостоятельного пользовательского поведения. - **Verification:** `npm run verify` включает OpenAPI drift, typecheck, lint, coverage, Knip, dependency boundaries и обе сборки. Отдельный результат браузерной проверки 6.1 сохраняет применимость; процент покрытия и Storybook build не заменяют его. - [x] 7.3 [workspace] После 4.1, 7.1 и 7.2 подтвердить двусторонний runtime OpenAPI contract по итоговому backend и frontend snapshot/types. - **Covers:** `N/A` — межрепозиторная verification из Contract Gate Decision, без владения кодом приложений или отдельным поведением. - **Verification:** из backend выполнить `npm run test:openapi-contract`; использовать актуальный frontend `openapi:check` из 7.2, не повторяя его при неизменном snapshot/types. Проверка не требует T-Bank и не изменяет snapshot автоматически. - [x] 7.4 [docs] После 6.2 выполнить canonical docs gate, ручную смысловую сверку и проверку затронутых маршрутов чтения. - **Covers:** `N/A` — проверка связности as-built документации из Delivery Map, без самостоятельного нормативного поведения. - **Verification:** из docs выполнить `npm run verify`; проверить новые Markdown-файлы и затронутые внешние относительно docs ссылки. Если изменены Mermaid-диаграммы, отдельно проверить доступным локальным renderer их вид либо явно сохранить ограничение; новый стек для этого не устанавливать. - [x] 7.5 [specs] После всех implementation-owned задач сверить поставленный результат со всеми Delta Requirements и обновить этот Tasks только по выполненным действиям и наблюдённым проверкам, затем выполнить canonical validation. - **Covers:** `N/A` — поддержание существующих SDD-артефактов и traceability реализации из Delivery Map; новые требования, архитектура и отдельный журнал доказательств не создаются. - **Verification:** каждое требование `manual-bank-cards` связано с выполненной задачей и подходящим доказательством; все оставшиеся ограничения названы. Выполнить `openspec validate add-manual-bank-cards --type change --strict --store wealth-ledger-specs`, `openspec validate --specs --strict --store wealth-ledger-specs` и `git diff --check`, отдельно просмотрев новые файлы и ссылки. Approval и завершение реализации не выводятся только из наличия файлов. Независимая проверка результата и завершение lifecycle выполняются после implementation-owned задач; они не маскируются checkbox, который исполнитель не может закрыть сам. Этот план не включает преждевременную синхронизацию нового capability в основные specs, архивирование или изменение истории. Такие действия относятся к последующему завершению выбранного SDD lifecycle. Итоговые результаты 7.2–7.4: frontend `npm run verify` — passed (`/tmp/bank-cards-final-frontend-verify.log`): OpenAPI drift, TypeScript, Biome, 43 files / 263 tests с coverage, Knip, обе dependency проверки, app и Storybook builds. Backend `npm run test:openapi-contract` — passed на уникальной базе (`/tmp/bank-cards-contract.log`); последний DTO fix не меняет wire shape, frontend snapshot/types после этого не изменялись. Docs `npm run verify` — passed, 54 Markdown-файла, 0 issues. Независимое ревью backend/frontend/docs завершено: найденные null-validation, category-form и auxiliary-query recovery дефекты исправлены и перепроверены; открытых actionable findings нет. Обычный Prisma fresh migrate deploy без `RUST_LOG=info` по-прежнему имеет указанное в 1.1 ограничение; причина не установлена. Подтверждён только штатный путь с переменной окружения. Предупреждение Storybook/Vite о размере chunks не препятствует сборке. Результат 7.5: все 17 Delta Requirements сверены с реализацией и Covers/проверками выше; 29 implementation-owned задач завершены. `openspec validate add-manual-bank-cards --type change --strict --store wealth-ledger-specs` — valid; `openspec validate --specs --strict --store wealth-ledger-specs` — 25 passed, 0 failed. Итоговый routing повторён по modified/untracked файлам четырёх repo; новые файлы и ссылки просмотрены, `git diff --check` — passed в каждом repo. Proposal, Delta Specs, Design, основные specs и архивы не изменялись. Последующие проверки перед commit: по запросу пользователя все четыре нативных поля дат/месяца заменены на Mantine DateInput/MonthPickerInput. Сохранение календарных дат, month/URL и календарь на 320 px проверены в браузере на новой временной базе; будущие даты недоступны. Полный frontend `npm run verify` повторно passed: 43 files / 263 tests и обе сборки (`/tmp/bank-mantine-verify.log`). После отдельного разрешения пользователя миграция применена к рабочей SQLite через `RUST_LOG=info npx prisma migrate deploy`. До применения создан согласованный SQLite backup; после — проверены integrity/FK и неизменность User/Session/TbankConnection. Это обновление рабочей среды, не тестовые записи в пользовательской базе. Backup исключён из Git; ограничение обычного запуска Prisma без `RUST_LOG=info` остаётся прежним.