feat: закрыть остаточные P1/P2 по профилю, когорте и ведомости

- профиль приведён к docs/09: variant/level, updatedBy/name, лимит попыток
- V14: NOT NULL и полная уникальность когорты, preflight legacy-групп
- возврат группы из архива и история членства на странице группы
- пагинация групп и плоского списка материалов
- пакетные сдачи ведомости одним запросом
- XLSX-preflight импорта и audit-тесты ростера
- LDD: 66 claims, 398 тестов, release PASS
This commit is contained in:
2026-10-29 20:51:27 +03:00
parent 7ef5c866a3
commit 82394cc688
48 changed files with 1196 additions and 156 deletions

View File

@@ -211,11 +211,11 @@
Номера уточняются при реализации, чтобы не конфликтовать с максимумами master
(identity V3, **academic V5**, progress V12). Максимумы этой ветки:
identity V13, **academic V12**, progress V13.
identity V14, **academic V12**, progress V13.
| Сервис | Миграции | Содержание |
|---|---|---|
| identity | V4–V13 | роли, поля групп, статусы студентов, `membership_history`, `promotion_runs/items`, `accounts.status`; `V10` — `audit_events`; `V11` — `refresh_tokens` (хеш, срок, отзыв, цепочка замен); `V12` — `absolute_expires_at`; `V13` — keyset-индексы аудита |
| identity | V4–V14 | роли, поля групп, статусы студентов, `membership_history`, `promotion_runs/items`, `accounts.status`; `V10` — `audit_events`; `V11` — `refresh_tokens` (хеш, срок, отзыв, цепочка замен); `V12` — `absolute_expires_at`; `V13` — keyset-индексы аудита; `V14` — NOT NULL/уникальность когорты |
| academic | V6–V12 + post-bootstrap step | курсы, преподаватели, группы курса, `labs.course_id` (nullable), материалы курса/аудитории, частичные индексы, активы; `V10` — уникальный индекс `lower(title)` по курсам; `V11` — backfill `audience='ALL'` для глобальных глав; `V12` — backfill связей `article_assets` из markdown-ссылок; `SET NOT NULL` — post-bootstrap шаг вне Flyway |
| progress | V13 | `grade_comments.author_account_id`, `lab_grades.updated_by_account_id`, индекс `lab_grades(lab_id)`; аудит реализован в identity `V10` |
@@ -336,24 +336,29 @@ identity V13, **academic V12**, progress V13.
- **Этап 6 — выполнен.** Права преподавателя на запись (лабораторные,
ведомость, группы, материалы), центральный аудит `identity.audit_events`
с `/admin/audit`, CI merge gate и полный hardening. Проверены прогон
тестов/линтеров (**385**, 0 failures), upgrade миграций с master-схемы
(identity `V4–V13`, academic `V6–V12`, progress `V13`), security-матрица,
gateway, видимость активов и deploy-runbook. LDD baseline: **59 claims
(27 P0 / 32 P1), P0 gaps = 0**, browser E2E 18/18 (3 браузера). Отчёт:
тестов/линтеров (**398**, 0 failures), upgrade миграций с master-схемы
(identity `V4–V14`, academic `V6–V12`, progress `V13`), security-матрица,
gateway, видимость активов и deploy-runbook. LDD baseline: **66 claims
(27 P0 / 39 P1), P0 gaps = 0**, browser E2E 18/18 (3 браузера). Отчёт:
[16-stage6-report.md](16-stage6-report.md).
### 9.1. Известные пробелы после этапа 6
- **Импорт.** Старый формат не поддерживается, принимается только шаблон v2.
Номер группы — числовой; отдельного требования к ведущим нулям нет.
CSV/XLSX preflight покрыт тестами; гонка с параллельной вставкой логина
всё ещё может дать общий 409.
- **Пагинация и N+1.** Аудит продолжается keyset-курсором `(occurred_at, id)`
с offset-fallback для старых ссылок; `GET /api/v1/labs/details` отдаёт все
лабораторные курса с уровнями и вариантами одним запросом; группы курса —
одним вызовом. Ведомость по-прежнему запрашивает сдачи по выбранной
лабораторной — это не N+1 по студентам.
с offset-fallback; `GET /api/v1/labs/details` отдаёт все лабораторные курса
одним запросом; группы курса — одним вызовом. Сдачи ведомости собираются
одним запросом на все выбранные лабы; группы и плоский список материалов
поддерживают `limit`/`offset`. Страница группы и профиль имеют собственные
лимиты; дерево материалов по-прежнему загружается целиком для навигации.
- **Legacy-активы.** Файлы без статьи уже не публичны; админская страница
`/admin/legacy-assets`, API и `scripts/legacy-assets.sh` готовы. Остаётся
прогнать инвентаризацию на production/staging и привязать или удалить файлы.
- **Визуальная регрессия.** Playwright покрывает сценарии и mobile, но
screenshot-diff отсутствует.
- **Сессии.** Web-сессия скользящая 7 дней, абсолютный потолок 30 дней
(`LMS_SESSION_ABSOLUTE_TIMEOUT`); истёкшие refresh-токены чистит ночная
задача (`LMS_REFRESH_TOKEN_RETENTION`).
@@ -378,7 +383,7 @@ identity V13, **academic V12**, progress V13.
- Access/refresh-токены и web-сессия работают по срокам 15 минут / 7 дней /
7 дней sliding с абсолютным потолком 30 дней; logout, смена пароля и
закрытие доступа отзывают refresh.
- Security-матрица и миграционный upgrade-тест зелёные (identity до `V13`).
- Security-матрица и миграционный upgrade-тест зелёные (identity до `V14`).
- CI (Gitea + Woodpecker) на каждом push/MR прогоняет
`test ktlintCheck detekt evidenceCheck` и smoke полного стека; полный
`release` обязателен для тегов.