docs(ORCH-079): ORCH-52f — sync README with code + reviewer overview-docs axis

Layer 5 (final) of epic ORCH-52. Docs + prompt-only; src/ untouched.

- README.md «Известные ограничения»: fix numbering (was 1,2,3,4,3,4),
  move 6 resolved/obsolete items to «Закрыто (история)» trail with ORCH
  refs, keep only really-open limitations (Telegram-48h ORCH-087,
  task-deps intra-repo ORCH-026, serial-gate ORCH-088). Point-sync stage
  table (development → check_ci_green) and event-routing (ORCH-045).
- reviewer.md: overview-docs axis (axis 4 + constraints) — closing a
  README limitation without updating README → finding ≥P1 (canon 52d
  «»; verdict key + 5 XML sections + 6 schema fields byte-intact).
- tests: new tests/test_readme_limitations.py (numbering + no resolved
  items as open); test_agent_prompts_canon.py asserts the new axis.
- CLAUDE.md / CHANGELOG.md updated; epic ORCH-52 closed (52b→…→52f).

Refs: ORCH-079

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-09 16:23:17 +03:00
committed by orchestrator-deployer
parent 07a2d6ad1e
commit d97b26a59f
6 changed files with 141 additions and 9 deletions

View File

@@ -48,6 +48,11 @@ tools:
`docs/architecture/README.md` и/или `docs/architecture/internals.md`? конфигурация → `README.md`
(таблица env)? новый компонент → `docs/architecture/README.md`? обновлён `CHANGELOG.md`?
архитектурное решение → есть ADR?
- **Обзорные доки (ORCH-079):** если PR закрывает/меняет пункт из `README.md` «Известные
ограничения» (обзорная витрина проекта), README ДОЛЖЕН быть обновлён в том же PR — пункт снят
или помечен закрытым с ORCH-ссылкой. Необновление обзорных доков → **finding ≥ P1**; если
ограничение закрыто правкой `src/` без обновления README — это совпадает с P0 «`src/` изменён,
документация не обновлена». Это усиление трактовки оси, а не отдельная ось.
</task>
<deliverables>
@@ -65,6 +70,9 @@ frontmatter-вердиктом, см. `<output_format>`).
-Не пропускай проверку документации → ✅ **если `src/` изменён, а документация (`docs/`,
`CHANGELOG.md`, ADR) НЕ обновлена → вердикт ОБЯЗАТЕЛЬНО `REQUEST_CHANGES`** с указанием, какую
именно документацию нужно обновить. Документация = golden source наравне с кодом.
- ❌ PR закрыл пункт из `README.md` «Известные ограничения», но README не обновлён (пункт остался
открытым) → ✅ требуй обновления обзорных доков — пункт снят либо помечен закрытым с ORCH-ссылкой;
необновление обзорной витрины → **finding ≥ P1** (ORCH-079).
**Severity:**
- **P0 (blocker):** не реализовано требование ТЗ; нарушен ADR; критическая уязвимость;

View File

