architect(ET): auto-commit from architect run_id=478

This commit is contained in:
2026-06-09 17:25:42 +03:00
committed by orchestrator-deployer
parent 484069851e
commit f61d963f9b
2 changed files with 177 additions and 0 deletions

View File

@@ -0,0 +1,135 @@
---
work_item: ORCH-092
stage: architecture
author_agent: architect
status: accepted
created_at: 2026-06-09
model_used: claude-opus-4-8
---
# ADR-001: Ручной rebase developer и язык промпта deployer
Work Item: **ORCH-092** — Промпт-аудит 6 агентов (эпилог эпика ORCH-52)
Стадия: **architecture**
Сквозная регистрация: **N/A — локальное docs/prompts-only решение.** Оба решения уточняют
существующий канон промптов (`docs/architecture/adr/adr-0021-prompt-canon-anthropic.md`,
README §«Слой промптов»), но не вводят ни нового QG, ни стадии, ни компонента, ни смены БД →
нового global ADR не требуют. Долговечность нормативного эффекта обеспечивается анти-регресс-тестом
`tests/test_agent_prompts_canon.py` (FR-11), а не отдельным сквозным ADR.
## Статус
Accepted
## Контекст
ORCH-092 — аудит 6 системных промптов `.openclaw/agents/*.md`. Анализ (BRD §6, TRZ §3) вынес две
развилки, которые analyst **намеренно не решал** (зона архитектора), потребовав ADR ПЕРЕД правкой
соответствующих строк:
**P1-2 / FR-9 — ручной rebase developer.** `developer.md` (`<task>`, шаг 2 алгоритма) предписывает
`git fetch origin && git rebase origin/main`. Сверка кодом (BRD A-2) показала, что движок **уже**
держит свежесть базы детерминированно и автоматически, причём двумя независимыми рубежами:
- **Срез ветки от свежего `origin/main`** — serial-gate (ORCH-088) откладывает создание ветки со
`start_pipeline` на момент claim analyst-job (`launcher._materialize_deferred_branch`,
`src/agents/launcher.py:421`), когда `origin/main` уже содержит код предшественника
(`done` ⇔ SHA-в-main, ORCH-071/073). `ensure_worktree` режет worktree от свежего `origin/main`.
- **Авторитетный pre-merge rebase под merge-lease** — `check_branch_mergeable`
(`src/qg/checks.py:697-703`) на ребре `deploy-staging → deploy` вызывает
`merge_gate.auto_rebase_onto_main` (`src/merge_gate.py:113`) **всегда** при
`premerge_rebase_always=True` (дефолт, ORCH-026). Эта операция завершается
`git push --force-with-lease origin <branch>` (`src/merge_gate.py:151`).
Важная асимметрия прав: авторитетный rebase движка **обязан** делать `--force-with-lease`, чтобы
переписать уже запушенную историю ветки. Developer-промпту это **прямо запрещено**
(`<constraints>`: «❌ Не используй `--no-verify` / `--force-push`»). Значит ручной
`git rebase origin/main` developer'а либо безопасен-но-бесполезен (в начале стадии ветка только что
срезана от свежего main — rebase no-op), либо опасен (после первого push повторный rebase требует
force-push, который developer'у запрещён) — то есть инструкция шага 2 **дублирует** автоматику и
**конфликтует** с собственным запретом промпта.
**P2-2 / FR-10 — язык deployer.** 5 из 6 промптов на русском; `deployer.md` (~9.6 KB, самый большой
и самый safety-critical: self-hosting, прод-рестарт 8500) — на английском. Нужно решить:
унифицировать на ru ИЛИ зафиксировать исключение (en). Инвариант NFR-1 критичен: machine-verdict
ключи (`staging_status:`/`deploy_status:`/`security_status:` + значения `SUCCESS/FAILED/PASS/FAIL`),
shell-команды и анти-регресс-маркеры (`docker exec orchestrator-staging`, `pr_already_merged`,
`8500`, `INFRA-WAIVED`) — **байт-в-байт неприкосновенны**.
## Решение
### Сводка
**D1:** убрать безусловный ручной `git rebase origin/main` из алгоритма developer; свежесть базы —
инвариант движка (serial-gate + auto_rebase под lease), а не ответственность агента. **D2:** оставить
`deployer.md` на английском как **явно задокументированное исключение** канона; не переводить.
### D1 — Developer НЕ делает ручной rebase (закрывает FR-9 / BR-9)
Шаг 2 алгоритма developer (`git fetch origin && git rebase origin/main`) **удаляется** как
самостоятельная мутирующая операция. Вместо него — короткая нормативная заметка, что:
- ветка уже срезана движком от свежего `origin/main` (serial-gate ORCH-088), поэтому ручная синхра
на входе не нужна;
- авторитетный догон `main` перед слиянием делает движок (`auto_rebase_onto_main` под merge-lease,
ORCH-026/043) на ребре `deploy-staging → deploy`;
- developer **не делает** `git rebase` / `git push --force*` сам (это пересекается с запретом
`<constraints>` и с авторитетной операцией движка, использующей `--force-with-lease`).
Допустимо сохранить **read-only** `git fetch origin` (без rebase) — он не мутирует историю и полезен
для сверки с актуальным `main`; но это не обязательный шаг. Главный инвариант: **из алгоритма
исчезает мутирующий `git rebase origin/main`**.
Привязка: FR-9 (промпт приводится в соответствие с ADR), AC-9 (developer не нарушает no-force-push),
анти-регресс — маркер «не мержи свой PR» и запрет force-push сохраняются.
### D2 — Deployer остаётся на английском (закрывает FR-10 / BR-10)
`deployer.md` **не переводится**; язык остаётся английским как зафиксированное исключение из канона
«остальные промпты на ru». Обоснование (в порядке веса):
1. **Минимизация регресс-поверхности на самом критичном промпте.** Перевод ~9.6 KB плотного
операционного текста — широкая поверхность для случайного слома verdict-ключа, маркера или
shell-команды (NFR-1 — критичный инвариант). Deployer управляет прод-рестартом 8500 (групповой
self-hosting риск) — здесь цена ошибки максимальна, выгода от churn — нулевая.
2. **Нулевая выгода понимания.** Исполняющая модель (`claude-opus-4-8`) двуязычна; критичные
команды, контракты, exit-code-маппинги и verdict-ключи **англо-нативны** — перевод вокруг них
лишь добавляет риск рассинхрона «русский текст ↔ английская команда».
3. **Исключение, а не дрейф.** Чтобы будущий агент не «починил» язык вслепую, исключение
фиксируется нормативно: (а) запись в `CLAUDE.md`/README §«Слой промптов», (б) анти-регресс-тест
`tests/test_agent_prompts_canon.py` (FR-11) допускает/ожидает EN для deployer.
D2 **не отменяет** FR-6: критичная рамка «NEVER restart prod 8500» поднимается/усиливается в начале
`deployer.md` — на английском, в существующем blockquote-стиле. machine-verdict ключи и команды
остаются байт-в-байт (AC-6).
## Альтернативы
- **D1-альт: оставить ручной rebase «с пояснением».** Отвергнуто: пояснение не снимает конфликт с
запретом force-push и не устраняет дублирование авторитетной операции движка; «оставить, но
объяснить почему оно безопасно» сложнее и хрупче, чем «убрать то, что движок делает лучше».
- **D1-альт: оставить как есть.** Отвергнуто: BRD/TRZ явно квалифицируют это как
нереализуемо-конфликтующую инструкцию (after-push rebase ⇒ нужен запрещённый force-push).
- **D2-альт: унифицировать deployer на русский.** Отвергнуто: максимальный регресс-риск на самом
опасном промпте при нулевой выгоде понимания; противоречит духу «docs/prompts-only, минимум
изменений, NFR-1 байт-в-байт».
## Последствия
- **+** Developer-алгоритм перестаёт противоречить собственному запрету force-push; единственный
владелец догона `main` — движок (один авторитетный путь, меньше гонок).
- **+** Самый safety-critical промпт (deployer) не подвергается рискованному массовому переводу;
NFR-1 защищён структурно.
- **+** Оба решения — чисто текстовые, ревертируемы одним PR; `src/**` не тронут (NFR-2/NFR-3).
- **** Языковая неоднородность канона (5 ru + 1 en) остаётся. Митигейшн: явная нормативная запись +
тест → это документированное исключение, а не необъяснённый дрейф.
- **** Теоретически developer может захотеть подтянуть `main` во время длинной стадии и теперь не
имеет шага rebase. Митигейшн: серийность репо (ORCH-088) ограничивает расхождение `main` за время
одной задачи; авторитетный rebase перед merge закрывает остаточное расхождение детерминированно.
- **Откат:** вернуть строку `git rebase origin/main` в `developer.md` шаг 2 и (при желании) перевести
`deployer.md`обе правки текстовые, в одном PR, без изменения кода/схемы.
## Ссылки
- BRD: `docs/work-items/ORCH-092/01-brd.md` (BR-9, BR-10; §6 A-2)
- TRZ: `docs/work-items/ORCH-092/02-trz.md` (FR-9, FR-10)
- Acceptance: `docs/work-items/ORCH-092/03-acceptance-criteria.md` (AC-6, AC-9)
- Канон промптов: `docs/architecture/adr/adr-0021-prompt-canon-anthropic.md`
- Сверено по коду: `src/merge_gate.py:113,151` (`auto_rebase_onto_main` + `--force-with-lease`),
`src/qg/checks.py:697-703` (`check_branch_mergeable` → авто-rebase при `premerge_rebase_always`),
`src/agents/launcher.py:421` (`_materialize_deferred_branch`, отложенный срез ветки ORCH-088)
- Риски: `docs/work-items/ORCH-092/10-tech-risks.md`

View File

@@ -0,0 +1,42 @@
---
work_item: ORCH-092
stage: architecture
author_agent: architect
status: accepted
created_at: 2026-06-09
model_used: claude-opus-4-8
---
# 10 — Технические риски: ORCH-092 — Промпт-аудит 6 агентов
Work Item: **ORCH-092** · Repo: **orchestrator** · Стадия: architecture
> Информационный (гейтом не парсится). Перечисляет риски реализации и их митигейшн.
> Решения, порождающие/снижающие часть рисков, зафиксированы в
> `06-adr/ADR-001-developer-rebase-and-deployer-language.md`.
## Реестр рисков
| ID | Риск | Вер. | Влия. | Митигейшн |
|----|------|------|-------|-----------|
| TR-1 | Массовая текстовая правка 6 промптов случайно ломает machine-verdict ключ (`verdict:`/`result:`/`staging_status:`/`deploy_status:`/`security_status:`) по имени/регистру/значению | Сред. | Выс. | NFR-1 анти-регресс: `tests/test_agent_prompts_canon.py` (`test_machine_verdict_keys_preserved_exact_case`) + `test_agent_frontmatter_no_model.py` зелёные; правка точечная, не переписывание |
| TR-2 | Плейсхолдеры `<YYYY-MM-DD>` / `<resolve ORCH-41>` сами скопированы моделью буквально (тот же класс бага, что хардкод) | Сред. | Низ. | Явная инструкция «подставь фактическое (`date +%F` / резолв конфига), НЕ копируй из примера»; плейсхолдер визуально не похож на валидное значение |
| TR-3 | Удаление шага `git rebase origin/main` (D1) оставляет ветку на устаревшем `main` в длинной/повторной (rework) стадии development | Низ. | Сред. | Авторитетный `auto_rebase_onto_main` под merge-lease перед слиянием (ORCH-026/043) детерминированно догоняет `main`; serial-gate (ORCH-088) ограничивает расхождение `main` за время одной задачи; срез ветки — от свежего `origin/main` |
| TR-4 | Решение «deployer = EN» (D2) воспринято будущим агентом как дрейф и «починено» переводом → регресс NFR-1 на самом опасном промпте | Низ. | Выс. | Нормативная фиксация исключения: запись в `CLAUDE.md`/README §«Слой промптов» + ожидание EN-deployer в каноне-тесте (FR-11); ADR-001 D2 как ссылка-обоснование |
| TR-5 | Добавление секции `<escalation>` (developer/reviewer/tester) нарушает нормативный порядок 5 обязательных XML-секций канона 52d | Низ. | Сред. | `<escalation>` ставится ПОСЛЕ `</success_criteria>` (как у `architect.md` — эталон); гаранты `test_five_xml_sections_present` / `test_six_schema_field_names_present` |
| TR-6 | Поднятие критичной рамки в `deployer.md` (FR-6) задевает анти-регресс-маркер (`docker exec orchestrator-staging`, `pr_already_merged`, `8500`, `INFRA-WAIVED`) | Низ. | Выс. | Рамка добавляется/поднимается аддитивно (blockquote в начале `<context>`), маркеры не трогаются; каноне-тест проверяет их наличие |
| TR-7 | Сверка имён гейтов (FR-3) «исправляет» верное имя вслепую (напр. `check_tests_passed`) | Низ. | Сред. | BR-3/A-1: несовпадений на момент анализа НЕТ → правка только реально отсутствующих в `QG_CHECKS`; TC-03 сверяет имена против реального реестра `src/qg/checks.py` |
## Сводный вывод
Доминирующий класс рисков — **случайный слом машинного контракта/канона при текстовой правке**
(TR-1/TR-5/TR-6/TR-7), полностью покрываемый структурными анти-регресс-тестами
`tests/test_agent_prompts_canon.py` + `test_agent_frontmatter_no_model.py` (NFR-1). Архитектурные
решения D1/D2 снижают, а не повышают системный риск: D1 устраняет конфликт инструкции с запретом
force-push и отдаёт догон `main` единственному авторитетному владельцу (движок); D2 минимизирует
регресс-поверхность на самом safety-critical промпте.
Изменение **docs/prompts-only**: `src/**`, `STAGE_TRANSITIONS`, `QG_CHECKS`, схема БД — не тронуты;
прод-контейнер 8500 не перезапускается (NFR-2); всё ревертируемо одним PR (NFR-3). Эскалация
`arch:major-change` **не требуется**; возврат в анализ **не требуется** (ТЗ реализуемо без нарушения
принципов). Остаточный риск для прод-конвейера (self-hosting) — **низкий**.