# Проверка членства в СРО: эксплуатация Источник `sro_membership_check`, группа/тип `sro_membership`, карточка `sro-membership-check`. Upstream — сайт `reestr-sro.ru`; внутренний справочник организаций используется для точной привязки UID и ОКПО. Другие внешние реестры загрузчик не опрашивает. ## Доступ и пределы запросов По умолчанию сбор закрыт. Для открытия необходимы одновременно `SRO_UPSTREAM_ACCESS_APPROVED=true` и непустой `SRO_UPSTREAM_APPROVAL_REFERENCE` — ссылка/номер документированного разрешения владельца сайта на автоматизированные query/search запросы. Согласование разработки внутри проекта не заменяет разрешение владельца upstream. Условия разрешения должны быть совместимы с настройками загрузчика; более строгие пределы задаются перед включением. Секреты в approval reference хранить нельзя. Оба ручных entrypoint проверяют этот gate до enqueue; task и HTTP клиент повторяют проверку перед внешним IO. Закрытый gate возвращает typed `409 upstream_access_not_approved`. Если разрешение отозвано после постановки, job завершается с ошибкой, без успешной пустой публикации. | Настройка | По умолчанию | Значение | | --- | --- | --- | | `SRO_REQUEST_INTERVAL_SECONDS` | `3` | Минимум 3 секунды между запросами, включая redirects/retries; можно увеличить. | | `SRO_HTTP_MAX_RESPONSE_BYTES` | `2097152` | Предел распакованного тела одного ответа. | | `SRO_HTTP_TIMEOUT_SECONDS` | `30` | Read timeout; connect timeout 10 секунд. | | `SRO_MAX_REQUESTS_PER_RUN` | `10000` | Общий предел запросов одного запуска, включая redirects/retries/sitemaps. | HTTP клиент делает не более трёх попыток при транспортной ошибке и ответах 429/500/502/503/504. `Retry-After` учитывается как секунды или HTTP date, ожидание свыше 300 секунд завершает запуск ошибкой. Цепочка redirects ограничена пятью ответами; каждый адрес проверяется до обращения. Разрешён только HTTPS без credentials/нестандартного порта, на `reestr-sro.ru` и его поддоменах. Переменные окружения HTTP proxy автоматически не используются. HTML/query с идентификаторами не выводится в application logs. ## Публикация и обновление Celery task: `parsers.sro_membership_check.refresh`; Python entrypoint: `apps.parsers.tasks_registry_snapshots.parse_sro_membership`, keyword args `requested_by_id=None`, `mode="incremental"|"full"`. Проверяются только организации текущего канонического справочника. Инкрементальный запуск выбирает новые организации и те, у которых изменились name/full_name, ИНН/ОГРН/ОКПО. Изменение определяется fingerprint; у модели Organization нет универсального `updated_at`. Полный запуск проверяет весь справочник. Lookup использует точный ОГРН, при его отсутствии/неподходящем формате — ИНН с проверкой нормализованного имени. Если нет пригодного идентификатора, запуск отклоняется с `sro_candidate_identifier_missing`, без удаления прежних записей. Ответ дополнительно сопоставляется по ИНН+ОГРН через внутренний индекс. Отсутствие ОКПО, конфликт идентификаторов, неоднозначная организация, неизвестный статус, неполные обязательные поля или missing/unsafe SRO URL отправляют строку в quarantine. Новые организации из внешнего ответа не создаются. Одна запись соответствует паре canonical organization UID + SRO ID. Record UID стабилен между загрузками, включая переход `active` → `excluded`. SRO ID/URL берётся из same-site ссылки или точного ID, разрешённого по same-site sitemap; по одному похожему названию URL не угадывается. Для даты допуска читается same-site страница members по ИНН. Неизвестная дата остаётся `null` с причиной `not_found`, `sro_page_unresolved` или `parse_error`. Наблюдаемая дата реестра не подменяется датой загрузки. Raw ответы сохраняются в приватном ZIP artifact с manifest URL→файл; отдельные staged rows содержат source fields, нормализованный payload и причину quarantine. Для SRO используется отдельный `FileSystemStorage`: `PARSER_PRIVATE_ARTIFACT_ROOT` по умолчанию равен `/private-parser-artifacts`, вне `MEDIA_ROOT`. Путь обязан быть абсолютным и не находиться внутри `MEDIA_ROOT`, в том числе после разрешения symlink. Ошибка конфигурации запрещает файловую операцию. Приватный storage не выдаёт публичный URL; файлы создаются с правами `0600`, каталоги `0700`. Остальные источники сохраняют прежний `default_storage`. В контейнерном окружении задайте `PARSER_PRIVATE_ARTIFACT_ROOT=/app/private-parser-artifacts` и подключите к web/worker один постоянный volume по этому пути. Каталог должен быть доступен пользователю приложения для записи и не должен монтироваться в public media, static или reverse-proxy document root. Существующая retention-команда удаляет private файлы через тот же `artifact.file.delete()`; удаление/пересоздание контейнера не должно удалять volume. Миграция `0037` меняет storage в состоянии модели без перемещения данных. До этой версии живой SRO сбор не запускался. Если оператор ранее создал SRO artifacts в старом MEDIA_ROOT вручную, их требуется отдельно перенести в private root с сохранением относительных имён и убрать публичные копии до открытия источника; автоматического fallback на public raw нет. Публикация записей, checkpoint организаций, load log и terminal job выполняется в одной транзакции. Инкрементальная публикация заменяет данные только успешно проверенного набора организаций; записи остальных организаций сохраняются. Transport/schema failure или отменённая job откатывает публикацию и checkpoints. Допустимый пустой ответ удаляет старые членства проверенной организации. Любая семантически некорректная строка остаётся в quarantine и отклоняет весь запуск с `sro_incomplete_membership_scan`. Это сохраняет прежние членства и checkpoints: неизвестный статус или неразрешённая ссылка не доказывают, что членство исчезло. Организация остаётся кандидатом следующего incremental запуска. Если incremental не нашёл изменившихся организаций, batch имеет нулевую дельту и наследует source registry date/version последней успешной публикации; ссылка на неё сохраняется в `metadata.base_snapshot_artifact_id`. Данные не переписываются. После commit безопасно ставится существующая задача обновления общих export artifacts. Ошибка брокера после commit не превращает успешную публикацию в failure; готовность выгрузки отражает собственный контракт export service. ## Счётчики и диагностика `artifact.published_count`, `result.published_records_count` и `load_log.records_count` содержат размер текущего опубликованного набора источника. Для incremental это общий результат, включая неизменённые организации. `metadata.snapshot_records_count` и `snapshot_organizations_count` дают такие же общие record/distinct organization counts; `batch_published_records_count` и `updated_records_count` — число опубликованных строк текущей дельты. `parsed_count`, quarantine и candidate/queried counts относятся к текущей проверке; складывать их с общим published count нельзя. Источник публикует весь собственный справочник; карточка/list/dashboard для трёх новых источников используют весь published scope. Coverage ОПК показывается отдельно. МСП и бюджет продолжают полную замену: новый scoped аргумент публикации по умолчанию `None` сохраняет их поведение. Для расследования используют job status/error, artifact status, `metadata.rejection_reason`, `rejection_reasons` и staged disposition/reason. Они содержат ограниченные коды ошибок, не raw HTML. Partial raw ZIP сохраняется при ошибке уже начатого сбора; предыдущая публикация остаётся доступна. Приватные raw artifacts удерживаются минимум 90 дней и минимум 10 последних успешных SRO batches; неуспешные batches не вытесняют эти десять успешных. ## Расписания и граница проверки Миграция `parsers.0036_sro_disabled_schedules` создаёт **выключенные** schedules: daily incremental в 04:00 Europe/Moscow; monthly full 16-го числа в 04:00 Europe/Moscow — после наблюдаемого обновления реестра 15-го. Перед включением нужно подтвердить фактическую периодичность/условия разрешения. Расписание само не открывает gate; оба механизма по умолчанию выключены. На 14.09.2026 query/search и живой SRO snapshot не запускались. Основная страница без query доступна через read-only web; свежий robots.txt получить не удалось. Указанные в source-first спецификации `Crawl-delay: 3` и query/search disallow учтены консервативно. DOM selector/mapping проверены синтетическими fixtures формы из `docs/source-integration/sources/sro-membership-check/backend-api.md`, а не полным live scan. После разрешения владельца обязательна ограниченная проверка фактической разметки перед первой полной загрузкой.