work_item: ORCH-098 stage: analysis author_agent: analyst status: ready-for-review created_at: 2026-06-10 model_used: claude-opus-4-8 title: "Журнал уроков: таблица, автозапись отклонений, выборка, ручная запись, never-raise" framework: pytest scope: > Покрывается: создание аддитивной таблицы lessons (идемпотентность, поля атрибуции), helper записи record(), автозапись из choke-point (gate-fail/HOLD/transient), read-only выборка get_lessons + snapshot, ручная запись/обновление, kill-switch, never-raise. Вне покрытия: ретроспективщик (E2), приоритизатор (E3), автоклассификация атрибуции, слой-3 детекция здоровья продукта. notes: > Тесты используют изолированную временную SQLite-БД (фикстура init_db во временном файле). Полный регресс tests/ должен оставаться зелёным. Self-hosting: журнал never-raise — ни один тест не должен показать, что сбой записи урока роняет конвейер. tests: - id: TC-01 type: unit description: "init_db() создаёт таблицу lessons идемпотентно (двойной вызов не падает, нет дублей); присутствуют все поля BR-1." module: tests/test_lessons.py expected: PASS - id: TC-02 type: unit description: "Схема lessons несёт нуллабельные колонки атрибуции attribution/target_repo/target_domain; запись без них проходит (NULL/unknown), update проставляет их позже." module: tests/test_lessons.py expected: PASS - id: TC-03 type: unit description: "lessons.record() вставляет строку с переданными полями (source=auto/manual), возвращает id; created_at заполняется." module: tests/test_lessons.py expected: PASS - id: TC-04 type: unit description: "never-raise: при замоканной падающей БД record/get/update/snapshot возвращают None/[]/{} и не бросают исключение (logger.warning)." module: tests/test_lessons.py expected: PASS - id: TC-05 type: unit description: "kill-switch: при lessons_enabled=False record/get/update/snapshot инертны (no-op, без обращения к БД)." module: tests/test_lessons.py expected: PASS - id: TC-06 type: unit description: "get_lessons фильтрует по type/status/repo/work_item и соблюдает limit; порядок ORDER BY id DESC." module: tests/test_lessons.py expected: PASS - id: TC-07 type: unit description: "update_lesson меняет status/attribution/target_*/related_task и стампит updated_at; несуществующий id безопасен." module: tests/test_lessons.py expected: PASS - id: TC-08 type: integration description: "Автозапись gate-fail: смоделированный откат на development в _handle_qg_failure_rollbacks создаёт строку lessons type=gate_failure с контекстом (stage/agent/work_item/repo)." module: tests/test_lessons.py expected: PASS - id: TC-09 type: integration description: "Автозапись transient/HOLD: транзиент-ветка merge_gate (или timeout/requeue launcher) пишет урок type=transient_retry/merge_hold; сбой записи не ломает основной путь (never-raise в горячем пути)." module: tests/test_lessons.py expected: PASS - id: TC-10 type: integration description: "GET /lessons возвращает 200 с массивом и фильтрами; GET /queue содержит read-only блок lessons; чтение не мутирует данные." module: tests/test_lessons.py expected: PASS - id: TC-11 type: integration description: "POST /lessons создаёт ручной урок (source=manual, с атрибуцией); POST /lessons/{id} обновляет его; при lessons_enabled=False эндпоинты отдают {enabled:false}." module: tests/test_lessons.py expected: PASS - id: TC-12 type: unit description: "Инварианты конвейера не тронуты: STAGE_TRANSITIONS/QG_CHECKS/machine-verdict-ключи неизменны (структурный анти-регресс по составу реестра)." module: tests/test_lessons.py expected: PASS