From 21a47e85d3613a993ba9d522a5615c8bb01d1d78 Mon Sep 17 00:00:00 2001 From: claude-bot Date: Wed, 10 Jun 2026 10:44:34 +0300 Subject: [PATCH] =?UTF-8?q?fix(lessons):=20resolve=20land-race=20with=20OR?= =?UTF-8?q?CH-100=20=E2=80=94=20renumber=20ADR=200033=E2=86=920034?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- CHANGELOG.md | 7 +++++++ CLAUDE.md | 2 +- ...0033-lessons-journal.md => adr-0034-lessons-journal.md} | 2 +- docs/work-items/ORCH-098/06-adr/ADR-001-lessons-journal.md | 4 ++-- docs/work-items/ORCH-098/12-review.md | 2 +- 5 files changed, 12 insertions(+), 5 deletions(-) rename docs/architecture/adr/{adr-0033-lessons-journal.md => adr-0034-lessons-journal.md} (99%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b65ad4..2108a0d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,13 @@ Формат: [Keep a Changelog](https://keepachangelog.com/). Записи — на смысловой PR/задачу. ## [Unreleased] +- **Машинный журнал уроков `lessons`** (ORCH-098, `feat`): шаг 1 («Фундамент», F2) эпика саморазвития — формализует свободнотекстовые «уроки» из `memory/` в **машинную структурированную таблицу отклонений конвейера** `lessons`, фундамент для будущих ретроспективщика (E2), приоритизатора RICE (E3) и Стрим. Чистый **observer-leaf** `src/lessons.py` (never-raise, kill-switch, паттерн `serial_gate`/`coverage_gate`/`metrics`): `record()`/`get()`/`update()`/`snapshot()`. **Инвариант:** журнал — наблюдатель, **не** Quality Gate — `STAGE_TRANSITIONS`/`QG_CHECKS`/`check_*`/machine-verdict/схемы существующих таблиц байт-в-байт не тронуты; enduro не затронут. ADR: `docs/work-items/ORCH-098/06-adr/ADR-001-lessons-journal.md`, сквозной `docs/architecture/adr/adr-0034-lessons-journal.md`. + - **Таблица (D1, FR-1):** аддитивная идемпотентная `lessons` (`CREATE TABLE IF NOT EXISTS` в `db.init_db()` + три индекса, restart-safe) — контекст (`work_item_id`/`task_id`/`stage`/`agent`/`repo`), анализ (`root_cause`/`suggestion`), статус (`status`/`related_task`), **колонки атрибуции — сразу и нуллабельно** (`attribution`/`target_repo`/`target_domain`, требование Славы 10.06 / NFR-6, заполняется позже через update; `_ensure_column` форвард-safe на старой таблице) + `source`/`detail`; без `enum`-констрейнтов (слаги forward-compatible). Хелперы `db.record_lesson`/`get_lessons`/`update_lesson`/`lessons_snapshot`/`lessons_recent_dup_exists`. + - **НЕ скоупится по репо (D2):** журнал observer-only → единственный регулятор — глобальный kill-switch `lessons_enabled` (env `ORCH_LESSONS_ENABLED`, дефолт `True`); **`lessons_repos` НЕ вводится**. Recorder пишет уроки про **любой** репо (включая enduro-trails); репо-разрез — на **выборке** (`get(repo=…)`). + - **Автозапись 4 типов (D3, FR-3):** тонкие best-effort врезки (`source="auto"`, never-raise, дедуп) — `gate_failure` (`stage_engine._handle_qg_failure_rollbacks`, откат на `development`), `merge_hold` (`stage_engine._handle_merge_verify` HOLD), `transient_retry` (`launcher._finalize_transient` на исчерпании бюджета ретраев), `deploy_degraded` (post-deploy `DEGRADED → set_repo_freeze`, урок слоя-3 «деплой OK / прод сломан» ET-8). + - **Дедуп (D4):** для `auto` — один indexed-SELECT по `idx_lessons_wi_type`: дубль `(work_item_id, lesson_type, stage)` в окне `lessons_dedup_window_s` (env, дефолт 3600с) → no-op; `manual` не дедупится. + - **Эндпоинты (D5, FR-4/5):** `GET /lessons` (read-only, фильтры `type`/`status`/`repo`/`work_item`/`limit`), `POST /lessons` (ручная запись), `POST /lessons/{id}` (доклассификация/update); read-only ключ `lessons` в `GET /queue`. Выключенный флаг → `{"enabled": false}`. + - **Регресс:** kill-switch `lessons_enabled=False` → полная инертность (no-op без обращения к БД); never-raise на всех публичных функциях/врезках — сбой журнала не роняет конвейер; аддитивно (новая таблица + leaf + эндпоинты + тонкие врезки). Флаги `config.py`: `lessons_enabled`/`lessons_query_limit_default`/`lessons_dedup_window_s`. Тесты `tests/test_lessons.py` (TC-01…TC-12, unit+integration). - **FND/F1b: sidecar-watchdog — мозг мониторинга в отдельном контейнере** (ORCH-100, `feat`): новая папка `watchdog/` (тонкий **Python-3.12-stdlib-only** демон) + сервис `orchestrator-watchdog` в `docker-compose.yml` (`network_mode: host`, read-only `docker.sock`, `mem_limit: 128m`). Вторая половина пары наблюдаемости домена 0: F1a (ORCH-099) отдаёт `GET /metrics` (сырьё), F1b — **мозг**, который это сырьё читает, дополняет внешними сигналами (хост/контейнеры/зависимости) и превращает в **алерты** через **собственный** независимый Telegram-канал. **`src/**` НЕ изменён** — F1b потребитель `/metrics`; `STAGE_TRANSITIONS`/`QG_CHECKS`/`check_*`/схема БД орка — байт-в-байт. Аддитивно, под kill-switch `WATCHDOG_ENABLED`, строго read-only к наблюдаемому (self-hosting-безопасно). ADR: `docs/work-items/ORCH-100/06-adr/ADR-001-sidecar-watchdog.md`, сквозной `docs/architecture/adr/adr-0033-sidecar-watchdog.md`. - **fix(test): изоляция `settings.runs_dir` в conftest** — устранена амбиентная prod-зависимость, валившая `test_queue.py::TestRetry::test_finalize_job_requeue_then_fail` в self-hosting-окружении (TC-14 «full tests/ regression green»). `launcher._finalize_job` классифицирует падение по хвосту `/.log`; `runs_dir` по умолчанию = живой prod-каталог `/app/data/runs`, где на хосте накоплены РЕАЛЬНЫЕ логи агентов (`2.log` содержит `429` → 'transient'), поэтому тест с литеральным `run_id=2` читал чужой prod-лог и получал requeue вместо `failed`. Новый autouse-фикстур `_isolate_runs_dir` в `tests/conftest.py` (по образцу `_no_telegram`/`_disable_merge_verify`) перенаправляет `runs_dir` в пер-тестовый tmp → `_run_log_path()` указывает на несуществующий файл → `classify_log_file()` отдаёт документированный дефолт 'permanent'. Детерминизм всей сюты восстановлен (1617 passed); `src/**` не тронут. - **Стек (D1):** Python 3.12 stdlib-only на `python:3.12-slim` — `urllib` (HTTP `/metrics` + пинги + Telegram POST), сырой HTTP-over-unix-socket для read-only `docker.sock` (БЕЗ pip-пакета `docker`), `shutil.disk_usage`/`/proc/meminfo` для хоста. Нет дерева зависимостей (тонкость, C-3). Отдельный образ `watchdog/Dockerfile` (build-контекст = корень репо; `src/**` НЕ копируется — изоляция C-1). diff --git a/CLAUDE.md b/CLAUDE.md index c1ab1e4..9595588 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -271,7 +271,7 @@ machine-verdict/схемы существующих таблиц байт-в-б безопасный дефолт) — сбой журнала не роняет конвейер. Self-hosting-безопасно: только читает/пишет свою таблицу, не деплоит/не рестартит прод/не трогает `main`/без процессов/сети. Детали — `docs/work-items/ORCH-098/06-adr/ADR-001-lessons-journal.md`, - `docs/architecture/adr/adr-0033-lessons-journal.md`. + `docs/architecture/adr/adr-0034-lessons-journal.md`. ## Конвенции - Conventional Commits (`feat:`, `fix:`, `docs:`, `refactor:`, `test:`) diff --git a/docs/architecture/adr/adr-0033-lessons-journal.md b/docs/architecture/adr/adr-0034-lessons-journal.md similarity index 99% rename from docs/architecture/adr/adr-0033-lessons-journal.md rename to docs/architecture/adr/adr-0034-lessons-journal.md index 25ac65f..379ff42 100644 --- a/docs/architecture/adr/adr-0033-lessons-journal.md +++ b/docs/architecture/adr/adr-0034-lessons-journal.md @@ -7,7 +7,7 @@ created_at: 2026-06-10 model_used: claude-opus-4-8 --- -# adr-0033: Машинный журнал уроков — таблица `lessons` + observer-leaf (ORCH-098) +# adr-0034: Машинный журнал уроков — таблица `lessons` + observer-leaf (ORCH-098) ## Статус Proposed diff --git a/docs/work-items/ORCH-098/06-adr/ADR-001-lessons-journal.md b/docs/work-items/ORCH-098/06-adr/ADR-001-lessons-journal.md index 2b20c91..03357dc 100644 --- a/docs/work-items/ORCH-098/06-adr/ADR-001-lessons-journal.md +++ b/docs/work-items/ORCH-098/06-adr/ADR-001-lessons-journal.md @@ -11,7 +11,7 @@ model_used: claude-opus-4-8 Work Item: **ORCH-098** — FND: машинный журнал уроков (структурированная база отклонений конвейера) Стадия: **architecture** -Сквозная регистрация: **`docs/architecture/adr/adr-0033-lessons-journal.md`** (решение +Сквозная регистрация: **`docs/architecture/adr/adr-0034-lessons-journal.md`** (решение кросс-каттинговое: новый компонент + новая таблица на общей прод-БД + фундамент эпика саморазвития). @@ -238,7 +238,7 @@ LIMIT 1; - Data: `docs/work-items/ORCH-098/08-data-requirements.md` - Infra: `docs/work-items/ORCH-098/07-infra-requirements.md` - Risks: `docs/work-items/ORCH-098/10-tech-risks.md` -- Сквозной ADR: `docs/architecture/adr/adr-0033-lessons-journal.md` +- Сквозной ADR: `docs/architecture/adr/adr-0034-lessons-journal.md` - Сверено по коду: `src/serial_gate.py`, `src/coverage_gate.py`, `src/metrics.py`, `src/db.py:251,341`, `src/stage_engine.py:728,~1993`, `src/merge_gate.py:811,1588`, `src/agents/launcher.py:997`, `src/main.py` (`GET /queue`, `POST /coverage/baseline`), `src/qg/checks.py:520`. diff --git a/docs/work-items/ORCH-098/12-review.md b/docs/work-items/ORCH-098/12-review.md index 0560cb4..0414999 100644 --- a/docs/work-items/ORCH-098/12-review.md +++ b/docs/work-items/ORCH-098/12-review.md @@ -84,7 +84,7 @@ version: 1 описании `GET /queue`. - `CHANGELOG.md` — запись `feat` (ORCH-098) с разбивкой D1…D5 + регресс. - ADR: локальный `06-adr/ADR-001-lessons-journal.md` (proposed) + сквозной - `docs/architecture/adr/adr-0033-lessons-journal.md` (существует). + `docs/architecture/adr/adr-0034-lessons-journal.md` (существует). - `README.md` «Известные ограничения» — данный PR не закрывает ни одного пункта витрины (обзорные доки обновлять не требуется).