@@ -3,6 +3,11 @@
Формат: [Keep a Changelog](https://keepachangelog.com/). Записи — на смысловой PR/задачу.
## [Unreleased]
- **Синхронизация обзорных доков (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).
- **Эпик ORCH-52 закрыт:** 52b (adr-0019) → 52c (adr-0020) → 52d (adr-0021) → 52e (adr-0022) → **52f (adr-0023)**. Сквозной `docs/architecture/adr/adr-0023-overview-docs-reviewer-axis-and-epic52-close.md` + per-work-item `docs/work-items/ORCH-079/06-adr/ADR-001-readme-sync-and-reviewer-overview-docs-axis.md`.
- **Анти-регресс (FR-6, AC-6):** новый структурный `tests/test_readme_limitations.py` (нумерация без повторов; решённые пункты не значатся открытыми; трейл «Закрыто» с ORCH-ссылками); расширен `tests/test_agent_prompts_canon.py` (assert наличия оси обзорных доков в `reviewer.md`); канон 52d (5 секций, 6 полей, регистр verdict-ключей) и `test_agent_frontmatter_no_model.py` зелёные; полный регресс `tests/` зелёный (1257). Документация: `README.md`, `docs/architecture/README.md` (слой 5 эпика 52), `CLAUDE.md`. Полностью обратимо `git revert` (нет машинного поведения/состояния/kill-switch).
- **Стандарт маркеров-трассировки `ORCH-NNN` + правило чтения ADR перед правкой** (ORCH-078 / ORCH-52e, `docs`): слой 4 (трассировка) эпика ORCH-52, замыкающий цепочку 52b (структура) / 52c (frontmatter) / 52d (промпты). Маркеры `ORCH-NNN`/`ET-NNN` в коде (де-факто 51 уникальный в `src/`) привязывают нетривиальные инварианты к породившему их work item — была сложившаяся практика без формального контракта. **Docs + prompts-only:** `src/**`, `STAGE_TRANSITIONS`, `QG_CHECKS`, `check_*`/`_parse_*`, `src/frontmatter.py`, схема БД — **не тронуты**; `frontmatter_validation_strict` остаётся `False`; новый QG не вводится; массовый ретро-фит 51 маркера вне объёма (стандарт нормативен «на будущее»).
- **Новый стандарт `docs/_standards/TRACEABILITY.md`** (рядом с `PIPELINE_DOCS.md`/`HANDOFF_PROTOCOL.md`): формат маркера, правило размещения (рядом с нетривиальным инвариантом), чтение истории с реальным проверяемым примером (`src/serial_gate.py` → ORCH-088 → `ADR-001-serial-gate.md`), fallback-доступ (`git show origin/main:docs/work-items/...`), анти-археология (3+ маркеров → сводный сквозной ADR), каноничный текст правила чтения (единый источник).
- **Точечные врезки в промпты (аддитивно, 52d-канон не переписан):** `developer.md` — правило чтения чужого маркера + fallback («❌ X → ✅ Y»); `architect.md` — правило чтения + анти-археология (3+ → сквозной ADR); `reviewer.md` — усиление оси «Соответствие ADR» под-пунктом «правка маркированного кода сверена с ADR; слом → finding ≥P1». Все три **ссылаются** на единый текст в `TRACEABILITY.md`, не копируют (анти-дубль BR-6).

View File

@@ -130,7 +130,7 @@ created → analysis → architecture → development → review → testing →
3. Никогда не править артефакты других этапов.
4. Никогда не комментировать ТЗ задним числом — если ТЗ не годится, возвращай в Анализ.
5. Никогда не закрывать задачу самостоятельно — это делает CI / финальная стадия.
6. **Reviewer проверяет: обновлена ли документация. Нет → REQUEST_CHANGES.**
6. **Reviewer проверяет: обновлена ли документация. Нет → REQUEST_CHANGES.** Это включает **обзорные доки** (ORCH-079, 52f — финал эпика 52): если PR закрывает пункт `README.md` «Известные ограничения», но README не обновлён → finding ≥P1 (витрина проекта не должна выдавать решённое за открытое).
7. Не использовать `--no-verify` без явного одобрения Owner.
8. Секреты — только в `.env`/`.env.staging` на хосте, в гит НЕ коммитятся (канон — `.env.example`).
9. **Трассировка маркеров (ORCH-078, ORCH-52e):** правишь строку/блок с маркером `ORCH-NNN`

View File

@@ -29,7 +29,7 @@ created → analysis → architecture → development → review → testing →
| created | — | — | Plane webhook (work_item.created) |
| analysis | analyst | Файлы BRD/TRZ/AC/TestPlan | Push docs/ |
| architecture | architect | ADR или infra-requirements | Push docs/ |
| development | developer | check_tests_local (орк сам гоняет `make test`) | Auto-advance после developer |
| development | developer | check_ci_green (Gitea CI зелёный на ветке) | Auto-advance после developer |
| review | reviewer | check_reviewer_verdict (`verdict:` во frontmatter 12-review.md) | Auto-advance после reviewer |
| testing | tester | check_tests_passed (test-report.md) | Auto-advance после tester |
| deploy-staging | deployer | check_staging_status (15-staging-log.md) | Auto-advance после tester |
@@ -223,7 +223,7 @@ stdout/stderr агента перенаправляются СРАЗУ в `/app/
Gitea events роутятся по типу:
- `push` → проверка файлов, advance architecture/development
- `pull_request*` (wildcard) → review approved/rejected, PR merge
- `status` → (legacy) Gitea CI; С-1: больше не authoritative, `failure` логируется на debug и не блокирует/не алертит (QG развития = локальный `check_tests_local`)
- `status` → Gitea CI статус; ORCH-045: авторитетный гейт развития (`development → review`) — `check_ci_green` читает статус ветки с polling-retry (устраняет гонку «pending сразу после push»)
## Тесты
@@ -233,9 +233,19 @@ pytest tests/ -v
## Известные ограничения
1. **Single-task / shared `/repos` checkout** — одновременно безопасно обрабатывается одна задача: все агенты и `check_tests_local` делают `git checkout` в одном `/repos/<repo>` → гонки при параллельных задачах. Исправление — git worktree per task (S-4, отдельно).
2. **Plane sync** — маппинг issue ID может быть некорректным (P3, в работе)
3. **In-process daemon-потоки**агенты живут в потоках uvicorn; при рестарте ловит orphan-recovery. Целевое — очередь задач (F-2b)
4. **Gitea CI не настроен**тесты гоняет сам оркестратор локально
3. **Tester timeout**e2e тесты с Playwright могут занимать >25 мин на тяжёлых фичах
4. **No retry on API errors** — httpx вызовы к Gitea/Plane без retry logic
Реально открытые ограничения (сверено с кодом, ORCH-079):
1. **Telegram 48h** — карточки-сироты старше 48 часов неудаляемы (лимит Telegram Bot API); зачистка сирот самозалечивает только свежие (ORCH-087).
2. **Зависимости задач — только intra-repo (v1)** — `job_deps` выражают связи в пределах одного репозитория; кросс-репо зависимости пока не поддержаны (ORCH-026).
3. **Пакетный автоном — Этап 1** — per-repo serial gate сериализует задачи одного репо (ORCH-088); полный пакетный автономный прогон «1020 задач за ночь» — в развитии (эпик ORCH-088).
### Закрыто (история)
Пункты, ранее значившиеся ограничениями, закрыты кодом — оставлены как трассировка:
- **Single-task / shared `/repos` checkout** → git worktree per task (`ensure_worktree`) + serial-gate (ORCH-088) + task-deps (ORCH-026).
- **In-process daemon-потоки** → персистентная очередь задач (SQLite `jobs`, `src/queue_worker.py`), restart-safe (ORCH-1).
- **Gitea CI не настроен** → активный гейт стадии `development` — `check_ci_green` (`src/qg/checks.py`); `check_tests_local` помечен DEPRECATED.
- **No retry on API errors** → exp-backoff + circuit breaker в `queue_worker.py` (`ORCH_BACKOFF_*` / `ORCH_BREAKER_*` / `ORCH_TRANSIENT_MAX_ATTEMPTS`) + retry-loop в `check_ci_green` (ORCH-1 resilience / ORCH-045).
- **Plane sync — маппинг issue ID** → зрелый `src/plane_sync.py` (`find_issue_id`, `fetch_issue_sequence_id`) со статус-моделью и TTL-самозалечиванием (ORCH-010 / 066 / 068).
- **Tester timeout — Playwright e2e** → orchestrator является pytest-сервисом (Playwright неприменим); реальный механизм — конфигурируемый watchdog (`agent_timeout_seconds`, ORCH-7).

View File

@@ -258,3 +258,24 @@ def test_claude_md_and_readme_reference_traceability_standard():
assert "TRACEABILITY.md" in _read_repo("docs", "architecture", "README.md"), (
"architecture README does not reference docs/_standards/TRACEABILITY.md"
)
# --------------------------------------------------------------------------- #
# ORCH-079 (ORCH-52f): reviewer overview-docs axis (layer 5 of epic ORCH-52).
# Pure-text anti-drift check (TRZ §FR-6 / AC-5), NO `src/` import.
# --------------------------------------------------------------------------- #
def test_reviewer_carries_overview_docs_axis():
"""ORCH-079 TC-01 (AC-5): reviewer.md covers the README overview-docs axis.
The reviewer must require README ("Известные ограничения") to be updated when
a PR closes a documented limitation. This guards the rule against silent drift
in a future prompt refactor, exactly like the traceability control axis.
"""
text = _read("reviewer")
assert "Известные ограничения" in text, (
"reviewer.md does not mention the README 'Известные ограничения' overview-docs axis"
)
assert "ORCH-079" in text, (
"reviewer.md does not anchor the overview-docs axis to ORCH-079"
)

View File

@@ -0,0 +1,88 @@
"""ORCH-079 (ORCH-52f): structural anti-drift for README "Известные ограничения".
Layer 5 (final) of epic ORCH-52: the root README overview showcase must not lie
about the project state. These are pure-text structural checks (NO `src/` import,
NO agent runs) guarding two invariants (TRZ §FR-1/FR-2, AC-1/AC-2):
* the OPEN limitations list is numbered strictly 1, 2, 3, … without repeats
(the historical bug was `1,2,3,4,3,4`);
* resolved/obsolete items (single-task worktree, in-process daemon, "Gitea CI
not configured", "no retry") are NOT listed as OPEN limitations — if mentioned
at all, only under the "Закрыто (история)" trail.
Covers test-plan TC-05.
"""
import os
import re
_REPO_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
_README = os.path.join(_REPO_ROOT, "README.md")
# Heading of the limitations section and the closed-history subsection that ends
# the OPEN portion.
_SECTION_HEAD = "## Известные ограничения"
_CLOSED_HEAD = "Закрыто (история)"
# Phrases that mark a RESOLVED/obsolete item. They must not appear in the OPEN
# portion of the section (only allowed under "Закрыто (история)").
_RESOLVED_MARKERS = (
"Single-task",
"shared `/repos`",
"daemon-потоки",
"не настроен", # "Gitea CI не настроен"
"No retry",
)
def _read_readme() -> str:
with open(_README, encoding="utf-8") as f:
return f.read()
def _limitations_section() -> str:
"""Text of the '## Известные ограничения' section up to the next '## ' heading."""
text = _read_readme()
idx = text.find(_SECTION_HEAD)
assert idx != -1, "README.md has no '## Известные ограничения' section"
rest = text[idx + len(_SECTION_HEAD):]
# Stop at the next top-level (##) heading, if any.
nxt = re.search(r"\n## ", rest)
return rest[: nxt.start()] if nxt else rest
def _open_portion(section: str) -> str:
"""The OPEN-limitations portion: everything before the 'Закрыто (история)' trail."""
cut = section.find(_CLOSED_HEAD)
return section[:cut] if cut != -1 else section
def test_open_limitations_numbered_sequentially():
"""AC-1: the OPEN limitations list is numbered 1, 2, 3, … with no repeats/gaps."""
open_part = _open_portion(_limitations_section())
# Leading numbered list items: lines like "1. **...".
numbers = [int(m) for m in re.findall(r"^(\d+)\.\s", open_part, flags=re.MULTILINE)]
assert numbers, "no numbered OPEN limitations found in README section"
assert numbers == list(range(1, len(numbers) + 1)), (
f"OPEN limitations numbering is not strictly sequential: {numbers}"
)
def test_resolved_items_not_listed_as_open():
"""AC-2: resolved/obsolete items are not present as OPEN limitations."""
open_part = _open_portion(_limitations_section())
leaked = [m for m in _RESOLVED_MARKERS if m in open_part]
assert not leaked, (
f"resolved items leaked into OPEN limitations (must be under 'Закрыто'): {leaked}"
)
def test_closed_history_trail_present_with_orch_refs():
"""AC-2: the 'Закрыто (история)' trail exists and carries ORCH references."""
section = _limitations_section()
assert _CLOSED_HEAD in section, (
"README limitations section lacks the 'Закрыто (история)' trail"
)
closed = section[section.find(_CLOSED_HEAD):]
assert re.search(r"ORCH-\d+", closed), (
"the 'Закрыто (история)' trail has no ORCH-NNN references"
)