Files
magistr/BUG_FIX_PROGRESS.md
2026-07-13 03:28:18 +03:00

279 lines
33 KiB
Markdown
Raw 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.
# Журнал исправления проблем
Обновлено: 2026-07-13 (Europe/Moscow).
## Область и неизменяемые данные
- В работе ровно 33 проблемы: `1` и `334`.
- Проблема № 2 исключена: демонстрационные учётные записи, seed-данные и документация по ним не изменяются.
- `BUG_REPORT.md` не редактируется.
- Существующие Flyway-миграции не редактируются.
Исходное рабочее дерево перед началом реализации было чистым (`git status --short` без вывода).
Контрольные суммы исходных миграций:
| Файл | SHA-256 |
|---|---|
| `V1__init.sql` | `404f65f270dd87f4db996495fc970456c4fd0f762e7c32638199dffaa812ea7b` |
| `V2__subgroups_active_unique_name.sql` | `68f5525a50ddba4f8800a63cab3cd0f84779e301c4a1f29a6e0399f97fbdb30b` |
## Исходная линия проверок
| Проверка | Результат до изменений |
|---|---|
| `mvn -f backend/pom.xml test` | Maven на хосте отсутствует. Контейнерный эквивалент `maven:3.9-eclipse-temurin-17` завершён успешно: 30 тестов, 0 failures, 0 errors. |
| `find frontend ... node --check` | Успешно. |
| `docker compose config --quiet` | Успешно. |
| `kubectl kustomize ../k8s >/dev/null` | Успешно. |
| `bash -n ../k8s/deploy.sh` | Успешно. |
| `git diff --check` | Успешно. |
## Порядок выполнения
1. Безопасная production-конфигурация и внешние секреты: № 1 (с зависимой частью № 14).
2. Аутентификация и границы запроса: № 3, 4, 24, 25, 28.
3. Высокоприоритетные инварианты расписания: № 58.
4. Tenant lifecycle и Kubernetes coordination: № 9, 10, 13, 14.
5. Frontend-сессия и достоверность UI: № 11, 12, 33, 34.
6. Календарные и DB-инварианты: № 1517, 21, 31, 32.
7. История, права, масштаб и производительность: № 1823.
8. Локальный запуск, CI/CD, воспроизводимые артефакты и timezone: № 26, 27, 29, 30.
9. AutoUpdateDocs, полный регрессионный прогон и финальный аудит 33 пунктов.
## Чек-лист
Статусы меняются только после реализации и воспроизводимой проверки.
| № | Статус | Подтверждённая первопричина | Решение и основные файлы | Проверка |
|---:|---|---|---|---|
| 1 | требуется внешнее действие | В коде и production YAML были встроенные/placeholder JWT и DB credentials; tenant и OTel credentials находились в отслеживаемой конфигурации; Secure-cookie не был обязательным для production. | Встроенный JWT default удалён; добавлен fail-fast validator; production использует только внешние `app-secret`, `tenants-secret`, `otel-postgres-secret`; создан `docs/SECURITY_RUNBOOK.md`. Репозиторная часть исправлена. Оператору остаются ротация, provisioning, rollout и очистка истории. | 11 JWT unit/context tests; общий backend: 45/0/0; `scripts/check-production-secrets.sh`; Kustomize и shell syntax — успешно. |
| 3 | исправлено и проверено | Ротация refresh-токена выполняла read-check-write без блокировки/условного update. | `RefreshTokenService.rotate()` использует блокирующий `findByTokenHashForUpdate()` с `PESSIMISTIC_WRITE` и `JOIN FETCH` внутри транзакции. | Два синхронных запроса к Spring proxy и PostgreSQL: один успех, один отказ, один активный новый токен. Полный backend: 46/0/0/0. |
| 4 | исправлено и проверено | `AuthContext` устанавливался до role-check и сохранялся в servlet-потоке, когда `preHandle=false` не приводил к `afterCompletion`. | `AuthorizationInterceptor` очищает контекст в начале каждого запроса и устанавливает пользователя только после успешной проверки ролей; финальная очистка сохранена. | Один MockMvc-поток: `403`, публичный запрос, разрешённый запрос и очистка после завершения. Полный backend: 47/0/0/0. |
| 5 | исправлено и проверено | Override сохранялся без доказательства исходной пары, action-specific полей и ресурсных конфликтов. | `ScheduleOverrideService` строит base/effective day, валидирует матрицу и фактическое изменение, проверяет ресурсы и сериализует CRUD через PostgreSQL advisory locks; V3 закрепляет структурные инварианты. | 27 целевых unit/MockMvc/PostgreSQL tests; полный backend: 74/0/0/0. |
| 6 | исправлено и проверено | Teacher-only поиск загружал только базовые правила исходного преподавателя до применения override. | Один snapshot overrides; релевантные `newTeacher`-замены достраивают exact base occurrences по уникальным датам, затем применяются overrides, финальный teacher-фильтр и deduplicate. | 5 новых A→B/phantom/cache/dedup/regression тестов; узкий прогон 10/0/0/0, полный backend 79/0/0/0. |
| 7 | исправлено и проверено | Контроллер сравнивал новые слоты только с сохранёнными правилами, не проверял роль преподавателя и enum формата, а нечётные часы приводили к перерасходу генератора. | `ScheduleRuleService` валидирует весь кандидат до мутации, попарно проверяет дубли/ресурсы, требует `TEACHER`, `Очно`/`Онлайн` и чётные часы; строка семестра сериализует запись. V4 закрепляет чётность и точный дубль в БД. | 23 service/MockMvc/Flyway/PostgreSQL tests; полный backend: 102/0/0/0. |
| 8 | исправлено и проверено | `saveGrid` удалял данные до полной валидации и ловил исключение внутри transactional controller-метода, поэтому Spring коммитил delete и уже обработанные строки. | `AcademicCalendarGridService` полностью валидирует и строит replacement до delete, атомарно заменяет строки и очищает кэш через `afterCommit`; контроллер стал HTTP-адаптером. | 8 unit + 1 PostgreSQL proxy test; fingerprint старой сетки после отказа неизменен; полный backend 111/0/0/0. |
| 9 | в работе | При update старый pool закрывается до создания нового; Hikari допускает ленивый нерабочий pool, Flyway проглатывает ошибку, а отказ Secret-персистенции оставляет локальное состояние изменённым. | Реализуются prepare/validate/migrate кандидата, persistence до atomic swap и компенсация Secret при ошибке активации. | Готовятся unit и PostgreSQL lifecycle tests для credentials/migration/persistence/swap. |
| 10 | не начато | Два pod перезаписывают целый tenant-документ без `resourceVersion`; TCP probes не отражают готовность tenant-БД. | Optimistic locking с retry; отдельные liveness/readiness semantics. | Конкурентные PATCH и health tests; Kustomize-проверка probes. |
| 11 | не начато | Sunday вычисляется повторным мутированием даты; ошибки запросов превращаются в пустые результаты и зелёный статус. | Чистая local-date утилита и три явных состояния проверки. | 2026-07-02, переход года, частичная/полная ошибка API. |
| 12 | не начато | Access JWT хранится в Web Storage; origin исполняет удалённые модули; CSP допускает несовместимые сценарии. | Access JWT только в памяти, восстановление через HttpOnly refresh-cookie; локальные pinned assets; строгий CSP и удаление inline JS. | Login/refresh/logout/reload tests; статический аудит CSP/storage/imports. |
| 13 | не начато | Watcher сравнивает только домены и фиксирует hash до успешной полной синхронизации. | Нормализованное сравнение полного `TenantConfig`, hash после успеха, bounded backoff. | Изменение URL/credentials и retry после ошибки. |
| 14 | исправлено и проверено | Kubernetes HTTP-клиент имел trust-all TLS fallback. | `KubernetesTenantSecretUpdater` загружает service-account CA, строит PKIX trust store, включает HTTPS hostname verification и не имеет небезопасного fallback. | Mock HTTPS: доверенный CA принят; чужой CA, неверный hostname и отсутствующий CA отклонены. Backend 45/0/0. |
| 15 | не начато | Не проверяются `start < end` и пересечения; duration доверяется клиенту; используемый DEFAULT scope можно изменить. | Централизованный валидатор, backend duration, DB/concurrency protection. | Overlap/adjacent/scope/concurrency tests. |
| 16 | не начато | Create/update проверяют лишь порядок дат, но не пересечения и принадлежность семестра году. | Единый calendar validator и PostgreSQL range/exclusion constraints. | Create/update + конкурентные PostgreSQL tests. |
| 17 | не начато | Изменение измерений группы/календаря оставляет несовместимые assignments/grid/subjects. | Отклонять несовместимое изменение транзакционно с `409`; контролировать `courseCount`. | Assignment сохранён после отказа; старшие строки не остаются. |
| 18 | не начато | Бизнес-решения смешивают `users.department_id` с датированными assignments и фильтруют архивные записи по текущему состоянию. | Date-aware resolver кафедры и единое использование истории. | Будущий перевод, прошлый период, архивный преподаватель, совместительство. |
| 19 | не начато | Workload принимает произвольный `departmentId` для роли `DEPARTMENT`. | Принудительный scope из `AuthContext`; глобальный доступ только разрешённым ролям. | Межкафедральный запрос кафедры не раскрывает данные. |
| 20 | не начато | Агрегаты переиспользуют интерактивный широкий поиск с лимитом 50 групп. | Специализированная batch/aggregate генерация без UI-лимита. | 51 и 100 активных групп. |
| 21 | не начато | Импорт ищет дисциплину по глобальному имени и меняет её владельца. | `409` для чужой записи, идемпотентность своей; scoped uniqueness при миграции. | Повторный свой импорт и совпадение чужой кафедры. |
| 22 | не начато | Видимые EDUCATION_OFFICE экраны вызывают backend endpoints с ADMIN-only правами. | Единая role matrix для backend/frontend; согласовать read/write capabilities. | Интеграционный сценарий календаря и форм обучения без штатного `403`. |
| 23 | не начато | Генератор повторно запрашивает semester/assignment/calendar day на каждой комбинации. | Батч-загрузка и request-scoped lookup maps. | Query-count test на 120 датах и десятках групп. |
| 24 | не начато | Истёкшие/отозванные refresh rows не удаляются. | Tenant-aware scheduled cleanup с retention и безопасностью двух pod. | Active/fresh rows сохраняются; cleanup идемпотентен и конкурентно безопасен. |
| 25 | не начато | Нет общего mapping DB constraints; контроллеры возвращают клиенту `getMessage()`. | Централизованный sanitizer/mapping `400`/`409`, убрать raw exception body. | Известный/неизвестный constraint без JDBC details. |
| 26 | не начато | Compose требует внешнюю сеть, не публикует HTTP, рассогласует DB/JWT и не имеет named volume. | Самодостаточная сеть/reverse proxy, согласованные env и volume. | Чистый `docker compose up -d --build`, HTTP `/` и `/api`. |
| 27 | не начато | Pipeline собирает/деплоит без обязательных тестов и для tag перезапускает mutable `main`. | Test gates, concurrency, immutable tag/digest rollout, rollback check. | Workflow lint/static inspection и shell tests. |
| 28 | не начато | Login не имеет shared rate limit; IP безусловно доверяет forwarded header. | PostgreSQL-backed tenant+trusted-IP+username limiter, lockout/audit, `429`/`Retry-After`. | Multi-instance integration tests, отсутствие user enumeration. |
| 29 | не начато | Business dates зависят от timezone процесса; frontend date-only строится через UTC. | Инъекция `Clock`/`Europe/Moscow`, UTC timestamps, локальное форматирование date-only, TZ контейнеров. | Boundary tests 00:0003:00 и первые дни месяца. |
| 30 | не начато | Образы/скачиваемые инструменты используют mutable tags/latest без checksum/SBOM/scan. | Pin versions+digests, SHA verification, SBOM и image scan gates. | Static scan production refs и CI configuration. |
| 31 | не начато | Group create/update допускает неположительные значения и уменьшение ниже активных подгрупп. | Общий validator, locking, новые CHECK constraints. | Create/update/concurrency и migration tests. |
| 32 | не начато | `between(start,end) > 120` разрешает 121 включительную дату. | Проверять `between + 1 <= 120` в обоих сервисах. | Границы 120/121. |
| 33 | не начато | Несколько `e.message` вставляются как HTML; парольные поля имеют `type=text`. | `textContent`/экранирование; `type=password` и autocomplete. | DOM/XSS static/unit test и HTML assertions. |
| 34 | не начато | В затронутых UI/log paths остаются английские метки/логи и raw exception text. | Русификация и безопасные сообщения на границе API/UI. | Поиск английских production сообщений и regression tests. |
## Завершённые этапы
### № 1 и зависимая № 14
- `JwtProperties` больше не содержит секрет по умолчанию; production startup отклоняет пустое, короткое, известное legacy/placeholder значение и `JWT_REFRESH_COOKIE_SECURE=false`.
- `../k8s/config.yaml` больше не создаёт Secret с литеральными значениями.
- `../k8s/backend.yaml` монтирует tenant-конфигурацию из `tenants-secret`.
- `../k8s/otel-collector.yaml` использует только `${env:...}` ссылки и `otel-postgres-secret`.
- `KubernetesTenantSecretUpdater` заменил `ConfigMapUpdater`, использует base64-поле Kubernetes Secret, service-account CA и hostname verification.
- `../k8s/deploy.sh` проверяет обязательные Secrets/ключи до rollout без вывода значений.
- Созданы `docs/SECURITY_RUNBOOK.md` и `scripts/check-production-secrets.sh`; документация синхронизирована через AutoUpdateDocs.
Фактические проверки этапа:
| Команда | Результат |
|---|---|
| Контейнерный `mvn test` | 45 тестов, 0 failures, 0 errors, BUILD SUCCESS. |
| `bash scripts/check-production-secrets.sh` | Успешно. |
| `kubectl kustomize ../k8s >/dev/null` | Успешно. |
| `bash -n ../k8s/deploy.sh` | Успешно. |
| `git diff --check` | Успешно. |
| Повторные SHA-256 V1/V2 | Совпадают с исходными. |
Изменённые вне Git-корня файлы: `../k8s/config.yaml`, `backend.yaml`,
`otel-collector.yaml`, `rbac.yaml`, `deploy.sh`, `README.md`. Их состояние проверяется
отдельно от `git diff`.
### № 3 — атомарная ротация refresh-токена
- Репозиторий захватывает строку исходного токена через `PESSIMISTIC_WRITE` и загружает
пользователя в том же запросе.
- `RefreshTokenService.rotate()` выполняет проверку активности, отзыв исходного токена и
создание следующего токена в одной транзакции после захвата блокировки.
- Добавлен интеграционный тест с двумя синхронными потоками, Spring transactional proxy,
Flyway V1/V2 и реальным PostgreSQL 16.3 через Testcontainers.
- Одновременная ротация подтверждает ровно один успешный результат, один отказ, две строки
в БД и ровно один активный следующий токен.
- `docs/ARCHITECTURE.md` и `docs/API.md` синхронизированы через AutoUpdateDocs.
Фактические проверки этапа:
| Команда | Результат |
|---|---|
| Контейнерный узкий `mvn -Dtest=RefreshTokenServiceTest,RefreshTokenServiceConcurrencyIntegrationTest test` | 5 тестов, 0 failures, 0 errors, 0 skipped, BUILD SUCCESS. |
| Контейнерный полный `mvn test` | 46 тестов, 0 failures, 0 errors, 0 skipped, BUILD SUCCESS. |
| `git diff --check` | Успешно. |
### № 4 — границы `AuthContext`
- `AuthorizationInterceptor.preHandle()` очищает `AuthContext` до проверки URI, метода,
публичного endpoint и bearer-токена.
- Аутентифицированный пользователь помещается в `ThreadLocal` только после успешной
проверки `@RequireRoles`; ответы `401` и `403` оставляют контекст пустым.
- `afterCompletion()` продолжает очищать контекст после успешно допущенного запроса.
- Регрессионный MockMvc-тест выполняет на одном JUnit-потоке запрещённый защищённый,
публичный и разрешённый защищённый запросы и проверяет контекст внутри обработчиков и
после завершения.
- `docs/ARCHITECTURE.md` синхронизирован через AutoUpdateDocs.
Фактические проверки этапа:
| Команда | Результат |
|---|---|
| Контейнерный `mvn -Dtest=AuthorizationInterceptorTest test` | 1 тест, 0 failures, 0 errors, 0 skipped, BUILD SUCCESS. |
| Контейнерный полный `mvn test` | 47 тестов, 0 failures, 0 errors, 0 skipped, BUILD SUCCESS. |
| `git diff --check` | Успешно. |
### № 5 — безопасные точечные изменения расписания
- `ScheduleOverrideController` стал тонким HTTP-адаптером, а CRUD перенесён в публичные
транзакционные методы `ScheduleOverrideService`.
- Перед сохранением строится реальная базовая пара на дату с учётом семестра, календаря,
чётности, lifecycle и остатка часов; несуществующая occurrence возвращает `400`.
- Матрица `CANCEL`/`MOVE`/`REPLACE`, формат и action-specific фактическое изменение
проверяются до мутации managed entity.
- Сохранённые overrides и кандидат применяются общей логикой `ScheduleQueryService`;
полуоткрытые интервалы проверяются по преподавателю, аудитории, целой группе и
подгруппам. Конфликт возвращается как русский `409 Conflict`.
- PostgreSQL transaction advisory lock сериализует изменения даты между pod. Update/delete
дополнительно блокируют ID; старая и новая даты update захватываются в стабильном порядке,
после чего строка перечитывается с `PESSIMISTIC_WRITE`.
- Добавлена `V3__schedule_override_invariants.sql`: preflight legacy-строк, CHECK для
payload каждого действия и формата. Миграционный тест подтверждает полный rollback на
невалидном legacy `REPLACE` и clean upgrade до V3.
- `docs/API.md`, `docs/BUSINESS_LOGIC.md`, `docs/DATABASE.md` и `docs/ARCHITECTURE.md`
синхронизированы через AutoUpdateDocs.
Фактические проверки этапа:
| Команда | Результат |
|---|---|
| Целевые контейнерные Maven-прогоны № 5 | 22 unit/MockMvc/PostgreSQL теста, затем 24 усиленных unit/MockMvc теста; оба прогона без failures/errors/skipped. Все 27 тестов № 5 входят в полный прогон. |
| Контейнерный полный `mvn test` | 74 теста, 0 failures, 0 errors, 0 skipped, BUILD SUCCESS. |
| `git diff --check` | Успешно. |
| Повторные SHA-256 V1/V2 | Совпадают с исходными. |
| SHA-256 новой V3 | `eb5315b58a2bad95b0ad36718052788168be0d8f347649c5c98782949865357b`. |
### № 6 — замена преподавателя в результирующем расписании
- `ScheduleQueryService.search()` читает overrides диапазона один раз и переиспользует
один снимок для достраивания и применения изменений.
- Teacher-only путь по-прежнему строит базовое расписание преподавателя напрямую. Только
при наличии overrides с совпадающим `newTeacher` он группирует целевые слоты по дате и
один раз строит полный базовый день каждой релевантной даты.
- Из дня добавляются только occurrences с точным `baseRuleSlotId + lessonDate`; отсутствующая
базовая пара не создаёт фантомное занятие.
- Overrides применяются до окончательного teacher-фильтра: новый преподаватель видит замену,
исходный — нет. Итоговая дедупликация удаляет совпавшие базовые и достроенные фрагменты.
- `CANCEL` и `MOVE` продолжают применяться из того же snapshot; при отсутствии релевантной
замены `groupRepository.findAll()` не вызывается.
- `docs/API.md`, `docs/BUSINESS_LOGIC.md` и `docs/ARCHITECTURE.md` синхронизированы через
AutoUpdateDocs.
Фактические проверки этапа:
| Команда | Результат |
|---|---|
| Узкий контейнерный Maven-прогон трёх `ScheduleQueryService*Test` | 10 тестов, 0 failures, 0 errors, 0 skipped, BUILD SUCCESS. |
| Контейнерный полный `mvn test` | 79 тестов, 0 failures, 0 errors, 0 skipped, BUILD SUCCESS. |
| `git diff --check` | Успешно. |
### № 7 — полная валидация правил расписания
- `ScheduleRuleAdminController` стал тонким HTTP-адаптером; create/update/archive перенесены
в публичные транзакционные методы `ScheduleRuleService`.
- До обращений к справочникам сервис проверяет обязательные поля, неотрицательность и
чётность каждого лимита часов, положительную сумму, недели начала и структуру payload.
Ноль разрешён для неиспользуемого типа занятия.
- Слот принимает только активного пользователя с ролью `TEACHER`, базовый временной слот и
формат `Очно`/`Онлайн`; отсутствующая вложенная ссылка возвращает безопасный `400`.
- Кандидат строится отдельно от managed entity. Точные дубли и ресурсные конфликты слотов
нового payload проверяются попарно до сохранения; `ODD` и `EVEN` разрешены одновременно,
а `BOTH` пересекается с ними. Конфликтный update не изменяет существующее правило.
- Создание первым DB-запросом блокирует строку семестра. Update блокирует строку правила и
старый/новый семестры в стабильном порядке; реальный PostgreSQL-тест подтверждает, что из
двух параллельных create проходит один, а второй после commit видит `409 Conflict`.
- `V4__schedule_rule_even_hours.sql` выполняет русский preflight нечётных legacy-часов и
точных дублей слотов, затем добавляет провалидированный CHECK чётности и UNIQUE точного
payload слота. При legacy-нарушении V4 полностью откатывается и остаётся версия V3.
- `docs/API.md`, `docs/BUSINESS_LOGIC.md`, `docs/DATABASE.md` и `docs/ARCHITECTURE.md`
синхронизированы через AutoUpdateDocs.
Фактические проверки этапа:
| Команда | Результат |
|---|---|
| Целевые service/MockMvc/Flyway/PostgreSQL прогоны № 7 | 23 теста, 0 failures, 0 errors, 0 skipped. |
| Контейнерный полный `mvn test` | 102 теста, 0 failures, 0 errors, 0 skipped, BUILD SUCCESS. |
| `git diff --check` | Успешно. |
| Повторные SHA-256 V1/V2/V3 | Совпадают с зафиксированными значениями. |
| SHA-256 новой V4 | `211babace0f1620cac2186b42b6d3039c8ee8518284473b49da0a4d47186e490`. |
### № 8 — атомарная замена календарной сетки
- `AcademicCalendarController.saveGrid()` больше не содержит транзакцию, bulk delete,
построчное сохранение и внутренний catch; endpoint делегирует публичному
`AcademicCalendarGridService.replaceGrid()`.
- Сервис до первого write полностью валидирует непустой payload: `null`-строки, границы
курса и учебного года, согласованность `weekNumber` и ISO `dayOfWeek` с датой, наличие
activity type и уникальность `(courseNumber, date)`.
- Все `AcademicCalendarDay` строятся в памяти до `deleteByCalendarId`. Валидный набор
сохраняется как `delete → flush → saveAll → flush` в одной транзакции; исключения выходят
за Spring proxy и приводят к rollback.
- Очистка кэша зарегистрирована как `afterCommit`, поэтому не выполняется при rollback и не
создаёт окно повторного кэширования старой сетки между flush и commit.
- PostgreSQL-тест сравнивает count+MD5 старой seed-сетки: payload с корректной первой и
ошибочной второй строкой оставляет fingerprint неизменным и не добавляет первую строку;
следующий валидный запрос атомарно оставляет ровно две новые строки.
- `docs/API.md`, `docs/BUSINESS_LOGIC.md` и `docs/ARCHITECTURE.md` синхронизированы через
AutoUpdateDocs.
Фактические проверки этапа:
| Команда | Результат |
|---|---|
| Unit `AcademicCalendarGridServiceTest` | 8 тестов, 0 failures, 0 errors, 0 skipped. |
| PostgreSQL `AcademicCalendarGridServiceIntegrationTest` | 1 тест, 0 failures, 0 errors, 0 skipped; rollback/fingerprint и after-commit подтверждены. |
| Контейнерный полный `mvn test` | 111 тестов, 0 failures, 0 errors, 0 skipped, BUILD SUCCESS. |
| `git diff --check` | Успешно. |
## Точка продолжения
Текущий этап: **№ 9 — безопасная замена tenant DataSource**.
Следующая операция:
1. проверить актуальные create/update потоки `DatabaseController`, swap/remove API
`TenantRoutingDataSource`, Flyway и персистенцию tenant-конфигурации;
2. определить порядок prepare → connection validation → Flyway → persistence → atomic swap,
не закрывающий рабочий pool до полной готовности кандидата;
3. добавить компенсирующий rollback для ошибок персистенции/активации и явные русские
ошибки без ложного `200`;
4. покрыть неверные credentials, ошибку миграции и ошибку персистенции тестами, доказывающими
сохранность старого подключения;
5. выполнить полный прогон и AutoUpdateDocs перед изменением статуса № 9.