Commit graph

521 commits

Author SHA1 Message Date
6eb1441508 Merge branch 'main' into fix/eesk-bind-in-comment
All checks were successful
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / changes (pull_request) Successful in 11s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI / changes (pull_request) Successful in 14s
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Successful in 2m24s
CI / backend-tests (pull_request) Successful in 17m40s
2026-09-09 21:47:07 +00:00
6a2d873c70 fix(rosseti): считать координату ключа в SQL тем же double, что в питоне
All checks were successful
CI Trade-In / changes (pull_request) Successful in 12s
CI / changes (pull_request) Successful in 14s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Successful in 2m24s
CI / backend-tests (pull_request) Successful in 17m41s
Ревью PR #3329. round(ST_X(geom)::numeric * 100000) округляет по кратчайшему
десятичному представлению float8, а питон — по двоичному double: расхождение на
0.19% реальных координат (761 из 400000), напр. 64.423605 → питон 6442360
(6442360.499999999), numeric-путь 6442361. Каждое расхождение = вечный дубль ЦП,
который сам не зарастёт — миграция применяется один раз (_schema_migrations).
Теперь в SQL sign/floor/abs над float8 без каста в numeric: IEEE754 бит в бит
как math.floor в питоне.

test_coord_e5 брал 60.123455, где двоичное и десятичное округление совпадают —
защита, которая не защищает. Добавлено расходящееся значение 64.423605.

RAISE WARNING при rows_after > 700 заменён на RAISE EXCEPTION: warning не
останавливает прогон, файл помечался бы applied навсегда вместе с дублями.

Refs #3322
2026-09-02 14:51:47 +05:00
b35f444d50 fix(rosseti): стабильный external_id ЦП вместо сессионного fid GeoServer
All checks were successful
CI Trade-In / changes (pull_request) Successful in 9s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI Trade-In / browser-tests (pull_request) Has been skipped
CI / changes (pull_request) Successful in 11s
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Successful in 2m22s
CI / backend-tests (pull_request) Successful in 17m46s
feature['id'] в WFS-ответе — сессионный fid, новый на каждый GetFeature.
ON CONFLICT (source, external_id) не срабатывал ни разу, каждый weekly-прогон
дописывал полный комплект ~488 фич: 4880 строк на 481 ЦП. Задуманный sha1-фолбэк
по атрибутам был мёртв — fid присутствует всегда.

Ключ теперь считается ТОЛЬКО по стабильным атрибутам (нормализованное имя, класс
напряжения, координаты в 1e-5 градуса), fid игнорируется. Координата квантуется
в целое, а не форматируется как float: ключ обязан совпадать байт-в-байт с
бэкфиллом в SQL. sha256 вместо sha1 — встроен в PG16, pgcrypto не нужен.

Починка разбора старые строки не убирает (ключи не совпадут, ON CONFLICT ничего
не перезапишет) → data/sql/99c_power_supply_centers_dedup.sql: пересчёт ключа
существующих строк + схлопывание копий (победитель — свежайший snapshot,
NULLS LAST явно), с печатью чисел до/после и идемпотентностью.

Refs #3322
2026-09-02 14:43:58 +05:00
a9de9bedf6 ЕЭСК-лоадер: бинд-параметр в SQL-комментарии ронял все 71 UPDATE с 18.08
All checks were successful
CI Trade-In / changes (pull_request) Successful in 9s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / changes (pull_request) Successful in 11s
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Successful in 2m1s
CI / backend-tests (pull_request) Successful in 17m30s
Комментарий #2464-B, объясняющий, почему из UPDATE убрали :load_pct, сам
содержал «CAST(:load_pct AS text)» — а SQLAlchemy text() парсит бинды и
внутри SQL-комментариев. Параметр стал обязательным, params его не содержит,
КАЖДАЯ строка батча падала на компиляции, per-row SAVEPOINT-except глотал
это как «битую строку», задача оставалась зелёной. Резервы ПС 35-220 не
обновлялись две недели, и никакой сторож этого не видел.

Найдено армейским аудитом 01-02.09 (линза ptica-workers), подтверждено
скептиком воспроизведением на проде.

Правка — переписан комментарий БЕЗ упоминания снятого параметра в живом
синтаксисе бинда, с предупреждением, почему это запрещено.

