Merge-gate auto_rebase_onto_main bounced this branch back: ORCH-100 landed in main first and claimed global ADR number adr-0033 (adr-0033-sidecar-watchdog), while this branch had created adr-0033-lessons-journal. Resolved the genuine land race: - rebased feature/ORCH-098-fnd onto current origin/main (linear history) - resolved docs/architecture/README.md component-list conflict — both the Lessons-journal and Sidecar-watchdog bullets now coexist - renamed docs/architecture/adr/adr-0033-lessons-journal.md → adr-0034-lessons-journal.md (next free global ADR number) + fixed the in-file header - updated all cross-references (CLAUDE.md, README.md, work-item ADR-001, 12-review.md) 0033→0034 for the lessons journal; ORCH-100's adr-0033 (sidecar) left intact - recovered the ORCH-098 CHANGELOG entry silently dropped by the rebase auto-merge (now above ORCH-100, ADR ref corrected to 0034) No code semantics changed; src/** auto-merged cleanly (ORCH-100 did not touch src/**). ruff: n/a locally (CI). pytest tests/ -q: 1630 passed. Refs: ORCH-098 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
7.4 KiB
work_item, stage, author_agent, status, created_at, model_used
| work_item | stage | author_agent | status | created_at | model_used |
|---|---|---|---|---|---|
| ORCH-098 | architecture | architect | proposed | 2026-06-10 | claude-opus-4-8 |
adr-0034: Машинный журнал уроков — таблица lessons + observer-leaf (ORCH-098)
Статус
Proposed
Контекст
Оркестратор автономно ведёт задачи по конвейеру (ORCH-54), но развивается вручную: инциденты →
уроки → задачи. Уроки живут свободным текстом в memory/ — не машиночитаемы: нельзя считать
паттерны, приоритизировать, предлагать улучшения. ORCH-098 — шаг 1 эпика саморазвития (домен 0
«Фундамент», F2): «топливо» петли самообучения 8A. Нужна структурированная таблица отклонений
конвейера, на которой позже встанут ретроспективщик (E2), приоритизатор RICE (E3) и Стрим.
Нормативное требование Славы (10.06): схема ДОЛЖНА сразу нести поля атрибуции урока
(platform/project/both/unknown + целевой репо + домен улучшения), иначе позже придётся
переделывать схему на живой общей прод-БД.
Кросс-каттинговость (почему сквозной ADR): новый компонент src/lessons.py + аддитивная
таблица на общей прод-БД (self-hosting, разделяемой с enduro-trails) + врезки автозаписи в
несколько горячих choke-point'ов (stage_engine/merge_gate/launcher) + новый раздел контракта
GET /queue. Фундамент для будущих задач-потребителей → регистрируется глобально.
Решение
Журнал уроков — observer (наблюдатель), НЕ Quality Gate. Аддитивная таблица + чистый leaf,
по образцу serial_gate/coverage_gate/metrics/bug_fast_track.
-
Таблица
lessons(db.init_db(),CREATE TABLE IF NOT EXISTS+ 3 индекса, идемпотентно, restart-safe) — поля контекста (work_item_id/task_id/stage/agent/repo), анализа (root_cause/suggestion), статуса (status/related_task), атрибуции сразу и нуллабельно (attribution/target_repo/target_domain) +source/detail. Безenum-констрейнтов (слаги forward-compatible). Будущие колонки —_ensure_column. -
Leaf
src/lessons.py(never-raise, импортирует толькоconfig+db):record()/get()/update()/snapshot(). Расхождение с гейт-шаблоном: журнал НЕ скоупится по репо — он observer-only и не действует ни на один репо; единственный регулятор — глобальный kill-switchlessons_enabled. Запись урока про enduro ценна и не затрагивает пайплайн enduro (чистая память орка); репо-разрез — на выборке (repo-колонка/фильтр). -
Автозапись 4 типов (
source="auto", best-effort, дедуп в окне;transient_retry— только на исчерпании бюджета ретраев):gate_failure(stage_engine._handle_qg_failure_rollbacks),merge_hold(merge_gate._handle_merge_verifyHOLD),transient_retry(merge-retry/launcher transient budget-exhaustion),deploy_degraded(post-deployDEGRADED → set_repo_freeze, урок слоя-3 «деплой OK / прод сломан», ET-8). Каждая врезка — одиночный вызов в защитномtry/except. -
Эндпоинты
GET /lessons(read-only, фильтры),POST /lessons(ручная запись,source="manual"),POST /lessons/{id}(update — доклассификацияunknown), + read-only ключ"lessons": snapshot()вGET /queue. При выключенном флаге →{"enabled": false}.
Инвариант (нерушимый): STAGE_TRANSITIONS / QG_CHECKS / check_* / machine-verdict-ключи
(verdict:/result:/staging_status:/deploy_status:/security_status:/coverage_status:) /
схемы существующих таблиц — байт-в-байт не тронуты. Журнал не влияет на продвижение по стадиям.
Композиция с существующими механизмами
- Self-hosting (общая БД): аддитивная таблица; enduro не затронут (NFR-3).
- serial-gate (ORCH-088) / post-deploy (ORCH-021): детектор
deploy_degradedврезан рядом сset_repo_freeze, не меняя freeze-логику. - merge-gate (ORCH-043/071/093):
merge_hold/transient_retryчитают исход актора, не меняя классификатор/ретрай. - metrics (ORCH-099): журнал — историческая память петли (best-effort запись),
/metrics— realtime-сырьё для sidecar; разные роли, оба observer-only.
Условность и откат
- Флаг
lessons_enabled(envORCH_LESSONS_ENABLED, дефолтTrue; kill-switch) +lessons_dedup_window_s/lessons_query_limit_default.False→ полная инертность, нулевая регрессия, конвейер байт-в-байт прежний. - never-raise на всех публичных функциях и врезках (NFR-1) — сбой журнала не роняет конвейер.
- Откат — флаг в
false(мгновенно) или revert диффа; таблица не касается существующих.
Последствия
- + Машиночитаемые уроки — фундамент E2/E3/Стрим; атрибуция forward-proof (без передела живой БД).
- + Нулевая регрессия; проверенный additive-observer-leaf шаблон → низкий риск; enduro изолирован.
- − Рост таблицы (митигейшн: лёгкие строки + дедуп + budget-exhaustion; ретенция — будущее).
- − Дедуп-запрос в
record()(один indexed-SELECT, толькоauto).
Ссылки
- Локальный ADR:
docs/work-items/ORCH-098/06-adr/ADR-001-lessons-journal.md - BRD/TRZ/AC:
docs/work-items/ORCH-098/01-brd.md,02-trz.md,03-acceptance-criteria.md - Data/Infra/Risks:
docs/work-items/ORCH-098/08-data-requirements.md,07-infra-requirements.md,10-tech-risks.md - Эпик:
docs/epics/self-evolution.md(домен 0 «Фундамент», F2; петля 8A) - Сверено по коду:
src/serial_gate.py,src/coverage_gate.py,src/db.py,src/stage_engine.py,src/merge_gate.py,src/agents/launcher.py,src/main.py,src/qg/checks.py.