AURORA-1843: свод 2 — серверные страницы, поиск, фильтры и варианты (1/3, сервер) #241

Open
aep wants to merge 16 commits from feat/236-aurora-1843-svod2-server-paging into feat/220-aurora-1842-svod2-column-virtual
Owner

Refs #236 (часть 1/3; Closes #236 будет в PR 3). Jira AURORA-1843 (эпик AURORA-1834). Spec: openspec/changes/aurora-1843-svod2-server-paging/.

Что сделано (фаза 1 — сервер)

  • Миграция 0053: локаленезависимые svod2_casefold, svod2_js_trim, svod2_js_number_text, проекция svod2_compact; общий отбор svod2_lines; новые v_svod2_go/v_svod2_rf (поиск, три колоночных фильтра, total_count, компактное data); v_svod2_facets.
  • API: GET /svod/2/{sheet}/{year} получает view=full|compact, search, smeta_type, smeta_direction, org_name; ответ нового режима содержит total. Без новых параметров ответ байт-в-байт прежний. Новый GET /svod/2/{sheet}/{year}/facets.
  • Репозиторий, сервис, схемы ответа. get_svod2_rows и v_svod2_go_export не менялись.
  • Значения поиска и фильтров не пишутся в лог db_time (только признаки и длина).

Тесты

  • JS-оракул экрана «до» (perf/analysis/svod2_search_oracle.mjs: matchesSearch и equals дословные копии) порождает векторы; Python-порт сверен с ними; оракул служит эталоном интеграционных тестов API на засеянных данных (поиск, фильтры, варианты, «Найдено», компактная проекция, права isp).
  • Эталон прежнего ответа svod2_legacy_golden.json (sha256, GO/RF × 5 случаев) записан на коде «до» и не менялся.
  • Полный pytest на PG16: 3 известных падения (test_backend_calls_form3_add_project_and_add_line_functions, два теста test_export_service_query_params), 499 passed, 4 skipped.
  • PG15.12 (контур, с коммитами AURORA-1922): те же 3 падения, 501 passed, 4 skipped.

Чек-лист ручной проверки (главный контроллер)

  • Старый фронт работает без изменений (запросы без новых параметров).
  • Прежний ответ байт-в-байт на данных стенда (GO/RF; admin и dfip; все и одна организация; offset=0&limit=50), sha256 распакованных тел до и после миграции.
  • Excel RF: значения ячеек (openpyxl).

Замер до/после — добавит контроллер после прогона на стенде.
PG15 — проверено контроллером SDD (см. выше).