Сторож на МЕХАНИЗМ: тест собирает text()-стейтмент из исходника и сверяет
его бинд-имена с ключами params. БД не нужна — дефект живёт на компиляции.
Фальсификация: возврат «:load_pct» в комментарий даёт красное по значению
(«стейтмент требует биндов ['load_pct']»).
2026-09-02 11:53:10 +05:00
84505b1e0b chore(site-finder): удалить мёртвый код — 14 неиспользуемых символов (#3235)
Some checks failed
Deploy / changes (push) Successful in 8s
Deploy / build-frontend (push) Has been skipped
Deploy / deploy-caddy (push) Has been skipped
Deploy / build-backend (push) Successful in 2m30s
Deploy / build-worker (push) Successful in 3m39s
Deploy / deploy (push) Successful in 1m18s
Deploy / deploy-status (push) Successful in 1s
Deploy / perimeter-smoke (push) Failing after 12s
2026-08-29 15:39:10 +00:00
cdf493f345 chore(format): нормализация под ruff 0.15.20 — 161 файл, только формат (#2864) (#3022)
All checks were successful
Deploy / changes (push) Successful in 9s
Deploy Trade-In / changes (push) Successful in 13s
Deploy / build-frontend (push) Has been skipped
Deploy / deploy-caddy (push) Has been skipped
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-browser (push) Has been skipped
Deploy / build-backend (push) Successful in 2m23s
Deploy Trade-In / test (push) Successful in 3m56s
Deploy / build-worker (push) Successful in 4m16s
Deploy Trade-In / build-backend (push) Successful in 1m19s
Deploy / deploy (push) Successful in 1m49s
Deploy / deploy-status (push) Successful in 1s
Deploy / perimeter-smoke (push) Successful in 12s
Deploy Trade-In / deploy (push) Successful in 2m25s
Deploy Trade-In / deploy-status (push) Successful in 1s
Deploy Trade-In / perimeter-smoke (push) Successful in 11s
2026-08-21 12:01:52 +00:00
ccf84b4adc fix(best-layouts): avg_area_m2 = NULL вместо 0 при пустом окне сделок (#2867) (#3018)
Some checks failed
Deploy / perimeter-smoke (push) Blocked by required conditions
Deploy / deploy-status (push) Blocked by required conditions
Deploy / changes (push) Successful in 11s
Deploy / build-backend (push) Successful in 2m48s
Deploy / deploy-caddy (push) Has been skipped
Deploy / build-worker (push) Successful in 5m23s
Deploy / build-frontend (push) Successful in 4m18s
Deploy / deploy (push) Has been cancelled
2026-08-21 10:27:02 +00:00
5cd3502493 fix(ptica): три места, где код делал не то, что говорил (#2464)
All checks were successful
Deploy / changes (push) Successful in 7s
Deploy / build-frontend (push) Has been skipped
Deploy / deploy-caddy (push) Has been skipped
Deploy / build-backend (push) Successful in 2m10s
Deploy / build-worker (push) Successful in 3m22s
Deploy / deploy (push) Successful in 1m33s
Deploy / deploy-status (push) Successful in 1s
Deploy / perimeter-smoke (push) Successful in 9s
Часть эпика #2464.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 19:18:00 +00:00
b12f506953 docs(ptica): две докстроки обещали то, чего в коде нет (#2464)
All checks were successful
Deploy / changes (push) Successful in 7s
Deploy / build-frontend (push) Has been skipped
Deploy / deploy-caddy (push) Has been skipped
Deploy / build-backend (push) Successful in 1m55s
Deploy / build-worker (push) Successful in 3m47s
Deploy / deploy (push) Successful in 1m22s
Deploy / deploy-status (push) Successful in 1s
Deploy / perimeter-smoke (push) Successful in 9s
Часть эпика #2464.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 19:08:32 +00:00
f227768a53 fix(ptica): три места, где код делал не то, что говорил (#2464)
All checks were successful
CI Trade-In / changes (pull_request) Successful in 10s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI / changes (pull_request) Successful in 10s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Successful in 2m17s
CI / backend-tests (pull_request) Successful in 17m20s
1. `load_water_reserves_from_docx` собирал `result` с ключом `period`,
   печатал ПОЛНЫЙ словарь в лог, а возвращал
   `{k: v for … if isinstance(v, int)}` — период это строка или None,
   поэтому выбрасывался всегда. По логам казалось, что период отдаётся;
   вызывающий не получал его ни разу.

   Фильтр стоял ради аннотации `dict[str, int]` и ничего не защищал:
   соседняя ветка `load_water_reserves` кладёт в тот же словарь
   `{"error": str(...)}`, а единственный потребитель — задача
   `sync_water_reserves` — результат логирует и возвращает как есть.
   Период полезен: без него «загружено 42 записи» не отличить от
   прошлогодних. Аннотация исправлена, иначе следующий проход mypy вернул
   бы фильтр обратно — на это поставлен отдельный тест.

2. `get_sqlite_info` — TOCTOU: `p.exists()`, затем незащищённый
   `p.stat()`. try/except покрывал только `sqlite3.connect` ниже, поэтому
   OSError из stat улетал наружу и превращал диагностическую функцию в
   источник отказа. Файл между проверками реально исчезает — его
   переписывает выгрузка Объектива. Теперь отдаём то, что успели узнать,
   с ключом `stat_error`.

3. `place_program` — предупреждение «участок мал» печатало КАТАЛОЖНЫЕ
   `house.footprint_*`, хотя ставили по `fp_w`/`fp_d`. При
   переопределённом в программе габарите сообщение называло размер,
   которым никто не пытался ставить, и уводило от причины.

Двусторонне: против origin/main четыре теста красные с конкретными
значениями («период выброшен из ответа: {'records': 1, 'inserted': 1,
'updated': 0}», «аннотация всё ещё требует только int: dict[str, int]»).

Контроли зелёные с обеих сторон: отсутствующий файл по-прежнему даёт
exists=False без ошибки; `fp_w`/`fp_d` — действительно те размеры,
которыми ставят (иначе первый тест сверял бы имена, а не смысл).

pytest backend/tests/services/ — 3207 passed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 23:59:02 +05:00
c979cf886a fix(ptica): подпись сетевого обременения называет все виды, а не первый (#2464)
All checks were successful
Deploy / changes (push) Successful in 7s
Deploy / build-frontend (push) Has been skipped
Deploy / deploy-caddy (push) Has been skipped
Deploy / build-backend (push) Successful in 2m49s
Deploy / build-worker (push) Successful in 6m39s
Deploy / deploy (push) Successful in 1m53s
Deploy / deploy-status (push) Successful in 1s
Deploy / perimeter-smoke (push) Successful in 13s
Часть эпика #2464.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 18:53:32 +00:00
c3c8674b3c docs(ptica): две докстроки обещали то, чего в коде нет (#2464)
All checks were successful
CI Trade-In / changes (pull_request) Successful in 8s
CI / changes (pull_request) Successful in 9s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Successful in 2m4s
CI / backend-tests (pull_request) Successful in 17m21s
1. `QuarterDump` (nspd_client): «Default = только core, чтобы не сжигать
   rate-limit на 17 запросов». Фактический дефолт `search_by_quarter` —
   `include_zouit=True`, то есть 5 ЗОУИТ-слоёв входят в дефолтный вызов.
   Числа 17 тоже нет: territorial_zones/red_lines/engineering и все ЗОУИТ
   идут через grid-walk при grid_n=7, по 49 запросов КАЖДЫЙ — дефолтный
   дамп это сотни запросов. Экономит rate-limit только include_risks=False.
   Докстрока самого метода 640 строками ниже говорит верно («Default
   True») — правильный образец лежал рядом с дефектом.

2. `find_active_on_demand_job` (cadastre_fetch): «Если в БД есть FAILED
   on-demand за последние 60 секунд — тоже None». В SQL нет ни слова
   'failed', ни какого-либо временного фильтра. Обещание вдвойне вредно:
   подразумевало, что неуспешная джоба СТАРШЕ минуты вернётся как
   активная (не вернётся), и отправляло отлаживающего искать окно,
   которого нет.

Гейты сверяют утверждение докстроки с кодом, а не читаемость текста:
обещание «только core» требует `include_zouit=False` в сигнатуре;
обещание минутного окна требует временного фильтра в теле.

Двусторонне: против origin/main три гейта красные с конкретными
сообщениями. Контроли зелёные с обеих сторон — характеризующий фиксирует
фактические три статуса в SQL, а test_docstrings_state_the_actual_behaviour
ловит «починку» через вычёркивание неудобной фразы.

Два подводных камня, на которые наступил и оставил защиту:
- гейт ищет обещание по тексту, поэтому старые формулировки в докстроках
  ПЕРЕСКАЗАНЫ, а не процитированы — иначе он не отличает цитату от
  утверждения (оговорено прямо в тексте докстроки);
- тело функции нельзя брать как последний кусок разбиения по тройным
  кавычкам: SQL сам в них обёрнут, и проверка шла бы по огрызку после
  запроса. Из-за этого один гейт проходил по случайности. Вынесен
  хелпер `_body`.

pytest backend/tests/services/ — 3199 passed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 23:43:40 +05:00
9810a946a6 fix(ptica): невозможные параметры регламента не превращаются в деньги (#2464)
All checks were successful
Deploy / changes (push) Successful in 7s
Deploy / build-frontend (push) Has been skipped
Deploy / deploy-caddy (push) Has been skipped
Deploy / build-backend (push) Successful in 2m29s
Deploy / build-worker (push) Successful in 3m33s
Deploy / deploy (push) Successful in 1m22s
Deploy / deploy-status (push) Successful in 1s
Deploy / perimeter-smoke (push) Successful in 9s
Часть эпика #2464.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 18:39:09 +00:00
b1dc5d5507 fix(ptica): подпись сетевого обременения называет все виды, а не первый (#2464)
All checks were successful
CI Trade-In / changes (pull_request) Successful in 7s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI / changes (pull_request) Successful in 10s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Successful in 2m0s
CI / backend-tests (pull_request) Successful in 17m6s
`cad_utility_label` брался от ПЕРВОГО overlap'а с распознанным
`network_kind`, а покрытие агрегировалось по ВСЕМУ bucket'у. Участок под
двумя видами охранных зон получал в отчёте конкретную причину, дающую
малую часть площади:

    «Сетевое обременение (теплоснабжение) покрывает 74% участка»
     ← теплоснабжение даёт 10%, остальные 64% — инженерные коммуникации

Это не редкость. Прод 20.08.2026: 316 пересечений охранных зон РАЗНЫХ
видов, 155 зон вовлечено; самая частая пара — «тепловых сетей» ×
«инженерных коммуникаций» (296 из 316).

Теперь копятся ВСЕ различённые виды и называются через запятую;
множественное число, когда их больше одного. Покрытие — их объединение,
и подпись это отражает.

Заодно снята зависимость от порядка: подпись бралась от первого overlap'а,
а он определяется `ORDER BY reg_numb_border, id`, который к покрытию
отношения не имеет. Виды сортируются.

Проверка попутно опровергла ДОВОД пункта эпика. Пункт говорит о смешении
сетевых зон с keyword-совпадениями без network_kind. На проде таких ноль:
из 3493 строк cad_zouit — 103 СЗЗ-предупреждения, 1936 с распознанной
сетью, 1454 generic warning, и НИ ОДНОЙ «только по ключевому слову»
(classify_network_zone уже покрывает все встречающиеся шаблоны). Вывод
пункта — «конкретная причина приписывается чужой площади» — верен, но по
другой причине: смешиваются РАЗНЫЕ ВИДЫ СЕТЕЙ, а не сети с keyword'ами.

Двусторонне: против origin/main три теста красные, головной — с полной
строкой, которую увидел бы пользователь. Контроли зелёные с обеих сторон:
один вид сохраняет прежнюю формулировку в единственном числе, две зоны
одного вида не дают дубль в подписи, area-gate не тронут (тонкая полоса
остаётся warning).

pytest test_gate_verdict + services/site_finder — 727 passed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 23:30:09 +05:00
3ecd7cffc8 fix(ptica): download_binary переживает транзиентный ответ, как и get_json (#2464)
All checks were successful
Deploy / changes (push) Successful in 8s
Deploy / deploy-caddy (push) Has been skipped
Deploy / build-backend (push) Successful in 1m57s
Deploy / build-frontend (push) Has been skipped
Deploy / build-worker (push) Successful in 4m6s
Deploy / deploy (push) Successful in 1m31s
Deploy / deploy-status (push) Successful in 1s
Deploy / perimeter-smoke (push) Successful in 8s
Часть эпика #2464.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 18:24:43 +00:00
06b0063c67 fix(ptica): невозможные параметры регламента не превращаются в деньги (#2464)
All checks were successful
CI Trade-In / changes (pull_request) Successful in 8s
CI / changes (pull_request) Successful in 10s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Successful in 1m56s
CI / backend-tests (pull_request) Successful in 17m8s
`synthesize_teap_from_buildability` проверяла параметры только на `> 0`.
Процент застройки 150 давал пятно БОЛЬШЕ участка (10 000 м² → 15 000 м²),
а дальше — жилую площадь, число квартир и выручку: физически невозможные
числа, поданные как обычные цифры финмодели. КСИТ 500 давал GFA
5 000 000 м² на гектаре.

Параметры приходят из ПЗЗ-регламента (`zone_regulation_cache`) — внешние
разобранные данные, то есть граница доверия.

Невозможное значение ОТБРАСЫВАЕТСЯ, а не роняет расчёт: если рядом есть
КСИТ, GFA считается по нему и остаётся верной. Лучше отсутствие
параметра, чем неверный. Если вменяемых не осталось — None, и caller
штатно показывает отсутствие финоценки с caveat, а не ноль.

Границы взяты с запасом к реальным данным прода 20.08.2026 (33 строки
zone_regulation_cache: pct 0..100, far 1..4, floors 0..5): pct ≤ 100,
far ≤ 30, этажей ≤ 100 — сито против порчи разбора, а не норматив.

ВТОРОЙ дефект, найденный этими же тестами и существовавший до правки:
ветка «нет ни процента, ни этажности → пятно = GFA» неявно предполагает
один этаж, и при КСИТ > 1 давала пятно больше участка (10 000 м² с far=2
→ 20 000 м²). Добавлен физический инвариант «пятно ≤ участок» — не
эвристика, а геометрия, и стоит он ОДИН раз после всех ветвей, чтобы
держаться и для будущих способов оценки пятна. GFA при этом не меняется.

Двусторонне: против origin/main четыре теста красные с конкретными
невозможными значениями («пятно 15000.0 больше участка 10000.0»,
«GFA=5000000.0»). Восемь контролей зелёные с обеих сторон — среди них
пять сочетаний (pct, far, floors), взятых ДОСЛОВНО с прода, и граница
100 % застройки, которая законна и на проде есть.

pytest test_parcel_financial + services/generative + новый файл — 185 passed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 23:14:38 +05:00
0185889f72 fix(ptica): ключ gisogd_permits — id документа на портале, а не (группа, номер) (#2986)
All checks were successful
Deploy / changes (push) Successful in 7s
Deploy / build-frontend (push) Has been skipped
Deploy / deploy-caddy (push) Has been skipped
Deploy / build-backend (push) Successful in 1m55s
Deploy / build-worker (push) Successful in 3m29s
Deploy / deploy (push) Successful in 1m21s
Deploy / deploy-status (push) Successful in 1s
Deploy / perimeter-smoke (push) Successful in 9s
Закрывает #2986.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 18:03:48 +00:00
f72a08eb80 fix(ptica): download_binary переживает транзиентный ответ, как и get_json (#2464)
All checks were successful
CI Trade-In / changes (pull_request) Successful in 8s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI Trade-In / browser-tests (pull_request) Has been skipped
CI / changes (pull_request) Successful in 9s
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Successful in 3m0s
CI / backend-tests (pull_request) Successful in 17m31s
Обе функции ходят через один браузерный контекст, под один и тот же WAF.
`get_json` держит до пяти попыток с экспоненциальным backoff на 429/5xx/0,
а `download_binary` не имел ретраев вовсе: один 429 ронял загрузку
картинки насовсем, и вызывающий (`download_plan_image`, `download_photos`)
писал в лог «не удалось» — неотличимо от «файла нет».

Правильный образец лежал в этом же классе, двадцатью строками выше.

Непереходные коды (403, 404) поднимаются сразу, без ожидания: повтор их
не изменит, а лишний стук под WAF вредит. Разбор статуса вынесен ЗА
семафор — sleep не должен держать слот.

Двусторонне: против origin/main транзиентные тесты красные с конкретным
значением («вместо байтов получили RuntimeError('binary http 429…');
попыток=1»), ни одного ImportError/TypeError.

Контроли зелёные с обеих сторон: 403 и 404 не повторяются, исчерпание
попыток даёт честную ошибку, а не пустые байты, успех с первой попытки не
порождает лишних запросов.

Отдельный контроль на паузы: без него «ретраит» и «долбит без пауз»
неотличимы в тесте, а под WAF разница между ними решающая — проверяется,
что задержки растут как 1, 2 секунды.

pytest backend/tests/services/scrapers/ — 340 passed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 22:57:14 +05:00
7d5ca247ca fix(ptica): ключ gisogd_permits — id документа на портале, а не (группа, номер) (#2986)
`UNIQUE (doc_group, doc_num)` вводился, чтобы склеивать ОДИН документ,
пришедший из двух схем портала. Замер 20.08.2026 показал, что задача,
ради которой ключ введён, почти отсутствует, а побочный эффект огромен:
docNum у ГИСОГД НЕ уникален — разрешение и изменения к нему носят один
номер.

    группа   документов   различных key   различных docNum   схлопывается
    DocRS         6098          6096            4305             1793
    DocRV         5419          5415            4969              450
    DocIZ          548           547             393              155

    общих docNum между схемами (DocRS): 2   ← ради этого ключ и вводился
    общих key    между схемами (DocRS): 2   ← те же два

На проде 9182 строки против 12 065 документов на портале — нет 23.9 %
реестра. Пример 66-06-06-2026: портал отдаёт два документа (key …719586 —
само разрешение, key …752293 — изменения к нему), а UPSERT с
предпочтением позднего date_reg оставлял только изменение. Так вытеснено
598 из 4320 строк РНС (13.8 %) — в §6 на месте разрешения показывается
изменение к нему, без признака подмены.

Ключ стал `UNIQUE (source_key)`: разделяет разрешение и изменения (разные
key) и по-прежнему склеивает настоящие межсхемные дубли (у них key
ОБЩИЙ — ровно 7 записей по всем группам). Дедуп перед сменой не нужен:
source_key на проде уже уникален (9182 из 9182, NOT NULL).

Заодно группа DocIZ добавлена в GROUP_CODE — её не было вовсе, 548
документов не грузились. CHECK расширен значением 'IZ'.

§6 сужена до РНС/РВЭ ЯВНО: агрегат обещает total_count = rs_count +
rv_count, а строки 'IZ' попадали бы в total и ни в один счётчик.
Показывать ли изменения отдельной строкой — вопрос продуктовый (#2986);
до его решения сужение стоит в запросе, а не держится на том, что таких
строк «пока нет».

Проверки:
- два гейта на лоадер (GROUP_CODE и цель ON CONFLICT) — БЕЗ базы,
  двусторонние: на origin/main дают конкретные неверные значения
  ({'DocRS','DocRV'} и старый ON CONFLICT в тексте запроса);
- гейт на §6 и контроль инварианта total = rs + rv на данных — красные
  на origin/main;
- герметичная репетиция миграции на временной копии: со старым ключом
  разрешение и изменение схлопываются в одну строку (и остаётся именно
  изменение — как на проде), после миграции живут раздельно; межсхемный
  дубль по-прежнему склеивается; CHECK принимает 'IZ' и отвергает мусор.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 22:41:27 +05:00
3267120499 docs(ptica): докстрока не обещает защиты, которой нет, и не шлёт на мёртвый хост (#2464)
All checks were successful
CI Trade-In / changes (pull_request) Successful in 9s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / changes (pull_request) Successful in 10s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Successful in 2m10s
CI / backend-tests (pull_request) Successful in 17m21s
`_page_contains_table` документировалась как «hint-только режим (оглавление,
перекрёстные ссылки) даёт false positive и подавляется». Кода подавления в
функции никогда не было — это `bool(cap.search(text))`. Обещание защиты,
которой нет, опаснее её отсутствия: читающий не станет её добавлять.

Замер уточнил и сам пункт эпика. Приведённый в нём пример перекрёстной
ссылки «показатели приведены в таблице 12» регекс НЕ ловит: он требует
именительное «Таблица N», поэтому «в таблице 12», «см. Таблицу 12»,
«табл. 12» дают False. Опасно ровно ОГЛАВЛЕНИЕ — там падеж тот же
именительный, и «Таблица 11 Баланс территории ..... 34» неотличима от
подписи. Это зафиксировано характеризующим тестом, чтобы следующая
попытка подавления целилась в признаки оглавления (точки-выноски, номер
страницы в конце), а не в падежи.

Подавление здесь не реализовано сознательно: `ekb_ppt_tep` на проде пуста
(0 строк), URL в `_SEED_DOCS` — заглушка, живых PDF нет. Эвристику отсева
не на чем откалибровать, а правило, придуманное без образцов, ловит ровно
те случаи, которые придумали вместе с ним.

Отдельно исправлен комментарий сида: хост `gisogd.ekburg.ru`, названный
там местом, «где лежит реальный URL», НЕ СУЩЕСТВУЕТ — DNS не резолвит его
ни с рабочей машины, ни с прод-хоста (20.08.2026). Комментарий отправлял
искать документ на портале, которого нет. Назван живой портал
`gisogd66.midural.ru` и способ перечислить его разделы.

Двусторонне: против origin/main два гейта красные — «докстрока обещает
подавление, а в теле только поиск подстроки» и «комментарий сида не
предупреждает, что хост мёртв». Характеризующие тесты зелёные с обеих
сторон: они фиксируют фактическое поведение, а контроль
test_docstring_names_the_actual_behaviour ловит «починку» через
вычёркивание неудобной фразы.

Гейт ищет обещание по слову, поэтому старая формулировка в докстроке
пересказана, а не процитирована — иначе он не отличил бы цитату от
утверждения; это оговорено прямо в тексте.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 22:36:23 +05:00
91a5207641 fix(ptica): лоты без площади — своя корзина, а не «<25 м²» (#2464)
All checks were successful
Deploy / changes (push) Successful in 7s
Deploy / build-frontend (push) Has been skipped
Deploy / deploy-caddy (push) Has been skipped
Deploy / build-backend (push) Successful in 2m4s
Deploy / build-worker (push) Successful in 3m16s
Deploy / deploy (push) Successful in 1m33s
Deploy / deploy-status (push) Successful in 1s
Deploy / perimeter-smoke (push) Successful in 10s
Часть эпика #2464.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 17:02:25 +00:00
508f2d6090 fix(ptica): act_date — дата акта об изъятии, а не первой попавшейся ссылки (#2464)
All checks were successful
Deploy / changes (push) Successful in 9s
Deploy / build-frontend (push) Has been skipped
Deploy / deploy-caddy (push) Has been skipped
Deploy / build-backend (push) Successful in 2m23s
Deploy / build-worker (push) Successful in 3m29s
Deploy / deploy (push) Successful in 1m35s
Deploy / deploy-status (push) Successful in 1s
Deploy / perimeter-smoke (push) Successful in 9s
Часть эпика #2464.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 16:43:27 +00:00
1b2a26bb9a fix(ptica): лоты без площади — своя корзина, а не «<25 м²» (#2464)
All checks were successful
CI Trade-In / changes (pull_request) Successful in 8s
CI / changes (pull_request) Successful in 9s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Successful in 2m27s
CI / backend-tests (pull_request) Successful in 17m13s
`_SUPPLY_ONLY_LOTS_SQL` раскладывал `area_pd IS NULL` в ту же корзину
`'<25'`, что и настоящие студии. Замер прода 20.08.2026 (последний
снапшот на физлот, premise_kind='квартира', не проданные):

    в продаже            181 353
    без area_pd           11 557   (6.4 %)
    реально < 25 м²        7 013

Корзина «<25» состояла из неизвестного на 62 % и завышала долю мелких
лотов в блоке «По предложению (без темпа продаж)».

Зеркала у такого отображения не было: `layout_signature.area_bin`
принимает float и NULL-ветки не имеет вовсе, а velocity-MV по площади
не группирует — то есть `NULL → '<25'` было выдумкой, а не переносом
чужого правила.

Исключать такие лоты нельзя: они реально в продаже, и без них
предложение занизилось бы на 6.4 %. Поэтому отдельная корзина «н/д».
Медиана площади у неё выйдет NULL (PERCENTILE_CONT игнорирует NULL) —
честно. Схема не меняется: area_bin остаётся str, OpenAPI прежний.

Тест герметичный и прогоняет НАСТОЯЩИЙ SQL: временная таблица
objective_lots затеняет боевую в пределах сессии, запрос берётся из
модуля дословно, прод-данные не читаются.

Двусторонне: против origin/main корзины распределяются как
{'<25': 2, '25-40': 1, '40-60': 1} — конкретное неверное значение, ни
одного TypeError/ImportError. Контроли (сумма лотов сохраняется,
обычные корзины не меняются) зелёные с обеих сторон.

pytest backend/tests/sql/ — 38 passed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 21:41:47 +05:00
46beeb4c13 fix(ptica): тип постановления — по первому упоминанию, а не по порядку проверок (#2464)
All checks were successful
Deploy / changes (push) Successful in 11s
Deploy / build-frontend (push) Has been skipped
Deploy / deploy-caddy (push) Has been skipped
Deploy / build-backend (push) Successful in 2m14s
Deploy / build-worker (push) Successful in 4m49s
Deploy / deploy (push) Successful in 1m52s
Deploy / deploy-status (push) Successful in 1s
Deploy / perimeter-smoke (push) Successful in 9s
Часть эпика #2464.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 16:28:26 +00:00
9a4acb67d1 fix(ptica): act_date — дата акта об изъятии, а не первой попавшейся ссылки (#2464)
All checks were successful
CI Trade-In / changes (pull_request) Successful in 7s
CI / changes (pull_request) Successful in 9s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Successful in 2m0s
CI / backend-tests (pull_request) Successful in 17m13s
`_extract_act_date` брал ПЕРВОЕ «от DD.MM.YYYY» во всём OCR-тексте.
«Сообщение о планируемом изъятии» открывается списком оснований, и первой
строкой там стоит

    «Решение Екатеринбургской городской Думы от 06.07.2004 № 60/1
     «Об утверждении Генерального плана города»»

— Генплан, а не акт об изъятии. На проде это дало 11 строк из 27 с датой
2004-07-06 при проектах 2020 и 2022 годов, причём одну и ту же дату
получили ДВА разных документа (развязка на Сибирском тракте и улица
Энергостроителей). Совпадение даты у несвязанных актов и было первым
признаком, что дата не своя.

Дата принимается, только если в 120 символах перед ней стоит слово
«постановлени». Ссылки-помехи в этих документах — «Решение … Думы» и
«Приказ Министерства» — его не содержат. Окно шире самой фразы, потому
что OCR перемешивает колонки таблицы и вклинивает в неё чужой текст
(«…Администрации города документами) Екатеринбурга от 19.04.2019…»).

Если подходящей даты нет — None. Дата чужого документа хуже пустоты: по
ней нельзя ни отфильтровать актуальные изъятия, ни сверить срок, и она
неотличима от настоящей.

Калибровка не на одном образце: все пять исходных PDF загружены и
распознаны тем же трактом, что использует загрузчик (ocr_pdf_text в
прод-контейнере). Окна 80/120/160 дают одинаковые 5 из 5. Более узкое
правило (плюс «администраци») давало те же 5 из 5, но ломало законный
случай «Постановление № 509-ПП» — областной акт без слова «администрация»,
уже закреплённый тестом test_act_date_extracted_from_text; взято широкое.

Сквозная проверка: патченный код прогнан по всем пяти распознанным
текстам целиком — 27 записей, ровно столько же, сколько строк в
land_reservation; 11 меняют 2004-07-06 на настоящую дату, 16 не двигаются.

Двусторонне: против origin/main три теста красные с реальным неверным
значением ('2004-07-06'), ни одного TypeError — тесты идут через
extract_izyatie_records, чья сигнатура одинакова на обеих сторонах.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 21:24:32 +05:00
71f27fee7d fix(ptica): тип постановления — по первому упоминанию, а не по порядку проверок (#2464)
All checks were successful
CI Trade-In / changes (pull_request) Successful in 7s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI Trade-In / browser-tests (pull_request) Has been skipped
CI / changes (pull_request) Successful in 9s
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Successful in 1m56s
CI / backend-tests (pull_request) Successful in 17m12s
`_detect_kind` проверял «резервир» первым и возвращал «резервирование»
безусловно. Постановление об изъятии, где резервирование упомянуто
вскользь — типовая формулировка «ранее зарезервированных земель», ссылка
на утративший силу акт, — классифицировалось как резервирование.

Ошибка не единичная: `kind` в `extract_reservations` один на весь
документ, поэтому неверный тип уходит в КАЖДУЮ строку land_reservation
по этому акту.

Побеждает то слово, что встретилось раньше. Тема документа стоит в
заголовке, поэтому позиция — сигнал сильнее порядка проверок, и он
симметричен: заголовок «О резервировании» так же выигрывает у «изъятия»
в теле. Простая смена порядка проверок этой симметрии не даёт — на неё
поставлен отдельный контроль.

Текущих ошибок на проде нет, и это измерено: в land_reservation 27
строк, все из источника izyatie_ekb_ocr, все «изъятие», ни в одной
выдержке слова «резервир» не встречается. Правка закрывает возможность,
а не чинит существующую порчу.

Двусторонне: против origin/main два теста красные с конкретным неверным
значением (`assert 'резервирование' == 'изъятие'`). Контроли —
симметрия заголовка, одиночные маркеры, откат к default_kind — зелёные с
обеих сторон. Формулировки в тестах взяты с прода дословно (basis_act).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 17:49:03 +05:00
91b0747b3e fix(ptica): «не продана» перестаёт означать «продана» (#2464)
All checks were successful
CI Trade-In / changes (pull_request) Successful in 8s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI Trade-In / browser-tests (pull_request) Has been skipped
CI / changes (pull_request) Successful in 12s
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Successful in 1m58s
CI / backend-tests (pull_request) Successful in 17m22s
Регекс статусов каталога ДОМ.РФ искал ключевые слова без защиты от
отрицания, поэтому русские отрицательные формы давали ОБРАТНЫЙ статус:

    «нереализована»    → содержит «реализована»   → sold
    «не продана»       → содержит «продана»       → sold
    «не забронирована» → содержит «забронирована» → reserved
    «не в продаже»     → содержит «в продаже»     → free

Свободная квартира попадала бы в domrf_kn_flats.status проданной.

Отрицание теперь гасит токен, а не переворачивает его. «Не забронирована»
не означает ни sold, ни free; «не продана → free» — это вывод, а не факт
со страницы. Лучше отсутствие статуса, чем неверный.

Заодно вылечен второй дефект того же места: разбор брал ПЕРВОЕ совпадение
в блоке, поэтому «Квартира не продана. Статус: в продаже» на main даёт
sold. Новый _status_in_text перебирает все вхождения и берёт первое
неотрицаемое — настоящий статус в блоке больше не теряется.

Текущий эффект на проде НУЛЕВОЙ, и это проверено, а не предположено:
catalog_updated_at пуст у всех 983 088 строк domrf_kn_flats (скрапер не
записал ни одной), таска scrape_kn_catalog_flats закомментирована в
beat_schedule.py из-за WAF-cooldown. Существующие значения status
(free 25 656 / sold 3 122 / booked 641) пришли из kn-API — среди них
'booked', которого нет в константах этого модуля. Правка
предупредительная: при включении пути дефект инвертировал бы статусы молча.

Двусторонне: против origin/main 7 тестов красные, каждый с конкретным
неверным значением («Нереализована» → 'sold'). Контроли (7 обычных форм
и «не» в хвосте слова «Цене») зелёные с обеих сторон. Старая сюита
парсера — 320 passed, регрессий нет.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 17:45:04 +05:00
131b83c2fe fix(ptica): у пользователя не может быть двух дефолтных профилей весов (#2464)
All checks were successful
Deploy / changes (push) Successful in 9s
Deploy / build-frontend (push) Has been skipped
Deploy / deploy-caddy (push) Has been skipped
Deploy / build-backend (push) Successful in 2m4s
Deploy / build-worker (push) Successful in 3m31s
Deploy / deploy (push) Successful in 1m27s
Deploy / deploy-status (push) Successful in 1s
Deploy / perimeter-smoke (push) Successful in 8s
Закрывает часть эпика #2464.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 12:23:28 +00:00
f7d4b5bccf fix(ptica): «объекта нет в БД» считается пропуском, а не сбоем (#2464) (#2974)
All checks were successful
Deploy / changes (push) Successful in 9s
Deploy / build-frontend (push) Has been skipped
Deploy / deploy-caddy (push) Has been skipped
Deploy / build-backend (push) Successful in 2m28s
Deploy / build-worker (push) Successful in 4m9s
Deploy / deploy (push) Successful in 1m25s
Deploy / deploy-status (push) Successful in 1s
Deploy / perimeter-smoke (push) Successful in 9s
2026-08-20 11:59:39 +00:00
afa648b21f fix(ptica): у пользователя не может быть двух дефолтных профилей весов (#2464)
All checks were successful
CI Trade-In / changes (pull_request) Successful in 8s
CI / changes (pull_request) Successful in 9s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Successful in 2m26s
CI / backend-tests (pull_request) Successful in 17m14s
create_profile/update_profile делают «снять is_default у всех → поставить новому»
двумя отдельными операторами. Между ними инвариант нарушен, и при одновременных
запросах у пользователя может оказаться ДВА профиля с is_default=TRUE. А читающий
_SELECT_DEFAULT брал LIMIT 1 БЕЗ ORDER BY — выбор молча перескакивал между ними от
запроса к запросу.

Два рубежа, а не один:

  миграция 190 — частичный уникальный индекс (user_id) WHERE is_default: два
                 дефолта становятся невозможными на уровне БД;
  ORDER BY id  — детерминированный выбор, если индекс когда-нибудь снимут.
                 Соседние запросы этого файла тай-брейк по id уже имеют.

Индекс не мешает штатной переустановке дефолта: порядок операторов в коде уже
правильный (сначала снять у всех, потом поставить), поэтому в момент проверки
дефолтов ноль. Это отдельно проверено тестом.

Безопасность миграции: на проде нарушений нет — у admin один дефолт, у __system__
ноль, ни одного пользователя с двумя. Таблица в 4 строки, индексируется мгновенно.
lock_timeout проставлен по #2752.

Тест проверяет ПОВЕДЕНИЕ на живом Postgres: вторая установка дефолта отвергается
базой. Плюс фальсификация — без индекса два дефолта вставляются молча; без неё
зелёный тест неотличим от «оно и так не вставлялось». Плюс два контроля:
переустановка дефолта работает, разные пользователи сохраняют свои.

Тест про ORDER BY вынесен в tests/services/site_finder, а НЕ внесён в
skip_allowlist: живой БД он не требует, и пропускаться вместе с DB-тестами ему
незачем. Против origin/main он краснеет, показывая запрос без тай-брейка.

Прогоны: без БД — 648 passed rc=0; с БД — 5 passed rc=0.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 16:58:48 +05:00
9b18c23a5a fix(ptica): загрузчик теплоснабжения фиксирует по организациям, а не одной транзакцией (#2464) (#2973)
All checks were successful
Deploy / changes (push) Successful in 9s
Deploy / build-frontend (push) Has been skipped
Deploy / deploy-caddy (push) Has been skipped
Deploy / build-backend (push) Successful in 2m26s
Deploy / build-worker (push) Successful in 3m29s
Deploy / deploy (push) Successful in 1m29s
Deploy / deploy-status (push) Successful in 1s
Deploy / perimeter-smoke (push) Successful in 8s
2026-08-20 11:51:57 +00:00
6e34928dd4 fix(ptica): серия WAF-блоков прерывает батч каталога, а не гонит его до конца (#2464) (#2971)
All checks were successful
Deploy / build-frontend (push) Has been skipped
Deploy / build-worker (push) Successful in 4m26s
Deploy / deploy (push) Successful in 1m33s
Deploy / deploy-status (push) Successful in 1s
Deploy / perimeter-smoke (push) Successful in 9s
Deploy / changes (push) Successful in 7s
Deploy / deploy-caddy (push) Has been skipped
Deploy / build-backend (push) Successful in 1m54s
2026-08-20 10:54:11 +00:00
a4b989ca36 fix(ptica): WAF-блок прерывает backfill_parcel_geom, а не гонит его дальше по кварталам (#2464) (#2969)
Some checks failed
Deploy / build-backend (push) Blocked by required conditions
Deploy / build-worker (push) Blocked by required conditions
Deploy / build-frontend (push) Blocked by required conditions
Deploy / deploy (push) Blocked by required conditions
Deploy / deploy-caddy (push) Blocked by required conditions
Deploy / perimeter-smoke (push) Blocked by required conditions
Deploy / deploy-status (push) Blocked by required conditions
Deploy / changes (push) Has been cancelled
2026-08-20 10:34:26 +00:00
88a2ebda02 docs(ptica): шесть мест, где документация расходилась с кодом (#2464) (#2968)
All checks were successful
Deploy / changes (push) Successful in 8s
Deploy / build-frontend (push) Has been skipped
Deploy / deploy-caddy (push) Has been skipped
Deploy / build-backend (push) Successful in 2m0s
Deploy / build-worker (push) Successful in 3m9s
Deploy / deploy (push) Successful in 1m25s
Deploy / deploy-status (push) Successful in 1s
Deploy / perimeter-smoke (push) Successful in 9s
2026-08-20 10:25:22 +00:00
7abc0ed431 fix(ptica): два запроса connection-capacity перестают травить общую сессию (#2464) (#2964)
Some checks failed
Deploy / deploy (push) Blocked by required conditions
Deploy / perimeter-smoke (push) Blocked by required conditions
Deploy / deploy-status (push) Blocked by required conditions
Deploy / changes (push) Successful in 7s
Deploy / build-frontend (push) Has been skipped
Deploy / deploy-caddy (push) Has been skipped
Deploy / build-backend (push) Has been cancelled
Deploy / build-worker (push) Has been cancelled
2026-08-20 09:53:34 +00:00
1470bc26de fix(ptica): число ЗОУИТ перестаёт подписываться как число типов (#2464) (#2961)
All checks were successful
Deploy / changes (push) Successful in 9s
Deploy / build-frontend (push) Has been skipped
Deploy / deploy-caddy (push) Has been skipped
Deploy / build-backend (push) Successful in 3m55s
Deploy / build-worker (push) Successful in 4m17s
Deploy / deploy (push) Successful in 2m15s
Deploy / deploy-status (push) Successful in 1s
Deploy / perimeter-smoke (push) Successful in 9s
2026-08-20 09:17:06 +00:00
edbaca1e87 fix(ptica): успехи батча каталога фиксируются по ходу, а не одним commit'ом в конце (#2464) (#2960)
All checks were successful
Deploy / changes (push) Successful in 8s
Deploy / build-frontend (push) Has been skipped
Deploy / deploy-caddy (push) Has been skipped
Deploy / build-backend (push) Successful in 2m24s
Deploy / build-worker (push) Successful in 3m38s
Deploy / deploy (push) Successful in 1m26s
Deploy / deploy-status (push) Successful in 1s
Deploy / perimeter-smoke (push) Successful in 8s
2026-08-20 08:54:26 +00:00
07b5e5e6da fix(ptica): HTML-экспортёр перестаёт выбрасывать легаси-зонирование, которое сам же проверил (#2464) (#2959)
All checks were successful
Deploy / changes (push) Successful in 8s
Deploy / build-frontend (push) Has been skipped
Deploy / deploy-caddy (push) Has been skipped
Deploy / build-backend (push) Successful in 2m31s
Deploy / build-worker (push) Successful in 3m32s
Deploy / deploy (push) Successful in 1m27s
Deploy / deploy-status (push) Successful in 1s
Deploy / perimeter-smoke (push) Successful in 9s
2026-08-20 08:44:11 +00:00
c93d6cbde1 fix(ptica): шесть lookup'ов /analyze глотали ошибку БД без SAVEPOINT (#2464) (#2951)
All checks were successful
Deploy / changes (push) Successful in 8s
Deploy / build-frontend (push) Has been skipped
Deploy / deploy-caddy (push) Has been skipped
Deploy / build-backend (push) Successful in 2m15s
Deploy / build-worker (push) Successful in 3m14s
Deploy / deploy (push) Successful in 1m28s
Deploy / deploy-status (push) Successful in 1s
Deploy / perimeter-smoke (push) Successful in 10s
2026-08-20 07:38:23 +00:00
6bca4f7ead fix(ptica): «0 мм осадков» перестаёт означать «данных нет» (#2464) (#2947)
Some checks failed
Deploy / changes (push) Successful in 13s
Deploy / deploy-caddy (push) Has been skipped
Deploy / build-backend (push) Successful in 2m57s
Deploy / build-worker (push) Successful in 4m25s
Deploy / build-frontend (push) Successful in 4m31s
Deploy / deploy (push) Failing after 5s
Deploy / perimeter-smoke (push) Has been skipped
Deploy / deploy-status (push) Failing after 1s
2026-08-20 07:00:01 +00:00
251eacc4b6 fix(ptica): сноска концепт-PDF печатает фактический срок распродажи, а не «30 мес» (#2464) (#2945)
All checks were successful
Deploy / changes (push) Successful in 8s
Deploy / build-frontend (push) Has been skipped
Deploy / deploy-caddy (push) Has been skipped
Deploy / build-backend (push) Successful in 2m13s
Deploy / build-worker (push) Successful in 4m8s
Deploy / deploy (push) Successful in 1m26s
Deploy / deploy-status (push) Successful in 1s
Deploy / perimeter-smoke (push) Successful in 9s
2026-08-19 17:39:37 +00:00
56868f2bde fix(ptica): нечисловое значение от OSRM/ORS больше не даёт 500 вместо деградации (#2464) (#2944)
All checks were successful
Deploy / changes (push) Successful in 9s
Deploy / build-frontend (push) Has been skipped
Deploy / deploy-caddy (push) Has been skipped
Deploy / build-backend (push) Successful in 2m16s
Deploy / build-worker (push) Successful in 6m44s
Deploy / deploy (push) Successful in 1m31s
Deploy / deploy-status (push) Successful in 1s
Deploy / perimeter-smoke (push) Successful in 9s
2026-08-19 17:20:28 +00:00
43c71a006d docs(ptica): три комментария, утверждавших не то, что делает код (#2464 кластер H) (#2943)
All checks were successful
Deploy / changes (push) Successful in 7s
Deploy / build-frontend (push) Has been skipped
Deploy / deploy-caddy (push) Has been skipped
Deploy / build-backend (push) Successful in 2m4s
Deploy / build-worker (push) Successful in 3m9s
Deploy / deploy (push) Successful in 1m27s
Deploy / deploy-status (push) Successful in 1s
Deploy / perimeter-smoke (push) Successful in 9s
2026-08-19 17:09:24 +00:00
8eeb35cee5 fix(ptica): дрейф формы ответа DaData не роняет весь гео-проход (#2464) (#2942)
All checks were successful
Deploy / changes (push) Successful in 9s
Deploy / build-frontend (push) Has been skipped
Deploy / deploy-caddy (push) Has been skipped
Deploy / build-backend (push) Successful in 2m24s
Deploy / build-worker (push) Successful in 3m45s
Deploy / deploy (push) Successful in 1m23s
Deploy / deploy-status (push) Successful in 1s
Deploy / perimeter-smoke (push) Successful in 9s
2026-08-19 16:59:49 +00:00
5a7d558a5c fix(ptica): пустой слой риск-зон перестаёт означать «рисков нет» (#2934) (#2940)
All checks were successful
Deploy / changes (push) Successful in 11s
Deploy / deploy-caddy (push) Has been skipped
Deploy / build-backend (push) Successful in 3m10s
Deploy / build-frontend (push) Successful in 4m33s
Deploy / build-worker (push) Successful in 4m42s
Deploy / deploy (push) Successful in 1m50s
Deploy / deploy-status (push) Successful in 2s
Deploy / perimeter-smoke (push) Successful in 13s
2026-08-19 16:34:26 +00:00
a2fbe4b400 fix(ptica): job_settings не отравляет чужую сессию при сбое БД (#2464 кластер A) (#2937)
All checks were successful
Deploy / changes (push) Successful in 8s
Deploy / build-frontend (push) Has been skipped
Deploy / deploy-caddy (push) Has been skipped
Deploy / build-backend (push) Successful in 2m24s
Deploy / build-worker (push) Successful in 3m50s
Deploy / deploy (push) Successful in 1m30s
Deploy / deploy-status (push) Successful in 1s
Deploy / perimeter-smoke (push) Successful in 10s
2026-08-19 15:42:39 +00:00
52349a39f6 fix(ptica): строка о подтоплении в PDF и DOCX перестаёт читаться как заключение (#2934) (#2935)
All checks were successful
Deploy / changes (push) Successful in 9s
Deploy / build-frontend (push) Has been skipped
Deploy / deploy-caddy (push) Has been skipped
Deploy / build-backend (push) Successful in 2m38s
Deploy / build-worker (push) Successful in 3m8s
Deploy / deploy (push) Successful in 1m50s
Deploy / deploy-status (push) Successful in 1s
Deploy / perimeter-smoke (push) Successful in 9s
2026-08-19 15:11:10 +00:00
bc489e1a6f fix(ptica): гео-проход не тратит геокод на занятых кандидатов и не «подтверждает» их (#2464) (#2929)
Some checks failed
Deploy / build-backend (push) Blocked by required conditions
Deploy / build-worker (push) Blocked by required conditions
Deploy / build-frontend (push) Blocked by required conditions
Deploy / deploy (push) Blocked by required conditions
Deploy / deploy-caddy (push) Blocked by required conditions
Deploy / perimeter-smoke (push) Blocked by required conditions
Deploy / deploy-status (push) Blocked by required conditions
Deploy / changes (push) Has been cancelled
2026-08-19 11:52:07 +00:00
e266f29d65 fix(ptica): защита таймаутом теперь реально ограничивает время (#2464-C) (#2927)
All checks were successful
Deploy / changes (push) Successful in 8s
Deploy / build-frontend (push) Has been skipped
Deploy / build-worker (push) Successful in 4m39s
Deploy / deploy-status (push) Successful in 1s
Deploy / perimeter-smoke (push) Successful in 9s
Deploy / deploy-caddy (push) Has been skipped
Deploy / build-backend (push) Successful in 2m9s
Deploy / deploy (push) Successful in 1m51s
2026-08-19 10:27:58 +00:00
67b98605e9 Merge pull request 'fix(ptica): загрузка ЦП больше не пишется в категориальную колонку (#2464-B)' (#2882) from fix/2464-eesk-load-index into main
All checks were successful
Deploy / changes (push) Successful in 7s
Deploy / build-frontend (push) Has been skipped
Deploy / build-backend (push) Successful in 2m35s
Deploy / build-worker (push) Successful in 3m15s
Deploy / deploy (push) Successful in 2m17s
Deploy / deploy-status (push) Successful in 2s
2026-08-15 19:27:50 +00:00