diff --git a/.openclaw/agents/architect.md b/.openclaw/agents/architect.md index a5a63bb..61488fa 100644 --- a/.openclaw/agents/architect.md +++ b/.openclaw/agents/architect.md @@ -71,6 +71,12 @@ deploy-staging → deploy → done. - ❌ Не предлагай рестарт прод-контейнера без staging-гейта → ✅ все деплой-решения ORCH идут через staging (8501) сначала; топология и риски — `docs/operations/INFRA.md`. - ❌ Не используй Kubernetes / Helm / k8s / облако → ✅ Docker Compose. +- ❌ Не правь компонент с маркером `ORCH-NNN`, не сверившись с его решением → ✅ ПЕРЕД изменением + маркированного инварианта прочитай ADR work item(ов), его породивших (`docs/work-items/ORCH-NNN/06-adr/`; + нет папки в ветке → `git show origin/main:docs/work-items/ORCH-NNN/06-adr/...`), и не сломай инвариант. +- ❌ Не плоди археологию маркеров → ✅ вводишь/правишь блок с **3+** маркерами `ORCH-NNN` — оформи/обнови + **сводный сквозной ADR** (`docs/architecture/adr/adr-NNNN-*`), агрегирующий эволюцию, вместо + перечисления всех work item. Стандарт маркеров и каноничное правило чтения — `docs/_standards/TRACEABILITY.md`. diff --git a/.openclaw/agents/developer.md b/.openclaw/agents/developer.md index b247799..b83270a 100644 --- a/.openclaw/agents/developer.md +++ b/.openclaw/agents/developer.md @@ -27,6 +27,8 @@ tools: 5. `docs/work-items//04-test-plan.yaml`. 6. `docs/work-items//06-adr/` — как реализовать. 7. Существующий код в `src/`, `tests/`. +8. `docs/_standards/TRACEABILITY.md` — стандарт маркеров `ORCH-NNN`: ПЕРЕД правкой строки/блока с + чужим маркером прочти ADR, который её ввёл (см. правило в ``). @@ -64,6 +66,12 @@ work item **ORCH-073** и **ORCH-088**. задним числом. - ❌ Не принимай архитектурные решения без ADR → ✅ реализуй по `06-adr/`; нужна новая развилка — эскалируй к архитектору. +- ❌ Не правь строку/блок с маркером `ORCH-NNN` вслепую → ✅ ПЕРЕД изменением прочитай ADR, который + её ввёл (`docs/work-items/ORCH-NNN/06-adr/`), и не сломай зафиксированный инвариант; не можешь + сохранить — эскалируй / верни в анализ. Стандарт и каноничное правило — `docs/_standards/TRACEABILITY.md`. + Папки нет в ветке → читай из main: `git show origin/main:docs/work-items/ORCH-NNN/06-adr/ADR-001-.md` + (листинг — `git ls-tree origin/main:docs/work-items/ORCH-NNN/06-adr/`). Это правило про *чужие* + маркеры в правимом коде — в дополнение к «реализуй по `06-adr/`» *своей* задачи. - ❌ Не коммить секреты (`.env`, токены) → ✅ секреты только в `.env`/`.env.staging` на хосте; канон — `.env.example`. - ❌ Не делай PR > 1500 строк без декомпозиции → ✅ разбивай на меньшие PR. diff --git a/.openclaw/agents/reviewer.md b/.openclaw/agents/reviewer.md index 08ed2bb..4331c3a 100644 --- a/.openclaw/agents/reviewer.md +++ b/.openclaw/agents/reviewer.md @@ -36,6 +36,10 @@ tools: выполнены? 2. **Соответствие ADR** — реализация соответствует `06-adr/`? Нет нарушений глобальных ADR (`docs/architecture/adr/`)? + - **Трассировка (`docs/_standards/TRACEABILITY.md`):** если PR правит строку/блок с **чужим** + маркером `ORCH-NNN`, проверь, что правка **сверена** с его `06-adr` и не ломает зафиксированный + инвариант. Правка маркированного инварианта без обоснования / со сломом → **finding ≥ P1** + (слом критического инварианта конвейера может быть P0). Это усиление оси, а не отдельная ось. 3. **Качество кода** — нет явных ошибок/утечек/security-дыр? Есть docstrings на публичных функциях? Тесты содержательные (не тривиальные)? 4. **Документация — ОБЯЗАТЕЛЬНАЯ ПРОВЕРКА** (приоритет над остальным): если PR меняет `src/` diff --git a/CHANGELOG.md b/CHANGELOG.md index 1fc66db..7afafbf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,10 @@ Формат: [Keep a Changelog](https://keepachangelog.com/). Записи — на смысловой PR/задачу. ## [Unreleased] +- **Стандарт маркеров-трассировки `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). + - **Сопутствующе:** `CLAUDE.md` (правило трассировки + ссылка), `docs/architecture/README.md` (слой 4 эпика 52), сквозной `adr-0022` + per-work-item `ORCH-078/06-adr/ADR-001`. **Анти-регресс:** расширен `tests/test_agent_prompts_canon.py` (наличие правила/ссылок в 3 промптах, существование примера в стандарте); проверки 52d (5 секций, 6 полей, регистр verdict-ключей) и `test_agent_frontmatter_no_model.py` остаются зелёными. Полностью обратимо `git revert` (нет машинного поведения/состояния/kill-switch). - **Канон Anthropic для 6 системных промптов + добровольная эмиссия frontmatter-схемы 52c** (ORCH-077 / ORCH-52d, `docs`): замыкающий слой эпика ORCH-52. 52c заложила writer + валидатор обязательной схемы (`REQUIRED_FIELDS`), но он работал warning-only «вхолостую» — 6 промптов `.openclaw/agents/*.md` **не эмитили** поля схемы. ORCH-077 учит все 6 промптов её эмитить и переписывает их в едином каноне Anthropic. **Docs/prompts-only:** `src/**`, `STAGE_TRANSITIONS`, `QG_CHECKS`, состав machine-verdict ключей и схема БД — **не тронуты**; `frontmatter_validation_strict` остаётся `False` (эмиссия добровольная, enforcement НЕ включён). - **Единый XML-скелет (5 обязательных секций, нормативный порядок):** `` → `` (+ опц. `` у решающих ролей: architect/reviewer/tester/deployer) → `` → `` (запреты «❌ X → ✅ Y») → ``. Доп. секции (``/``) — после пяти обязательных. - **Аддитивная схема 52c:** `` каждого промпта перечисляет 6 полей (`work_item`/`stage`/`author_agent`/`status`/`created_at`/`model_used`) с роле-специфичными значениями (`stage`/`author_agent` по карте ролей; `model_used: claude-opus-4-8` по резолву ORCH-41) и ставит их **рядом** с machine-verdict ключом, **не меняя его имя/регистр/значения** (`verdict:` `APPROVED|REQUEST_CHANGES`; `result:` `PASS|FAIL`; `staging_status:`/`deploy_status:` `SUCCESS|FAILED`; `security_status:` `PASS|FAIL`). Для `04-test-plan.yaml` — top-level YAML-ключи. Гейты читают вердикты 1:1 как раньше (NFR-1). diff --git a/CLAUDE.md b/CLAUDE.md index 86e7a93..abf64b3 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -133,6 +133,10 @@ created → analysis → architecture → development → review → testing → 6. **Reviewer проверяет: обновлена ли документация. Нет → REQUEST_CHANGES.** 7. Не использовать `--no-verify` без явного одобрения Owner. 8. Секреты — только в `.env`/`.env.staging` на хосте, в гит НЕ коммитятся (канон — `.env.example`). +9. **Трассировка маркеров (ORCH-078, ORCH-52e):** правишь строку/блок с маркером `ORCH-NNN` → + ПЕРЕД изменением прочитай его `docs/work-items/ORCH-NNN/06-adr/` и не сломай зафиксированный + инвариант; блок с 3+ маркерами → опирайся на сводный сквозной ADR. Стандарт маркеров (формат, + размещение, fallback-доступ, анти-археология, каноничное правило чтения) — `docs/_standards/TRACEABILITY.md`. ## ⚠️ Self-hosting — оркестратор правит САМ СЕБЯ Задачи проекта ORCH меняют инструмент, который СЕЙЧАС работает в продакшене и обслуживает ДРУГИЕ проекты (enduro-trails) из ОДНОГО инстанса с ОБЩЕЙ БД и общей очередью. diff --git a/docs/_standards/TRACEABILITY.md b/docs/_standards/TRACEABILITY.md new file mode 100644 index 0000000..8b468a3 --- /dev/null +++ b/docs/_standards/TRACEABILITY.md @@ -0,0 +1,147 @@ +# TRACEABILITY — стандарт маркеров-трассировки `ORCH-NNN` (golden source трассировки) + +> **Назначение.** Единый нормативный контракт: как нетривиальная строка/блок/инвариант в коде +> привязывается к work item, который его ввёл, и к его архитектурному решению (ADR). Это **слой 4 +> (трассировка)** эпика **ORCH-52** — рядом с `PIPELINE_DOCS.md` (слой 1, структура документов) и +> `HANDOFF_PROTOCOL.md` (слой 2, машинный frontmatter-контракт). +> +> **Статус истины.** Документ **кодифицирует сложившуюся практику**, а не вводит новый синтаксис. +> Источник истины о *поведении* остаётся код (`src/stages.py`, `src/qg/checks.py`, +> `src/stage_engine.py`); этот стандарт — описательно-нормативный, **не машинный гейт конвейера**. +> Соблюдение держится на дисциплине агентов + оси ревью (`reviewer.md`), а не на CI-lint. + +Введён задачей **ORCH-078** (ORCH-52e). Сквозной ADR: +[`docs/architecture/adr/adr-0022-traceability-marker-standard.md`](../architecture/adr/adr-0022-traceability-marker-standard.md); +детально — `docs/work-items/ORCH-078/06-adr/ADR-001-traceability-marker-standard.md`. Продолжает +цепочку стандартов эпика 52: adr-0019 (52b), adr-0020 (52c), adr-0021 (52d). + +--- + +## 1. Назначение и определение + +**Маркер `ORCH-NNN`** (а для проекта enduro-trails — `ET-NNN`) в коде = обязательный стандарт +трассировки: он привязывает нетривиальную строку / блок / инвариант к work item, который его ввёл, +и к его ADR. Это даёт читающему агенту прямой путь «строка кода → решение, которое её породило», +вместо `git blame`-археологии. + +**Факт (сверено на 2026-06-09):** в `src/` де-факто живёт **51 уникальный** маркер `ORCH-NNN` +(`grep -rhoE 'ORCH-[0-9]+' src/ | sort -u | wc -l` → `51`) — сложившаяся практика. Этот стандарт её +формализует. **Массовый ретро-фит существующих 51 маркера вне объёма** — стандарт нормативен «на +будущее»: его правила применяются к **новому и правимому** коду. + +--- + +## 2. Формат маркера + +Маркер — это **inline-комментарий** (или фрагмент docstring модуля/функции), содержащий идентификатор +work item `ORCH-NNN`. Рекомендуется рядом указывать ссылку на конкретное решение в ADR, чтобы трасса +вела не просто к задаче, а к пункту решения: + +```python +# Ordering term — ``t2.id < jobs.task_id`` (FIFO, ORCH-088, ADR-001 D1 / FR-2): a task +# does not enter `analysis` while an earlier unfinished task exists in the same repo. +``` + +Нового синтаксиса не вводится — кодифицируется уже сложившийся стиль (`ORCH-NNN[, ADR-001 D1]`). + +--- + +## 3. Где ставится маркер + +Маркер ставится рядом с **нетривиальным инвариантом**, понимание которого требует контекста решения: + +- выбор fail-open / fail-closed поведения; +- точное условие сериализации / упорядочивания (FIFO, lease, барьер); +- идемпотентность / защита от повторной обработки; +- обходимая «дыра» конвейера, которую блок закрывает; +- любое условие, чьё «почему именно так» зафиксировано в ADR. + +Маркер **НЕ ставится** на тривиальном/самоочевидном коде (геттеры, простые присваивания, очевидные +проверки) — это только зашумляет. + +**Правило для нового кода:** вводишь значимый инвариант → ставь маркер своей задачи (`ORCH-NNN`) +рядом, по возможности со ссылкой на пункт ADR. + +--- + +## 4. Как читать историю (с реальным проверяемым примером) + +Пошагово, от строки кода к решению: + +1. Видишь в коде маркер `ORCH-NNN` у строки/блока, который собираешься менять. +2. Открываешь его архитектурное решение: `docs/work-items/ORCH-NNN/06-adr/`. +3. Читаешь зафиксированный инвариант ПЕРЕД правкой; не ломаешь его (см. §7). + +**Проверяемый пример из реального кода (`main`):** + +> `src/serial_gate.py` несёт условие сериализации `t2.id < jobs.task_id` с маркером **ORCH-088** +> и отсылкой `ADR-001 D1 / FR-2` (FIFO-уточнение serial-gate). Чтобы понять, почему задача не входит +> в `analysis`, пока в репо есть более ранняя незавершённая задача, читаешь: +> `docs/work-items/ORCH-088/06-adr/ADR-001-serial-gate.md`. + +Пример ссылается на **реально существующие** в `main` файл и ADR — иначе стандарт опровергал бы сам +себя (нерабочая трассировка). + +--- + +## 5. Fallback-доступ к чужому ADR + +Папки `docs/work-items/ORCH-NNN/` может **не быть в текущей ветке** (она срезана от `main` без неё — +типично для ветки другой задачи). Штатный способ прочитать чужой ADR — взять его из `origin/main`: + +```bash +git fetch origin # при необходимости заранее +git ls-tree origin/main:docs/work-items/ORCH-NNN/06-adr/ # листинг доступных ADR +git show origin/main:docs/work-items/ORCH-NNN/06-adr/ADR-001-.md # прочитать конкретный +``` + +Это не блокер: отсутствие папки в ветке ≠ отсутствие решения — оно всегда есть в `main`. + +--- + +## 6. Анти-археология: 3+ маркеров → сводный сквозной ADR + +Если функция/блок несёт **3+** маркеров `ORCH-NNN` (эволюционировал через много задач), раскопки по +каждому work item нечитаемы. Вместо перечисления всех задач ставится **одна сводная ссылка на +сквозной ADR** (`docs/architecture/adr/adr-NNNN-*`), агрегирующий эволюцию. + +Числовой порог `3` — граница, за которой inline-перечисление перестаёт быть читаемым (один-два +маркера ещё информативны, три и больше — уже археология). + +**Пример из кода:** `src/merge_gate.py` несёт маркеры ORCH-043/065/071/073 (и ещё несколько) → +читать сводные сквозные `adr-0006` (merge-gate), `adr-0013` (merge-verify-gate), +`adr-0014` (sha-source-of-truth), `adr-0016` (ensure-open-PR) в `docs/architecture/adr/`, а не 8 +отдельных work item. + +Это конвенция для **нового/правимого** блока; массовая переразметка существующих файлов вне объёма. + +--- + +## 7. Правило чтения (каноничная формулировка — единый источник) + +Это **единственное** место, где живёт каноничный текст правила. Промпты агентов +(`developer.md`/`architect.md`/`reviewer.md`) **ссылаются** на него, а не копируют — чтобы не было +дрейфа формулировок между файлами. + +> **Правишь код с маркером `ORCH-NNN` → прочитай его `docs/work-items/ORCH-NNN/06-adr/` ПЕРЕД +> изменением; не сломай зафиксированный инвариант. Не можешь сохранить инвариант — эскалируй / +> верни задачу в анализ, не правь вслепую.** Папки нет в ветке → читай из `origin/main` (§5). Блок +> несёт 3+ маркеров → опирайся на сводный сквозной ADR (§6). + +Кто и как применяет правило: + +- **developer / architect** — обязаны выполнить чтение ПЕРЕД правкой маркированного кода. +- **architect** — при введении/правке блока с 3+ маркерами оформляет/обновляет сводный сквозной ADR. +- **reviewer** — проверяет соблюдение: правка маркированного (`ORCH-NNN`) кода без сверки с его ADR + или со сломом инварианта → finding (рекомендуемая severity **P1**; слом критического инварианта + конвейера — на усмотрение reviewer вплоть до P0). + +--- + +## Связи + +- Сквозной ADR: [`adr-0022`](../architecture/adr/adr-0022-traceability-marker-standard.md). +- Стандарты-соседи: [`PIPELINE_DOCS.md`](PIPELINE_DOCS.md) (слой 1), + [`HANDOFF_PROTOCOL.md`](HANDOFF_PROTOCOL.md) (слой 2). +- Цепочка эпика 52: adr-0019 (52b) / adr-0020 (52c) / adr-0021 (52d) / adr-0022 (52e). +- Прецедент класса ошибки (слом инварианта без чтения ADR): `docs/history/LESSONS_2026-06-08_phantom-merge.md`. diff --git a/docs/architecture/README.md b/docs/architecture/README.md index f3fbf1b..f335cee 100644 --- a/docs/architecture/README.md +++ b/docs/architecture/README.md @@ -83,6 +83,26 @@ 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-NNN` (ORCH-078, 52e — слой 4 эпика 52) +**Слой 4 (трассировка).** Маркеры `ORCH-NNN`/`ET-NNN` в коде (де-факто **51 уникальный** в `src/`) +привязывают нетривиальные инварианты к породившему их work item, но это была **сложившаяся практика +без формального контракта**. ORCH-078 кодифицирует её как нормативный стандарт +[`docs/_standards/TRACEABILITY.md`](../_standards/TRACEABILITY.md) (рядом с `PIPELINE_DOCS.md` и +`HANDOFF_PROTOCOL.md`) и точечно дополняет 3 промпта правилом чтения. Это **docs/prompts-only** +изменение: `src/**`, `STAGE_TRANSITIONS`, `QG_CHECKS`, схема БД — **не трогаются**; стандарт — +описательно-нормативный, **не машинный гейт** (массовый ретро-фит 51 маркера вне объёма). +- **Каноничное правило чтения (единый источник):** правишь код с маркером `ORCH-NNN` → прочитай его + `06-adr` ПЕРЕД изменением, не сломай инвариант. Промпты `developer`/`architect`/`reviewer` + **ссылаются** на текст в `TRACEABILITY.md`, а не копируют его (нет дрейфа между файлами). +- **Fallback-доступ:** папки `docs/work-items/ORCH-NNN/` нет в ветке → `git show origin/main:...`. +- **Анти-археология:** блок с **3+** маркерами → одна сводная ссылка на сквозной ADR + (`docs/architecture/adr/`) вместо перечисления всех work item. +- **Контроль:** reviewer ловит правку маркированного кода без сверки с ADR → finding ≥P1. +- **Анти-регресс:** расширенный `tests/test_agent_prompts_canon.py` (наличие правила/ссылок); канон + 52d (5 секций, 6 полей, регистр verdict-ключей) и `test_agent_frontmatter_no_model.py` зелёные. +- ADR: [adr-0022](adr/adr-0022-traceability-marker-standard.md); детально — + `docs/work-items/ORCH-078/06-adr/ADR-001-traceability-marker-standard.md`. + ### Модель и эффорт по ролям (ORCH-41, валидация ORCH-74) Модель и `--effort` каждого агента берутся из config (`src/config.py`), резолвятся `launcher.resolve_agent_model` / `resolve_agent_effort` по приоритету **project-override (`projects_json` `agent_models`/`agent_efforts`) > `ORCH_AGENT_MODEL_`/`ORCH_AGENT_EFFORT_` > `*_default` > CLI-дефолт (без флага)**. **Эффорт (ORCH-081):** ниже `*_default` добавлен непустой **per-role floor** — class-default поля `agent_effort_` из `config.py` (его пустой env перебить не может). Floor — строго последний уровень (ниже default) и срабатывает ТОЛЬКО когда все уровни пусты, поэтому пустые прод-`ORCH_AGENT_EFFORT_*=` (которые pydantic трактует как явное `''` и обнуляют дефолт) больше не приводят к запуску без `--effort`: каждая роль получает свой канонический пол (developer=`xhigh`, tester/deployer=`medium`, прочие=`high`). Непустой явный конфиг по-прежнему побеждает floor; опечатка вне `VALID_EFFORTS` дропается валидацией ДО floor (never-break, не маскируется). См. `docs/work-items/ORCH-081/06-adr/ADR-001-effort-resolution-floor.md`. frontmatter `model:` в `.openclaw/agents/*.md` **удалён** (ORCH-74 G1) — он был мёртвой/лживой декларацией (launcher его не читает); config — единственный источник правды о модели. Model-routing (G3) НЕ включён — все 6 агентов на `claude-opus-4-8`. diff --git a/tests/test_agent_prompts_canon.py b/tests/test_agent_prompts_canon.py index 268dbc0..2051c36 100644 --- a/tests/test_agent_prompts_canon.py +++ b/tests/test_agent_prompts_canon.py @@ -17,10 +17,11 @@ import pytest _AGENTS = ("analyst", "architect", "developer", "reviewer", "tester", "deployer") # tests/ is one level under the repo root; .openclaw/agents lives at the root. -_AGENTS_DIR = os.path.join( - os.path.dirname(os.path.dirname(os.path.abspath(__file__))), - ".openclaw", "agents", -) +_REPO_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +_AGENTS_DIR = os.path.join(_REPO_ROOT, ".openclaw", "agents") + +# ORCH-078 (ORCH-52e): the traceability-marker standard (layer 4 of epic ORCH-52). +_TRACEABILITY = os.path.join(_REPO_ROOT, "docs", "_standards", "TRACEABILITY.md") # The 5 mandatory XML sections, in normative order (D1 / AC-1). _REQUIRED_SECTIONS = ("context", "task", "deliverables", "constraints", "output_format") @@ -94,6 +95,11 @@ def _read(agent: str) -> str: return f.read() +def _read_repo(*parts: str) -> str: + with open(os.path.join(_REPO_ROOT, *parts), encoding="utf-8") as f: + return f.read() + + @pytest.mark.parametrize("agent", _AGENTS) def test_five_xml_sections_present(agent): """TC-01: each prompt carries all 5 XML sections (open + close tag).""" @@ -163,3 +169,92 @@ def test_role_anti_regress_markers(agent): text = _read(agent) for marker in _ANTI_REGRESS[agent]: assert marker in text, f"{agent}.md lost anti-regress marker {marker!r}" + + +# --------------------------------------------------------------------------- # +# ORCH-078 (ORCH-52e): traceability-marker standard + reading-rule anti-regress +# (TRZ §FR-1..FR-8; AC-1..AC-5, AC-8). Pure-text checks, NO `src/` import. +# --------------------------------------------------------------------------- # + +def test_traceability_standard_exists_and_nonempty(): + """TC-01 (AC-1): docs/_standards/TRACEABILITY.md exists and is non-empty.""" + assert os.path.isfile(_TRACEABILITY), "docs/_standards/TRACEABILITY.md is missing" + assert _read_repo("docs", "_standards", "TRACEABILITY.md").strip(), ( + "TRACEABILITY.md is empty" + ) + + +def test_traceability_describes_marker_format_and_placement(): + """TC-02 (AC-1): standard describes the ORCH-NNN marker and where it is placed.""" + text = _read_repo("docs", "_standards", "TRACEABILITY.md") + assert "ORCH-NNN" in text, "TRACEABILITY.md does not describe the ORCH-NNN marker" + # placement rule: next to a non-trivial invariant (not on trivial code). + assert "инвариант" in text, "TRACEABILITY.md does not state the placement rule" + + +def test_traceability_has_real_verifiable_example(): + """TC-03 (AC-1): the worked example points at files that really exist in main. + + A traceability standard whose example references a missing file/ADR would + refute itself, so the example must be checkable against the repo tree. + """ + text = _read_repo("docs", "_standards", "TRACEABILITY.md") + assert "src/serial_gate.py" in text and "ORCH-088" in text, ( + "TRACEABILITY.md lacks the serial_gate/ORCH-088 worked example" + ) + assert os.path.isfile(os.path.join(_REPO_ROOT, "src", "serial_gate.py")), ( + "example references src/serial_gate.py which does not exist" + ) + assert os.path.isfile(os.path.join( + _REPO_ROOT, "docs", "work-items", "ORCH-088", "06-adr", + "ADR-001-serial-gate.md", + )), "example references an ORCH-088 ADR that does not exist" + + +def test_traceability_documents_fallback_access(): + """TC-04 (AC-4): standard documents the git show origin/main fallback.""" + text = _read_repo("docs", "_standards", "TRACEABILITY.md") + assert "git show origin/main:docs/work-items/" in text, ( + "TRACEABILITY.md does not document the cross-branch ADR fallback" + ) + + +def test_traceability_documents_anti_archeology(): + """TC-05 (AC-5): standard documents the 3+ markers -> cross-cutting ADR rule.""" + text = _read_repo("docs", "_standards", "TRACEABILITY.md") + assert "docs/architecture/adr/" in text, ( + "TRACEABILITY.md anti-archeology rule does not point at the cross-cutting ADR dir" + ) + assert "3+" in text, "TRACEABILITY.md does not state the 3+ markers threshold" + + +def test_developer_carries_reading_rule_and_fallback(): + """TC-06 (AC-2, AC-4): developer.md carries the reading rule + standard + fallback.""" + text = _read("developer") + assert "TRACEABILITY.md" in text, "developer.md does not reference TRACEABILITY.md" + assert "git show origin/main:docs/work-items/" in text, ( + "developer.md does not carry the cross-branch ADR fallback" + ) + + +def test_architect_carries_reading_rule_and_anti_archeology(): + """TC-07 (AC-2, AC-5): architect.md carries reading rule + anti-archeology.""" + text = _read("architect") + assert "TRACEABILITY.md" in text, "architect.md does not reference TRACEABILITY.md" + assert "3+" in text, "architect.md does not carry the 3+ markers anti-archeology rule" + + +def test_reviewer_carries_traceability_control_axis(): + """TC-08 (AC-3): reviewer.md carries the traceability-compliance control axis.""" + text = _read("reviewer") + assert "TRACEABILITY.md" in text, "reviewer.md does not reference TRACEABILITY.md" + + +def test_claude_md_and_readme_reference_traceability_standard(): + """TC-12 (AC-8): CLAUDE.md and architecture README reference the standard.""" + assert "TRACEABILITY.md" in _read_repo("CLAUDE.md"), ( + "CLAUDE.md does not reference docs/_standards/TRACEABILITY.md" + ) + assert "TRACEABILITY.md" in _read_repo("docs", "architecture", "README.md"), ( + "architecture README does not reference docs/_standards/TRACEABILITY.md" + )