72 lines
4.9 KiB
Markdown
72 lines
4.9 KiB
Markdown
---
|
||
verdict: APPROVED
|
||
work_item: ORCH-098
|
||
stage: review
|
||
author_agent: reviewer
|
||
status: approved
|
||
created_at: 2026-06-10
|
||
model_used: claude-opus-4-8
|
||
type: review
|
||
work_item_id: ORCH-098
|
||
version: 1
|
||
---
|
||
|
||
# Review ORCH-098 — FND: машинный журнал уроков
|
||
|
||
## Summary
|
||
|
||
Реализация полностью соответствует ТЗ (`02-trz.md`), критериям приёмки (`03-acceptance-criteria.md`)
|
||
и ADR-001/adr-0034. Введён чистый observer-leaf `src/lessons.py` (never-raise, единственный
|
||
kill-switch `lessons_enabled`, без repo-скоупа — по решению D2), аддитивная идемпотентная таблица
|
||
`lessons` с нуллабельными колонками атрибуции сразу (NFR-6, требование Славы 10.06), 4 типа
|
||
автозаписи best-effort, дедуп для `auto`, три HTTP-эндпоинта + блок `lessons` в `GET /queue`.
|
||
|
||
**Инварианты конвейера не тронуты (AC-8):** `src/stages.py` (`STAGE_TRANSITIONS`), `src/qg/checks.py`
|
||
(`QG_CHECKS`/`check_*`), `src/merge_gate.py`, machine-verdict-ключи и схемы существующих таблиц —
|
||
**диффом не затронуты** (подтверждено `git diff --name-only`). `tests/test_lessons.py` (TC-01…TC-12,
|
||
13 тестов) — **зелёный** локально. Документация обновлена в том же PR.
|
||
|
||
Все findings — P2/P3 (advisory), блокеров нет.
|
||
|
||
## Findings
|
||
|
||
### P0 — Blocker
|
||
- Нет.
|
||
|
||
### P1 — Must fix
|
||
- Нет.
|
||
|
||
### P2 — Should fix
|
||
- [ ] **Кросс-задачный дедуп `transient_retry` теряет сигнал.** Врезка в
|
||
`launcher._finalize_transient` (`src/agents/launcher.py:~1024`) передаёт `task_id`, но **не**
|
||
`work_item_id` и **не** `stage` → ключ дедупа `db.lessons_recent_dup_exists` становится
|
||
`(work_item_id IS NULL, lesson_type='transient_retry', stage IS NULL)`. В окне
|
||
`lessons_dedup_window_s` (дефолт 1ч) **разные** задачи, исчерпавшие бюджет ретраев, схлопываются в
|
||
одну запись — теряется урок про вторую задачу. Поскольку `task_id` локально доступен, дедуп-ключ
|
||
стоило бы доопределять им при `work_item_id is None` (или включать `task_id` в ключ дедупа).
|
||
Это observer/best-effort (не влияет на конвейер, AC-3 формально выполнен — 4 типа автозаписи
|
||
работают), потому не блокер, но ослабляет ценность самого сигнала, ради которого фича вводится.
|
||
Ссылка: ADR-001 D4 («ключ `work_item_id+stage+lesson_type`»).
|
||
|
||
### P3 — Nice to have
|
||
- [ ] **Мелкая неточность ADR vs код.** `06-adr/ADR-001` (D3, таблица) и `adr-0034` указывают
|
||
choke-point `merge_hold` как `merge_gate._handle_merge_verify`, фактически `_handle_merge_verify`
|
||
живёт в `src/stage_engine.py` (туда и врезан `merge_hold`; `merge_gate.py` диффом не тронут).
|
||
Функционально корректно; рекомендуется поправить адрес в ADR для трассировки. Также
|
||
`transient_retry` в `merge_gate` (merge-retry exhausted) не реализован — но ADR формулирует это как
|
||
«**and/or** launcher», т.е. опционально; реализация через launcher достаточна.
|
||
|
||
## Документация
|
||
|
||
**Обновлена полностью в том же PR — ось «документация» PASS:**
|
||
- `CLAUDE.md` — добавлен раздел «Машинный журнал уроков (ORCH-098)» (D1–D5, флаги, инвариант).
|
||
- `docs/architecture/README.md` — компонент «Lessons journal», строка таблицы `lessons` в разделе
|
||
схемы БД, три новых эндпоинта в таблице API, обновлена строка `GET /queue` (`+ lessons (ORCH-098)`).
|
||
- `docs/architecture/adr/adr-0034-lessons-journal.md` — сквозной ADR (новый).
|
||
- `docs/work-items/ORCH-098/06-adr/ADR-001-lessons-journal.md` — локальный ADR (присутствует).
|
||
- `CHANGELOG.md` — запись `[Unreleased]` с разбивкой D1–D5 + регресс.
|
||
- `README.md` «Известные ограничения» — пунктов, закрываемых этой задачей, нет (ORCH-079 N/A).
|
||
|
||
Изменение `src/` ⇒ требование «документация = golden source» выполнено; основание для
|
||
`REQUEST_CHANGES` по оси документации отсутствует.
|