Rulings I made

  • R-1 (design #35): Ruling: решения плана #23–#34 одобрены главным контроллером как есть — цена ошибки: нет.
  • R-2 (design #36): Ruling: хелпер legacy_full живёт в api/tests/integration/conftest.py (не в test_svod2_paging_api.py); Task 3 создаёт его там, Task 4 импортирует оттуда — иначе test_svod2_sql.py импортировал бы из тестового модуля. Цена ошибки: перенос функции.
  • R-3 (design #37): Ruling: проверки главного контроллера разделены: PG15.12 — я сам скриптом pg15check.sh <ветка> после снятия замка и пуша ветки; байт-в-байт на стенде, ручная проверка фазы 2, playwright --list/типы perf/e2e и замеры — главный контроллер, одним списком в финальном отчёте, не жду. Цена ошибки: нет.
  • R-4 (design #38): Ruling: в коммитах исполнителей трейлер Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> — как требуют план (Global Constraints) и WAVE2-RULES (явное указание человека приоритетнее общей подсказки). Цена ошибки: amend/rebase трейлера.
  • R-5 (design #39): Ruling: I-1 финального ревью фазы 1 (стоимость серверного поиска) — deferred, не правим предикат без замера; цена: правка в 0053 до слияния, если замер даст недобор.
  • R-6 (design #40): Ruling: facets считает svod2_lines дважды (отклонение от design §4 из-за N² в коррелированном подзапросе) — цена: время facets с поиском.
  • R-7 (design #41): Ruling: значения поиска/фильтров не пишутся в лог db_time (M-2) — цена: нет.
  • R-8 (design #42): Ruling: M-4 (NUL в search → вероятно 5xx) deferred — цена: 5xx на искусственном запросе.

Находки (маркер, суть, статус)

Маркер Суть Статус
O-1 Перекрытие полей поиска: contract_summary перекрыто contract_detail при поиске на экране «до» (design «Найденные странности») deferred
O-2 Поиск по техническим id (design «Найденные странности») deferred
T1.1 NBSP/U+FEFF в SYNTHETIC_ROWS записаны сырыми символами (хрупко при форматировании) deferred
T1.2 SCREEN_CASES не ищет значения с NBSP/U+FEFF внутри deferred
T2.1 _JS_WHITESPACE сырыми невидимыми символами в svod2_oracle.py deferred
T2.2 расхождение Python lower() и JS toLowerCase не задокументировано в модуле deferred
T2.3 мёртвые ветки в js_number_text; OverflowError для огромных int deferred
T2.4 ключи фасетов без js_string (колонки строковые) deferred
T2.5 test_js_number_text_matches_node_vectors не параметризован deferred
T3.1 Эталон и порядок org_name зависят от collation БД (perf-pg): на PG15 контура golden-тест проверен rejected: прогон PG15.12 — эталон совпал, те же 3 падения
T3.2 svod2_seed.sql режется по ; — точка с запятой в тексте засева сломает скрипт deferred
T3.3 cd_id = expense_item_id (общий у нескольких строк), имя ключа вводит в заблуждение deferred
T3.4 добавлен фильтр search+type-hit (search+type из брифа даёт пусто) rejected: сохранены оба, это усиление
T3.5 / O-3 трейлер коммита b134cfd Sonnet 5.5 вместо Opus 5.5 (T1,T2 — Opus) deferred (документальное)
T4.1 тест trim не проверяет U+180E и U+0085 (не должны обрезаться) deferred
T4.2 casefold-тесты не покрывают весь U+0400–040F и Ø/İ deferred
T4.3 test_compact проверяет проекцию только на одной RF-строке deferred
T5.I-1 Important: коррелированный IN в v_svod2_facets (N^2) fixed e81df1d
T5.1 ELSE kv.value::text для object/array расходится с String(), но недостижимо (поля скалярные) deferred
T5.2 откат засева зависел от запроса к API fixed e81df1d
T5.3 "" и " " в тесте поиска передавались как NULL fixed e81df1d, f5bccc5
T5.4 фильтр из пробелов в SQL снимается NULLIF, в MRT не снимается (по design §3) rejected: решено в design §3
T5.5 EXISTS поиска заново соединяет таблицы вместо presence_*; header jsonb_build_object продублирован в трёх местах deferred (perf-фаза)
T5.6 test_facets_match_screen_oracle не передаёт " " (search or None) deferred
O-4 Исполнитель Task 5 в fix-раунде завёл собственного ревьюера (нарушение контракта «без сабагентов»); результат ревью не использован deferred (процесс)
T6.1 ORDER BY kind,value в facets-запросе по collation БД (клиент сортирует сам) deferred
T6.2 total по индексу result[0][4] вместо имени fixed 782a043 (= M-5)
T6.3 past-end второй вызов равен по цене первому deferred
T6.4 нет тестов на offset=None и пустой второй вызов deferred
T7.1 лог db_time пишет значения поиска/фильтров fixed 782a043 (= M-2)
T7.2 test_summary2_mode покрывает 4 комбинации из 32 deferred
T7.3 _parse_org_ids вызывается внутри веток, а не до ветвления deferred
I-1 Серверный поиск: ~1,46 с на 10 тыс. строк (translate-casefold, plpgsql числа); предложено регулярное выражение deferred (R-5, design #39; замер контроллера; закрывается вместе с T5.5)
M-1 нет теста перекрытия contract_summary пустой секцией (ооо лютик, дс-2) fixed 782a043
M-2 / T7.1 текст поиска/фильтров в логе db_time fixed 782a043
M-3 двойной svod2_lines в facets не в design.md fixed (design #40, docs-коммит)
M-4 NUL в search/фильтре → вероятно 5xx deferred (R-8, design #42)
M-5 / T6.2 total по индексу fixed 782a043

🤖 Generated with Claude Code

Refs #236 (часть 1/3; `Closes #236` будет в PR 3). Jira AURORA-1843 (эпик AURORA-1834). Spec: `openspec/changes/aurora-1843-svod2-server-paging/`. ## Что сделано (фаза 1 — сервер) - Миграция `0053`: локаленезависимые `svod2_casefold`, `svod2_js_trim`, `svod2_js_number_text`, проекция `svod2_compact`; общий отбор `svod2_lines`; новые `v_svod2_go`/`v_svod2_rf` (поиск, три колоночных фильтра, `total_count`, компактное `data`); `v_svod2_facets`. - API: `GET /svod/2/{sheet}/{year}` получает `view=full|compact`, `search`, `smeta_type`, `smeta_direction`, `org_name`; ответ нового режима содержит `total`. Без новых параметров ответ байт-в-байт прежний. Новый `GET /svod/2/{sheet}/{year}/facets`. - Репозиторий, сервис, схемы ответа. `get_svod2_rows` и `v_svod2_go_export` не менялись. - Значения поиска и фильтров не пишутся в лог `db_time` (только признаки и длина). ## Тесты - JS-оракул экрана «до» (`perf/analysis/svod2_search_oracle.mjs`: `matchesSearch` и `equals` дословные копии) порождает векторы; Python-порт сверен с ними; оракул служит эталоном интеграционных тестов API на засеянных данных (поиск, фильтры, варианты, «Найдено», компактная проекция, права isp). - Эталон прежнего ответа `svod2_legacy_golden.json` (sha256, GO/RF × 5 случаев) записан на коде «до» и не менялся. - Полный `pytest` на PG16: 3 известных падения (`test_backend_calls_form3_add_project_and_add_line_functions`, два теста `test_export_service_query_params`), 499 passed, 4 skipped. - PG15.12 (контур, с коммитами AURORA-1922): те же 3 падения, 501 passed, 4 skipped. ## Чек-лист ручной проверки (главный контроллер) - [ ] Старый фронт работает без изменений (запросы без новых параметров). - [ ] Прежний ответ байт-в-байт на данных стенда (GO/RF; admin и dfip; все и одна организация; `offset=0&limit=50`), sha256 распакованных тел до и после миграции. - [ ] Excel RF: значения ячеек (openpyxl). Замер до/после — добавит контроллер после прогона на стенде. PG15 — проверено контроллером SDD (см. выше). ## Rulings I made - R-1 (design #35): Ruling: решения плана #23–#34 одобрены главным контроллером как есть — цена ошибки: нет. - R-2 (design #36): Ruling: хелпер `legacy_full` живёт в `api/tests/integration/conftest.py` (не в test_svod2_paging_api.py); Task 3 создаёт его там, Task 4 импортирует оттуда — иначе test_svod2_sql.py импортировал бы из тестового модуля. Цена ошибки: перенос функции. - R-3 (design #37): Ruling: проверки главного контроллера разделены: PG15.12 — я сам скриптом pg15check.sh <ветка> после снятия замка и пуша ветки; байт-в-байт на стенде, ручная проверка фазы 2, `playwright --list`/типы perf/e2e и замеры — главный контроллер, одним списком в финальном отчёте, не жду. Цена ошибки: нет. - R-4 (design #38): Ruling: в коммитах исполнителей трейлер `Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>` — как требуют план (Global Constraints) и WAVE2-RULES (явное указание человека приоритетнее общей подсказки). Цена ошибки: amend/rebase трейлера. - R-5 (design #39): Ruling: I-1 финального ревью фазы 1 (стоимость серверного поиска) — deferred, не правим предикат без замера; цена: правка в 0053 до слияния, если замер даст недобор. - R-6 (design #40): Ruling: facets считает svod2_lines дважды (отклонение от design §4 из-за N² в коррелированном подзапросе) — цена: время facets с поиском. - R-7 (design #41): Ruling: значения поиска/фильтров не пишутся в лог db_time (M-2) — цена: нет. - R-8 (design #42): Ruling: M-4 (NUL в search → вероятно 5xx) deferred — цена: 5xx на искусственном запросе. ## Находки (маркер, суть, статус) | Маркер | Суть | Статус | |---|---|---| | O-1 | Перекрытие полей поиска: contract_summary перекрыто contract_detail при поиске на экране «до» (design «Найденные странности») | deferred | | O-2 | Поиск по техническим id (design «Найденные странности») | deferred | | T1.1 | NBSP/U+FEFF в SYNTHETIC_ROWS записаны сырыми символами (хрупко при форматировании) | deferred | | T1.2 | SCREEN_CASES не ищет значения с NBSP/U+FEFF внутри | deferred | | T2.1 | _JS_WHITESPACE сырыми невидимыми символами в svod2_oracle.py | deferred | | T2.2 | расхождение Python lower() и JS toLowerCase не задокументировано в модуле | deferred | | T2.3 | мёртвые ветки в js_number_text; OverflowError для огромных int | deferred | | T2.4 | ключи фасетов без js_string (колонки строковые) | deferred | | T2.5 | test_js_number_text_matches_node_vectors не параметризован | deferred | | T3.1 | Эталон и порядок org_name зависят от collation БД (perf-pg): на PG15 контура golden-тест проверен | rejected: прогон PG15.12 — эталон совпал, те же 3 падения | | T3.2 | svod2_seed.sql режется по `;` — точка с запятой в тексте засева сломает скрипт | deferred | | T3.3 | cd_id = expense_item_id (общий у нескольких строк), имя ключа вводит в заблуждение | deferred | | T3.4 | добавлен фильтр search+type-hit (search+type из брифа даёт пусто) | rejected: сохранены оба, это усиление | | T3.5 / O-3 | трейлер коммита b134cfd Sonnet 5.5 вместо Opus 5.5 (T1,T2 — Opus) | deferred (документальное) | | T4.1 | тест trim не проверяет U+180E и U+0085 (не должны обрезаться) | deferred | | T4.2 | casefold-тесты не покрывают весь U+0400–040F и Ø/İ | deferred | | T4.3 | test_compact проверяет проекцию только на одной RF-строке | deferred | | T5.I-1 | Important: коррелированный IN в v_svod2_facets (N^2) | fixed e81df1d | | T5.1 | ELSE kv.value::text для object/array расходится с String(), но недостижимо (поля скалярные) | deferred | | T5.2 | откат засева зависел от запроса к API | fixed e81df1d | | T5.3 | "" и " " в тесте поиска передавались как NULL | fixed e81df1d, f5bccc5 | | T5.4 | фильтр из пробелов в SQL снимается NULLIF, в MRT не снимается (по design §3) | rejected: решено в design §3 | | T5.5 | EXISTS поиска заново соединяет таблицы вместо presence_*; header jsonb_build_object продублирован в трёх местах | deferred (perf-фаза) | | T5.6 | test_facets_match_screen_oracle не передаёт " " (search or None) | deferred | | O-4 | Исполнитель Task 5 в fix-раунде завёл собственного ревьюера (нарушение контракта «без сабагентов»); результат ревью не использован | deferred (процесс) | | T6.1 | ORDER BY kind,value в facets-запросе по collation БД (клиент сортирует сам) | deferred | | T6.2 | total по индексу result[0][4] вместо имени | fixed 782a043 (= M-5) | | T6.3 | past-end второй вызов равен по цене первому | deferred | | T6.4 | нет тестов на offset=None и пустой второй вызов | deferred | | T7.1 | лог db_time пишет значения поиска/фильтров | fixed 782a043 (= M-2) | | T7.2 | test_summary2_mode покрывает 4 комбинации из 32 | deferred | | T7.3 | _parse_org_ids вызывается внутри веток, а не до ветвления | deferred | | I-1 | Серверный поиск: ~1,46 с на 10 тыс. строк (translate-casefold, plpgsql числа); предложено регулярное выражение | deferred (R-5, design #39; замер контроллера; закрывается вместе с T5.5) | | M-1 | нет теста перекрытия contract_summary пустой секцией (ооо лютик, дс-2) | fixed 782a043 | | M-2 / T7.1 | текст поиска/фильтров в логе db_time | fixed 782a043 | | M-3 | двойной svod2_lines в facets не в design.md | fixed (design #40, docs-коммит) | | M-4 | NUL в search/фильтре → вероятно 5xx | deferred (R-8, design #42) | | M-5 / T6.2 | total по индексу | fixed 782a043 | 🤖 Generated with [Claude Code](https://claude.com/claude-code)
aep added 16 commits 2026-10-28 21:16:04 +03:00
Свод 2: серверная постраничная загрузка (поиск, три фильтра, total,
варианты фильтров) и компактный ответ; фронт догружает строки окном
страниц; замеры памяти, времени до первых строк, байтов и шага скролла.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Поиск, колоночные фильтры, варианты фильтров и «Найдено N из M» — с точной
семантикой экрана (matchesSearch, MRT equals, faceting TanStack); регистр
без зависимости от локали БД; пороги — цели, а не условие слияния;
решения контроллера (три фазы/ветки, prefetch страницы 1, окружение тестов).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Три фазы — три PR стеком: сервер (SQL, API, тесты-оракулы первыми),
фронт (окно страниц в той же таблице), стенд замеров. Решения плана
дописаны в design.md (#23–#34).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- M-1: поиск по перекрытым полям contract_summary (ооо лютик, дс-2, дс-1) и неперекрытому (70), явные assert для строки 910004
- M-2: в лог db_time вместо значений поиска и фильтров пишутся признаки has_*/search_len; unit-тест на caplog
- M-5: total читается по имени столбца total_count

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This pull request can be merged automatically.
You are not authorized to merge this pull request.

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin feat/236-aurora-1843-svod2-server-paging:feat/236-aurora-1843-svod2-server-paging
git checkout feat/236-aurora-1843-svod2-server-paging
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: RSHB/DFiP_Budget_planing#241
No description provided.