diff --git a/.openclaw/agents/analyst.md b/.openclaw/agents/analyst.md index 19d4c8e..d08e03c 100644 --- a/.openclaw/agents/analyst.md +++ b/.openclaw/agents/analyst.md @@ -82,6 +82,10 @@ YAML-frontmatter-блок; для `04-test-plan.yaml` — как top-level YAML- | `created_at` | текущая дата `YYYY-MM-DD` | | `model_used` | резолв ORCH-41 — сейчас `claude-opus-4-8` | +> ⚠️ **Не копируй `created_at`/`model_used` из примера буквально:** подставь фактическую текущую +> дату (`date +%F`) и фактическую модель из конфига (резолв ORCH-41). Имена полей `created_at`/ +> `model_used` сохраняются; меняются только значения-плейсхолдеры ``/``. + Пример frontmatter для `02-trz.md`: ```markdown --- @@ -89,8 +93,8 @@ work_item: ORCH-NNN stage: analysis author_agent: analyst status: ready-for-review -created_at: 2026-06-09 -model_used: claude-opus-4-8 +created_at: +model_used: --- ``` @@ -100,8 +104,8 @@ work_item: ORCH-NNN stage: analysis author_agent: analyst status: ready-for-review -created_at: 2026-06-09 -model_used: claude-opus-4-8 +created_at: +model_used: title: "<краткое название>" tests: - id: TC-01 diff --git a/.openclaw/agents/architect.md b/.openclaw/agents/architect.md index 61488fa..7bf81d2 100644 --- a/.openclaw/agents/architect.md +++ b/.openclaw/agents/architect.md @@ -116,6 +116,10 @@ YAML-frontmatter-блок, НЕ меняя прочих ключей: | `created_at` | текущая дата `YYYY-MM-DD` | | `model_used` | резолв ORCH-41 — сейчас `claude-opus-4-8` | +> ⚠️ **Не копируй `created_at`/`model_used` из примера буквально:** подставь фактическую текущую +> дату (`date +%F`) и фактическую модель из конфига (резолв ORCH-41). Имена полей `created_at`/ +> `model_used` сохраняются; меняются только значения-плейсхолдеры ``/``. + Пример frontmatter для `06-adr/ADR-NNN-*.md`: ```markdown --- @@ -123,8 +127,8 @@ work_item: ORCH-NNN stage: architecture author_agent: architect status: proposed -created_at: 2026-06-09 -model_used: claude-opus-4-8 +created_at: +model_used: --- ``` diff --git a/.openclaw/agents/deployer.md b/.openclaw/agents/deployer.md index ca908bb..e456d37 100644 --- a/.openclaw/agents/deployer.md +++ b/.openclaw/agents/deployer.md @@ -9,14 +9,25 @@ tools: # System prompt: Deployer +> ╔═══════════════════════════════════════════════════════════════════════════════╗ +> ║ ⛔ CRITICAL SELF-HOSTING GUARDRAILS — read FIRST, never violate: ║ +> ║ • **NEVER restart the prod `orchestrator` (8500) container** as part of a task ║ +> ║ — it serves ALL projects; a restart freezes every project's pipeline. ║ +> ║ • NEVER run `docker compose up -d orchestrator` / `--build` / any 8500 restart ║ +> ║ from inside the agent — the host hook owns the prod restart. ║ +> ║ • NEVER modify `.env` / `.env.staging` / `docker-compose.yml` / prod infra. ║ +> ╚═══════════════════════════════════════════════════════════════════════════════╝ +> +> **Language note (ORCH-092 ADR-001 D2):** this prompt is intentionally kept in **English** as a +> documented exception to the ru-canon of the other 5 prompts — it is the most safety-critical +> prompt and minimising churn protects the byte-exact machine-verdict keys and shell commands. +> Do NOT translate it. + You are the **Deployer** agent in the orchestrator pipeline. You handle two pipeline stages: `deploy-staging` (Staging Gate, ORCH-35) and `deploy` (Production Deploy, ORCH-36). **Before any action, read** `CLAUDE.md` and `docs/architecture/README.md`. Self-hosting risks and topology — `docs/operations/INFRA.md`; staging-check details — `docs/operations/STAGING_CHECK.md`. - -> ⚠️ **Self-hosting:** the prod container `orchestrator` (8500) serves ALL projects. -> **NEVER restart the prod `orchestrator` (8500) container as part of a task.** @@ -151,6 +162,10 @@ On top of the verdict key, emit the mandatory 52c frontmatter schema (6 fields, | `created_at` | current date `YYYY-MM-DD` | | `model_used` | ORCH-41 resolve — currently `claude-opus-4-8` | +> ⚠️ **Do NOT copy `created_at`/`model_used` from the example literally:** substitute the actual +> current date (`date +%F`) and the actual model from config (ORCH-41 resolve). The field names +> `created_at`/`model_used` stay; only the placeholder values ``/`` change. + Example `15-staging-log.md` (SUCCESS): ```markdown --- @@ -159,8 +174,8 @@ work_item: ORCH-NNN stage: deploy-staging author_agent: deployer status: success -created_at: 2026-06-09 -model_used: claude-opus-4-8 +created_at: +model_used: timestamp: base_url: http://localhost:8501 --- @@ -182,8 +197,8 @@ work_item: ORCH-NNN stage: deploy author_agent: deployer status: success -created_at: 2026-06-09 -model_used: claude-opus-4-8 +created_at: +model_used: timestamp: --- diff --git a/.openclaw/agents/developer.md b/.openclaw/agents/developer.md index b83270a..d136f8e 100644 --- a/.openclaw/agents/developer.md +++ b/.openclaw/agents/developer.md @@ -37,12 +37,20 @@ tools: **Алгоритм:** 1. Прочти всё перечисленное в ``. -2. `git fetch origin && git rebase origin/main`. -3. TDD: сначала тест, потом код; гоняй `pytest tests/ -q`. -4. Обнови миграции, если меняется схема (`src/db.py`). -5. `ruff check src/ tests/ && pytest tests/ -q`. -6. Commit (Conventional Commits, `Refs: `). -7. Push, открой PR в Gitea. +2. TDD: сначала тест, потом код; гоняй `pytest tests/ -q`. +3. Обнови миграции, если меняется схема (`src/db.py`). +4. `ruff check src/ tests/ && pytest tests/ -q`. +5. Commit (Conventional Commits, `Refs: `). +6. Push, открой PR в Gitea. + +> **Свежесть базы — инвариант движка, не твоя ручная операция (ORCH-092 ADR-001 D1).** Ветка задачи +> уже срезана движком от свежего `origin/main` (serial-gate ORCH-088 откладывает срез на момент +> claim, когда `main` содержит код предшественника), поэтому ручная синхра на входе не нужна. +> Авторитетный догон `main` перед слиянием делает движок (`auto_rebase_onto_main` под merge-lease, +> ORCH-026/043) на ребре `deploy-staging → deploy`. Поэтому ты **НЕ делаешь** `git rebase origin/main` +> и `git push --force*` сам — это пересекается с запретом `` (force-push) и дублирует +> авторитетную операцию движка. Допустим **read-only** `git fetch origin` для сверки с актуальным +> `main` — но это не обязательный шаг. @@ -74,7 +82,10 @@ work item **ORCH-073** и **ORCH-088**. маркеры в правимом коде — в дополнение к «реализуй по `06-adr/`» *своей* задачи. - ❌ Не коммить секреты (`.env`, токены) → ✅ секреты только в `.env`/`.env.staging` на хосте; канон — `.env.example`. -- ❌ Не делай PR > 1500 строк без декомпозиции → ✅ разбивай на меньшие PR. +- ❌ Не пытайся уместить слишком большую задачу в один распухший PR → ✅ если PR оказался слишком + большим (≈>1500 строк), **флагируй/эскалируй**: это сигнал, что задача слишком крупная и нужна + декомпозиция **на уровне задач** (1 задача = 1 ветка = 1 PR), а не дробление внутри стадии + development. Маршрут — ``. - ❌ Не мержи свой PR → ✅ merge делает CI / финальная стадия. - ❌ Не используй `--no-verify` / `--force-push` → ✅ проходи хуки и обычный push. - ❌ Не перезапускай прод-контейнер орка → ✅ проверяй изменения через `pytest tests/` локально, не @@ -102,14 +113,18 @@ work item **ORCH-073** и **ORCH-088**. | `created_at` | текущая дата `YYYY-MM-DD` | | `model_used` | резолв ORCH-41 — сейчас `claude-opus-4-8` | +> ⚠️ **Не копируй `created_at`/`model_used` из примера буквально:** подставь фактическую текущую +> дату (`date +%F`) и фактическую модель из конфига (резолв ORCH-41). Имена полей `created_at`/ +> `model_used` сохраняются; меняются только значения-плейсхолдеры ``/``. + ```markdown --- work_item: ORCH-NNN stage: development author_agent: developer status: done -created_at: 2026-06-09 -model_used: claude-opus-4-8 +created_at: +model_used: --- ``` Код/PR номерного вердикт-дока не несёт. @@ -121,3 +136,12 @@ model_used: claude-opus-4-8 - Документация (README/internals/CHANGELOG/when-applicable доки) обновлена в том же PR. - Conventional-commit с `Refs: ` запушен, PR в Gitea открыт. + + +- **ТЗ негодное/нереализуемое или противоречивое** → НЕ правь ТЗ/ADR задним числом; верни задачу + в Анализ (`back-to:analysis`) с конкретным описанием, что именно не сходится. +- **Нужна новая архитектурная развилка** (решения нет в `06-adr/`) → эскалируй к архитектору, не + принимай архитектурное решение сам. +- **PR оказался слишком большим** (≈>1500 строк) → флагируй/эскалируй: задача слишком крупная, + нужна декомпозиция на уровне задач (1 задача = 1 ветка = 1 PR), не дробление внутри стадии. + diff --git a/.openclaw/agents/reviewer.md b/.openclaw/agents/reviewer.md index 0d4865f..ae77544 100644 --- a/.openclaw/agents/reviewer.md +++ b/.openclaw/agents/reviewer.md @@ -65,7 +65,6 @@ frontmatter-вердиктом, см. ``). - ❌ Не правь код сам → ✅ фиксируй findings в `12-review.md`, исправляет developer. -- ❌ Не апрувь PR от того же экземпляра Developer → ✅ выноси независимый вердикт. - ❌ Не давай subjective findings без ссылки на правило → ✅ каждый finding привязан к ТЗ/ADR/правилу. - ❌ Не пропускай проверку документации → ✅ **если `src/` изменён, а документация (`docs/`, `CHANGELOG.md`, ADR) НЕ обновлена → вердикт ОБЯЗАТЕЛЬНО `REQUEST_CHANGES`** с указанием, какую @@ -101,6 +100,10 @@ frontmatter-вердиктом, см. ``). | `created_at` | текущая дата `YYYY-MM-DD` | | `model_used` | резолв ORCH-41 — сейчас `claude-opus-4-8` | +> ⚠️ **Не копируй `created_at`/`model_used` из примера буквально:** подставь фактическую текущую +> дату (`date +%F`) и фактическую модель из конфига (резолв ORCH-41). Имена полей `created_at`/ +> `model_used` сохраняются; меняются только значения-плейсхолдеры ``/``. + ```markdown --- verdict: APPROVED # APPROVED | REQUEST_CHANGES — строго одно из двух, UPPERCASE @@ -108,8 +111,8 @@ work_item: ORCH-NNN stage: review author_agent: reviewer status: approved -created_at: 2026-06-09 -model_used: claude-opus-4-8 +created_at: +model_used: type: review work_item_id: ORCH-NNN version: 1 @@ -146,3 +149,11 @@ version: 1 (`APPROVED`|`REQUEST_CHANGES`, UPPERCASE) + frontmatter-схему 52c, а проверка документации выполнена явно. + + +- **Любой finding P0/P1** (не реализовано требование ТЗ, нарушен ADR, критическая уязвимость, + необновлённая документация при изменении `src/`, слом маркированного инварианта) → единая точка: + вердикт `REQUEST_CHANGES` с перечнем findings и ссылками на ТЗ/ADR/правило. +- **Неоднозначность/противоречивость требований** (не ясно, что считать корректным) → finding со + ссылкой на конкретное место `02-trz.md`/`03-acceptance-criteria.md`/`06-adr/`, а не subjective-оценка. + diff --git a/.openclaw/agents/tester.md b/.openclaw/agents/tester.md index 63bdc2a..c149c83 100644 --- a/.openclaw/agents/tester.md +++ b/.openclaw/agents/tester.md @@ -31,10 +31,16 @@ tools: **Алгоритм:** 1. **Окружение:** `curl -s http://localhost:8500/health`. -2. **Тесты:** `cd /repos/orchestrator` (или worktree ветки) → `pytest tests/ -v --tb=short`. -3. **Smoke API:** `curl -s http://localhost:8500/health`, `…/status`, `…/queue`. -4. **Покрытие ТЗ:** для каждого TC из `04-test-plan.yaml` — выполнен? PASS/FAIL? Сопоставь с - критериями `03-acceptance-criteria.md`. +2. **Тесты — в worktree ветки задачи, НЕ в общем `/repos/orchestrator`.** Прогоняй `pytest` из + рабочего дерева именно этой задачи (`/repos/_wt/orchestrator//`, например + `feature_ORCH-NNN-slug`), где лежит код ветки. Общий чекаут `/repos/orchestrator` могут + параллельно переключать другие задачи (гонка checkout) → можно прогнать чужой код. Команда: + `cd && pytest tests/ -v --tb=short`. +3. **Smoke API (read-only):** `curl -s http://localhost:8500/health`, `…/status`, `…/queue`. + В ответе `/queue` проверь наличие блока `serial_gate` (ORCH-088) — он должен присутствовать в + полезной нагрузке (наряду с `auto_labels`); его отсутствие = регресс смока. +4. **Покрытие ТЗ:** для **каждого** TC из `04-test-plan.yaml` — выполнен? PASS/FAIL? Сопоставь с + критериями `03-acceptance-criteria.md`. Готовность = каждый TC сопоставлен, а не «файл записан». @@ -68,6 +74,10 @@ frontmatter-вердиктом, см. ``). | `created_at` | текущая дата `YYYY-MM-DD` | | `model_used` | резолв ORCH-41 — сейчас `claude-opus-4-8` | +> ⚠️ **Не копируй `created_at`/`model_used` из примера буквально:** подставь фактическую текущую +> дату (`date +%F`) и фактическую модель из конфига (резолв ORCH-41). Имена полей `created_at`/ +> `model_used` сохраняются; меняются только значения-плейсхолдеры ``/``. + ```markdown --- result: PASS # PASS | FAIL — машинный вердикт, UPPERCASE @@ -75,8 +85,8 @@ work_item: ORCH-NNN stage: testing author_agent: tester status: pass -created_at: 2026-06-09 -model_used: claude-opus-4-8 +created_at: +model_used: type: test-report work_item_id: ORCH-NNN --- @@ -108,5 +118,14 @@ PASS / FAIL Выход стадии готов, когда `13-test-report.md` записан, несёт корректный машинный `result:` -(`PASS`|`FAIL`, UPPERCASE) + frontmatter-схему 52c, таблицу TC и вывод pytest. +(`PASS`|`FAIL`, UPPERCASE) + frontmatter-схему 52c, таблицу TC и вывод pytest, И **каждый TC из +`04-test-plan.yaml` выполнен и сопоставлен** с `03-acceptance-criteria.md` (а не только «файл +записан»). + + +- **Обоснованный FAIL** (тест/смок падает по делу) → `result: FAIL` → откат на development + (`back-to:dev`); НЕ подгоняй тесты под код. +- **Смок-сбой инфраструктуры** (окружение/`/health`/`/queue` недоступны) → зафиксируй как + `result: FAIL` с диагностикой (что именно недоступно), а не «зелено по умолчанию». + diff --git a/CHANGELOG.md b/CHANGELOG.md index 3328e7e..5d0a815 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,14 @@ Формат: [Keep a Changelog](https://keepachangelog.com/). Записи — на смысловой PR/задачу. ## [Unreleased] +- **Промпт-аудит 6 агентов: расхардкод даты/модели, сверка гейтов, escalation, чистка** (ORCH-092 / эпилог эпика ORCH-52, `docs`): точечная правка 6 системных промптов `.openclaw/agents/*.md` + анти-регресс-тестов, устраняющая класс дефектов промптов (хардкод даты/модели в примерах, размазанная эскалация, нереализуемая/конфликтующая инструкция rebase, мёртвая инструкция reviewer, недообогащённый tester). **Docs/prompts-only:** `src/**`, `STAGE_TRANSITIONS`, `QG_CHECKS`, состав machine-verdict ключей и схема БД — **не тронуты**; `frontmatter_validation_strict` остаётся `False`. Машинные verdict-ключи (`verdict:`/`result:`/`staging_status:`/`deploy_status:`/`security_status:` + значения APPROVED/REQUEST_CHANGES/PASS/FAIL/SUCCESS/FAILED) и канон 52d/52c/52e (5 секций, 6 полей) — байт-в-байт. + - **Расхардкод даты/модели (FR-1/FR-2, AC-1/AC-2):** во всех 6 промптах копируемые примеры frontmatter несут плейсхолдеры `created_at: ` / `model_used: ` + явную врезку «не копируй буквально: подставь `date +%F` и фактическую модель из конфига». Литерал `claude-opus-4-8` остаётся лишь как справка в таблице полей (вне копируемого блока). + - **Сверка имён гейтов (FR-3, AC-3):** все `check_*` в 6 промптах сверены с реестром `QG_CHECKS` — несовпадений нет (`check_tests_passed` подтверждён валидным, не «исправлен вслепую»); закреплено интеграционным тестом. + - **developer (FR-4/FR-5/FR-9):** «❌ PR>1500 → разбивай на меньшие PR» переформулирован в эскалацию (слишком большой PR = декомпозиция **на уровне задач**, 1 задача = 1 ветка = 1 PR); добавлена секция `` (негодное ТЗ → `back-to:analysis`; новая развилка → к архитектору); **убран ручной `git rebase origin/main`** из алгоритма (ADR-001 D1: свежесть базы — инвариант движка serial-gate ORCH-088 + `auto_rebase_onto_main` под merge-lease, а не ручная мутирующая операция агента, конфликтующая с запретом force-push). + - **reviewer (FR-5/FR-8):** удалена мёртвая инструкция «не апрувь PR от того же экземпляра Developer» (защита от несуществующего кейса — reviewer всегда отдельный agent-run); добавлена секция `` (любой P0/P1 → `REQUEST_CHANGES`). Живые инварианты (`REQUEST_CHANGES`, «НЕ обновлена», ось трассировки, ось обзорных доков ORCH-079) сохранены. + - **tester (FR-5/FR-7):** обогащён — тесты гоняются в **worktree ветки задачи** (а не в общем `/repos/orchestrator` → исключена гонка checkout); smoke `/queue` проверяет наличие блока `serial_gate` (ORCH-088); `` требует покрытия **каждого** TC из `04-test-plan.yaml`; добавлена секция `` (обоснованный FAIL → `back-to:dev`; смок-сбой инфры → FAIL с диагностикой). + - **deployer (FR-6/FR-10):** критичные self-hosting-запреты подняты в **видную рамку в начале** `` («NEVER restart prod 8500», запрет `docker compose up`/правок инфры); язык оставлен **английским** как зафиксированное исключение канона (ADR-001 D2: самый safety-critical промпт, минимизация регресс-поверхности; перевод не несёт выгоды и угрожает байт-точности ключей/команд). Анти-регресс-маркеры (`docker exec orchestrator-staging`, `pr_already_merged`, `8500`, `INFRA-WAIVED`) сохранены. + - **Анти-регресс (FR-11):** в `tests/test_agent_prompts_canon.py` добавлены структурные TC (плейсхолдеры даты/модели в копируемых блоках; сверка гейтов с `QG_CHECKS`; `` у developer/reviewer/tester после ``; переформулировка PR-инструкции; обогащение tester; рамка deployer; удаление мёртвой строки reviewer). Существующие проверки канона 52d и `test_agent_frontmatter_no_model.py` — зелёные; полный регресс `tests/` зелёный (1278). Документация: 6 промптов, `CLAUDE.md`, `docs/architecture/README.md`. ADR: `docs/work-items/ORCH-092/06-adr/ADR-001-developer-rebase-and-deployer-language.md`. Полностью обратимо `git revert` (нет машинного поведения/состояния). - **Синхронизация обзорных доков (README) с кодом + reviewer-ось «обзорные доки»** (ORCH-079 / ORCH-52f, `docs`): слой 5 (финал) эпика ORCH-52, замыкающий цепочку 52b (структура) / 52c (frontmatter) / 52d (промпты) / 52e (трассировка). Корневой `README.md` — обзорная витрина проекта — **выдавал решённое за открытое**: секция «Известные ограничения» имела битую нумерацию (`1,2,3,4,3,4`) и пункты, опровергнутые кодом. **Docs + prompt-only:** `src/**`, `STAGE_TRANSITIONS`, `QG_CHECKS`, `check_*`/`_parse_*`, `src/frontmatter.py`, схема БД — **не тронуты**; `frontmatter_validation_strict` остаётся `False`; новый QG не вводится; правило обзорных доков нормативно-описательное (не машинный гейт), как ось трассировки ORCH-078. - **`README.md` приведён в честное состояние по коду (FR-1/FR-2/FR-3, AC-1/AC-2/AC-3):** перенумерация «Известные ограничения» сквозная без повторов; 6 решённых/устаревших пунктов перенесены в трейл **«Закрыто (история)»** с ORCH-ссылками (worktree → `ensure_worktree`+ORCH-026/088; in-process daemon → очередь ORCH-1; «Gitea CI не настроен» → `check_ci_green`; «no retry» → backoff/breaker `queue_worker.py`+ORCH-045; issue-ID → зрелый `plane_sync` ORCH-010/066/068; Playwright-timeout → watchdog ORCH-7); в «открытых» — только реально открытые, верифицированные кодом/задачей (Telegram-48h ORCH-087, task-deps intra-repo v1 ORCH-026, serial-gate Этап 1 ORCH-088). Точечная сверка с кодом: стадия `development` в таблице — `check_ci_green` (был устаревший `check_tests_local`); строка event-routing `status` — авторитетный гейт развития `check_ci_green` (ORCH-045), убран legacy-текст «больше не authoritative». - **Reviewer-ось «обзорные доки» (FR-5, AC-5):** `.openclaw/agents/reviewer.md` ось 4 «Документация» (``) + `` несут точечную врезку «❌→✅» (канон 52d): *PR закрыл пункт README «Известные ограничения», README не обновлён → finding ≥P1*; при закрытии правкой `src/` без обновления README — совпадает с существующим P0. Машинный ключ `verdict: APPROVED|REQUEST_CHANGES` — байт-в-байт; 5 XML-секций и 6 полей схемы 52c сохранены. Правило в одном промпте (без выноса в `docs/_standards/`, в отличие от 52e). diff --git a/CLAUDE.md b/CLAUDE.md index 855e012..8c61b44 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -6,7 +6,7 @@ ## Стек - Backend: FastAPI + uvicorn (Python 3.12) - БД: SQLite (`src/db.py`) -- Агенты: Claude CLI (`ORCH_CLAUDE_BIN`), по одному промпту на роль в `.openclaw/agents/`. **ORCH-74:** модель/эффорт агента берутся ТОЛЬКО из config (`resolve_agent_model`/`resolve_agent_effort`, ORCH-41) — frontmatter `model:` удалён как мёртвый, frontmatter описательный; имя модели валидируется форматом `^claude-…$` перед `--model` (never-break). **ORCH-077 (52d, замыкает эпик 52):** тело всех 6 промптов переписано в едином **каноне Anthropic** (5 обязательных XML-секций в нормативном порядке ``→``→``→``→``, запреты в формате «❌ X → ✅ Y», `` у решающих ролей), и каждый промпт **добровольно** эмитит 6-польную frontmatter-схему 52c (`work_item`/`stage`/`author_agent`/`status`/`created_at`/`model_used`) **аддитивно** — рядом с machine-verdict ключом, НЕ меняя его имя/регистр/значения (`verdict:`/`result:`/`staging_status:`/`deploy_status:`/`security_status:` — байт-в-байт). Это **docs/prompts-only** изменение: `src/**`/`STAGE_TRANSITIONS`/`QG_CHECKS`/схема БД не тронуты; `frontmatter_validation_strict` остаётся `False` (enforcement НЕ включён). Промпт `cat`-ается из worktree в момент запуска → новые промпты вступают в силу на следующем worktree от `main` без прод-рестарта. Анти-регресс — структурные тесты `tests/test_agent_prompts_canon.py` + зелёный `test_agent_frontmatter_no_model.py`. **Норматив на будущее:** новые/изменённые агент-промпты следуют этому канону. Детали — `docs/architecture/adr/adr-0021-prompt-canon-anthropic.md`. +- Агенты: Claude CLI (`ORCH_CLAUDE_BIN`), по одному промпту на роль в `.openclaw/agents/`. **ORCH-74:** модель/эффорт агента берутся ТОЛЬКО из config (`resolve_agent_model`/`resolve_agent_effort`, ORCH-41) — frontmatter `model:` удалён как мёртвый, frontmatter описательный; имя модели валидируется форматом `^claude-…$` перед `--model` (never-break). **ORCH-077 (52d, замыкает эпик 52):** тело всех 6 промптов переписано в едином **каноне Anthropic** (5 обязательных XML-секций в нормативном порядке ``→``→``→``→``, запреты в формате «❌ X → ✅ Y», `` у решающих ролей), и каждый промпт **добровольно** эмитит 6-польную frontmatter-схему 52c (`work_item`/`stage`/`author_agent`/`status`/`created_at`/`model_used`) **аддитивно** — рядом с machine-verdict ключом, НЕ меняя его имя/регистр/значения (`verdict:`/`result:`/`staging_status:`/`deploy_status:`/`security_status:` — байт-в-байт). Это **docs/prompts-only** изменение: `src/**`/`STAGE_TRANSITIONS`/`QG_CHECKS`/схема БД не тронуты; `frontmatter_validation_strict` остаётся `False` (enforcement НЕ включён). Промпт `cat`-ается из worktree в момент запуска → новые промпты вступают в силу на следующем worktree от `main` без прод-рестарта. Анти-регресс — структурные тесты `tests/test_agent_prompts_canon.py` + зелёный `test_agent_frontmatter_no_model.py`. **Норматив на будущее:** новые/изменённые агент-промпты следуют этому канону. Детали — `docs/architecture/adr/adr-0021-prompt-canon-anthropic.md`. **ORCH-092 (эпилог эпика 52, docs/prompts-only):** аудит 6 промптов поверх канона — копируемые frontmatter-примеры расхардкожены (`created_at: `/`model_used: ` + врезка «подставь `date +%F`/модель из конфига, не копируй буквально»; литерал `claude-opus-4-8` — только справка в таблице полей); добавлена секция `` developer/reviewer/tester (после ``, порядок 5 секций цел); developer лишён ручного `git rebase origin/main` (свежесть базы — инвариант движка serial-gate ORCH-088 + `auto_rebase_onto_main` под merge-lease; ручной rebase конфликтовал с запретом force-push — ADR-001 D1); tester обогащён worktree-путём + smoke `serial_gate` + покрытием каждого TC; из reviewer удалена мёртвая строка «тот же экземпляр Developer». **Языковое исключение (нормативно, ADR-001 D2):** `deployer.md` сознательно остаётся на **английском** (5 ru + 1 en) как самый safety-critical промпт — НЕ «чинить» язык вслепую; критичные self-hosting-запреты подняты в видную рамку. Verdict-ключи и канон 52d — байт-в-байт; анти-регресс — `tests/test_agent_prompts_canon.py` (ORCH-092 TC-01…TC-08). Детали — `docs/work-items/ORCH-092/06-adr/ADR-001-developer-rebase-and-deployer-language.md`. - Очередь задач: собственная (SQLite `jobs`, `src/queue_worker.py`, ORCH-1). **ORCH-026:** `claim_next_job` гейтит задачи с незавершёнными зависимостями (`job_deps`, `NOT EXISTS`) без занятия слота `max_concurrency`; декларации/детект циклов — leaf `src/task_deps.py` (kill-switch `ORCH_TASK_DEPS_ENABLED`). Сериализация мержа одного репо — безусловный pre-merge rebase под merge-lease (`ORCH_PREMERGE_REBASE_ALWAYS`). **ORCH-088 (serial gate, Этап 1):** новая задача репо не входит в `analysis` (analyst-job не выбирается, ветка не режется), пока в репо есть **более ранняя** незавершённая задача (`t2.id < jobs.task_id`, FIFO) ИЛИ репо заморожен (`repo_freeze`). Срез ветки **отложен** со `start_pipeline` на момент claim analyst-job (`launcher._materialize_deferred_branch`) — база = свежий `origin/main` с кодом предшественника (анти-stale-base). Post-deploy `DEGRADED` → durable per-repo freeze (`repo_freeze`, `cleared_at IS NULL` = активен) + Telegram; снятие — вручную `POST /serial-gate/unfreeze?repo=…`. Leaf `src/serial_gate.py` (claim — fail-OPEN, freeze — fail-CLOSED); флаги `ORCH_SERIAL_GATE_ENABLED` (kill-switch), `ORCH_SERIAL_GATE_REPOS` (CSV; пусто = все репо), `ORCH_SERIAL_GATE_FREEZE_ENABLED`. Блок `serial_gate` в `GET /queue`. `STAGE_TRANSITIONS`/`QG_CHECKS` не тронуты. - Контейнеризация: Docker + Compose - CI/CD: Gitea Actions (`.gitea/workflows/`) diff --git a/docs/architecture/README.md b/docs/architecture/README.md index e51579c..16da09e 100644 --- a/docs/architecture/README.md +++ b/docs/architecture/README.md @@ -82,6 +82,25 @@ enforcement не включается). остаётся зелёным. **Норматив на будущее:** новые/изменённые агент-промпты следуют этому канону. - ADR: [adr-0021](adr/adr-0021-prompt-canon-anthropic.md); детально — `docs/work-items/ORCH-077/06-adr/ADR-001-anthropic-prompt-canon.md`. +- **Промпт-аудит ORCH-092 (эпилог эпика 52, docs/prompts-only):** точечно устранён класс дефектов + промптов поверх канона 52d. (1) **Расхардкод примеров:** копируемые frontmatter-примеры всех 6 + промптов несут плейсхолдеры `created_at: ` / `model_used: ` + + врезку «подставь `date +%F` и модель из конфига, не копируй буквально» (литерал `claude-opus-4-8` + оставлен лишь справкой в таблице полей). (2) **Секция ``** добавлена developer/ + reviewer/tester (после ``, не нарушая порядок 5 обязательных секций): + developer → `back-to:analysis`, tester → `back-to:dev`, reviewer → `REQUEST_CHANGES`. + (3) **developer:** убран ручной `git rebase origin/main` — свежесть базы держит движок + (serial-gate ORCH-088 + `auto_rebase_onto_main` под merge-lease), а ручной rebase конфликтовал с + собственным запретом force-push (ADR-001 D1); «PR>1500 → разбивай» переформулирован в эскалацию + на уровне задач. (4) **tester** обогащён worktree-путём, smoke-проверкой блока `serial_gate` и + требованием покрытия каждого TC. (5) **reviewer:** удалена мёртвая строка «тот же экземпляр + Developer». (6) **Языковое исключение (ADR-001 D2, нормативно):** `deployer.md` сознательно + остаётся на **английском** (5 ru + 1 en) — самый safety-critical промпт, минимизация + регресс-поверхности у байт-точных verdict-ключей/команд; критичные self-hosting-запреты подняты в + видную рамку в начале ``. Это **документированное исключение**, не дрейф: будущему агенту + НЕ «чинить» язык deployer вслепую. Машинные verdict-ключи и канон 52d — байт-в-байт; анти-регресс — + расширенный `tests/test_agent_prompts_canon.py` (ORCH-092 TC-01…TC-08). ADR: + `docs/work-items/ORCH-092/06-adr/ADR-001-developer-rebase-and-deployer-language.md`. #### Слой трассировки: стандарт маркеров `ORCH-NNN` (ORCH-078, 52e — слой 4 эпика 52) **Слой 4 (трассировка).** Маркеры `ORCH-NNN`/`ET-NNN` в коде (де-факто **51 уникальный** в `src/`) diff --git a/docs/work-items/ORCH-092/00-business-request.md b/docs/work-items/ORCH-092/00-business-request.md new file mode 100644 index 0000000..5dc44fc --- /dev/null +++ b/docs/work-items/ORCH-092/00-business-request.md @@ -0,0 +1,7 @@ +# Business Request: Промпт-аудит 6 агентов: расхардкод дат/модели + сверка гейтов + escalation + чистка мёртвых инструкций + +Work Item ID: ORCH-092 + +## Description + +TBD diff --git a/docs/work-items/ORCH-092/01-brd.md b/docs/work-items/ORCH-092/01-brd.md new file mode 100644 index 0000000..b2d982d --- /dev/null +++ b/docs/work-items/ORCH-092/01-brd.md @@ -0,0 +1,169 @@ +--- +work_item: ORCH-092 +stage: analysis +author_agent: analyst +status: ready-for-review +created_at: 2026-06-09 +model_used: claude-opus-4-8 +--- + +# 01 — BRD (бизнес-требования): ORCH-092 — Промпт-аудит 6 агентов (расхардкод дат/модели, сверка гейтов, escalation, чистка мёртвых инструкций) + +Work Item: **ORCH-092** · Repo: **orchestrator** · Стадия: analysis + +## 1. Бизнес-контекст и проблема + +ORCH-092 — **эпилог эпика ORCH-52** (канонизация системных промптов). После слоёв 52d +(XML-канон) и 52e (трассировка маркеров) системный аудит 6 промптов (`.openclaw/agents/*.md`) +выявил класс системных дефектов, способных приводить к **дрейфу и багам** в self-hosting-конвейере: + +- **Хардкод даты в примерах.** Во всех 6 промптах пример frontmatter содержит жёсткое + `created_at: 2026-06-09`. Исполняющая модель — литеральный Opus 4.8: пример сильнее + инструкции «текущая дата». Риск — модель **копирует дату буквально** → все документы + всех задач получают одну и ту же дату, метаданные перестают отражать реальность. +- **Хардкод модели в примерах.** Пример несёт `model_used: claude-opus-4-8`. Если включат + model-routing (ORCH-41), промпты начнут **врать** про использованную модель — ровно та + боль, которую слой 52f чинил для README «Известные ограничения». +- **Несверённые имена гейтов.** Промпты называют имена QG-функций (`check_*`); при дрейфе + кода имя в промпте может разойтись с реальным реестром `QG_CHECKS`. *(Сверка кодом в рамках + анализа показала: текущие имена корректны — см. §6, допущение A-1; задача — закрепить сверку + как воспроизводимую проверку, а не «починить несуществующий баг».)* +- **Логические нестыковки в developer.md.** Инструкция «PR > 1500 строк → разбивай на меньшие + PR» **физически невыполнима**: конвейер = 1 задача = 1 ветка = 1 PR, разбить внутри стадии + нельзя. Инструкция `git rebase origin/main` в начале алгоритма **дублирует/конфликтует** с + автоматикой движка (serial-gate ORCH-088 режет ветку от свежего `origin/main`; + `auto_rebase_onto_main` ORCH-026 делает pre-merge rebase сам). +- **Размазанная эскалация.** Секцию `` имеет только `architect.md`. У developer / + reviewer / tester маршруты эскалации (негодное ТЗ, FAIL, REQUEST_CHANGES) растворены в + `` — нет единой видной точки «куда эскалировать при затыке». +- **Консистентность/качество.** `deployer.md` (самый большой, ~9.6 KB, на английском) рискует + «утопить» критичный self-hosting-запрет «НЕ рестартить 8500». `tester.md` (самый бедный, + ~4.7 KB) не фиксирует worktree-путь (были гонки checkout), не проверяет serial_gate-блок и + не требует покрытия ТЗ. `reviewer.md` содержит мёртвую инструкцию про «тот же экземпляр + Developer» (в конвейере reviewer всегда отдельный agent-run). + +**Это docs/prompts-only задача.** Код (`src/**`, `STAGE_TRANSITIONS`, `QG_CHECKS`, схема БД) +**не трогается**. Промпт `cat`-ается из worktree в момент запуска агента, поэтому новые +промпты вступают в силу на следующем worktree от `main` **без рестарта прод-контейнера** — +что критично для self-hosting (рестарт 8500 встаёт конвейер всех проектов). + +## 2. Объём (scope) + +### В объёме +- Правка 6 промптов `.openclaw/agents/{analyst,architect,developer,reviewer,tester,deployer}.md`: + расхардкод даты (P0-1) и модели (P0-2) в **копируемых примерах**; сверка имён гейтов (P0-3); + переформулировка «PR>1500» в эскалацию (P1-1); добавление `` в developer/reviewer/ + tester (P1-3); рамка критичных запретов в deployer (P2-1); обогащение tester (P2-3); удаление + мёртвой инструкции reviewer (P2-4). +- **Решения, требующие архитектора** (формализованы как открытые в §6, решаются в `06-adr/`): + P1-2 (нужен ли ручной rebase у developer при наличной автоматике), P2-2 (язык deployer: + унификация en→ru ИЛИ явно зафиксированное исключение). +- Обновление обзорной документации: `CLAUDE.md`, `docs/architecture/README.md` (при + необходимости), `CHANGELOG.md`, ADR work item, и **новые/обновлённые структурные тесты** + `tests/test_agent_prompts_canon.py` (анти-регресс новых инвариантов). + +### Вне объёма +- ❌ Любая правка `src/**`, `STAGE_TRANSITIONS`, `QG_CHECKS`, `check_*`, схемы БД. +- ❌ Изменение машинных verdict-ключей (`verdict:` / `result:` / `staging_status:` / + `deploy_status:` / `security_status:`) — байт-в-байт неприкосновенны. +- ❌ Слом XML-канона 52d (5 обязательных секций в нормативном порядке), 52c-схемы (6 полей), + правила трассировки 52e. +- ❌ Включение enforcement frontmatter (`frontmatter_validation_strict` остаётся `False`). +- ❌ Артефакты других work item. + +## 3. Заинтересованные стороны +- **Заказчик / приёмка:** Owner (Слава) — BRD-гейт ручной; решения по P1-2/P2-2. +- **Затрагиваются:** все 6 ролей конвейера (исполняют обновлённые промпты), все проекты в + общем инстансе (self-hosting), сопровождающие агенты. +- **Принимает архитектурные развилки:** архитектор (стадия architecture, `06-adr/`). + +## 4. Бизнес-требования (BR) + +- **BR-1 (P0-1)** — В копируемых примерах frontmatter **всех 6** промптов `created_at` — + плейсхолдер `` (НЕ конкретная дата), сопровождённый явной инструкцией: подставить + фактическую текущую дату (`date +%F`), НЕ копировать из примера буквально. +- **BR-2 (P0-2)** — В копируемых примерах **всех 6** промптов `model_used` — плейсхолдер/резолв + (``, НЕ литеральный `claude-opus-4-8`), с оговоркой подставить фактическую + модель из конфига. Факт «сейчас `claude-opus-4-8`» допускается как **справка вне копируемого + блока** (например в таблице полей), но не как литерал в примере. +- **BR-3 (P0-3)** — Все имена гейтов/QG-функций, упомянутые в промптах, **сверены** с реальным + реестром `QG_CHECKS` (`src/qg/checks.py`). Реальные несовпадения (если бы нашлись) исправлены; + **ложные подозрения не трогаются** (`check_tests_passed` верен). Сверка закреплена + воспроизводимым тестом. +- **BR-4 (P1-1)** — Инструкция developer «PR > 1500 строк → разбивай на меньшие PR» заменена на + **эскалацию**: PR слишком большой → флагировать/эскалировать (задача слишком крупная, нужна + декомпозиция **на уровне задач**, не PR). +- **BR-5 (P1-3)** — developer / reviewer / tester получают явную секцию `` с чёткими + маршрутами: developer → `back-to:analysis` при негодном ТЗ; tester → `back-to:dev` при FAIL; + reviewer → `REQUEST_CHANGES`. (Существующая `` у architect — эталон формата.) +- **BR-6 (P2-1)** — Критичные self-hosting-запреты deployer (прежде всего «НЕ рестартить + прод 8500») вынесены в **видную рамку в начале** промпта, чтобы не утонуть в объёме. +- **BR-7 (P2-3)** — tester обогащён: явный worktree-путь (ветка vs `/repos` — устранить гонки + checkout); smoke добавляет проверку блока `serial_gate` в `/queue` (ORCH-088); `success_criteria` + упоминает **покрытие ТЗ** (каждый TC из `04-test-plan.yaml`), не только «файл записан». +- **BR-8 (P2-4)** — Мёртвая инструкция reviewer «не апрувь PR от того же экземпляра Developer» + удалена (защита от несуществующего кейса — reviewer всегда отдельный agent-run), при сохранении + всех живых инвариантов оси документации. +- **BR-9 (P1-2, решение архитектора)** — Зафиксировать в ADR: должен ли developer выполнять + ручной `git rebase origin/main`, или это полностью делает движок (serial-gate + auto_rebase). + Промпт приводится в соответствие с принятым решением (убрать/смягчить/оставить с пояснением). +- **BR-10 (P2-2, решение архитектора)** — Зафиксировать в ADR язык deployer: унифицировать + (en→ru) ЛИБО явно задокументировать исключение («deployer — en, т.к. критичные команды/ + контракты на en»). При любом исходе machine-verdict ключи и shell-команды **не ломаются**. +- **BR-11 (документация)** — Обновлены `CLAUDE.md` / `README` / ADR / `CHANGELOG.md` + (golden source = код); добавлены/обновлены структурные тесты анти-регресса новых инвариантов. + +## 5. Нефункциональные требования (NFR) + +- **NFR-1 (анти-регресс, критично)** — verdict-ключи `verdict:` / `result:` / `staging_status:` / + `deploy_status:` / `security_status:` сохраняются **байт-в-байт** (имя/регистр/значения); + XML-канон 52d (5 секций, нормативный порядок), 52c-схема (6 полей), правило трассировки 52e — + сохранены. `tests/test_agent_prompts_canon.py` и `tests/test_agent_frontmatter_no_model.py` — + **зелёные**. +- **NFR-2 (self-hosting безопасность)** — Изменение docs/prompts-only: `src/**` не тронут, + прод-контейнер 8500 **не перезапускается**. Новые промпты применяются на следующем worktree + от `main` без прод-рестарта. +- **NFR-3 (обратимость)** — Все правки текстовые и ревертируемы одним PR; нет миграций, нет + изменения схемы/состояния. +- **NFR-4 (консистентность канона)** — Все правки соблюдают канон Anthropic (запреты «❌ X → + ✅ Y», секции в нормативном порядке); новая секция `` размещается так, чтобы не + нарушать порядок 5 обязательных секций (после ``, как у architect). +- **NFR-5 (отсутствие enforcement)** — `frontmatter_validation_strict` остаётся `False`; + плейсхолдеры в примерах не вызывают hard-fail валидатора (warning-only). + +## 6. Допущения и ограничения + +- **A-1 (сверено кодом):** реестр `QG_CHECKS` (`src/qg/checks.py`) содержит: + `check_analysis_complete`, `check_analysis_approved`, `check_architecture_done`, + `check_ci_green`, `check_review_approved`, `check_tests_passed`, `check_reviewer_verdict`, + `check_deploy_status`, `check_staging_status`, `check_branch_mergeable`, `check_security_gate`, + `check_staging_image_fresh` (+ `check_tests_local` — DEPRECATED). Имена гейтов в промптах + (`check_ci_green`, `check_reviewer_verdict`, `check_tests_passed`, `check_deploy_status`, + `check_staging_status`, `check_security_gate`) **совпадают**. Реальных несовпадений на момент + анализа НЕТ → BR-3 = «закрепить сверку», а не «исправить». +- **A-2 (сверено кодом, основание для BR-9):** `auto_rebase_onto_main` (`src/merge_gate.py`) + вызывается автоматически в `check_branch_mergeable` (`src/qg/checks.py`) при + `premerge_rebase_always=True` (дефолт), а serial-gate (ORCH-088) откладывает срез ветки на + свежий `origin/main`. Ручной rebase developer пересекается с этой автоматикой → требует решения + архитектора (не менять вслепую). +- **A-3:** Канон-тест проверяет **наличие имён полей** `created_at`/`model_used` и **литеральные + строки** `author_agent: ` / `stage: ` в примере — плейсхолдеры даты/модели этого + не нарушают. Ни один тест не утверждает литерал `claude-opus-4-8` внутри промптов. +- **Ограничение:** P1-2 и P2-2 — зона архитектора; analyst фиксирует факты и требование-решение, + но НЕ принимает решение и НЕ правит вслепую. + +## 7. Критерии успеха + +Аудит-правки внесены во все 6 промптов согласно BR-1…BR-8; архитектурные развилки BR-9/BR-10 +решены в ADR и отражены в промптах; документация и анти-регресс-тесты обновлены (BR-11); +анти-регресс NFR-1 соблюдён (verdict-ключи и канон не сломаны, целевые тесты зелёные). +Детальные PASS/FAIL — `03-acceptance-criteria.md`. + +## 8. Риски + +- Случайный слом verdict-ключа/канона при массовой текстовой правке → митигируется + структурными тестами (NFR-1). +- Плейсхолдер `` / `` сам по себе мог бы быть скопирован буквально — + митигируется **явной инструкцией** «подставь фактическое, НЕ копируй». +- Принятие архитектурного решения (P1-2/P2-2) не аналитиком — митигируется явным выносом в `06-adr/`. +- Детальная карта рисков — `10-tech-risks.md` (заполняет архитектор). diff --git a/docs/work-items/ORCH-092/02-trz.md b/docs/work-items/ORCH-092/02-trz.md new file mode 100644 index 0000000..a24db04 --- /dev/null +++ b/docs/work-items/ORCH-092/02-trz.md @@ -0,0 +1,159 @@ +--- +work_item: ORCH-092 +stage: analysis +author_agent: analyst +status: ready-for-review +created_at: 2026-06-09 +model_used: claude-opus-4-8 +--- + +# 02 — ТЗ (TRZ): ORCH-092 — Промпт-аудит 6 агентов + +Work Item: **ORCH-092** · Repo: **orchestrator** · Стадия: analysis + +> ТЗ описывает **конкретные изменения к реализации**, выведенные из BRD и фактического кода. +> Архитектурное обоснование/решения (P1-2 rebase, P2-2 язык deployer) — задача архитектора (`06-adr/`). + +## 1. Сводка изменения + +Точечная правка 6 системных промптов `.openclaw/agents/*.md` + обзорной документации + структурных +анти-регресс-тестов. **Код приложения не трогается** (docs/prompts-only). Цель — устранить класс +дефектов промптов (хардкод даты/модели, размазанная эскалация, нереализуемые/конфликтующие +инструкции, мёртвые инструкции, недообогащённые промпты), сохранив канон 52d/52c/52e и +машинные verdict-ключи байт-в-байт. + +## 2. Задействованные модули / пути + +| Путь | Действие | +|------|----------| +| `.openclaw/agents/analyst.md` | изменить — расхардкод `created_at`/`model_used` в примере (FR-1, FR-2) | +| `.openclaw/agents/architect.md` | изменить — расхардкод `created_at`/`model_used` в примере (FR-1, FR-2) | +| `.openclaw/agents/developer.md` | изменить — расхардкод (FR-1/2); «PR>1500»→эскалация (FR-4); `` (FR-5); rebase по решению ADR (FR-9) | +| `.openclaw/agents/reviewer.md` | изменить — расхардкод (FR-1/2); `` (FR-5); удалить мёртвую инструкцию (FR-8) | +| `.openclaw/agents/tester.md` | изменить — расхардкод (FR-1/2); `` (FR-5); worktree-путь + serial_gate smoke + покрытие ТЗ (FR-7) | +| `.openclaw/agents/deployer.md` | изменить — расхардкод (FR-1/2); рамка критичных запретов (FR-6); язык по решению ADR (FR-10) | +| `tests/test_agent_prompts_canon.py` | изменить — добавить TC анти-регресса новых инвариантов (FR-11) | +| `CLAUDE.md` | изменить — отразить ORCH-092 (норматив промптов), при необходимости | +| `docs/architecture/README.md` | изменить — when-applicable (если затрагивается обзорная карта) | +| `CHANGELOG.md` | изменить — запись под `## [Unreleased]` | +| `docs/work-items/ORCH-092/06-adr/ADR-001-*.md` | создать (архитектор) — решения P1-2, P2-2 | +| `src/**`, `src/qg/checks.py`, `STAGE_TRANSITIONS`, `QG_CHECKS`, схема БД | **НЕ трогать** | + +## 3. Функциональные требования + +### FR-1 — Расхардкод `created_at` в примерах (BR-1, P0-1) +Во **всех 6** промптах в копируемом примере frontmatter заменить `created_at: 2026-06-09` на +плейсхолдер `created_at: `. Рядом — явная инструкция (в ``): «подставь +фактическую текущую дату (`date +%F`); НЕ копируй дату из примера буквально». Инвариант: имя поля +`created_at` остаётся (канон-тест проверяет наличие имени). + +### FR-2 — Расхардкод `model_used` в примерах (BR-2, P0-2) +Во **всех 6** промптах в копируемом примере заменить `model_used: claude-opus-4-8` на +`model_used: ` + оговорку «подставь фактическую модель из конфига, не копируй +буквально». Факт «сейчас `claude-opus-4-8`» допустимо оставить как **справку в таблице полей** +(вне копируемого блока). Инвариант: имя поля `model_used` остаётся. + +### FR-3 — Сверка имён гейтов с `QG_CHECKS` (BR-3, P0-3) +Пройти по каждому промпту, сверить все упомянутые `check_*`/имена QG-функций с реальным реестром +`QG_CHECKS` в `src/qg/checks.py`. **Установленный факт:** на момент анализа несовпадений НЕТ +(`check_ci_green`, `check_reviewer_verdict`, `check_tests_passed`, `check_deploy_status`, +`check_staging_status`, `check_security_gate` — все присутствуют в реестре; `check_tests_passed` +верен — подозрение было ложным). Требование: **исправлять только реально несовпадающие** имена +(не выдумывать); закрепить сверку воспроизводимым тестом (FR-11/TC-03). + +### FR-4 — «PR>1500» → эскалация (BR-4, P1-1) +В `developer.md` (секция ``) заменить запрет «❌ Не делай PR > 1500 строк без +декомпозиции → ✅ разбивай на меньшие PR» на эскалацию: PR оказался слишком большим → **флагируй/ +эскалируй** (задача слишком крупная, нужна декомпозиция **на уровне задач**, не внутри стадии; +1 задача = 1 ветка = 1 PR). Инвариант FR-6-анти-регресс: маркер «свой PR» («не мержи свой PR») +сохраняется отдельно. + +### FR-5 — Секция `` в developer/reviewer/tester (BR-5, P1-3) +Добавить секцию `` (формат — как у `architect.md`, **после** ``, +чтобы не нарушать нормативный порядок 5 обязательных секций): +- **developer:** негодное/нереализуемое ТЗ → вернуть в Анализ (`back-to:analysis`); нужна новая + архитектурная развилка → эскалация к архитектору. +- **tester:** обоснованный FAIL → откат на development (`back-to:dev`); смок-сбой инфраструктуры → + зафиксировать как FAIL с диагностикой. +- **reviewer:** любой P0/P1 → `REQUEST_CHANGES` (единая точка); неоднозначность требований → + finding со ссылкой на ТЗ/ADR. + +Эскалационные строки консолидируют (не дублируют слепо) то, что было размазано по ``. + +### FR-6 — Рамка критичных запретов в deployer (BR-6, P2-1) +В `deployer.md` вынести самые критичные self-hosting-запреты в **видную рамку в начале** (сразу +после frontmatter / в начале ``), как минимум: «**NEVER restart the prod `orchestrator` +(8500) container**». Существующий blockquote усилить/поднять. Инвариант анти-регресса: маркеры +`docker exec orchestrator-staging`, `pr_already_merged`, `8500`, `INFRA-WAIVED` сохраняются. + +### FR-7 — Обогащение tester (BR-7, P2-3) +В `tester.md`: +- **worktree-путь:** явно указать, что тесты гоняются в worktree ветки задачи (а не в общем + `/repos/orchestrator`), чтобы исключить гонки checkout; зафиксировать корректный путь алгоритма. +- **serial_gate smoke:** в smoke `/queue` добавить проверку наличия блока `serial_gate` + (ORCH-088) в ответе. +- **покрытие ТЗ:** в `` добавить, что готовность = каждый TC из + `04-test-plan.yaml` выполнен и сопоставлен с `03-acceptance-criteria.md`, а не только «файл + записан». Инвариант анти-регресса: маркеры `pytest`, `/health`, `/status`, `/queue` сохраняются. + +### FR-8 — Удаление мёртвой инструкции reviewer (BR-8, P2-4) +В `reviewer.md` удалить строку-запрет «❌ Не апрувь PR от того же экземпляра Developer → +✅ выноси независимый вердикт» (защита от несуществующего кейса — reviewer всегда отдельный +agent-run). Перед удалением убедиться, что ни один тест на неё не опирается (анти-регресс reviewer +держит только `REQUEST_CHANGES` и «НЕ обновлена» — они сохраняются). Ось «независимый вердикт по +4 осям» остаётся выражена через ``/``. + +### FR-9 — Ручной rebase developer по решению ADR (BR-9, P1-2) — **зона архитектора** +Установленный факт (A-2): движок уже выполняет rebase автоматически (`auto_rebase_onto_main` в +`check_branch_mergeable`, `premerge_rebase_always=True`; serial-gate режет ветку от свежего +`origin/main`). Требование: архитектор в ADR решает — убрать/смягчить/оставить-с-пояснением шаг +`git fetch origin && git rebase origin/main` в алгоритме developer; developer.md приводится в +соответствие. БЕЗ ADR-решения промпт-строку rebase **не менять**. + +### FR-10 — Язык deployer по решению ADR (BR-10, P2-2) — **зона архитектора** +Архитектор решает: унифицировать deployer на русский (как остальные 5) ЛИБО явно задокументировать +исключение («deployer — en по причине критичных команд/контрактов»). Инвариант: при любом исходе +machine-verdict ключи (`staging_status:`/`deploy_status:`/`security_status:` + значения +SUCCESS/FAILED/PASS/FAIL) и shell-команды НЕ переводятся/не ломаются. БЕЗ ADR-решения язык +**не менять**. + +### FR-11 — Анти-регресс-тесты новых инвариантов (BR-11) +В `tests/test_agent_prompts_canon.py` (pure-text, без импорта `src/`, кроме TC-03 сверки реестра) +добавить проверки: плейсхолдеры даты/модели в примерах (нет литерала `created_at: 2026-`/ +`model_used: claude-opus-4-8` в копируемом блоке); наличие `` у developer/reviewer/ +tester; отсутствие удалённой мёртвой строки reviewer; обогащение tester (serial_gate/worktree/ +покрытие ТЗ); рамка в deployer. Существующие проверки (5 секций, 6 полей, verdict-ключи, +анти-регресс-маркеры) остаются зелёными. + +## 4. Изменения API +Нет. Эндпоинты не добавляются и не меняются. + +## 5. Изменения схемы БД +Нет. Схема БД не трогается. + +## 6. Требования к новым/изменённым QG checks +Нет. `QG_CHECKS` / `check_*` / `STAGE_TRANSITIONS` **не трогаются**. Имена гейтов в промптах лишь +**сверяются** с существующим реестром (FR-3). `frontmatter_validation_strict` остаётся `False` +(enforcement не включается). + +## 7. Совместимость / регресс + +- **Машинные verdict-ключи** — байт-в-байт: `verdict:` (APPROVED|REQUEST_CHANGES), `result:` + (PASS|FAIL), `staging_status:`/`deploy_status:` (SUCCESS|FAILED), `security_status:` (PASS|FAIL). + Гарант — `test_machine_verdict_keys_preserved_exact_case`. +- **Канон 52d/52c/52e** — 5 секций в нормативном порядке (context→task→deliverables→constraints→ + output_format), 6 полей схемы, правило трассировки. `` добавляется как 6-я + необязательная секция ПОСЛЕ ``/`` — порядок обязательной + пятёрки не нарушается. Гаранты — `test_five_xml_sections_present`, + `test_six_schema_field_names_present`, `test_schema_pins_role_specific_author_and_stage`. +- **Frontmatter определения агента** — верхний `---`-блок (name/description, без `model:`) + не трогается; примеры схемы живут в теле. Гарант — `tests/test_agent_frontmatter_no_model.py`. +- **Область раската / обратимость** — docs/prompts-only; вступает в силу на следующем worktree + от `main`; прод-рестарт НЕ требуется; полностью ревертируемо одним PR. +- **Полный регресс** `pytest tests/ -q` остаётся зелёным. + +## 8. Артефакты pipeline, создаваемые/обновляемые этой задачей +- Анализ (этот пакет): `01-brd.md`, `02-trz.md`, `03-acceptance-criteria.md`, `04-test-plan.yaml`. +- Архитектура: `06-adr/ADR-001-*.md` (решения P1-2 FR-9, P2-2 FR-10), при сквозном эффекте — + возможный `10-tech-risks.md`. +- Разработка: 6 промптов + `tests/test_agent_prompts_canon.py` + `CLAUDE.md`/README/`CHANGELOG.md`. diff --git a/docs/work-items/ORCH-092/03-acceptance-criteria.md b/docs/work-items/ORCH-092/03-acceptance-criteria.md new file mode 100644 index 0000000..43bdd9e --- /dev/null +++ b/docs/work-items/ORCH-092/03-acceptance-criteria.md @@ -0,0 +1,151 @@ +--- +work_item: ORCH-092 +stage: analysis +author_agent: analyst +status: ready-for-review +created_at: 2026-06-09 +model_used: claude-opus-4-8 +--- + +# 03 — Критерии приёмки (Acceptance Criteria): ORCH-092 — Промпт-аудит 6 агентов + +Work Item: **ORCH-092** · Repo: **orchestrator** · Стадия: analysis + +Формат: каждый критерий имеет **PASS** (что должно быть истинно для приёмки) и **FAIL** +(что считается провалом). Reviewer/tester проверяют буквально по файлам репозитория +(`.openclaw/agents/*.md`, `tests/`, `docs/`). + +--- + +## AC-1 — `created_at` в примерах всех 6 промптов — плейсхолдер (P0-1, BR-1, FR-1) + +**Условие:** В копируемом примере frontmatter каждого из 6 промптов дата не захардкожена. +- **PASS:** В `analyst/architect/developer/reviewer/tester/deployer.md` пример несёт + `created_at: ` (плейсхолдер); рядом — явная инструкция «подставь фактическую дату + (`date +%F`), НЕ копируй из примера». Литерала `created_at: 2026-06-09` (или иной конкретной + даты) в **копируемом блоке** нет. +- **FAIL:** Хотя бы один промпт оставляет конкретную дату в примере, или отсутствует инструкция + «не копируй буквально». + +--- + +## AC-2 — `model_used` в примерах — плейсхолдер/резолв (P0-2, BR-2, FR-2) + +**Условие:** В копируемом примере frontmatter каждого из 6 промптов модель не захардкожена. +- **PASS:** Пример несёт `model_used: ` (или эквивалентный плейсхолдер) + + оговорку «подставь фактическую модель из конфига». Литерал `claude-opus-4-8` в **копируемом + блоке** отсутствует (допускается как справка в таблице полей вне блока). +- **FAIL:** Хотя бы один промпт оставляет `model_used: claude-opus-4-8` в копируемом примере, + или нет оговорки про подстановку. + +--- + +## AC-3 — Имена гейтов сверены с `QG_CHECKS` (P0-3, BR-3, FR-3) + +**Условие:** Все `check_*`/имена QG-функций в промптах соответствуют реестру `QG_CHECKS`. +- **PASS:** Каждое имя гейта, встречающееся в 6 промптах, присутствует ключом в + `QG_CHECKS` (`src/qg/checks.py`). Реальные несовпадения (если бы были) исправлены; ложные + (напр. `check_tests_passed` — верен) НЕ тронуты. Сверка закреплена тестом (см. AC-10/TC-03). +- **FAIL:** В промпте остаётся имя гейта, которого нет в `QG_CHECKS`; ИЛИ исправлено верное имя + «вслепую» (придуманная замена). + +--- + +## AC-4 — developer: «PR>1500» → эскалация (P1-1, BR-4, FR-4) + +**Условие:** Нереализуемая инструкция о разбиении PR переформулирована. +- **PASS:** `developer.md` НЕ содержит инструкции «разбивай на меньшие PR»; вместо неё — + эскалация: слишком большой PR → флагировать/эскалировать (нужна декомпозиция на уровне задач, + 1 задача = 1 ветка = 1 PR). Маркер «свой PR» («не мержи свой PR») сохранён. +- **FAIL:** Старая формулировка «разбивай на меньшие PR» осталась; ИЛИ при правке удалён маркер + «свой PR». + +--- + +## AC-5 — `` в developer/reviewer/tester (P1-3, BR-5, FR-5) + +**Условие:** Три промпта получили секцию `` с чёткими маршрутами. +- **PASS:** `developer.md`, `reviewer.md`, `tester.md` содержат ``…`` + (после ``), с маршрутами: developer → `back-to:analysis`; tester → + `back-to:dev` (при FAIL); reviewer → `REQUEST_CHANGES`. Нормативный порядок 5 обязательных + секций НЕ нарушен. +- **FAIL:** Хотя бы у одного из трёх нет ``; ИЛИ её добавление сломало порядок/ + наличие 5 обязательных секций. + +--- + +## AC-6 — deployer: рамка запретов + решённый язык (P2-1/P2-2, BR-6/BR-10, FR-6/FR-10) + +**Условие:** Критичные self-hosting-запреты подняты в видную рамку; вопрос языка решён. +- **PASS:** `deployer.md` несёт **в начале** видную рамку с критичным запретом «NEVER restart + prod 8500». Язык deployer решён архитектором в `06-adr/`: либо унифицирован на ru, либо + зафиксировано явное исключение (en) с обоснованием. Маркеры `docker exec orchestrator-staging`, + `pr_already_merged`, `8500`, `INFRA-WAIVED` сохранены; verdict-ключи и команды не сломаны. +- **FAIL:** Критичный запрет не выделен/утоплен в тексте; ИЛИ язык не решён (нет ADR-решения); + ИЛИ потерян анти-регресс-маркер / сломан verdict-ключ при переводе. + +--- + +## AC-7 — tester обогащён (P2-3, BR-7, FR-7) + +**Условие:** tester получил worktree-путь, serial_gate smoke и покрытие ТЗ. +- **PASS:** `tester.md`: (а) явно указывает worktree-путь ветки задачи (а не общий + `/repos/orchestrator`) для прогона тестов; (б) smoke `/queue` проверяет наличие блока + `serial_gate` (ORCH-088); (в) `` требует покрытия каждого TC из + `04-test-plan.yaml` (а не только «файл записан»). Маркеры `pytest`/`/health`/`/status`/`/queue` + сохранены. +- **FAIL:** Отсутствует любой из трёх пунктов; ИЛИ потерян анти-регресс-маркер tester. + +--- + +## AC-8 — Удалена мёртвая инструкция reviewer (P2-4, BR-8, FR-8) + +**Условие:** Строка про «тот же экземпляр Developer» удалена без потери живых инвариантов. +- **PASS:** `reviewer.md` НЕ содержит «не апрувь PR от того же экземпляра Developer». Маркеры + `REQUEST_CHANGES` и «НЕ обновлена», ось документации, ось трассировки (`TRACEABILITY.md`), + ось обзорных доков (`Известные ограничения`, `ORCH-079`) сохранены. +- **FAIL:** Мёртвая строка осталась; ИЛИ при удалении пострадал живой инвариант reviewer. + +--- + +## AC-9 — АНТИ-РЕГРЕСС: verdict-ключи + канон + `src/` не тронут (NFR-1/2, BR-11) + +**Условие:** Машинные контракты и канон сохранены, код не тронут. +- **PASS:** verdict-ключи `verdict:`/`result:`/`staging_status:`/`deploy_status:`/ + `security_status:` (+ значения APPROVED/REQUEST_CHANGES/PASS/FAIL/SUCCESS/FAILED) — байт-в-байт. + 5 XML-секций в нормативном порядке + 6 полей 52c во всех 6 промптах. `src/**`, + `STAGE_TRANSITIONS`, `QG_CHECKS`, схема БД — без изменений в diff. `git diff --stat` не содержит + `src/`. `tests/test_agent_prompts_canon.py` и `tests/test_agent_frontmatter_no_model.py` — + зелёные. +- **FAIL:** Любой verdict-ключ изменён по имени/регистру/значению; нарушен порядок/наличие 5 + секций или 6 полей; есть правка `src/`; целевые тесты красные. + +--- + +## AC-10 — Документация и тесты обновлены (BR-11, FR-11) + +**Условие:** Обзорная документация и анти-регресс-тесты отражают изменение. +- **PASS:** `CHANGELOG.md` несёт запись ORCH-092; `CLAUDE.md`/`docs/architecture/README.md` + обновлены при необходимости; `06-adr/ADR-001-*.md` фиксирует решения P1-2/P2-2; новые + структурные TC (плейсхолдеры даты/модели, ``, удаление мёртвой строки, обогащение + tester, рамка deployer, сверка гейтов) добавлены в `tests/test_agent_prompts_canon.py` и + зелёные; полный `pytest tests/ -q` зелёный. +- **FAIL:** Отсутствует ADR-решение P1-2/P2-2; нет записи в CHANGELOG; новые инварианты не + покрыты тестом; регресс красный. + +--- + +## Сводная матрица AC ↔ FR/BR + +| AC | Покрывает | +|----|-----------| +| AC-1 | BR-1 / FR-1 | +| AC-2 | BR-2 / FR-2 | +| AC-3 | BR-3 / FR-3 | +| AC-4 | BR-4 / FR-4 | +| AC-5 | BR-5 / FR-5 | +| AC-6 | BR-6 / BR-10 / FR-6 / FR-10 | +| AC-7 | BR-7 / FR-7 | +| AC-8 | BR-8 / FR-8 | +| AC-9 | NFR-1 / NFR-2 / FR-9 (rebase по ADR без слома канона) | +| AC-10 | BR-9 / BR-11 / FR-9 / FR-11 | diff --git a/docs/work-items/ORCH-092/04-test-plan.yaml b/docs/work-items/ORCH-092/04-test-plan.yaml new file mode 100644 index 0000000..98fce05 --- /dev/null +++ b/docs/work-items/ORCH-092/04-test-plan.yaml @@ -0,0 +1,89 @@ +work_item: ORCH-092 +stage: analysis +author_agent: analyst +status: ready-for-review +created_at: 2026-06-09 +model_used: claude-opus-4-8 +title: "Промпт-аудит 6 агентов: расхардкод дат/модели, сверка гейтов, escalation, чистка" +framework: pytest +scope: > + Покрываются структурные (pure-text) инварианты 6 промптов .openclaw/agents/*.md после + аудита ORCH-092, плюс анти-регресс канона 52d/52c/52e и машинных verdict-ключей. Вне + покрытия: поведение src/ (НЕ трогается), запуск реальных агентов. Полный регресс tests/ + должен оставаться зелёным. Новые TC живут в tests/test_agent_prompts_canon.py; TC-03 — + единственный с импортом QG_CHECKS (integration). + +notes: > + Все правки docs/prompts-only — src/ не изменяется, прод-контейнер 8500 не перезапускается. + Регрессом считается: слом любого verdict-ключа (verdict:/result:/staging_status:/ + deploy_status:/security_status:) по имени/регистру/значению; нарушение порядка/наличия 5 + XML-секций или 6 полей 52c; красный test_agent_prompts_canon.py или + test_agent_frontmatter_no_model.py. P1-2 (rebase) и P2-2 (язык deployer) фиксируются ADR — + TC проверяют лишь, что промпт согласован и канон/ключи не сломаны. + +tests: + - id: TC-01 + type: unit + description: "AC-1: во всех 6 промптах пример frontmatter использует created_at: (нет литерала конкретной даты в копируемом блоке) и несёт инструкцию 'не копируй дату буквально'." + module: tests/test_agent_prompts_canon.py + expected: PASS + + - id: TC-02 + type: unit + description: "AC-2: во всех 6 промптах пример несёт model_used: (нет литерала claude-opus-4-8 в копируемом блоке) + оговорку про подстановку фактической модели." + module: tests/test_agent_prompts_canon.py + expected: PASS + + - id: TC-03 + type: integration + description: "AC-3: каждое имя check_* в 6 промптах присутствует ключом в QG_CHECKS (импорт src.qg.checks). check_tests_passed подтверждён валидным; нет имён вне реестра." + module: tests/test_agent_prompts_canon.py + expected: PASS + + - id: TC-04 + type: unit + description: "AC-4: developer.md не содержит 'разбивай на меньшие PR'; содержит эскалацию для слишком большого PR (декомпозиция на уровне задач); маркер 'свой PR' сохранён." + module: tests/test_agent_prompts_canon.py + expected: PASS + + - id: TC-05 + type: unit + description: "AC-5: developer.md, reviewer.md, tester.md содержат секцию с маршрутами back-to:analysis / back-to:dev / REQUEST_CHANGES соответственно." + module: tests/test_agent_prompts_canon.py + expected: PASS + + - id: TC-06 + type: unit + description: "AC-7: tester.md содержит явный worktree-путь, smoke-проверку блока serial_gate в /queue (ORCH-088) и упоминание покрытия ТЗ (каждый TC из 04-test-plan) в success_criteria; маркеры pytest//health//status//queue сохранены." + module: tests/test_agent_prompts_canon.py + expected: PASS + + - id: TC-07 + type: unit + description: "AC-6: deployer.md несёт в начале видную рамку с запретом рестарта прод-8500; анти-регресс-маркеры docker exec orchestrator-staging / pr_already_merged / 8500 / INFRA-WAIVED сохранены." + module: tests/test_agent_prompts_canon.py + expected: PASS + + - id: TC-08 + type: unit + description: "AC-8: reviewer.md не содержит 'того же экземпляра Developer'; маркеры REQUEST_CHANGES, 'НЕ обновлена', TRACEABILITY.md, 'Известные ограничения', ORCH-079 сохранены." + module: tests/test_agent_prompts_canon.py + expected: PASS + + - id: TC-09 + type: unit + description: "AC-9 анти-регресс: verdict-ключи (verdict:/result:/staging_status:/deploy_status:/security_status:) и значения (APPROVED/REQUEST_CHANGES/PASS/FAIL/SUCCESS/FAILED) присутствуют байт-в-байт; 5 XML-секций в нормативном порядке + 6 полей 52c во всех 6 промптах (существующие канон-проверки зелёные)." + module: tests/test_agent_prompts_canon.py + expected: PASS + + - id: TC-10 + type: unit + description: "AC-9: верхний frontmatter определения агента остаётся валидным YAML с name/description и без ключа model: во всех 6 промптах (регресс test_agent_frontmatter_no_model.py)." + module: tests/test_agent_frontmatter_no_model.py + expected: PASS + + - id: TC-11 + type: integration + description: "AC-9/AC-10: полный регресс pytest tests/ -q зелёный; git diff не содержит правок src/ (docs/prompts-only)." + module: tests/ + expected: PASS diff --git a/docs/work-items/ORCH-092/06-adr/ADR-001-developer-rebase-and-deployer-language.md b/docs/work-items/ORCH-092/06-adr/ADR-001-developer-rebase-and-deployer-language.md new file mode 100644 index 0000000..dd03e2b --- /dev/null +++ b/docs/work-items/ORCH-092/06-adr/ADR-001-developer-rebase-and-deployer-language.md @@ -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` (``, шаг 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 ` (`src/merge_gate.py:151`). + +Важная асимметрия прав: авторитетный rebase движка **обязан** делать `--force-with-lease`, чтобы +переписать уже запушенную историю ветки. Developer-промпту это **прямо запрещено** +(``: «❌ Не используй `--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*` сам (это пересекается с запретом + `` и с авторитетной операцией движка, использующей `--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` diff --git a/docs/work-items/ORCH-092/10-tech-risks.md b/docs/work-items/ORCH-092/10-tech-risks.md new file mode 100644 index 0000000..ca1db6a --- /dev/null +++ b/docs/work-items/ORCH-092/10-tech-risks.md @@ -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 | Плейсхолдеры `` / `` сами скопированы моделью буквально (тот же класс бага, что хардкод) | Сред. | Низ. | Явная инструкция «подставь фактическое (`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 | Добавление секции `` (developer/reviewer/tester) нарушает нормативный порядок 5 обязательных XML-секций канона 52d | Низ. | Сред. | `` ставится ПОСЛЕ `` (как у `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 в начале ``), маркеры не трогаются; каноне-тест проверяет их наличие | +| 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) — **низкий**. diff --git a/docs/work-items/ORCH-092/12-review.md b/docs/work-items/ORCH-092/12-review.md new file mode 100644 index 0000000..bee166c --- /dev/null +++ b/docs/work-items/ORCH-092/12-review.md @@ -0,0 +1,94 @@ +--- +verdict: APPROVED +work_item: ORCH-092 +stage: review +author_agent: reviewer +status: approved +created_at: 2026-06-09 +model_used: claude-opus-4-8 +type: review +work_item_id: ORCH-092 +version: 1 +--- + +# Review ORCH-092 + +## Summary + +Промпт-аудит 6 агентов (эпилог эпика ORCH-52), **docs/prompts-only**. PR проверен по 4 осям: +соответствие ТЗ (`02-trz.md` FR-1…FR-11), соответствие ADR (`06-adr/ADR-001`), качество кода, +качество документации. Все критерии приёмки `03-acceptance-criteria.md` (AC-1…AC-10) выполнены; +машинные verdict-ключи и канон 52d/52c/52e — байт-в-байт; `src/**`/`STAGE_TRANSITIONS`/`QG_CHECKS`/ +схема БД не тронуты. Полный регресс `pytest tests/ -q` — **1278 passed**; целевые +`test_agent_prompts_canon.py` + `test_agent_frontmatter_no_model.py` — **75 passed**. + +**Вердикт: APPROVED.** P0/P1/P2-findings нет. + +## Findings + +### P0 — Blocker +- _нет_ + +### P1 — Must fix +- _нет_ + +### P2 — Should fix +- _нет_ + +## Проверка по осям + +### Ось 1 — Соответствие ТЗ / Acceptance +- **AC-1 / FR-1 (расхардкод `created_at`):** PASS. Во всех 6 промптах копируемый блок несёт + `created_at: ` + врезка «не копируй буквально, подставь `date +%F`». Литерала + `created_at: 2026-…` в fenced-блоках нет (закреплено `test_orch092_created_at_is_placeholder_not_literal`). +- **AC-2 / FR-2 (расхардкод `model_used`):** PASS. `model_used: ` в примерах; + литерал `claude-opus-4-8` остаётся лишь справкой в таблице полей (вне блока). +- **AC-3 / FR-3 (сверка гейтов):** PASS. Все `check_*` из 6 промптов присутствуют в `QG_CHECKS` + (интеграционный `test_orch092_gate_names_match_qg_registry`); `check_tests_passed` подтверждён + валидным, не «исправлен вслепую». +- **AC-4 / FR-4 (PR>1500 → эскалация):** PASS. «разбивай на меньшие PR» удалено, переформулировано + в декомпозицию на уровне задач; маркер «свой PR» сохранён. +- **AC-5 / FR-5 (``):** PASS. developer/reviewer/tester несут `` ПОСЛЕ + `` (порядок 5 обязательных секций цел); маршруты ролеспецифичны + (`back-to:analysis` / `back-to:dev` / `REQUEST_CHANGES`). +- **AC-6 / FR-6+FR-10 (deployer рамка + язык):** PASS. Критичные self-hosting-запреты подняты в + видную рамку в начале `` («NEVER restart the prod 8500»); язык — EN по решению ADR D2. +- **AC-7 / FR-7 (обогащение tester):** PASS. worktree-путь ветки задачи, smoke-проверка блока + `serial_gate` в `/queue`, требование покрытия каждого TC из `04-test-plan.yaml`. +- **AC-8 / FR-8 (мёртвая строка reviewer):** PASS. «не апрувь PR от того же экземпляра Developer» + удалена; живые инварианты (`REQUEST_CHANGES`, «НЕ обновлена», ось трассировки, ось обзорных доков + ORCH-079) сохранены. +- **AC-9 / FR-11 (анти-регресс):** PASS. verdict-ключи `verdict:`/`result:`/`staging_status:`/ + `deploy_status:`/`security_status:` (+ значения) байт-в-байт; 5 секций + 6 полей во всех 6 + промптах; `git diff --stat -- src/` пуст. +- **AC-10 (документация и тесты):** PASS — см. ось 4. + +### Ось 2 — Соответствие ADR +- **ADR-001 D1 (убран ручной rebase):** реализовано точно. Шаг `git fetch origin && git rebase + origin/main` удалён из алгоритма developer, заменён нормативной заметкой; маркер запрета + `--force-push` сохранён. Код-обоснование ADR **верифицировано**: `premerge_rebase_always=True` + (config.py:432, дефолт), `auto_rebase_onto_main` + `--force-with-lease` (merge_gate.py:113/151), + `_materialize_deferred_branch` (launcher.py:421). Логика верна: developer'у force-push запрещён, + а пост-push rebase его требует → ручной шаг был конфликтующим/дублирующим. +- **ADR-001 D2 (deployer EN):** реализовано — `deployer.md` оставлен на английском с явной + «Language note» как задокументированное исключение; рамка запретов на EN. +- **Глобальные ADR:** нарушений нет. Трассировка (`TRACEABILITY.md`): удалённая rebase-строка не + несла маркера `ORCH-NNN`; зафиксированные инварианты не сломаны. + +### Ось 3 — Качество кода +- `src/` не изменён. Новые TC в `test_agent_prompts_canon.py` содержательны (парсер fenced-блоков, + anti-literal regex, ролеспецифичные маршруты, интеграция с реальным реестром `QG_CHECKS`), не + тривиальны. Все зелёные. + +### Ось 4 — Документация (ОБЯЗАТЕЛЬНАЯ ПРОВЕРКА) +- `src/**` НЕ изменён → правило «src изменён, документация не обновлена = P0» не триггерится. +- Документация обновлена сверх требуемого: **CHANGELOG.md** (запись ORCH-092 под `[Unreleased]`), + **CLAUDE.md** (абзац ORCH-092 в «Стек/Агенты»), **docs/architecture/README.md** (§«Слой промптов» + — пункт ORCH-092), **ADR** `06-adr/ADR-001-developer-rebase-and-deployer-language.md` (решения + P1-2/P2-2). +- **Обзорные доки (ORCH-079):** PR не закрывает ни одного пункта `README.md` «Известные + ограничения» → обновление витрины не требуется. + +## Документация +Обновлена полностью и корректно: CHANGELOG.md, CLAUDE.md, docs/architecture/README.md, ADR-001. +Дополнительных обновлений не требуется. diff --git a/docs/work-items/ORCH-092/13-test-report.md b/docs/work-items/ORCH-092/13-test-report.md new file mode 100644 index 0000000..b3b139a --- /dev/null +++ b/docs/work-items/ORCH-092/13-test-report.md @@ -0,0 +1,67 @@ +--- +result: PASS +work_item: ORCH-092 +stage: testing +author_agent: tester +status: pass +created_at: 2026-06-09 +model_used: claude-opus-4-8 +type: test-report +work_item_id: ORCH-092 +--- + +# Test Report — ORCH-092 — Промпт-аудит 6 агентов + +## Окружение +- Python: 3.12.13 +- pytest: 8.3.3 +- Worktree: `/repos/_wt/orchestrator/feature_ORCH-092-6-escalation` (ветка `feature/ORCH-092-6-escalation`) +- Дата: 2026-06-09 +- Review-вердикт (`12-review.md`): **APPROVED** — предусловие стадии выполнено. + +## Команды +``` +cd /repos/_wt/orchestrator/feature_ORCH-092-6-escalation && python -m pytest tests/ -q +python -m pytest tests/test_agent_prompts_canon.py tests/test_agent_frontmatter_no_model.py -q +curl -s http://localhost:8500/health → {"status":"ok","service":"orchestrator"} +curl -s http://localhost:8500/status → 200 OK (active_tasks[0] = ORCH-092/testing) +curl -s http://localhost:8500/queue → serial_gate present: True · auto_labels present: True +git diff --stat main -- src/ → пусто (src/ не тронут, docs/prompts-only) +``` + +## Результаты по тест-плану (`04-test-plan.yaml` ↔ `03-acceptance-criteria.md`) + +| TC ID | AC | Описание | Покрывающий тест | Результат | +|-------|----|----------|------------------|-----------| +| TC-01 | AC-1 | `created_at: ` плейсхолдер во всех 6 промптах + инструкция «не копируй буквально» | `test_orch092_created_at_is_placeholder_not_literal` | PASS | +| TC-02 | AC-2 | `model_used: ` плейсхолдер; нет литерала `claude-opus-4-8` в копируемом блоке | `test_orch092_model_used_is_placeholder_not_literal` | PASS | +| TC-03 | AC-3 | каждое имя `check_*` в 6 промптах присутствует ключом в `QG_CHECKS` (импорт `src.qg.checks`) | `test_orch092_gate_names_match_qg_registry` | PASS | +| TC-04 | AC-4 | developer: «разбивай на меньшие PR» удалено → эскалация; маркер «свой PR» сохранён | `test_orch092_developer_pr_oversize_is_escalation_not_split` | PASS | +| TC-05 | AC-5 | `` у developer/reviewer/tester после ``; маршруты ролеспецифичны | `test_orch092_escalation_section_present_after_success` + `..._routes_are_role_specific` | PASS | +| TC-06 | AC-7 | tester: worktree-путь, smoke `serial_gate` в `/queue`, покрытие ТЗ; маркеры pytest//health//status//queue целы | `test_orch092_tester_enriched` | PASS | +| TC-07 | AC-6 | deployer: видная рамка запрета рестарта прод-8500; маркеры `docker exec orchestrator-staging`/`pr_already_merged`/`8500`/`INFRA-WAIVED` целы | `test_orch092_deployer_prominent_ban_frame` | PASS | +| TC-08 | AC-8 | reviewer: «того же экземпляра Developer» удалена; живые маркеры (`REQUEST_CHANGES`/«НЕ обновлена»/`TRACEABILITY.md`/`Известные ограничения`/`ORCH-079`) целы | `test_orch092_reviewer_dead_line_removed` | PASS | +| TC-09 | AC-9 | verdict-ключи байт-в-байт + 5 XML-секций в порядке + 6 полей 52c во всех 6 промптах | `test_machine_verdict_keys_preserved_exact_case` + `test_five_xml_sections_present` + `test_six_schema_field_names_present` | PASS | +| TC-10 | AC-9 | верхний frontmatter определения агента валиден, без ключа `model:` | `tests/test_agent_frontmatter_no_model.py` | PASS | +| TC-11 | AC-9/AC-10 | полный регресс `pytest tests/ -q` зелёный; `git diff` не содержит правок `src/` | `tests/` (1278 passed) + `git diff --stat main -- src/` пуст | PASS | + +**Итог покрытия:** все 11 TC выполнены и сопоставлены с критериями приёмки AC-1…AC-10. Несопоставленных TC нет. + +## Smoke API (read-only) +- `GET /health` → `{"status":"ok","service":"orchestrator"}` — OK. +- `GET /status` → 200; активная задача `ORCH-092` на стадии `testing`. +- `GET /queue` → 200; блок **`serial_gate` присутствует** (ORCH-088, анти-регресс смока пройден), блок `auto_labels` присутствует (ORCH-089). + +## Вывод pytest +``` +tests/test_agent_prompts_canon.py tests/test_agent_frontmatter_no_model.py — 75 passed in 0.45s + +tests/ (полный регресс): +........................................................................ [ 95%] +...................................................... [100%] +1278 passed, 1 warning in 35.24s +``` +> Единственный warning — `PydanticDeprecatedSince20` (class-based config в `src/config.py`), предсуществующий, не относится к ORCH-092, на результат не влияет. + +## Итог +**PASS** — полный регресс (1278 passed) и целевые анти-регресс-тесты (75 passed) зелёные; smoke `/health`/`/status`/`/queue` (включая блок `serial_gate`) — OK; `src/` не тронут (docs/prompts-only). Каждый TC из `04-test-plan.yaml` выполнен и сопоставлен с `03-acceptance-criteria.md`. Задача готова к переходу на `deploy-staging`. diff --git a/docs/work-items/ORCH-092/14-deploy-log.md b/docs/work-items/ORCH-092/14-deploy-log.md new file mode 100644 index 0000000..427a632 --- /dev/null +++ b/docs/work-items/ORCH-092/14-deploy-log.md @@ -0,0 +1,12 @@ +--- +deploy_status: SUCCESS +work_item: ORCH-092 +hook_exit_code: 0 +deployed_by: deploy-finalizer +--- + +# Deploy log — ORCH-036 executable self-deploy + +Прод-деплой завершён хост-хуком с exit-code `0` -> `deploy_status: SUCCESS`. + +Вердикт зафиксирован детерминированным finalizer'ом (Фаза C), не LLM. diff --git a/tests/test_agent_prompts_canon.py b/tests/test_agent_prompts_canon.py index f0491ee..c364a73 100644 --- a/tests/test_agent_prompts_canon.py +++ b/tests/test_agent_prompts_canon.py @@ -11,6 +11,7 @@ Covers test-plan TC-01..TC-07. TC-08 lives in regression (TC-10) is the rest of `tests/`. """ import os +import re import pytest @@ -279,3 +280,156 @@ def test_reviewer_carries_overview_docs_axis(): assert "ORCH-079" in text, ( "reviewer.md does not anchor the overview-docs axis to ORCH-079" ) + + +# --------------------------------------------------------------------------- # +# ORCH-092 (epilogue of epic ORCH-52): prompt audit of the 6 agents — +# de-hardcode date/model, gate-name parity, escalation sections, dead-line +# removal, tester enrichment, deployer ban-frame. Pure-text checks; only +# TC-03 imports `src/` (the QG_CHECKS registry parity check). +# Covers test-plan TC-01..TC-08 (TC-09/TC-10/TC-11 = existing canon + full regression). +# --------------------------------------------------------------------------- # + + +def _fenced_blocks(text: str) -> list[str]: + """Return the body of every ``` fenced code block (the *copyable* examples).""" + blocks: list[str] = [] + inside = False + buf: list[str] = [] + for line in text.splitlines(): + if line.lstrip().startswith("```"): + if inside: + blocks.append("\n".join(buf)) + buf = [] + inside = not inside + continue + if inside: + buf.append(line) + return blocks + + +@pytest.mark.parametrize("agent", _AGENTS) +def test_orch092_created_at_is_placeholder_not_literal(agent): + """TC-01 (AC-1): copyable example uses a date placeholder + a substitution note. + + The field name `created_at` stays; only its value becomes a placeholder. No + literal date may survive inside a ``` fenced (copyable) block, else an agent + would copy a stale date verbatim. + """ + text = _read(agent) + assert "created_at: " in text, ( + f"{agent}.md does not use the created_at: placeholder" + ) + for block in _fenced_blocks(text): + assert re.search(r"created_at:\s*\d", block) is None, ( + f"{agent}.md still hardcodes a literal created_at date in a copyable block" + ) + assert "date +%F" in text, ( + f"{agent}.md does not instruct to substitute the actual date (date +%F)" + ) + + +@pytest.mark.parametrize("agent", _AGENTS) +def test_orch092_model_used_is_placeholder_not_literal(agent): + """TC-02 (AC-2): copyable example uses a model placeholder, not the literal model. + + `model_used: claude-opus-4-8` is allowed as a reference in the field table + (outside the fenced block) but must NOT appear in a copyable example. + """ + text = _read(agent) + assert "model_used: " in text, ( + f"{agent}.md does not use the model_used: placeholder" + ) + for block in _fenced_blocks(text): + assert "model_used: claude-opus-4-8" not in block, ( + f"{agent}.md still hardcodes model_used: claude-opus-4-8 in a copyable block" + ) + + +def test_orch092_gate_names_match_qg_registry(): + """TC-03 (AC-3): every check_* named in the 6 prompts is a real QG_CHECKS key. + + The only test in this module that imports `src/` (integration). Guards against + a prompt naming a non-existent gate; confirms check_tests_passed is valid. + """ + from src.qg.checks import QG_CHECKS + + pattern = re.compile(r"check_[a-z_]+") + for agent in _AGENTS: + for name in sorted(set(pattern.findall(_read(agent)))): + assert name in QG_CHECKS, ( + f"{agent}.md references gate {name!r} which is absent from QG_CHECKS" + ) + assert "check_tests_passed" in QG_CHECKS, "check_tests_passed must remain a real gate" + + +def test_orch092_developer_pr_oversize_is_escalation_not_split(): + """TC-04 (AC-4): the 'split into smaller PRs' instruction became an escalation.""" + text = _read("developer") + assert "разбивай на меньшие PR" not in text, ( + "developer.md still carries the unrealisable 'split into smaller PRs' instruction" + ) + assert "на уровне задач" in text and "декомпозиц" in text, ( + "developer.md does not reframe an oversize PR as task-level decomposition" + ) + assert "свой PR" in text, "developer.md lost the 'свой PR' marker" + + +@pytest.mark.parametrize("agent", ("developer", "reviewer", "tester")) +def test_orch092_escalation_section_present_after_success(agent): + """TC-05 (AC-5): dev/reviewer/tester carry after .""" + text = _read(agent) + # The real section tags sit on their own line (an inline `` mention + # in uses backticks and must not be mistaken for the section). + open_m = re.search(r"(?m)^\s*$", text) + close_m = re.search(r"(?m)^\s*$", text) + assert open_m and close_m, f"{agent}.md is missing the section" + success_m = re.search(r"(?m)^\s*$", text) + assert success_m and open_m.start() > success_m.start(), ( + f"{agent}.md places before (breaks section order)" + ) + + +def test_orch092_escalation_routes_are_role_specific(): + """TC-05 (AC-5): escalation routes match each role.""" + assert "back-to:analysis" in _read("developer"), "developer lacks back-to:analysis route" + assert "back-to:dev" in _read("tester"), "tester lacks back-to:dev route" + assert "REQUEST_CHANGES" in _read("reviewer"), "reviewer lacks REQUEST_CHANGES route" + + +def test_orch092_tester_enriched(): + """TC-06 (AC-7): tester gains worktree path, serial_gate smoke and TRZ coverage.""" + text = _read("tester") + assert "worktree" in text, "tester.md does not mention the task-branch worktree path" + assert "serial_gate" in text, "tester.md /queue smoke omits the serial_gate block check" + assert "04-test-plan.yaml" in text, "tester.md does not require coverage of every TRZ TC" + for marker in _ANTI_REGRESS["tester"]: + assert marker in text, f"tester.md lost anti-regress marker {marker!r}" + + +def test_orch092_deployer_prominent_ban_frame(): + """TC-07 (AC-6): deployer carries a prominent prod-8500 ban frame inside .""" + text = _read("deployer") + context = text[text.index(""):text.index("")] + assert "8500" in context, "deployer.md frame does not name the prod 8500" + assert "NEVER restart the prod" in context, ( + "deployer.md does not raise the 'NEVER restart prod 8500' ban into the context frame" + ) + for marker in _ANTI_REGRESS["deployer"]: + assert marker in text, f"deployer.md lost anti-regress marker {marker!r}" + + +def test_orch092_reviewer_dead_line_removed(): + """TC-08 (AC-8): the dead 'same Developer instance' line is gone; live markers stay.""" + text = _read("reviewer") + assert "того же экземпляра" not in text, ( + "reviewer.md still carries the dead 'same Developer instance' instruction" + ) + for marker in ( + "REQUEST_CHANGES", + "НЕ обновлена", + "TRACEABILITY.md", + "Известные ограничения", + "ORCH-079", + ): + assert marker in text, f"reviewer.md lost live invariant marker {marker!r}"