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:
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
---
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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).
|
||||
|
||||
@@ -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/`)
|
||||
|
||||
@@ -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/`)
|
||||
|
||||
@@ -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}"
|
||||
|
||||
Reference in New Issue
Block a user