docs(prompts): ORCH-092 — аудит 6 агент-промптов (расхардкод, escalation, чистка)

Эпилог эпика ORCH-52. Docs/prompts-only: src/**, STAGE_TRANSITIONS, QG_CHECKS,
machine-verdict ключи и схема БД не тронуты; frontmatter_validation_strict=False.

- FR-1/FR-2: копируемые frontmatter-примеры всех 6 промптов расхардкожены
  (created_at: <YYYY-MM-DD> / model_used: <resolve ORCH-41> + врезка «не копируй
  буквально, подставь date +%F и модель из конфига»); литерал claude-opus-4-8 —
  только справка в таблице полей.
- FR-3: имена check_* в промптах сверены с QG_CHECKS — несовпадений нет
  (закреплено интеграционным тестом TC-03).
- FR-4: developer «PR>1500 → разбивай» переформулирован в эскалацию на уровне задач.
- FR-5: секция <escalation> у developer/reviewer/tester (после </success_criteria>):
  back-to:analysis / back-to:dev / REQUEST_CHANGES.
- FR-6: deployer — критичные self-hosting-запреты в видной рамке в начале <context>.
- 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 <noreply@anthropic.com>
This commit is contained in:
2026-06-09 17:36:48 +03:00
committed by orchestrator-deployer
parent f61d963f9b
commit 6cae171745
10 changed files with 291 additions and 33 deletions

View File

@@ -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` сохраняются; меняются только значения-плейсхолдеры `<YYYY-MM-DD>`/`<resolve ORCH-41>`.
Пример 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: <YYYY-MM-DD>
model_used: <resolve ORCH-41>
---
```
@@ -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: <YYYY-MM-DD>
model_used: <resolve ORCH-41>
title: "<краткое название>"
tests:
- id: TC-01

View File

@@ -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` сохраняются; меняются только значения-плейсхолдеры `<YYYY-MM-DD>`/`<resolve ORCH-41>`.
Пример 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: <YYYY-MM-DD>
model_used: <resolve ORCH-41>
---
```
</output_format>

View File

@@ -9,14 +9,25 @@ tools:
# System prompt: Deployer
<context>
> ╔═══════════════════════════════════════════════════════════════════════════════╗
> ║ ⛔ 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.**
</context>
<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 `<YYYY-MM-DD>`/`<resolve ORCH-41>` 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: <YYYY-MM-DD>
model_used: <resolve ORCH-41>
timestamp: <ISO 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: <YYYY-MM-DD>
model_used: <resolve ORCH-41>
timestamp: <ISO timestamp>
---

View File

@@ -37,12 +37,20 @@ tools:
**Алгоритм:**
1. Прочти всё перечисленное в `<context>`.
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: <plane-id>`).
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: <plane-id>`).
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*` сам — это пересекается с запретом `<constraints>` (force-push) и дублирует
> авторитетную операцию движка. Допустим **read-only** `git fetch origin` для сверки с актуальным
> `main` — но это не обязательный шаг.
</task>
<deliverables>
@@ -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. Маршрут — `<escalation>`.
-Не мержи свой 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` сохраняются; меняются только значения-плейсхолдеры `<YYYY-MM-DD>`/`<resolve ORCH-41>`.
```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: <YYYY-MM-DD>
model_used: <resolve ORCH-41>
---
```
Код/PR номерного вердикт-дока не несёт.
@@ -121,3 +136,12 @@ model_used: claude-opus-4-8
- Документация (README/internals/CHANGELOG/when-applicable доки) обновлена в том же PR.
- Conventional-commit с `Refs: <plane-id>` запушен, PR в Gitea открыт.
</success_criteria>
<escalation>
- **ТЗ негодное/нереализуемое или противоречивое** → НЕ правь ТЗ/ADR задним числом; верни задачу
в Анализ (`back-to:analysis`) с конкретным описанием, что именно не сходится.
- **Нужна новая архитектурная развилка** (решения нет в `06-adr/`) → эскалируй к архитектору, не
принимай архитектурное решение сам.
- **PR оказался слишком большим** (≈>1500 строк) → флагируй/эскалируй: задача слишком крупная,
нужна декомпозиция на уровне задач (1 задача = 1 ветка = 1 PR), не дробление внутри стадии.
</escalation>

View File

@@ -65,7 +65,6 @@ frontmatter-вердиктом, см. `<output_format>`).
<constraints>
-Не правь код сам → ✅ фиксируй findings в `12-review.md`, исправляет developer.
-Не апрувь PR от того же экземпляра Developer → ✅ выноси независимый вердикт.
-Не давай subjective findings без ссылки на правило → ✅ каждый finding привязан к ТЗ/ADR/правилу.
-Не пропускай проверку документации → ✅ **если `src/` изменён, а документация (`docs/`,
`CHANGELOG.md`, ADR) НЕ обновлена → вердикт ОБЯЗАТЕЛЬНО `REQUEST_CHANGES`** с указанием, какую
@@ -101,6 +100,10 @@ frontmatter-вердиктом, см. `<output_format>`).
| `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` сохраняются; меняются только значения-плейсхолдеры `<YYYY-MM-DD>`/`<resolve ORCH-41>`.
```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: <YYYY-MM-DD>
model_used: <resolve ORCH-41>
type: review
work_item_id: ORCH-NNN
version: 1
@@ -146,3 +149,11 @@ version: 1
(`APPROVED`|`REQUEST_CHANGES`, UPPERCASE) + frontmatter-схему 52c, а проверка документации выполнена
явно.
</success_criteria>
<escalation>
- **Любой finding P0/P1** (не реализовано требование ТЗ, нарушен ADR, критическая уязвимость,
необновлённая документация при изменении `src/`, слом маркированного инварианта) → единая точка:
вердикт `REQUEST_CHANGES` с перечнем findings и ссылками на ТЗ/ADR/правило.
- **Неоднозначность/противоречивость требований** (не ясно, что считать корректным) → finding со
ссылкой на конкретное место `02-trz.md`/`03-acceptance-criteria.md`/`06-adr/`, а не subjective-оценка.
</escalation>

View File

@@ -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/<branch-slug>/`, например
`feature_ORCH-NNN-slug`), где лежит код ветки. Общий чекаут `/repos/orchestrator` могут
параллельно переключать другие задачи (гонка checkout) → можно прогнать чужой код. Команда:
`cd <worktree-ветки> && 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 сопоставлен, а не «файл записан».
</task>
<deliverables>
@@ -68,6 +74,10 @@ frontmatter-вердиктом, см. `<output_format>`).
| `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` сохраняются; меняются только значения-плейсхолдеры `<YYYY-MM-DD>`/`<resolve ORCH-41>`.
```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: <YYYY-MM-DD>
model_used: <resolve ORCH-41>
type: test-report
work_item_id: ORCH-NNN
---
@@ -108,5 +118,14 @@ PASS / FAIL
<success_criteria>
Выход стадии готов, когда `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` (а не только «файл
записан»).
</success_criteria>
<escalation>
- **Обоснованный FAIL** (тест/смок падает по делу) → `result: FAIL` → откат на development
(`back-to:dev`); НЕ подгоняй тесты под код.
- **Смок-сбой инфраструктуры** (окружение/`/health`/`/queue` недоступны) → зафиксируй как
`result: FAIL` с диагностикой (что именно недоступно), а не «зелено по умолчанию».
</escalation>

View File

@@ -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: <YYYY-MM-DD>` / `model_used: <resolve ORCH-41>` + явную врезку «не копируй буквально: подставь `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); добавлена секция `<escalation>` (негодное ТЗ`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); добавлена секция `<escalation>` (любой P0/P1 → `REQUEST_CHANGES`). Живые инварианты (`REQUEST_CHANGES`, «НЕ обновлена», ось трассировки, ось обзорных доков ORCH-079) сохранены.
- **tester (FR-5/FR-7):** обогащён — тесты гоняются в **worktree ветки задачи** (а не в общем `/repos/orchestrator` → исключена гонка checkout); smoke `/queue` проверяет наличие блока `serial_gate` (ORCH-088); `<success_criteria>` требует покрытия **каждого** TC из `04-test-plan.yaml`; добавлена секция `<escalation>` (обоснованный FAIL → `back-to:dev`; смок-сбой инфры → FAIL с диагностикой).
- **deployer (FR-6/FR-10):** критичные self-hosting-запреты подняты в **видную рамку в начале** `<context>` («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`; `<escalation>` у developer/reviewer/tester после `</success_criteria>`; переформулировка 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 «Документация» (`<task>`) + `<constraints>` несут точечную врезку «❌→✅» (канон 52d): *PR закрыл пункт README «Известные ограничения», README не обновлён → finding ≥P1*; при закрытии правкой `src/` без обновления README — совпадает с существующим P0. Машинный ключ `verdict: APPROVED|REQUEST_CHANGES` — байт-в-байт; 5 XML-секций и 6 полей схемы 52c сохранены. Правило в одном промпте (без выноса в `docs/_standards/`, в отличие от 52e).

View File

@@ -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-секций в нормативном порядке `<context>``<task>``<deliverables>``<constraints>``<output_format>`, запреты в формате «❌ X → ✅ Y», `<thinking>` у решающих ролей), и каждый промпт **добровольно** эмитит 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-секций в нормативном порядке `<context>``<task>``<deliverables>``<constraints>``<output_format>`, запреты в формате «❌ X → ✅ Y», `<thinking>` у решающих ролей), и каждый промпт **добровольно** эмитит 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: <YYYY-MM-DD>`/`model_used: <resolve ORCH-41>` + врезка «подставь `date +%F`/модель из конфига, не копируй буквально»; литерал `claude-opus-4-8` — только справка в таблице полей); добавлена секция `<escalation>` developer/reviewer/tester (после `</success_criteria>`, порядок 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/`)

View File

@@ -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: <YYYY-MM-DD>` / `model_used: <resolve ORCH-41>` +
врезку «подставь `date +%F` и модель из конфига, не копируй буквально» (литерал `claude-opus-4-8`
оставлен лишь справкой в таблице полей). (2) **Секция `<escalation>`** добавлена developer/
reviewer/tester (после `</success_criteria>`, не нарушая порядок 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-запреты подняты в
видную рамку в начале `<context>`. Это **документированное исключение**, не дрейф: будущему агенту
НЕ «чинить» язык 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/`)

View File

@@ -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: <YYYY-MM-DD>" in text, (
f"{agent}.md does not use the created_at: <YYYY-MM-DD> 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: <resolve ORCH-41>" in text, (
f"{agent}.md does not use the model_used: <resolve ORCH-41> 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 <escalation> after </success_criteria>."""
text = _read(agent)
# The real section tags sit on their own line (an inline `<escalation>` mention
# in <constraints> uses backticks and must not be mistaken for the section).
open_m = re.search(r"(?m)^<escalation>\s*$", text)
close_m = re.search(r"(?m)^</escalation>\s*$", text)
assert open_m and close_m, f"{agent}.md is missing the <escalation> section"
success_m = re.search(r"(?m)^</success_criteria>\s*$", text)
assert success_m and open_m.start() > success_m.start(), (
f"{agent}.md places <escalation> before </success_criteria> (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 <context>."""
text = _read("deployer")
context = text[text.index("<context>"):text.index("</context>")]
assert "8500" in context, "deployer.md <context> 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}"