From 6cae171745281a2728be2a59bc8a1842ba326703 Mon Sep 17 00:00:00 2001 From: claude-bot Date: Tue, 9 Jun 2026 17:36:48 +0300 Subject: [PATCH] =?UTF-8?q?docs(prompts):=20ORCH-092=20=E2=80=94=20=D0=B0?= =?UTF-8?q?=D1=83=D0=B4=D0=B8=D1=82=206=20=D0=B0=D0=B3=D0=B5=D0=BD=D1=82-?= =?UTF-8?q?=D0=BF=D1=80=D0=BE=D0=BC=D0=BF=D1=82=D0=BE=D0=B2=20(=D1=80?= =?UTF-8?q?=D0=B0=D1=81=D1=85=D0=B0=D1=80=D0=B4=D0=BA=D0=BE=D0=B4,=20escal?= =?UTF-8?q?ation,=20=D1=87=D0=B8=D1=81=D1=82=D0=BA=D0=B0)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Эпилог эпика ORCH-52. Docs/prompts-only: src/**, STAGE_TRANSITIONS, QG_CHECKS, machine-verdict ключи и схема БД не тронуты; frontmatter_validation_strict=False. - FR-1/FR-2: копируемые frontmatter-примеры всех 6 промптов расхардкожены (created_at: / model_used: + врезка «не копируй буквально, подставь date +%F и модель из конфига»); литерал claude-opus-4-8 — только справка в таблице полей. - FR-3: имена check_* в промптах сверены с QG_CHECKS — несовпадений нет (закреплено интеграционным тестом TC-03). - FR-4: developer «PR>1500 → разбивай» переформулирован в эскалацию на уровне задач. - FR-5: секция у developer/reviewer/tester (после ): back-to:analysis / back-to:dev / REQUEST_CHANGES. - FR-6: deployer — критичные self-hosting-запреты в видной рамке в начале . - FR-7: tester обогащён worktree-путём, smoke serial_gate (ORCH-088), покрытием TC. - FR-8: из reviewer удалена мёртвая строка «тот же экземпляр Developer». - FR-9 (ADR-001 D1): убран ручной git rebase origin/main — свежесть базы держит движок (serial-gate ORCH-088 + auto_rebase_onto_main под merge-lease). - FR-10 (ADR-001 D2): deployer.md оставлен на английском как нормативное исключение. - FR-11: расширен tests/test_agent_prompts_canon.py (ORCH-092 TC-01…TC-08); канон 52d и test_agent_frontmatter_no_model.py зелёные; полный регресс 1278 зелёный. Документация: 6 промптов, CLAUDE.md, docs/architecture/README.md, CHANGELOG.md. Refs: ORCH-092 Co-Authored-By: Claude Opus 4.8 --- .openclaw/agents/analyst.md | 12 ++- .openclaw/agents/architect.md | 8 +- .openclaw/agents/deployer.md | 29 ++++-- .openclaw/agents/developer.md | 42 ++++++-- .openclaw/agents/reviewer.md | 17 +++- .openclaw/agents/tester.md | 33 +++++-- CHANGELOG.md | 8 ++ CLAUDE.md | 2 +- docs/architecture/README.md | 19 ++++ tests/test_agent_prompts_canon.py | 154 ++++++++++++++++++++++++++++++ 10 files changed, 291 insertions(+), 33 deletions(-) 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/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}"