Files
main/audits/2026-10-11/reports/04-dead-code-and-legacy-report.md
codex-bot d07fbfabb1 docs: archive technical audit 2026-10-11 in develop (#36)
Переносит архив `audits/2026-10-11/` (19 файлов) из codex/audit-remediation-2026-10-11 в develop и добавляет точечный allowlist в .gitignore.
2026-10-17 19:38:03 +03:00

747 lines
83 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Аудит 04 — Dead Code & Legacy
## 1. Audit metadata
- **Audit ID / name:** 04 / Dead Code & Legacy.
- **Дата:** 2026-10-11.
- **Исполнитель:** один независимый исполнитель направления 04; другие
субагенты не запускались.
- **Результат:** исследование reachability и безопасных границ удаления.
Изменён только этот отчёт вне application checkout.
- **Workspace:** `/Users/ksv741/Project/vibe/WealthLedger`.
| Checkout | Абсолютный путь | HEAD, проверенный самостоятельно | Начальный status |
| --- | --- | --- | --- |
| root | `/Users/ksv741/Project/vibe/WealthLedger` | `dc030f84c979cedb2628ef4bd97c62d6c63a9256` | чистый |
| frontend | `/Users/ksv741/Project/vibe/WealthLedger/frontend` | `c8821200ce699dab1ce65360d1968d5877a2e78b` | чистый |
| backend | `/Users/ksv741/Project/vibe/WealthLedger/backend` | `0ad84ff4f0b4712abc7e69dc299856f14af6eadd` | чистый |
| specs | `/Users/ksv741/Project/vibe/WealthLedger/specs` | `fcdc805f6c1b3be6a20a7e858e3e80cbd83a9121` | чистый |
| docs | `/Users/ksv741/Project/vibe/WealthLedger/docs` | `e6fd4f64b81dd73b33959b845a1babcfac1891c8` | чистый |
**Метод фиксации:** в каждом checkout выполнены
`git rev-parse HEAD && git status --porcelain=v1`; пустой porcelain означает
отсутствие staged, unstaged и видимых Git untracked changes, а не отсутствие
ignored файлов. Root allowlist исключает `audit-results/`
(`.gitignore:1–7`), поэтому отчёт не должен появляться в обычном root status.
**Инструкции:** прочитаны AGENTS каждого checkout. Для backend и frontend
прочитаны их собственные architecture, engineering и активированные
conventions: HTTP/data/testing/integrations/security для backend;
TypeScript/React/data/UI/testing/Storybook для frontend. Root conventions
использованы только для root-границы отчёта. Specs выбраны через `AI_INDEX.md`
и точные тематические targets; три актуальных требования нового потока,
которых нет в индексе, прочитаны по известным абсолютным путям. Вложенных
AGENTS в source-областях frontend/backend поиск не обнаружил.
**Skills:** установленный `ponytail-audit` загружен первым через Skill tool.
Применены его `delete:` / `shrink:` теги, ранжирование по сокращению ненужной
maintenance surface и запрет исправлений. Схема отчёта расширена обязательным
протоколом 04. Числовой `net` не выдуман: deletion diff не создавался.
Также загружены `graphify` и `verification-before-completion`.
**Graphify:** существующего `graphify-out/graph.json` не обнаружено в root
и четырёх checkout: проверены списки корневых директорий, дополнительно Glob
в frontend/backend. Graph query не выполнялся; graph generation, cache,
manifest и другие artifacts не создавались. Отсутствие существующего graph
— ограничение; выводы получены статической трассировкой актуальных source.
**Что выполнялось:** Read/Glob/Grep из соответствующих repo directories,
read-only `rg -n -c` для явно указанных символов и `git` inspection.
`technical-audit-2026-10-11.md` прочитан только как список гипотез;
его выводы и оценка `~550 lines` не использованы как доказательство.
Другие новые отчёты аудиторов не читались и не изменялись.
**Что не выполнялось:** Node-команды, install, build, codegen, migrations,
full-suite, targeted tests, запуск приложения, запросы к реальным API,
изменение config/source/lockfiles, commits, PR и генерация исследовательских
инструментов. Это статический аудит, не runtime-воспроизведение.
`npm run knip`, `deps`, frontend test scripts предварительно вызывают
`build:packages` (`frontend/package.json:14–26`); их запуск нарушил бы
заданную read-only границу. Их результаты из прежнего отчёта не наследуются.
## 2. Executive verdict
1. **Факт:** command-часть `TbankSyncService.request → dispatch` не имеет
production entrypoint. Nest создаёт service для `status`, но controller
предоставляет только GET. Удаление command-closure обосновано — DEAD-001.
2. **Факт:** вслед за ней обнаружен менее очевидный dead producer:
`CatalogDiscoveryService.request/refresh/drain`. Реальные HTTP search,
detail и ticker читают онлайн либо вызывают persisted reader только с
`observe=true`; старый request-driven импорт каталога запускают прямые
тесты и недоступная manual orchestration — DEAD-002.
3. **Факт:** frontend `observe()` используется только двумя прямыми
тестовыми вызовами; все восемь production consumers `useScreenSync`
игнорируют его return value, а `_scope` вообще не читается.
Можно удалить эти API-хвосты, сохранив действующий timer — DEAD-004/005.
4. **Факт и граница:** статусный GET и backend `sync=observe` действительно
публично достижимы. Отсутствие их использования в текущей SPA не доказывает
отсутствия внешних consumers или старых DB rows. Их удаление сейчас
**не подтверждено** — DEAD-006/007, `INVESTIGATE`.
5. **Риск ошибочной зачистки:** planner операций, hourly lifecycle, candle
loader, shared source repository и connection/attempt fences остаются
живыми. Массовое удаление по словам `screen`, `catalog` или `sync` может
остановить данные либо каскадно удалить свечи — DEAD-008–011, `KEEP`.
6. **Итог:** 5 actionable findings: **P0 = 0, P1 = 0, P2 = 2, P3 = 3**.
Отдельно **KEEP = 4**, **INVESTIGATE = 2**. Для непроверенной внешней
совместимости указан `UNVERIFIED`; экономия времени/строк не измерена.
## 3. Inventory & coverage
### 3.1. Фактическая карта entrypoints
| Вход / поток | Проверенная цепочка и owner | Следствие для reachability |
| --- | --- | --- |
| SPA bootstrap | `frontend/src/main.tsx:1–23` → `src/app/ui/App.tsx:23–43` → `src/app/routes/AppRoutes/AppRoutes.tsx:9–103,105–257` | Lazy imports являются настоящими consumers page exports. CSS, i18n и dayjs locale — side effects, не orphan imports. |
| SPA routes | `AppRoutes.tsx:105–257`: login/register; cards/card; stocks/stock; bonds/bond; accounts/account/events/income/operations; settings; wildcard | Страницы доступны через router, включая динамические chunks; отсутствие статического прямого import не делает их мёртвыми. |
| Backend HTTP | `backend/src/main.ts:44–60` → `src/app.module.ts:18–36` → controllers | Prefix, validation, cookies, error boundary и shutdown hooks задаются `src/main.ts:19–42`; controller decorators являются registration. |
| Controller families | `backend/src/modules/auth/presentation/auth.controller.ts:51`; `backend/src/modules/banking/bank-cards/presentation/bank-cards.controller.ts:79`; `backend/src/modules/investments/brokerage-accounts/brokerage-accounts.controller.ts:15`; `backend/src/modules/investments/portfolio/presentation/portfolio.controller.ts:15`; `backend/src/modules/investments/operations/presentation/brokerage-operations.controller.ts:18`; `backend/src/modules/investments/income/income-analytics.controller.ts:20`; `backend/src/modules/investments/upcoming-events/upcoming-events.controller.ts:18`; `backend/src/modules/investments/instruments/stocks/presentation/stock.controller.ts:48`; `backend/src/modules/investments/instruments/bonds/presentation/bond.controller.ts:37`; `backend/src/modules/investments/tbank-connection/tbank-connection.controller.ts:33`; `backend/src/modules/investments/tbank-sync/tbank-sync.controller.ts:31` | Проверены registrations всех этих семейств поиском `@Controller`, root module и нужными application chains; это не полный семантический аудит CRUD/auth. |
| Hourly job | `backend/src/modules/investments/tbank-sync/tbank-sync.module.ts:24–25` → scheduler `onModuleInit:48–50` → `tick:56–71` → `reserveHourly` / `walk` | Один native timer, durable work и source owners; запуск не зависит от frontend. |
| Source queues | Portfolio `queueSnapshot`, operations sync, metadata, enrichment, candles используют `setImmediate`; declarations найдены в source, живые примеры traced ниже | Удаление manual dispatcher не даёт права удалять все очереди с похожими именами. |
| Production CLI | `backend/package.json:15–18`; `compose.yaml:24–38` | Backend запускает `dist/src/main.js`; отдельная migration service — Prisma CLI. В manifest не найден CLI, вызывающий manual sync или persisted catalog producer. |
| Test dynamic entrypoint | `backend/test/fixtures/tbank-sync-crash.cjs:1–17` | Динамические require ведут в operations sync/repository, не в `TbankSyncService` или `CatalogDiscoveryService`. Fixture не запускался. |
| OpenAPI tooling | `backend/scripts/generate-openapi.cjs:19–31` → AppModule preview → `src/openapi.ts:6–10` | Generator читает зарегистрированный controller contract; не вызывает service methods по именам. Наличие DTO decorator само по себе не публикует DTO. |
| Storybook / packages | `frontend/.storybook/main.ts:3–7`; `packages/ui/package.json:9–22`; `src/app/ui/App.tsx:3–8` | Stories — дополнительные entrypoints; UI CSS явно side-effectful. Feature/query candidates не экспортируются public UI package. |
| Env / imports | `backend/src/config/env.validation.ts:9–33,102–113`, `.env.example:1–10`, `src/app.module.ts:18–36`; `frontend/vite.config.ts:54–61`, `tsconfig.app.json:8–20` | Env задаёт транспорт/config, но не включает manual sync module/method или старую catalog producer ветку. Alias `@/` ведёт в source. |
### 3.2. Метод отбора и счёта
Исследованы **12 кандидатов C01–C12**, отобранных по стоимости сопровождения
и риску ошибочного удаления: старая orchestration, связанные producer paths,
orphan DTO, frontend API-хвосты, публичная compatibility и защищённые live
owners. Число 12 — число строк Reachability Ledger, не результат analyzer.
Findings — 11: C01/C02 образуют одну deletion closure DEAD-001.
Порядок: declaration → exports/DI/registration → все найденные callers →
реальный runtime entrypoint → external/public accessibility → контрпример.
Поиск применялся к source, затем отдельно tests/scripts/artifacts, чтобы
не принять direct test invocation за пользовательский путь.
**Воспроизводимые search counts:** это количество matching lines, а не число
AST references; вывод вручную разделён по declarations/imports/calls.
Поиск не считается самостоятельным доказательством отсутствия callers.
| Поиск и границы | Результат и независимая проверка |
| --- | --- |
| Q01, backend source/tests/scripts, команда ниже | `TbankSyncService` найден только в своём service, controller и module; `sync.request(` — 0 строк. Controller calls только `status`. Для `normalizeScreen(`: 1 declaration, 1 вызов в dead request, 9 строк вызовов в `tbank-screen.spec.ts`. DTO Accepted — 1 declaration, 0 consumers. |
| Backend: owner-aware поиск `this.discovery.*` в instruments и чтение обоих catalog services | 6 live delegation call sites: search/detail/byTicker у stocks и bonds. Ни один не вызывает `read/request/refresh` напрямую. `discovery.refresh` найден в manual dispatcher. В discovery owner три вызова `this.read` находятся после online-return ветки и получают `observe=true`; `this.request` — в `read(!observe)` и `refresh`. |
| Q02, frontend source/packages/Storybook/scripts, команда ниже | `observe()` — 2 строки, обе в `resource-query.test.tsx:63,167`; production вызовов 0. `useScreenSync(` — 8 production call lines, 2 test call lines, 1 declaration. Все 8 VM прочитаны целиком: return hook не захватывается. |
| Q03, frontend source/packages/Storybook, команда ниже | `tbank-sync` — 2 строки JSON snapshot и 1 generated route declaration; runtime call site 0. Литерал `sync: "observe"` — 1 строка в `observationKey`. Accepted DTO — 0. |
| Q04, backend OpenAPI artifact, команда ниже | 2 matching lines: GET path и operationId; Accepted DTO отсутствует. Artifact не регенерировался, поэтому свежесть runtime parity — `UNVERIFIED`; controller/source metadata дополнительно проверены. |
Команды выполнены с `workdir` соответствующего checkout, без `cd` и записи
output-файлов. Exact regex приведены вне таблицы, чтобы Markdown не подменял
alternation и не создавал лишние колонки.
```sh
# Q01 — backend
rg -n -c 'TbankSyncService|sync\.request\(|normalizeScreen\(|TbankSyncAcceptedDto|discovery\.(read|request|refresh)\(' src test scripts --glob '*.{ts,cjs,mjs}'
# Q02 — frontend
rg -n -c '\.observe\(|\["observe"\]|\[.observe.\]|\buseScreenSync\(' src packages .storybook scripts --glob '*.{ts,tsx,cjs,mjs}' --glob '!**/generated/**' --glob '!**/dist/**'
# Q03 — frontend
rg -n -c 'tbank-sync|TbankSyncAcceptedDto|sync: "observe"' src packages .storybook --glob '!**/dist/**'
# Q04 — backend
rg -n -c 'TbankSyncAcceptedDto|TbankSyncController_status|/api/v1/tbank-sync' openapi.json
```
**Dynamic checks:** source search по `ModuleRef`, reflection/getMetadata,
computed `request/observe`, `import()/require`, factory/alias DI и cron/timers
не выявил alternate entrypoint кандидатов на DELETE. Проверены явные
`@Injectable`, `@Module`, `@Controller`, `@Get`, `ApiExtraModels`, `useExisting`
и module exports. В backend `useFactory` используется для logging/auth,
`TBANK_ACCESS` alias — для instruments (`instruments.module.ts:24`);
кандидатов он не dispatch-ит. В frontend dynamic imports представлены явными
router chunks; Storybook собирает stories по glob. No-match ограничен
этими checkout: неизвестный deployed consumer не исключён.
### 3.3. Coverage и ограничения выборки
- Сфокусированы investments synchronization/catalog и frontend consumers,
где после перехода архитектуры есть сильные гипотезы с большими closures.
- Банковские/auth routes проверены на существование root registration;
correctness их бизнес-логики здесь не исследовалась.
- Tests прочитаны как callers и deletion dependencies. Тестовое покрытие
проекта, mutation score и качество всех assertions не оценивались.
- Specs/docs рассматриваются как intended behavior и compatibility context,
а не доказательство текущего исполнения. Архивы не являются candidates.
- Generated/dist, coverage, реальные `.env`, DB, deployment logs и секреты
не служили source call graph; lockfiles/dependency removal вне scope 04.
- Runtime trace, bundle size, CI minutes, tokens и размер потенциального diff
не измерены. Статическая карта не доказывает отсутствие внешних клиентов.
## 4. Findings
Severity actionable findings отражает текущую engineering cost, а не обещанную
экономию. В KEEP/INVESTIGATE Severity обозначает риск ошибочного удаления;
эти строки не являются подтверждёнными дефектами и не включены в actionable
severity totals. Ни один finding не предлагает исправление в этом аудите.
### DEAD-001 — Удалить недоступную manual screen orchestration вместе с closure
- **Decision:** DELETE.
- **Severity:** P2.
- **Confidence:** High.
- **Evidence:** `backend/src/modules/investments/tbank-sync/tbank-sync.service.ts:41–51,54–311`; `tbank-sync.controller.ts:31–41`; `tbank-sync.module.ts:23–25`; `tbank-sync.repository.ts:62–109,135–169`; `tbank-screen.ts:8–122`; `tbank-screen.spec.ts:1–75`. Последние пять путей относительны той же `backend/src/modules/investments/tbank-sync/`.
- **Claim:** у `request → normalizeScreen → reserve → setImmediate(dispatch)`
нет production entrypoint; соответственно manual child planner и writer
progress поддерживаются без пользовательского сценария.
- **Current mechanism / runtime consumer:** service зарегистрирован Nest,
но его единственный production consumer — constructor controller,
вызывающий `status`. `request` не имеет route/scheduler decorator,
service не экспортирован TbankSyncModule, reflection dispatcher не найден.
`normalizeScreen` фактически вызывают direct unit tests и недоступный request.
- **Impact / actual cost:** лишняя orchestration source owners, ручные
reservations, field normalization и отдельный тестовый сценарий;
**Impact unmeasured**. Runtime RPC savings не заявляются: путь уже недоступен.
- **Why now:** текущие source entrypoints и
`specs/openspec/specs/tbank-data-sync/spec.md:9–25` передали обновление hourly
owner и запрещают manual commands. Это не предположение только по ADR.
- **Minimum change:** удалить request/dispatch, `reservations`, exclusive
DI/imports/constants, repository `reserve/progress`, normalization и её
tests. Сохранить весь status reader и его compatibility types, пока DEAD-006
не решён; сохранить shared source owners и operations planner.
- **Guarantees at risk:** access/revision checks внутри живого status,
source CAS, hourly lifecycle, shared metadata/portfolio/candle owners.
Не удалять эти owners вместе с исключёнными constructor injections.
- **Counterarguments / trade-offs:** создание service Nest не равно вызову
всех его методов. Возвращение manual sync было бы новым продуктовым решением;
restore из Git возможно. Старые screen work могут ещё читаться status:
поэтому reader не входит в DELETE.
- **Verification or rollback:** после отдельного cleanup — type/DI proof,
отсутствие POST command в configured OpenAPI, HTTP status compatibility,
существующие local-read/hourly scenarios. Rollback — вернуть code-only
closure одним revert; DB/schema не менять.
- **Effort:** M.
- **Related finding IDs:** DEAD-002, DEAD-006, DEAD-008, DEAD-009, DEAD-011.
### DEAD-002 — Удалить недоступный persisted catalog discovery producer
- **Decision:** DELETE.
- **Severity:** P2.
- **Confidence:** High.
- **Evidence:** `backend/src/modules/investments/instruments/application/catalog-discovery.service.ts:39–64,66–86,143–149,220–227,340–496`; `instruments/stocks/application/stock-catalog.service.ts:21–31,59–83`; `instruments/bonds/application/bond-catalog.service.ts:24–34,58–82`; `instruments/infrastructure/catalog-discovery.repository.ts:341–464`; `backend/test/tbank-catalog-discovery.e2e-spec.ts:85–118,179–196,231–244,270–299`. Три укороченных `instruments/` пути относительны `backend/src/modules/investments/`.
- **Claim:** producer `request/refresh/drain` production-недостижим в текущем
graph. После DEAD-001 исчезает даже его единственный caller из недоступной
ветки. HTTP `sync=observe` сохраняет reader, но не включает этот writer.
- **Current mechanism / runtime consumer:** ordinary search вызывает
`onlineSearch`; ordinary detail/byTicker выходят через online ветки.
Только observe-ветки доходят до `read`, всегда с `true`, и потому пропускают
`if (!observe) request`. Единственный caller `refresh` в production source
— недоступный manual dispatcher.
Прямые тесты `service.read(..., false/default)` и `service.request(...)`
реально запускают queue, но не являются HTTP consumers.
- **Impact / actual cost:** producer map/queue, forced joins, completions,
logger dependency и отдельная persisted discovery publication поддерживают
прежнее поколение поведения. **Impact unmeasured**; это сокращение maintenance,
не устранение измеренного fan-out живого online search.
- **Why now:** онлайн-входы уже существуют; persisted discovery writer не
обслуживает ни их, ни hourly events. Online-only requirement независимо
прочитан: `specs/openspec/specs/online-instrument-information/spec.md:8–17`.
- **Minimum change:** после DEAD-001 удалить Producer/state/request/refresh/drain,
dead `read(!observe)` branch и только exclusive repository `publish`.
Сохранить reader `read/work`, `enrichment`, normalization/selection и online
error projection. Не удалять repository `reserve`, `finish`, `readSources`,
`publishSource`: их использует live CatalogEnrichmentService.
- **Guarantees at risk:** shared source writes, CAS/invalid-token isolation,
legacy observe read, UID/FIGI identity и candle FK. Schema и данные не входят
в эту code-only deletion.
- **Counterarguments / trade-offs:** public `instruments.api.ts:2` экспортирует
provider внутри приложения, а не published npm method contract;
`backend/package.json:6,11–38` — private application без CLI producer.
HTTP compatibility может оправдать persisted reader (DEAD-007), но не writer,
которого эти routes не вызывают. Прямые tests доказывают механизм,
не пользовательскую reachability.
- **Verification or rollback:** переназначить/удалить direct producer-only
cases в `test/tbank-catalog-discovery.e2e-spec.ts`; сохранить проверку живого
online selection/whitelist и legacy reader через существующие HTTP suites.
Проверить unchanged `publishSource`/candles на isolated SQLite в cleanup PR.
Rollback — code-only revert без DB migration.
- **Effort:** M.
- **Related finding IDs:** DEAD-001, DEAD-007, DEAD-010, DEAD-011.
### DEAD-003 — Удалить orphan `TbankSyncAcceptedDto`
- **Decision:** DELETE.
- **Severity:** P3.
- **Confidence:** High.
- **Evidence:** `backend/src/modules/investments/tbank-sync/tbank-sync.dto.ts:212–218`; `tbank-sync.controller.ts:13–27,34–40`; `backend/test/openapi-contract.e2e-spec.ts:27–55`; `frontend/src/shared/api/spec/openapi.json:5102–5123`. Controller путь относителен той же backend sync directory.
- **Claim:** DTO command acceptance объявлен, но не используется controller,
service, ApiExtraModels, scripts или публикуемым snapshot.
- **Current mechanism / runtime consumer:** module DTO загружается живым GET,
и decorators этой orphan class регистрируют её собственные metadata.
Но нет consumer, который включал бы её в response schema или исполнял её
validation. Status class — отдельный live consumer `investmentScreenSchema`.
- **Impact / actual cost:** небольшой устаревший export рядом с действующими
response DTO; **Impact unmeasured**. Его наличие может создавать ложное
впечатление сохранённой POST capability.
- **Why now:** POST отсутствует в actual controller и snapshot; existing
contract test явно ожидает отсутствие `/api/v1/tbank-sync` command route.
- **Minimum change:** удалить только class `TbankSyncAcceptedDto`.
`InvestmentScreenDto`, screen schema, derived DTO и `TbankSyncStatusDto`
остаются нужны public status response.
- **Guarantees at risk:** accidental изменение GET schema при избыточном
удалении shared schema; сама Accepted class не является опубликованным DTO.
- **Counterarguments / trade-offs:** `export class` и decorators не равны
published contract; generator следует зарегистрированным routes.
Artifact parity со свежей сборкой здесь не запускалась — `UNVERIFIED`.
- **Verification or rollback:** targeted OpenAPI schema comparison при cleanup;
Accepted absent, Status и его discriminator unchanged. Rollback — вернуть class.
- **Effort:** S.
- **Related finding IDs:** DEAD-001, DEAD-006.
### DEAD-004 — Удалить only-test frontend `observe` API
- **Decision:** DELETE.
- **Severity:** P3.
- **Confidence:** High.
- **Evidence:** `frontend/src/shared/api/resource-query.ts:69–82,104–127,144`; `src/shared/api/resource-query.test.tsx:40–71,139–173`; `src/shared/api/index.ts:12–15`; `frontend/package.json:2–5`; `src/features/tbank-connection/model/screen-sync.ts:28–50`. Укороченные `src/` пути относительны frontend.
- **Claim:** `observationKey` и возвращаемый `observe` не используются
production callers; они поддерживают отдельный transport режим только для
direct tests. Удаление frontend callback не означает удаления backend query.
- **Current mechanism / runtime consumer:** live `useResourceQuery` вызывается
entity hooks, но `observe()` — только tests `:63,167`. Callback вручную
переписывает queryKey для transport, cancel/query/select policy;
production screen timer вызывает обычный `refetch`.
- **Impact / actual cost:** дополнительный путь cancel/cache replacement и
его own tests без UI consumer; **Impact unmeasured**. Bundle/runtime эффект
не измерен, tree-shaking не проверялся.
- **Why now:** текущий runtime использует resource GET и `refetch`; источник
attempt state — metadata, а не orchestration notification.
- **Minimum change:** убрать `observationKey`, `observe`, его return member
и exclusive imports (`QueryFunctionContext`, локальный import session snapshot).
Удалить первый observe-only test; race-сценарий `:139–173` перенести на живой
cancellation/refetch boundary, если он нужен для общей гарантии.
- **Guarantees at risk:** не менять `useAuthenticatedQuery`,
`invalidateResourceAccess`, projected data/meta, canonical query identity,
session switching и обычный `refetch` envelope.
- **Counterarguments / trade-offs:** `.ai/conventions/data.md:61–63` всё ещё
описывает наблюдение, но правило не создаёт отсутствующий production caller.
SPA private, feature не public package; dynamic/router/story consumers
проверены. Backend `sync=observe` остаётся доступен внешнему HTTP клиенту;
наличие реального такого клиента здесь не подтверждено.
- **Verification or rollback:** существующие resource tests `:72–138,174–210`
и auth/connection boundary tests при cleanup; synthetic MSW, без реальной сети.
Rollback — вернуть callback/tests, никакой data migration.
- **Effort:** S.
- **Related finding IDs:** DEAD-005, DEAD-007, DEAD-011.
### DEAD-005 — Сократить unused facade `useScreenSync`, сохранив live timer
- **Decision:** SIMPLIFY.
- **Severity:** P3.
- **Confidence:** High.
- **Evidence:** `frontend/src/features/tbank-connection/model/screen-sync.ts:1–18,22–61`; `screen-sync.test.tsx:21–80` в той же directory; `src/pages/accounts/ui/AccountsPage/AccountsPage.vm.ts:21–24`; `src/pages/account/ui/AccountPage/AccountPage.vm.ts:78–84`; `src/pages/account-operations/ui/AccountOperationsPage/AccountOperationsPage.vm.ts:96–99`; `src/pages/account-events/ui/AccountEventsPage/AccountEventsPage.vm.ts:67–75`; `src/pages/account-income/ui/AccountIncomePage/AccountIncomePage.vm.ts:66–69`; `src/pages/bonds/ui/BondsPage/BondsPage.vm.ts:12–13`; stock/bond VM `:45–55`. Все `src/` пути относительны frontend; полные stock/bond пути перечислены в Ledger C06.
- **Claim:** `_scope`, returned manual refresh/retry/status facade и требуемые
только этой facade resource fields не имеют production consumers.
Timer `:28–35` имеет восемь route-reachable callers и не является dead code.
- **Current mechanism / runtime consumer:** все 8 VM вызывают hook как отдельное
expression statement. Возвращённые `refresh/refreshing/retry/sourceError/
transportError` не попадают в VM/view. Scope не читается даже самим hook.
Единственные вызовы returned commands — прямые tests `:50,78`.
- **Impact / actual cost:** caller собирает screen descriptor, hook создаёт
неиспользуемые commands/status, тип навязывает `queryKey/meta/isError/isFetching`.
**Impact unmeasured**; allocations/время render не измерены.
- **Why now:** hook уже стал периодическим GET reader, но сохранил facade
прежней screen orchestration.
- **Minimum change:** убрать scope аргумент и все восемь descriptor objects;
убрать only-test returned commands/status и exclusive fields/imports.
Оставить `resourcesRef`, актуализацию resources, native interval и cleanup.
Перепривязку timer к Query options этот finding не требует.
- **Guarantees at risk:** нельзя удалить сам hook вызов: он обеспечивает
reread открытого экрана. Нельзя убрать cleanup или обновление resourcesRef:
смена route/filter должна читать актуальные queries.
- **Counterarguments / trade-offs:** тесты refresh/retry действительно исполняют
callbacks, но не делают их доступными пользователю. UX потребность в retry
может потребовать нового подключения к view; сейчас такого consumer нет.
Таймерная политика живого пути — отдельный audit 02/07 вопрос.
- **Verification or rollback:** удалить direct facade-only cases,
проверить существующий hook с fake time: reread актуального resource и
прекращение после unmount. Page behavior и GET-only boundary сохранить.
Rollback — вернуть facade/caller descriptors без изменений данных.
- **Effort:** S.
- **Related finding IDs:** DEAD-004, DEAD-009.
### DEAD-006 — Статусный GET и старые `screen:` rows требуют compatibility proof
- **Decision:** INVESTIGATE.
- **Severity:** P2.
- **Confidence:** High.
- **Evidence:** `backend/src/modules/investments/tbank-sync/tbank-sync.controller.ts:21–41`; `tbank-sync.service.ts:313–391`; `tbank-sync.repository.ts:111–133`; `frontend/src/shared/api/spec/openapi.json:5102–5185`; `backend/test/openapi-contract.e2e-spec.ts:30–55`; `docs/architecture/http-api.md:46–52`. Укороченные sync пути относительны backend sync directory.
- **Claim:** GET `/api/v1/tbank-sync/{id}` локально не вызывается SPA, но
зарегистрирован, авторизован и опубликован. Нельзя назвать его confirmed dead
или удалить screen reader вместе с недоступным writer. Наличие реальных
внешних consumers — UNVERIFIED; High относится к reachability.
- **Current mechanism / runtime consumer:** любой авторизованный HTTP consumer
может вызвать controller → `status`. Reader обслуживает `screen:` intent и
source works; access/revision проверяется до и после observation.
Старые screen work могли сохраниться от предыдущей версии приложения.
- **Impact / actual cost:** retained compatibility несёт reader/types/schema
complexity; **Impact unmeasured**. Ошибочное удаление — breaking response/route.
- **Why now:** writer можно удалить отдельно, оставив минимальную проверенную
границу совместимости. Deadline поддержки в прочитанных источниках не найден.
- **Minimum change:** сейчас не удалять endpoint/reader/derived DTO. Получить
список поддерживаемых consumers и решить срок поддержки; затем отдельно
согласовать withdrawal route либо поддержку только source status.
- **Guarantees at risk:** user/connection ownership, old work observation,
stable scope/date discriminator и публичная wire compatibility.
- **Counterarguments / trade-offs:** docs говорит, что command/status не текущая
AS-IS модель; это подтверждает legacy intent, но не отсутствие live HTTP route
и external clients. Отсутствие frontend calls — слабое отрицательное evidence.
- **Verification or rollback:** самый дешёвый proof — ответ owner API о
consumers плюс существующая обезличенная access-log выборка и aggregate count
`screen:` work из disposable DB copy. Ничего из этого здесь не получено.
Rollback будущего изменения — возврат endpoint/schema до destructive migration.
- **Effort:** M.
- **Related finding IDs:** DEAD-001, DEAD-003, DEAD-007.
### DEAD-007 — Backend observe/persisted reference compatibility внешне достижима
- **Decision:** INVESTIGATE.
- **Severity:** P2.
- **Confidence:** High.
- **Evidence:** `backend/src/modules/investments/instruments/stocks/presentation/stock.controller.ts:58–82,112–144`; `instruments/bonds/presentation/bond.controller.ts:55–89,102–114`; `instruments/application/catalog-discovery.service.ts:84–141,168–217,251–293`; `backend/src/modules/investments/investment-resource.dto.ts:15–24`; `docs/architecture/http-api.md:50–52`; `docs/adr/019-hourly-reads-and-online-instrument-reference.md:22–26`. Укороченные instruments пути относительны `backend/src/modules/investments/`.
- **Claim:** persisted catalog/detail/ticker read branches являются legacy
public contract, а не confirmed dead. Из DELETE old producer не следует
DELETE observe readers или shared instrument storage. Нужность долгой
поддержки — UNVERIFIED; High относится к подтверждённому механизму.
- **Current mechanism / runtime consumer:** query DTO допускает `sync=observe`;
controllers переводят его в boolean, передают catalog services, которые
достигают reader branches. Это реальный внешний вход даже без SPA callback.
- **Impact / actual cost:** две модели справочного чтения рядом — online и
persisted compatibility; **Impact unmeasured**. API withdrawal требует
consumer transition, а blanket data purge рискует candle coverage.
- **Why now:** online-only intended behavior уже принят, но compatibility
сохранена source/HTTP metadata. Нужно ограничить именно поддержку старого
reader, а не удалять shared business/persistence primitives по названию.
- **Minimum change:** сохранить текущий wire parameter/reader; отдельно решить
endpoint-specific retirement. При подтверждённой ненужности можно перестать
направлять reference reads в old branches, затем удалить их closure.
Не удалять общую `InvestmentReadQueryDto` для всех ресурсов автоматически.
- **Guarantees at risk:** passive observation, distinction unknown/empty/
ambiguous lookup, account access, precision, persisted calendars и candles.
- **Counterarguments / trade-offs:** requirement запрещает долговременную
самостоятельную reference copy, но не доказывает безопасность удаления
`TbankInstrument` identity: у неё cascade FK на candles/source rows
(`backend/prisma/schema.prisma:148–229`). Внешние consumers не проверены.
- **Verification or rollback:** consumer list + metadata/access logs без
реальных API calls; agreed contract transition и isolated DB/FK proof перед
любым data removal. Rollback code routing возможен, восстановление данных
потребует backup; поэтому data purge сейчас не предложен.
- **Effort:** M.
- **Related finding IDs:** DEAD-002, DEAD-004, DEAD-006, DEAD-010, DEAD-011.
### DEAD-008 — Planner и interval helpers живы через Account Ledger
- **Decision:** KEEP.
- **Severity:** P2.
- **Confidence:** High.
- **Evidence:** `backend/src/modules/investments/investment-sync-plan.ts:67–140,271–328`; `operations/application/brokerage-operations.service.ts:16–19,69–85,120–150,203–209`; `operations/application/brokerage-operations-sync.service.ts:9,106–112`; `investment-sync-plan.spec.ts:100–134`. Укороченные пути относительны `backend/src/modules/investments/`.
- **Claim:** нельзя удалить `planInvestmentScreen`, `accountHistory`,
`ledgerRange` или их файл вслед за manual sync: они обслуживают живой Ledger.
- **Current mechanism / runtime consumer:** operations controller → read →
planner для `screen='operations'`, membership gating и selected interval →
`sync.readPlan`. History/read metadata также вызывает accountHistory;
interval helpers используются независимо от dead request.
- **Impact / actual cost:** broad screen vocabulary содержит прежние ветки,
но ядро живо; **Impact unmeasured**. Whole-file deletion ломает получение
первого отсутствующего интервала, границы времени и history state.
- **Why now:** это конкретный counterexample прежней гипотезе «screen planner
принадлежит только manual orchestration».
- **Minimum change:** сохранить live planner/helpers. После DEAD-001 допустим
отдельный узкий reachability pass по вариантам screen, если это стоит усилий;
автоматическое удаление всех planner tests недопустимо.
- **Guarantees at risk:** account scope, membershipKnown gating, inclusive/
exclusive end, historical boundaries, нерасширение произвольного интервала.
- **Counterarguments / trade-offs:** немало branches после removal manual writer
станет only-test reachable. Это повод сузить конкретные варианты с equivalence
proof, а не удалить финансовые interval rules. В 04 это не generic refactor.
- **Verification or rollback:** current ledger HTTP/local-read/interval tests
при любом будущем narrowing; сравнить selected intervals, unknown membership,
all/account и date-only/instant boundaries. Сейчас deletion отсутствует.
- **Effort:** S.
Здесь и в других KEEP effort относится к сохранению deletion boundary;
narrowing/migration оцениваются отдельно.
- **Related finding IDs:** DEAD-001, DEAD-009.
### DEAD-009 — Hourly scheduler является runtime owner, а не legacy provider
- **Decision:** KEEP.
- **Severity:** P2.
- **Confidence:** High.
- **Evidence:** `backend/src/app.module.ts:14,35`; `src/modules/investments/tbank-sync/tbank-sync.module.ts:24–25`; `tbank-hourly.scheduler.ts:28–87,119–175`; `tbank-sync.repository.ts:174–243,245–286`; `compose.yaml:32–35`; `specs/openspec/specs/hourly-investment-sync/spec.md:8–40`. Укороченные sync пути относительны backend sync directory.
- **Claim:** отсутствие HTTP callers `tick` или прямых тестов не делает
scheduler dead; Nest lifecycle запускает его native timer.
- **Current mechanism / runtime consumer:** `onModuleInit → setInterval(tick)`
читает connections, резервирует work и через setImmediate запускает owners
membership/portfolio/operations/events; `onModuleDestroy` очищает timer.
Метаданные ресурсов читают repository hourly result.
- **Impact / actual cost:** runtime coordination необходима для обновления
данных при закрытой SPA; **Impact unmeasured**. Удаление отключило бы
normal personal refresh. Корректность lifecycle отдельно не сертифицируется.
- **Why now:** whole-folder cleanup `tbank-sync/` выглядел бы коротким,
но смешивает dead manual writer с current root entrypoint.
- **Minimum change:** сохранить scheduler, registration, repository hourly
методы и DTO; убирать только manual command closure DEAD-001.
- **Guarantees at risk:** запуск без экрана, single-attempt fence, generation,
остановка timer, section result visibility и отсутствие catch-up очереди.
- **Counterarguments / trade-offs:** наличие lifecycle hook не доказывает
правильность всех failure branches. Подозрения на их correctness направлены
владельцу audit 13/08, а не использованы для рекомендации DELETE.
- **Verification or rollback:** перед cleanup DI/module proof и существующий
source-owner behavior; scheduler error/time scenarios — отдельные проверки
при изменении его поведения. Сейчас механизм сохраняется.
- **Effort:** S.
- **Related finding IDs:** DEAD-001, DEAD-005, DEAD-008, DEAD-011.
### DEAD-010 — Candle loader и staging не удаляются вместе с manual force caller
- **Decision:** KEEP.
- **Severity:** P2.
- **Confidence:** High.
- **Evidence:** `backend/src/modules/investments/instruments/application/candle.service.ts:101–124,206–261,264–326`; `instruments/stocks/application/stock-market.service.ts:54–64`; `instruments/bonds/application/bond-market.service.ts:58–68`; `instruments/infrastructure/candle.repository.ts:137–228,257–284`; `backend/test/tbank-candles-persistence.e2e-spec.ts:117–200,486–501`; `specs/openspec/specs/tbank-data-sync/spec.md:16–22`. Укороченные instruments пути относительны `backend/src/modules/investments/`.
- **Claim:** `CandleService.request`, queue/stage/publication и coverage
runtime-required: read запускает первое получение отсутствующего диапазона.
- **Current mechanism / runtime consumer:** GET stock/bond candles → market
service → `history.read` → при отсутствии coverage `request → drain/load`.
Отдельно enrichment.ensureDetail использует shared sources. Manual force
— не единственный caller source owner.
- **Impact / actual cost:** механизм обеспечивает запрошенный диапазон,
точные decimal strings, empty coverage и transactional publication;
**Impact unmeasured**. Удаление owner сделает cold candles недоступными.
- **Why now:** соседство с manual request повышает риск удалить весь loader,
его source repository или associated tests без проверки hidden read caller.
- **Minimum change:** сохранять ordinary request/staging/coverage. Нужность
отдельно forced/priority вариантов после DEAD-001 **UNVERIFIED**: нужен
variant-level pass, в этом отчёте whole-loader deletion отвергнута.
- **Guarantees at risk:** точность, rollback неполного reconciliation,
absence ≠ empty, unchanged coverage вне окна и connection fencing.
- **Counterarguments / trade-offs:** часть direct force tests относится к old
command variants; это не делает всю suite или transactional primitive legacy.
Default method caller в `read:121–123` — положительное доказательство жизни.
- **Verification or rollback:** при любой variant deletion оставить ordinary
period, reopen, exact decimals, closed empty и failed publication cases;
isolated SQLite proof, не mock транзакции. Сейчас deletion не требуется.
- **Effort:** S.
- **Related finding IDs:** DEAD-001, DEAD-002, DEAD-007, DEAD-011.
### DEAD-011 — Shared source CAS, identity и FK защищают живые use cases
- **Decision:** KEEP.
- **Severity:** P1.
- **Confidence:** High.
- **Evidence:** `backend/src/modules/investments/instruments/application/catalog-enrichment.service.ts:64–87,164–225,545–579`; `instruments/infrastructure/catalog-discovery.repository.ts:154–179,184–249,280–339,466–487`; `backend/src/modules/investments/upcoming-events/upcoming-events.service.ts:182–239`; `backend/src/modules/investments/portfolio/application/portfolio.service.ts:264–283`; `backend/prisma/schema.prisma:148–229,232–262`. Укороченные instruments пути относительны `backend/src/modules/investments/`.
- **Claim:** общая `CatalogDiscoveryRepository` не является legacy repository
целиком. `reserve/finish/publishSource`, attempt/initiator fences и instrument
identities остаются runtime-required после removal discovery producer.
- **Current mechanism / runtime consumer:** hourly `events.refresh → readSources`
запускает enrichment source work; candles.ensureDetail делает то же для
dependency. Enrichment.reserve/drain обращается в shared repository, где
transaction проверяет origin и observer attempt до публикации public payload.
Live portfolio owner также вызывает metadata.request для своих positions.
- **Impact / actual cost:** защита от late/superseded/invalid-token publication
и сохранение data relationships; **Impact unmeasured**. Удаление ради меньшего
числа проверок может изменить чужую source revision/timestamp; удаление
instrument identity каскадирует source/candle/coverage rows.
- **Why now:** это важная граница closure DEAD-002: method `publish` exclusive
старому discovery producer, method `publishSource` — живому enrichment.
- **Minimum change:** сохранять shared owner, registration, transaction/CAS,
identity и schema. Code-only deletion может затронуть только exclusive
discovery writer `publish`, а не весь repository или таблицы.
- **Guarantees at risk:** connection isolation, conditional publication,
invalid-token boundary, identity reuse, candle data и PII-safe projection.
- **Counterarguments / trade-offs:** online-only reference requirement не
делает DB identity disposable. Название repository и один прежний caller
не определяют его реальную область ответственности. Data migration, если
потребуется, должна быть совместимой и отдельно доказанной.
- **Verification or rollback:** targeted source/candle persistence и late
connection-switch proof при cleanup; перед destructive migration —
disposable backup/restore и FK проверка. В этом аудите DB не открывалась.
- **Effort:** S.
- **Related finding IDs:** DEAD-001, DEAD-002, DEAD-004, DEAD-007, DEAD-009, DEAD-010.
## 5. DELETE / SIMPLIFY / KEEP / INVESTIGATE
| Decision | IDs | Подтверждённая граница |
| --- | --- | --- |
| DELETE | DEAD-001 | Только manual writer/reservation/progress/normalization closure; status и live owners сохраняются. |
| DELETE | DEAD-002 | Только persisted discovery producer и exclusive repository publish; shared source repository и observe reader сохраняются. |
| DELETE | DEAD-003 | Только Accepted DTO; status screen schema сохраняется. |
| DELETE | DEAD-004 | Только frontend observe callback/transport key rewrite; auth/access/refetch и backend API сохраняются. |
| SIMPLIFY | DEAD-005 | Scope/returned command facade убрать; timer/current resources/cleanup сохранить. |
| KEEP | DEAD-008 | Live Ledger planner, history и interval boundaries. |
| KEEP | DEAD-009 | Registered hourly scheduler, durable work и lifecycle. |
| KEEP | DEAD-010 | Candle first-range load, staging, rollback и coverage. |
| KEEP | DEAD-011 | Shared enrichment repository, CAS, identity/FK и metadata source owner. |
| INVESTIGATE | DEAD-006 | Public status GET/old screen rows: consumers/retirement неизвестны. |
| INVESTIGATE | DEAD-007 | Public backend observe/persisted reference readers: transition и безопасная data граница неизвестны. |
FIX не назначен: correctness живых путей не превращалась в параллельный аудит.
### 5.1. Special result — Reachability Ledger
`confirmed dead` означает отсутствие функционального production consumer в
проверенном source graph; import metadata registration при этом может исполняться.
`only-test reachable` означает реальный тестовый вызов при отсутствии production
entrypoint. External-use неизвестность всегда указана отдельно.
| Candidate | Reachability evidence: declaration → exports/registration → callers → runtime entrypoints → external accessibility | Dynamic/external risks | Affected tests/docs/config | Minimum safe deletion | Rollback | Confidence |
| --- | --- | --- | --- | --- | --- | --- |
| C01 — `TbankSyncService.request/dispatch`; **confirmed dead**; DEAD-001 | Declaration `backend/src/modules/investments/tbank-sync/tbank-sync.service.ts:54–78`; DI `tbank-sync.module.ts:24`, только class construction → controller calls `status:40`, command callers 0 → no method decorator/timer → no command route, только GET. Все короткие пути в backend sync directory. | DI не auto-dispatch-ит methods; module exports только scheduler. Alternate computed/reflection/script invocation не найден. | Normalize tests C02; controller/status DTO остаются; intended boundary `specs/openspec/specs/tbank-data-sync/spec.md:9–25`. | Writer closure, не service целиком. | Code-only revert. | High |
| C02 — `normalizeScreen`, repo screen writers; **only-test reachable**; DEAD-001 | `backend/src/modules/investments/tbank-sync/tbank-screen.ts:9–11` export → import в dead service `:28,55` → 9 test call lines → no runtime entrypoint → не public HTTP validator текущего GET. Repo `reserve:62–109/progress:135–169` callers только request/dispatch. | `InvestmentScreenDto` жив через Swagger/status; нельзя удалить его по association. Helpers не имеют top-level work/timer. | `tbank-screen.spec.ts:1–75` только manual descriptor behavior; status schema tests сохранить. | Helper/test file и exclusive repo methods удалить после C01. | Вернуть closure. | High |
| C03 — `CatalogDiscoveryService.request/refresh/drain`; **only-test reachable + dead-parent reachable**; DEAD-002 | `backend/src/modules/investments/instruments/application/catalog-discovery.service.ts:340–496` → provider/API registration `instrument-sources.module.ts:18–21`, `instruments.api.ts:2` → public methods search/detail/byTicker, но online return или observe=true reader → HTTP не попадает в producer; dead manual refresh `tbank-sync.service.ts:197`. | Живые exported provider/HTTP reader не равны live producer. No lifecycle writer hook; setImmediate только внутри недоступного request. | Direct service cases `backend/test/tbank-catalog-discovery.e2e-spec.ts:85–322`; update factory/selection checks, reader compatibility. | Producer state/methods + exclusive repo `publish`, оставить shared `reserve/finish/publishSource`. | Code-only revert, rows/schema untouched. | High |
| C04 — `TbankSyncAcceptedDto`; **confirmed dead export**; DEAD-003 | `backend/src/modules/investments/tbank-sync/tbank-sync.dto.ts:212–218` → export with decorators → 0 consumers/ApiExtraModels → no POST response metadata → отсутствует в обоих известных OpenAPI artifacts. | Class decorators исполняются на import, но не создают route. Shared `investmentScreenSchema` используется Status. Fresh parity не запускалась. | `backend/test/openapi-contract.e2e-spec.ts:30–55`; no Accepted-only test/config consumer. | Только orphan class. | Вернуть class. | High |
| C05 — frontend `observe/observationKey`; **only-test reachable**; DEAD-004 | `frontend/src/shared/api/resource-query.ts:71,104,144` → hook public index `src/shared/api/index.ts:12–15` → tests `resource-query.test.tsx:63,167` → production pages используют refetch → private SPA, не public package export. | Dynamic router imports и stories не используют callback; backend wire mode остаётся reachable C09. | Два observe scenarios; сохранить successful-null/refetch/access tests. Convention data `:61–63` — intended old flow, не runtime. | Callback/helper/exclusive imports; safe race proof перевести на живой caller при необходимости. | Вернуть code/tests. | High |
| C06 — `_scope` и returned screen facade; **live timer / only-test commands**; DEAD-005 | `frontend/src/features/tbank-connection/model/screen-sync.ts:15–22,37–61` → feature `index.ts:1` → 8 VM call sites → все calls expression statements в lazy routed pages → callback exposure вне SPA не найден. | Удаление всего hook опасно: `:28–35` timer жив. Typed resource fields не означают usage после facade removal. | `screen-sync.test.tsx:22–80` direct commands; all page callers. | Только scope/unused commands/status/fields; live effect сохранить. | Вернуть facade/call signatures. | High |
| C07 — `planInvestmentScreen/accountHistory/ledgerRange`; **runtime required**; DEAD-008 | `backend/src/modules/investments/investment-sync-plan.ts:67,287,301` exports → operations source imports → `brokerage-operations.service.ts:80,120,203`, sync service `:106` → Account Ledger HTTP → real public read capability. | Частично legacy vocabulary не делает файл orphan. No DI требуется чистым exported functions. | `investment-sync-plan.spec.ts:100–134`, interval/ledger cases; historical screen cases отдельно проверить после C01. | Whole-file deletion запрещён; variant narrowing требует proof. | Not applicable: KEEP. | High |
| C08 — status GET и screen reader; **externally reachable/unknown + legacy documented contract**; DEAD-006 | `backend/src/modules/investments/tbank-sync/tbank-sync.service.ts:313–391` → `@Get(':id')` → controller `:40` → guarded HTTP + snapshot `frontend/src/shared/api/spec/openapi.json:5102–5185` → любой authorized consumer с work ID. | Local SPA calls 0; external consumers/old rows UNVERIFIED. Reflection не нужна: route явный. | Status DTO, screen discriminator, repository read/progress types, contract tests, old DB work. | Пока deletion запрещён; отдельное compatibility решение. | Вернуть route/schema; data не удалять. | High reachability |
| C09 — catalog `observe` reader branches; **externally reachable + legacy documented contract**; DEAD-007 | `backend/src/modules/investments/instruments/application/catalog-discovery.service.ts:84–141,168–217,251–293` → stock/bond catalog services → controller query.sync → runtime HTTP `?sync=observe` → snapshot query enum и docs compatibility. | External use/retirement UNVERIFIED. Pub DTO decorators и router сохраняют вход без frontend observe. | Legacy reference schemas/read tests, shared DTO, persisted data; online requirement `online-instrument-information/spec.md:8–17`. | Сначала consumer transition; не удалить общую storage identity/все query modes. | Code routing revert; data restore потребует backup. | High reachability |
| C10 — hourly scheduler; **runtime required**; DEAD-009 | `backend/src/modules/investments/tbank-sync/tbank-hourly.scheduler.ts:33–56` → providers/root import → Nest `onModuleInit` → interval.tick + setImmediate.walk → personal source owners, а не внешняя command API. | Lifecycle/reflection registration — positive entrypoint. Env feature switch не найден; single-process deployment `compose.yaml:34`. | Hourly resource DTO/repo + mapper/source tests, hourly spec; scheduler stubs не доказательство смерти. | KEEP provider/root import/durable hourly rows. | Not applicable: KEEP. | High |
| C11 — candle request/staging; **runtime required**; DEAD-010 | `backend/src/modules/investments/instruments/application/candle.service.ts:101–123,206–261` → source module exports → stock/bond market.history.read → controller candle GET → first missing range source work. | Dead manual force caller не отменяет ordinary read caller. Variant-level force нужность UNVERIFIED. | `backend/test/tbank-candles-persistence.e2e-spec.ts:117–200,486–501`; candle coverage/decimal tests; shared source/FK. | KEEP loader/repository; force variants не удалять без отдельного trace. | Not applicable: KEEP. | High |
| C12 — shared source repository/CAS/identity; **runtime required**; DEAD-011 | `backend/src/modules/investments/instruments/infrastructure/catalog-discovery.repository.ts:154–179,280–339,466–487` → DI `instrument-sources.module.ts:19` → enrichment request/publishSource/finish → hourly events and candle ensureDetail → resource HTTP consumes snapshots. | Имя `CatalogDiscovery` вводит в заблуждение; FK Cascade на instrument → candles/coverage `backend/prisma/schema.prisma:199,213,227`. Source origin/observer fence — live invariant. | Source/candle persistence, calendar consumers, Prisma schema; data purge не выполнялся. | KEEP shared repo/CAS/identity; exclusive discovery publish отделим C03. | Code rollback; destructive data rollback только backup. | High |
**Полные production call sites C06:**
| Page | Exact path:line-range |
| --- | --- |
| Accounts | `frontend/src/pages/accounts/ui/AccountsPage/AccountsPage.vm.ts:21–24` |
| Account | `frontend/src/pages/account/ui/AccountPage/AccountPage.vm.ts:78–84` |
| Operations | `frontend/src/pages/account-operations/ui/AccountOperationsPage/AccountOperationsPage.vm.ts:96–99` |
| Events | `frontend/src/pages/account-events/ui/AccountEventsPage/AccountEventsPage.vm.ts:67–75` |
| Income | `frontend/src/pages/account-income/ui/AccountIncomePage/AccountIncomePage.vm.ts:66–69` |
| Bonds | `frontend/src/pages/bonds/ui/BondsPage/BondsPage.vm.ts:12–13` |
| Stock | `frontend/src/pages/stock/ui/StockPage/StockPage.vm.ts:45–55` |
| Bond | `frontend/src/pages/bond/ui/BondPage/BondPage.vm.ts:45–55` |
### 5.2. Special result — Deletion Closure Plan
План предназначен для последующей отдельной реализации. В рамках аудита
ни один пункт не применён. Это method-level closure, а не whole-folder purge.
| Candidate | Reachability evidence | Dynamic/external risks | Affected tests/docs/config | Minimum safe deletion | Rollback | Confidence |
| --- | --- | --- | --- | --- | --- | --- |
| C01/C02 / DEAD-001 | No request root; normalize/repo writers only under dead writer, Ledger planner live | Status route и old screen rows отдельно C08; service/controller DI сохраняются | `tbank-screen.ts`, `tbank-screen.spec.ts`; service imports/injections/fields; repo reserve/progress; `tbank-sync.module.ts` reviewed, сохраняет hourly. Existing docs hourly model уже не требует command; archived ADR/spec не менять. | Удалить request/dispatch и reservation field; helper/test files; repo screen reserve/progress; exclusive instrument/candle/metadata/portfolio/membership DI service dependencies. Оставить repository/access/operations, runId/startedAt/status и screen intent/progress types reader. | Revert только code/test deletion. | High |
| C03 / DEAD-002 | HTTP ordinary online; observe reader read(true); refresh only dead C01 | Source repository жив и backend observe externally reachable | Service imports `randomUUID`, logger, attempt type, missing-revision constant/state; constructor factory `backend/test/tbank-catalog-discovery.e2e-spec.ts:28–35`; producer-only cases; source module remains. Online selection/whitelist cases сохранить/перепривязать. Docs online intended model не требует producer; archives retain. | Удалить Producer/state/request/refresh/drain и request branch read; удалить только repository `publish:341–464`. Убрать exclusive logger constructor/import. Не удалять `CatalogDiscoveryService` provider целиком, shared repository registration, `reserve/finish/publishSource/readSources/workMany`, catalog selection, online client или schema. | Code/test revert; сохранённые reference rows остаются readable. | High |
| C04 / DEAD-003 | Declaration only, not in response/ApiExtraModels/artifacts | Status shared screen schema остаётся published | DTO class only; contract test existing. No script/config exclusive to Accepted найден. | Удалить `TbankSyncAcceptedDto:212–218`; сохранить соседние live classes/imports, которыми они пользуются. | Вернуть class. | High |
| C05 / DEAD-004 | 2 direct tests, no production observe symbol consumer | Auth/access effects и server query mode сохраняются | Helper/callback/imports/return member; tests `:40–71,139–173`; frontend data convention old observe paragraph requires owner follow-up, not archive deletion. Feature index `useResourceQuery` остаётся. | Удалить only-test observe path; не удалять resource hook или session snapshot implementation, используемую authenticated-query. Guard/cancellation race проверять через оставшийся live boundary. | Вернуть path/tests. | High |
| C06 / DEAD-005 | 8 callers ignore return, scope unused | Live timer — dangerous to remove automatically | 8 VM descriptor arguments; screen hook resource type; direct refresh/retry tests; mocked facade `frontend/src/pages/account-events/ui/AccountEventsPage/AccountEventsPage.test.tsx:15` пересмотреть по новым exports. Feature barrel остаётся. | Убрать scope/facade, narrowing inputs до использованных timer полей; оставить useEffect/ref/interval cleanup. Rename/move/new helper не требуются. | Вернуть signature/facade/caller descriptors. | High |
| C08/C09 / DEAD-006/007 | Explicit HTTP GET/query registration | **ОПАСНА автоматическая зачистка:** external compatibility и existing rows UNVERIFIED | Contract/source DTO/read tests и published frontend artifact; old rows и FK; potential API retirement notice | Не удалять до согласованного support/consumer решения. Получить evidence, затем отдельная route/reader closure. Destructive schema/data removal — отдельный совместимый шаг. | Restore routing/schema, не полагаться на revert для удалённых данных. | High reachability; deletion unverified |
| C07/C10/C11/C12 / DEAD-008–011 | Positive Ledger/lifecycle/candle/enrichment call traces | **ОПАСНА автоматическая зачистка:** live behavior, generation/CAS, rollback и cascade data loss | Planner/interval, hourly, source/candle/metadata tests; schema; module registration | KEEP; никакой whole-file/whole-folder/schema deletion по совпадению legacy vocabulary. | Не требуется без изменения; data migration отдельно. | High |
**Важная closure-граница:** убрать injection из TbankSyncService не означает
убрать соответствующий provider из TbankModule/PortfolioModule/InstrumentSourcesModule.
Наличие status branch и shared repository читается независимо от отсутствия writer.
Документальные references закрываются корректировкой текущего описания только
при реальном изменении поведения; история ADR/OpenSpec не стирается.
## 6. Cost & benefit
| IDs | Ожидаемый класс экономии | Фактическое измерение | Обязательные регрессии / безопасная граница |
| --- | --- | --- | --- |
| DEAD-001/002 | Maintenance, cognitive load, navigation/agent context; меньше old producer tests | not measured; нет готового diff, baseline CI timings или token профиля | Не менять public status/observe, source owners, CAS, hourly/candle/ledger; DI compilation + applicable isolated tests при cleanup. Runtime запросы уже недоступных paths не объявлены экономией. |
| DEAD-003 | Малое сокращение obsolete API surface | not measured | Status OpenAPI schema unchanged; no whole DTO/schema purge. |
| DEAD-004/005 | Maintenance, test surface, caller descriptors и unused facade | not measured | Session/access invalidation/refetch/cached envelope; periodic current-resource read и cleanup. Bundle/render выгода не заявлена. |
| DEAD-006/007 | Возможная будущая экономия compatibility maintenance | UNVERIFIED / not measured | Нужны consumer/retirement proof; shared identity/FK сохраняются. |
| DEAD-008–011 | Предотвращение ошибочного cleanup и регрессий | not measured | Сохранить финансовые interval/decimal/transaction/generation/lifecycle invariants. |
**Ponytail-audit shortlist, от крупной closure к малой:**
- `delete:` manual screen writer/reservation/normalization. Замена: ничего;
hourly source owners уже обслуживают текущий сценарий.
[`backend/src/modules/investments/tbank-sync/tbank-sync.service.ts:54–311`]
- `delete:` persisted discovery producer и exclusive publish. Замена: существующие
online paths; compatibility reader сохранить.
[`backend/src/modules/investments/instruments/application/catalog-discovery.service.ts:340–496`]
- `delete:` frontend only-test observe callback/key rewrite. Замена: существующий
ordinary refetch.
[`frontend/src/shared/api/resource-query.ts:69–127`]
- `shrink:` screen scope/returned facade. Замена: оставить только используемый
periodic GET effect.
[`frontend/src/features/tbank-connection/model/screen-sync.ts:15–61`]
- `delete:` orphan Accepted DTO. Замена: ничего.
[`backend/src/modules/investments/tbank-sync/tbank-sync.dto.ts:212–218`]
**net:** сокращение строк **not measured**, dependencies **not assessed**
(направление 12). Оценка прежнего аудита `~550` не повторяется; ни процент,
ни скорость, ни CI minutes из inspection не выводятся.
## 7. Top actions
Максимум подтверждённых code cleanup actions — пять, а не искусственно десять.
Последовательность учитывает dependency closures и цену ошибки.
### Quick wins
1. **DEAD-003:** удалить одну orphan class; проверить Status schema.
2. **DEAD-004:** убрать only-test frontend observe path с сохранением живых
resource/auth guarantees и meaningful race boundary.
3. **DEAD-005:** удалить unused scope и facade, не удаляя periodic effect;
обновить 8 callers и проверить timer current-resource/unmount.
### Более крупные closures с тестовым подтверждением
4. **DEAD-001:** удалить manual writer плюс normalization/repo writer closure;
оставить status compatibility и live DI/source owners.
5. **DEAD-002:** после DEAD-001 удалить discovery producer и только exclusive
repository publish; перепривязать нужные online/shared-source assertions.
**Решения, требующие compatibility/ADR или test proof:** DEAD-006/007 не являются
подтверждёнными шагами DELETE. API owner должен решить external support до
withdrawal route/query semantics. Отдельное ADR оправдано, только если меняется
сохранённый public contract/support policy; внутренние dead methods нового ADR
сами по себе не требуют. Любая data migration отдельно доказывает FK/restore.
## 8. Cross-audit signals
| Тема | Evidence | Рекомендуемый audit ID |
| --- | --- | --- |
| Timer/refetch policy и disabled queries — живой путь, не DELETE | `frontend/src/features/tbank-connection/model/screen-sync.ts:28–31`; `src/pages/bonds/ui/BondsPage/BondsPage.vm.ts:10–13` | 02 / 07 |
| Old observe convention при новых consumers и documented legacy support | `frontend/.ai/conventions/data.md:61–63`; `docs/architecture/http-api.md:50–52` | 15 |
| Income read сохранил request-driven membership branch; это достижимое поведение | `backend/src/modules/investments/operations/application/brokerage-operations.service.ts:408–435` | 07 / 13 |
| Проверка scheduler failure/time lifecycle нужна независимо от cleanup | `backend/src/modules/investments/tbank-sync/tbank-hourly.scheduler.ts:93–110,178–218` | 08 / 13 |
| Producer-only tests должны быть пересмотрены по текущему entrypoint | `backend/test/tbank-catalog-discovery.e2e-spec.ts:85–118,179–196`; `frontend/src/shared/api/resource-query.test.tsx:139–173` | 08 |
Это сигналы владельцам, не отдельные findings этих направлений. Дублирование
живых mappers, dependencies, generic VM complexity и архивные docs не аудировались.
## 9. Open questions / verification plan
| Вопрос, меняющий решение | Текущее состояние | Самый дешёвый proof |
| --- | --- | --- |
| Есть ли поддерживаемый consumer GET `/tbank-sync/{id}`? | Local SPA consumers 0; actual external use UNVERIFIED. | Owner API/deployment отвечает о clients; существующая обезличенная access-log выборка по route, без запросов к real API. |
| Нужны ли старые `screen:` work после перехода? | Writer недостижим, reader публичен; сохранённые DB rows не проверялись. | Aggregate count/age/scope на disposable копии SQLite + agreed support horizon. Не печатать intent/token/account payloads. |
| Кто пользуется `sync=observe` именно у справочных routes? | Source/DTO публикуют режим; потребитель может быть вне SPA. | Endpoint-specific consumer list/log sample и контрактное решение; не выводить removal всех modes из удаления frontend callback. |
| Можно ли удалить persisted reference storage целиком? | Нет: есть positive live source/metadata reads и cascade candle FK. | Отдельная source/FK карта нужных identities и backup/restore proof; сначала code-only producer cleanup. |
| Какие variants planner/candle force останутся после manual deletion? | Owners живы; unused variant removal отдельно UNVERIFIED. | После узкого diff пересчитать callers конкретных variants; current Ledger/candle behavior comparison, не whole-file purge. |
| Пройдут ли cleanup DI/schema/regressions? | Изменений приложения нет, tests/build не запускались. | В отдельной implementation task сначала targeted owner/HTTP/isolated persistence checks, затем локальные обязательные gates по affected repos. |
### 9.1. Проверка полноты и сохранности результата
- Проверены 9 общих разделов, полная finding schema для всех 11 IDs,
12 candidate chains, explicit DI/dynamic/public/external checks,
Reachability Ledger и Deletion Closure Plan.
- Финальная consolidation table содержит по одной строке на finding;
C01/C02 не удваивают actionable count. KEEP/INVESTIGATE не смешаны с DELETE.
- Source pointers просмотрены по локальным файлам и строкам; отрицательный
поиск дополнен положительными traces/counterexamples, ограничения сохранены.
- После записи отчёта повторно проверены SHA/status всех пяти checkout и
`git diff --check`; source/config четырёх репозиториев и root tracking чисты.
Отчёт проверен отдельно, потому что root `.gitignore` его игнорирует.
- Semantic/Boy Scout review ограничен самим report: не добавлены новые helpers,
scripts, обязательные workflows или неподтверждённые измерения; source edits
отсутствуют. Existing dated audits, archives и references не менялись.
## Final machine-readable consolidation table
| ID | Decision | Severity | Confidence | Repo | Files | One-line impact | Effort | Related IDs |
| --- | --- | --- | --- | --- | --- | --- | --- | --- |
| DEAD-001 | DELETE | P2 | High | backend | src/modules/investments/tbank-sync/tbank-sync.service.ts; src/modules/investments/tbank-sync/tbank-screen.ts; src/modules/investments/tbank-sync/tbank-screen.spec.ts; src/modules/investments/tbank-sync/tbank-sync.repository.ts | Недоступная manual orchestration и exclusive writer/test closure | M | DEAD-002,DEAD-006,DEAD-008,DEAD-009,DEAD-011 |
| DEAD-002 | DELETE | P2 | High | backend | src/modules/investments/instruments/application/catalog-discovery.service.ts; src/modules/investments/instruments/infrastructure/catalog-discovery.repository.ts; test/tbank-catalog-discovery.e2e-spec.ts | Producer старого persisted discovery недоступен production; shared readers/writers живы | M | DEAD-001,DEAD-007,DEAD-010,DEAD-011 |
| DEAD-003 | DELETE | P3 | High | backend | src/modules/investments/tbank-sync/tbank-sync.dto.ts | Orphan Accepted DTO без wire consumer | S | DEAD-001,DEAD-006 |
| DEAD-004 | DELETE | P3 | High | frontend | src/shared/api/resource-query.ts; src/shared/api/resource-query.test.tsx | Observe callback и key rewrite используются только direct tests | S | DEAD-005,DEAD-007,DEAD-011 |
| DEAD-005 | SIMPLIFY | P3 | High | frontend | src/features/tbank-connection/model/screen-sync.ts; src/features/tbank-connection/model/screen-sync.test.tsx; src/pages/accounts/ui/AccountsPage/AccountsPage.vm.ts; src/pages/account/ui/AccountPage/AccountPage.vm.ts; src/pages/account-operations/ui/AccountOperationsPage/AccountOperationsPage.vm.ts; src/pages/account-events/ui/AccountEventsPage/AccountEventsPage.vm.ts; src/pages/account-income/ui/AccountIncomePage/AccountIncomePage.vm.ts; src/pages/bonds/ui/BondsPage/BondsPage.vm.ts; src/pages/stock/ui/StockPage/StockPage.vm.ts; src/pages/bond/ui/BondPage/BondPage.vm.ts | Scope/returned facade unused; live periodic GET effect сохранить | S | DEAD-004,DEAD-009 |
| DEAD-006 | INVESTIGATE | P2 | High | backend | src/modules/investments/tbank-sync/tbank-sync.controller.ts; src/modules/investments/tbank-sync/tbank-sync.service.ts; src/modules/investments/tbank-sync/tbank-sync.dto.ts | Public status GET и old screen rows требуют external compatibility proof | M | DEAD-001,DEAD-003,DEAD-007 |
| DEAD-007 | INVESTIGATE | P2 | High | backend | src/modules/investments/instruments/application/catalog-discovery.service.ts; src/modules/investments/investment-resource.dto.ts; src/modules/investments/instruments/stocks/presentation/stock.controller.ts; src/modules/investments/instruments/bonds/presentation/bond.controller.ts | Backend observe readers externally reachable; retirement UNVERIFIED | M | DEAD-002,DEAD-004,DEAD-006,DEAD-010,DEAD-011 |
| DEAD-008 | KEEP | P2 | High | backend | src/modules/investments/investment-sync-plan.ts; src/modules/investments/operations/application/brokerage-operations.service.ts; src/modules/investments/operations/application/brokerage-operations-sync.service.ts | Ledger использует planner/history/interval helpers | S | DEAD-001,DEAD-009 |
| DEAD-009 | KEEP | P2 | High | backend | src/modules/investments/tbank-sync/tbank-hourly.scheduler.ts; src/modules/investments/tbank-sync/tbank-sync.module.ts; src/modules/investments/tbank-sync/tbank-sync.repository.ts | Nest lifecycle запускает живой hourly owner без frontend | S | DEAD-001,DEAD-005,DEAD-008,DEAD-011 |
| DEAD-010 | KEEP | P2 | High | backend | src/modules/investments/instruments/application/candle.service.ts; src/modules/investments/instruments/infrastructure/candle.repository.ts | Ordinary candle read требует first-range loader/staging/coverage | S | DEAD-001,DEAD-002,DEAD-007,DEAD-011 |
| DEAD-011 | KEEP | P1 | High | backend | src/modules/investments/instruments/infrastructure/catalog-discovery.repository.ts; src/modules/investments/instruments/application/catalog-enrichment.service.ts; prisma/schema.prisma | Shared source CAS/identity live; blanket deletion рискует isolation и candle data | S | DEAD-001,DEAD-002,DEAD-004,DEAD-007,DEAD-009,DEAD-010 |