diff --git a/av-dev-docs/agents/doc-consistency.md b/av-dev-docs/agents/doc-consistency.md index 0bf1992..95133c2 100644 --- a/av-dev-docs/agents/doc-consistency.md +++ b/av-dev-docs/agents/doc-consistency.md @@ -26,7 +26,7 @@ color: yellow | почему решено так | `adr/`, источник — архивный `design.md` | | граница домена, «чем не является» | `passport.md` | | инвариант и его severity | `CLAUDE.md` | -| что приложение умеет и чего не умеет; порядок работ | `docs/tasks/ROADMAP.md` | +| что приложение умеет и чего не умеет; порядок работ | `tasks/ROADMAP.md` | | измеренное число | `research/` | | настройка с числовым значением | `database.md` | | периметр и модель угроз | `security.md` | @@ -45,8 +45,10 @@ color: yellow ## Что тебе дают -Корень проекта. Твоё чтение — `docs/**` (кроме `docs/tasks/`, его ведёт -`tasks.py`), `CLAUDE.md`, `openspec/specs/**` и `openspec/config.yaml`. Плюс +Корень проекта. Твоё чтение — `docs/**`, `CLAUDE.md`, `openspec/specs/**` и +`openspec/config.yaml`. **Каталог задач не твой** — он лежит в `tasks/` (или в +`docs/tasks/` на непереехавшем проекте), принадлежит другому плагину и ведётся +своим скриптом; не открывай его ни в той форме, ни в другой. Плюс `openspec/changes/archive/`, когда проверяешь ADR: там лежат `design.md`, из которых записи промоутятся. diff --git a/av-dev-docs/skills/canon/references/canon.md b/av-dev-docs/skills/canon/references/canon.md index 30bdeb0..794fc14 100644 --- a/av-dev-docs/skills/canon/references/canon.md +++ b/av-dev-docs/skills/canon/references/canon.md @@ -75,8 +75,8 @@ docs/ adr.md | adr/ почему решено так; статусы, правило замены review.md | review/ настройка конвейера под проект + журнал дефектов <своя тема>.md | <своя тема>/ всё, что проект счёл нужным проверять - tasks/ каталог задач — плагин av-dev-tasks, не канон: - место зарезервировано, наличия канон не требует +tasks/ каталог задач — плагин av-dev-tasks, не канон; + лежит в корне, вне docs/, и канон его не требует openspec/ config.yaml только нужды генерации артефактов + ссылки specs//spec.md что система делает — нормативно @@ -456,7 +456,7 @@ kebab-case.** Причина не эстетическая: имя файла с | почему решено так | `adr/`, источник — архивный `design.md` | | граница домена, «чем не является» | `passport.md` | | инвариант и его severity | `CLAUDE.md` | -| что приложение умеет и чего не умеет; порядок работ | `docs/tasks/ROADMAP.md` | +| что приложение умеет и чего не умеет; порядок работ | `tasks/ROADMAP.md` | | измеренное число | `research/` | | настройка с числовым значением | `database.md` | | периметр и модель угроз | `security.md` | @@ -491,9 +491,9 @@ kebab-case.** Причина не эстетическая: имя файла с | `docs/review-brief.md` | документы канона и есть бриф; остаток — в `review.md` | | `docs/specs/` | `openspec/specs/` (поведение) и `architecture.md` (обзор) | | `docs/drafts/` | идея → запись `research`; отказ → ADR; порядок → `ROADMAP.md`; размышление → `opsx:explore` | -| `docs/plan.md` | `docs/tasks/ROADMAP.md` | +| `docs/plan.md` | `tasks/ROADMAP.md` | | `BRIEF.md` | `passport.md` | -| `docs/backlog/` | `docs/tasks/` | +| `docs/backlog/` | `tasks/` в корне репозитория | | `docs/review-journal.md`, `docs/review/journal.md` | `docs/review.md` | ## Что проверяет машина, а что человек @@ -539,7 +539,7 @@ kebab-case.** Причина не эстетическая: имя файла с ```json { - "canon": 10, + "canon": 11, "migrations": "internal/store/migrations" } ``` diff --git a/av-dev-docs/skills/canon/references/changelog.md b/av-dev-docs/skills/canon/references/changelog.md index 660a498..ac7c8d9 100644 --- a/av-dev-docs/skills/canon/references/changelog.md +++ b/av-dev-docs/skills/canon/references/changelog.md @@ -13,6 +13,37 @@ upgrade` идёт по записям снизу вверх от версии п --- +## Версия 11 — 2026-08-09 + +Каталог задач уехал из `docs/` в корень репозитория. Версия 8 отпустила его из +канона — перестала требовать, перестала проверять, — но место он занимал всё то +же, `docs/tasks/`. Полдела: каталог, принадлежащий одному плагину, лежал внутри +дерева, которым владеет другой. Проекту, поставившему учёт работ без канона +документов, приходилось заводить `docs/` ради одной вложенной папки. + +**Что изменилось.** Дом задач — `tasks/` в корне репозитория. `tasks.py` ищет его +там первым; `docs/tasks/` и `doc/tasks/` остаются в списке поиска для +непереехавших проектов, а `init` заводит только в корне. Настройки — там же, +`tasks/.tasks.json`. + +**Что осталось терпимым.** `docs.py` по-прежнему не считает `docs/tasks/` файлом +вне канона: непереехавший проект не должен получать выдуманную ошибку вдобавок к +этой записи, которая и так велит ему переехать. + +**Что сделать проекту.** + +1. `git mv docs/tasks tasks` — одним коммитом вместе с шагом 2, чтобы ссылки не + жили битыми между коммитами. +2. **Починить относительные ссылки внутри записей.** Файл `tasks/items/x.md` + стал на уровень ближе к корню: `../../passport.md` в теле записи теперь + `../docs/passport.md`. Тот же сдвиг у ссылок из индексов. Это самая тихая + часть переезда: битая относительная ссылка не мешает `tasks.py check`, её + ловит только `docs.py check` и только у документов канона. +3. Проверить ссылки **на** задачи снаружи: `CLAUDE.md`, `README.md`, гейт, + `docs/review.md`. Путь `docs/tasks/...` в них теперь ведёт в никуда. +4. Поправить путь в гейте: `tasks.py check --dir tasks`. +5. `docs/.pm.json`: `"canon": 11`. + ## Версия 10 — 2026-08-09 Проверка формы `config.yaml` ушла к тому, кто файл заводит. Версия 9 перенесла в diff --git a/av-dev-docs/skills/canon/references/skeletons.md b/av-dev-docs/skills/canon/references/skeletons.md index df46cf5..0d613a7 100644 --- a/av-dev-docs/skills/canon/references/skeletons.md +++ b/av-dev-docs/skills/canon/references/skeletons.md @@ -431,7 +431,7 @@ severity стоит здесь, а не выводится каждым прох ```json { - "canon": 10 + "canon": 11 } ``` diff --git a/av-dev-docs/skills/canon/scripts/docs.py b/av-dev-docs/skills/canon/scripts/docs.py index 403146f..9c24d6f 100644 --- a/av-dev-docs/skills/canon/scripts/docs.py +++ b/av-dev-docs/skills/canon/scripts/docs.py @@ -25,7 +25,7 @@ from dataclasses import dataclass, field from pathlib import Path from typing import NoReturn -CANON_VERSION = 10 +CANON_VERSION = 11 OK, DRIFT, USAGE, ENV, INTERNAL = 0, 1, 2, 3, 4 @@ -76,12 +76,15 @@ DOC_EXTRA = { "adr": {"template.md": "шаблон записи ADR"}, } -# Служебное в docs/ и каталог задач. Формы у них скрипт не проверяет, и по разным -# причинам: `.pm.json` не markdown, а `tasks/` **принадлежит другому плагину** — -# `av-dev-tasks`, со своим скриптом, своим конфигом и своей версией формата. -# Канон резервирует за ним место в `docs/`, но наличия не требует и внутрь не -# смотрит: проект, поставивший только этот плагин, задач не ведёт вовсе, и -# отказом это быть не может. +# Служебное в docs/ и каталог задач, оставшийся там от прежней раскладки. Формы +# у них скрипт не проверяет, и по разным причинам: `.pm.json` не markdown, а +# задачи **принадлежат другому плагину** — `av-dev-tasks`, со своим скриптом, +# своим конфигом и своей версией формата. +# +# Дом задач с версии 11 — `tasks/` в корне репозитория, то есть вне `docs/` +# вовсе. `docs/tasks/` здесь терпится потому, что непереехавший проект не должен +# получать «файл вне канона» вдобавок к записи журнала, которая и так велит ему +# переехать. Внутрь скрипт не смотрит ни в том, ни в другом случае. NOT_DOCS = {".pm.json", "tasks"} # Слоты, которых в каноне нет, — с адресом, куда уезжает содержимое. Имена, @@ -90,11 +93,11 @@ NOT_DOCS = {".pm.json", "tasks"} RETIRED = { "review-brief.md": "документы канона и есть бриф; остаток — в review", "review-journal.md": "→ документ review", - "plan.md": "→ docs/tasks/ROADMAP.md", + "plan.md": "→ tasks/ROADMAP.md (плагин av-dev-tasks)", "local-research.md": "→ документ research", "specs": "поведение → openspec/specs/, обзор → тема architecture", "drafts": "идея → запись research, отказ → ADR, порядок → ROADMAP.md", - "backlog": "→ docs/tasks/", + "backlog": "→ tasks/ в корне репозитория (плагин av-dev-tasks)", } # --- Слаги в именах файлов -------------------------------------------------- @@ -211,7 +214,7 @@ def strip_code(text: str) -> str: """Выкинуть блоки кода и вставки в обратных кавычках. Путь в примере или в шаблоне — не ссылка, и краснеть на нём значит краснеть - на каждом образце документа. Инлайн-код тоже: `[docs/backlog](docs/tasks/…)` + на каждом образце документа. Инлайн-код тоже: `[docs/backlog](tasks/…)` в тексте про подписи ссылок — иллюстрация, а не ссылка.""" out, inside = [], False for line in text.splitlines(): diff --git a/av-dev-docs/skills/init/SKILL.md b/av-dev-docs/skills/init/SKILL.md index 7267457..357c641 100644 --- a/av-dev-docs/skills/init/SKILL.md +++ b/av-dev-docs/skills/init/SKILL.md @@ -26,7 +26,7 @@ description: "Завести новый проект — сессия вопро | `passport.md` | `architecture.md` | | `CLAUDE.md` | `database.md` | | `security.md` | `conventions/` | -| `docs/tasks/ROADMAP.md` — первые цели | `research/`, `adr/` | +| `tasks/ROADMAP.md` — первые цели | `research/`, `adr/` | | `docs/.pm.json` | `review.md` — журнал пуст, настройка появится с первым ревью | Честная строка информативна, а не «TBD»: «архитектуры пока нет: кода нет, diff --git a/av-dev-pipeline/agents/review-scope.md b/av-dev-pipeline/agents/review-scope.md index aa3f19f..76b1a26 100644 --- a/av-dev-pipeline/agents/review-scope.md +++ b/av-dev-pipeline/agents/review-scope.md @@ -333,7 +333,7 @@ security docs/security.md разбор basics operations docs/architecture.md, «Эксплуатация» разбор basics дома нет: docs/database.md отсутствует -процессные: docs/tasks/, docs/review.md, docs/adr/, docs/research/ +процессные: tasks/, docs/review.md, docs/adr/, docs/research/ директивы: CLAUDE.md найден, AGENTS.md отсутствует ``` diff --git a/av-dev-pipeline/skills/review-pipeline/SKILL.md b/av-dev-pipeline/skills/review-pipeline/SKILL.md index 831391c..45c5320 100644 --- a/av-dev-pipeline/skills/review-pipeline/SKILL.md +++ b/av-dev-pipeline/skills/review-pipeline/SKILL.md @@ -81,7 +81,7 @@ description: "Конвейер ревью изменения, устроенны |---|---|---| | **тема** | заводит направление проверки и требует исполнителя | `conventions.*`, `security.*`, `architecture.*`, свои документы проекта | | **источник темы** | читает как материал чужой темы, своей не порождает | `passport.*`, `database.*`, `CLAUDE.md`/`AGENTS.md`, `openspec/specs/` | -| **процессный документ** | не судит по нему изменение | `docs/tasks/`, `docs/review.*`, `docs/adr.*`, `docs/research.*`, `docs/.pm.json` | +| **процессный документ** | не судит по нему изменение | `tasks/`, `docs/review.*`, `docs/adr.*`, `docs/research.*`, `docs/.pm.json` | **Одна процессная запись всё же читается — `docs/review.*`.** В ней лежит настройка самого конвейера: вопросы по темам, журнал дефектов, типовые узлы, diff --git a/av-dev-tasks/agents/task-form.md b/av-dev-tasks/agents/task-form.md index 040cc7f..adb759f 100644 --- a/av-dev-tasks/agents/task-form.md +++ b/av-dev-tasks/agents/task-form.md @@ -27,7 +27,7 @@ color: green ## Что тебе дают -Список файлов записей (`docs/tasks/items/.md`) или каталог задач целиком. +Список файлов записей (`tasks/items/.md`) или каталог задач целиком. Каталог тебе нужен и сам по себе: задача несёт тег `goal:<слаг>`, и **файл цели ты открываешь**, иначе седьмое правило не проверить. diff --git a/av-dev-tasks/skills/session/SKILL.md b/av-dev-tasks/skills/session/SKILL.md index f76a642..052db0d 100644 --- a/av-dev-tasks/skills/session/SKILL.md +++ b/av-dev-tasks/skills/session/SKILL.md @@ -200,7 +200,7 @@ python3 $tk sprint close --dir D # конец спринта; --d python3 $tk reopen <слаг> --dir D --reason … # приёмка не сошлась после закрытия ``` -`D` — каталог задач проекта, по канону всегда `docs/tasks`; `--dir` передаётся +`D` — каталог задач проекта, всегда `tasks/` в корне; `--dir` передаётся явно каждой командой. Вызов из чужого контекста описан в скилле `tasks` («Переносимость»). **Коды выхода** — там же: 1 это дрейф в беклоге, 3 это «каталога нет», и ветвиться на них надо по-разному. diff --git a/av-dev-tasks/skills/tasks/SKILL.md b/av-dev-tasks/skills/tasks/SKILL.md index 06b2935..faa2d2a 100644 --- a/av-dev-tasks/skills/tasks/SKILL.md +++ b/av-dev-tasks/skills/tasks/SKILL.md @@ -60,13 +60,14 @@ description: Ведение задач и целей как каталога mar ## Раскладка -Каталог задач — **`docs/tasks`, жёстко**: это часть канона документов, и -подгоняется под него проект, а не наоборот. Канон ведёт другой плагин -(`av-dev-docs`), и **путь известен скиллу сам** — ссылки в чужое дерево здесь -нет намеренно: скилл работает и там, где того плагина не поставили. +Каталог задач — **`tasks/` в корне репозитория, жёстко.** Он принадлежит этому +плагину, а не канону документов: `docs/` ведёт другой плагин (`av-dev-docs`), и +проект, поставивший учёт работ без него, каталога `docs/` не имеет вовсе. Внутри +`docs/` задачи лежали до версии канона 11; непереехавший проект скрипт +по-прежнему находит, но новый заводит только в корне. ``` -docs/tasks/ +tasks/ items/ задачи и цели файлами, .md, слаги английские ROADMAP.md состояние проекта: что уже умеет и чего ещё не умеет BACKLOG.md что можно взять — только задачи, целей здесь нет @@ -133,7 +134,7 @@ docs/tasks/ **У сделанной задачи записи не остаётся** — файл и строка удаляются (`close --implemented`). Ей хватает коммита и документации проекта; вторая запись была бы вторым домом для того же факта. Вопрос «что было в спринте N» отвечается -даром: `SPRINT.md` лежит под git, `git log -p docs/tasks/SPRINT.md` отдаёт историю +даром: `SPRINT.md` лежит под git, `git log -p tasks/SPRINT.md` отдаёт историю всех наборов без отдельного журнала. **У достигнутой цели запись остаётся, и это единственное исключение.** Файл @@ -359,7 +360,7 @@ stateDiagram-v2 ## Инструмент (`tasks.py`) Пусть `tk="$CLAUDE_PLUGIN_ROOT/skills/tasks/scripts/tasks.py"`, а `D` — -`docs/tasks` от корня проекта. `--dir` стоит в примерах намеренно: вызов из +`tasks` от корня проекта. `--dir` стоит в примерах намеренно: вызов из подкаталога — обычное дело. ``` @@ -618,7 +619,7 @@ python3 $tk adopt scan --from … | apply --plan … # разовая адап просто каталог markdown. Текст задач — русский (язык документации проекта); зашита только латиница слага. OpenSpec ему тоже не нужен. -- **Каталог задач — `docs/tasks`, жёстко**, и `--dir` передаётся явно всегда: +- **Каталог задач — `tasks/` в корне, жёстко**, и `--dir` передаётся явно всегда: раскладка канона одинакова во всех проектах, и искать больше нечего. Каталога нет — код 3 и вопрос человеку; `init` заводит его **только** когда проект действительно новый, а перевод чужой раскладки делает `av-dev-docs:canon`. diff --git a/av-dev-tasks/skills/tasks/references/adopt.md b/av-dev-tasks/skills/tasks/references/adopt.md index c2af1a6..bb76d8e 100644 --- a/av-dev-tasks/skills/tasks/references/adopt.md +++ b/av-dev-tasks/skills/tasks/references/adopt.md @@ -38,7 +38,7 @@ tk="$CLAUDE_PLUGIN_ROOT/skills/tasks/scripts/tasks.py" python3 $tk adopt scan --from docs/backlog docs/plan.md TODO.md \ - --target docs/tasks --out tasks-adopt-plan.json # только чтение + --target tasks --out tasks-adopt-plan.json # только чтение python3 $tk adopt apply --plan tasks-adopt-plan.json \ --refs docs openspec CLAUDE.md README.md # запись ``` @@ -63,7 +63,7 @@ python3 $tk adopt apply --plan tasks-adopt-plan.json \ ## Порядок 1. **Осмотрись.** Где лежат задачи, роадмап, заметки. Каталог задач по канону — - всегда `docs/tasks`. Секции беклога (`--sections`) — по умолчанию + всегда `tasks`. Секции беклога (`--sections`) — по умолчанию `Ядро,Инфра`; если у проекта деление другое по существу, оно называется здесь, а не подгоняется под умолчание, и становится **заголовками `##` индекса** — их единственным домом. В `docs/.pm.json` секции не пишутся. @@ -106,7 +106,7 @@ take` такую задачу не возьмёт). Закрывается эт - **Не удаляет источники.** Старый каталог остаётся на месте: сверить и убрать — дело человека, удалять чужое молча нельзя. В доклад идёт готовая команда. -- **Не переписывает подписи ссылок.** `[docs/backlog](docs/tasks/BACKLOG.md)` — +- **Не переписывает подписи ссылок.** `[docs/backlog](tasks/BACKLOG.md)` — цель поправлена, текст остался; это правится глазами, и таких мест немного. - **Не сочиняет критерии приёмки и не придумывает цели**, которых в материале нет. Придуманная цель хуже отсутствующей: под неё соберут спринт. diff --git a/av-dev-tasks/skills/tasks/scripts/tasks.py b/av-dev-tasks/skills/tasks/scripts/tasks.py index 4798daf..3db8a02 100755 --- a/av-dev-tasks/skills/tasks/scripts/tasks.py +++ b/av-dev-tasks/skills/tasks/scripts/tasks.py @@ -6,11 +6,12 @@ спринт — замороженный набор задач, обычно под одну цель. Индексов теперь четыре, и задача живёт ровно в одном из них за раз. -Раскладка. Путь каталога — `docs/tasks`, жёстко: это часть канона документов -av-dev, и подгоняется под него проект. Имена внутри настраиваются через -`docs/.pm.json`, ключ `tasks`. +Раскладка. Путь каталога — `tasks/` в корне репозитория, жёстко. Каталог +принадлежит этому плагину, а не канону документов: `docs/` ведёт другой плагин, и +проект, поставивший учёт работ без него, каталога `docs/` не имеет вовсе. Имена +внутри настраиваются через `tasks/.tasks.json`. - docs/tasks/ + tasks/ items/ задачи и цели файлами, .md ROADMAP.md состояние проекта: что уже умеет и чего ещё не умеет. Секции канонические и в этом порядке: Запланировано | @@ -75,9 +76,9 @@ goal | feature | fix | chore | research, по-английски, как и пр tasks.py adopt scan --from PATH [PATH …] [--target DIR] [--out PLAN.json] tasks.py adopt apply --plan PLAN.json [--refs PATH …] [--dry-run] -Каталог задач: `--dir` (обязан быть внутри рабочего каталога) → `docs/tasks` -вверх от текущего каталога. Прежние раскладки (`tasks`, `doc/tasks`) читаются, -пока живы непереехавшие проекты; переводит их скилл av-dev-docs:canon. +Каталог задач: `--dir` (обязан быть внутри рабочего каталога) → `tasks/` вверх +от текущего каталога. Прежние раскладки (`docs/tasks`, `doc/tasks`) читаются, +пока живы непереехавшие проекты; переезд — запись 11 журнала версий канона. Коды выхода (единый словарь, на нём ветвятся скиллы): @@ -572,7 +573,7 @@ def resolve_layout(explicit: str | None) -> Layout: return Layout(root, load_config(root)) here = Path.cwd().resolve() for base in (here, *here.parents): - for candidate in (base, base / "docs/tasks", base / "tasks", base / "doc/tasks"): + for candidate in (base, base / "tasks", base / "docs/tasks", base / "doc/tasks"): if looks_like_tasks(candidate): try: rel = candidate.relative_to(here) @@ -581,10 +582,10 @@ def resolve_layout(explicit: str | None) -> Layout: return Layout(rel if str(rel) != "." else candidate, load_config(candidate)) if (base / ".git").exists(): break # выше корня репозитория не ищем - raise Env("каталог задач не найден: ни --dir, ни docs/tasks вверх от" - f" {here}. По канону путь всегда docs/tasks; чужую раскладку" - " переводит скилл av-dev-docs:canon, новый проект —" - " tasks.py init --dir docs/tasks") + raise Env("каталог задач не найден: ни --dir, ни tasks/ вверх от" + f" {here}. Путь всегда tasks/ в корне репозитория; прежний" + " docs/tasks переезжает по записи 11 журнала версий канона," + " новый проект — tasks.py init --dir tasks") # --- Чтение индексов --- @@ -3090,9 +3091,9 @@ def cmd_adopt_scan(a: argparse.Namespace) -> int: def requalify_links(text: str, old_dir: Path, new_dir: Path) -> str: """Относительные ссылки тела после переезда файла глубже. - `docs/backlog/x.md` знал соседей как `../passport.md`; из - `docs/tasks/items/x.md` тот же файл — уже `../../passport.md`. Молча - съехавшая на уровень ссылка — самый дешёвый способ развалить документацию. + `docs/backlog/x.md` знал соседей как `../passport.md`; из `tasks/items/x.md` + тот же файл — уже `../docs/passport.md`. Молча съехавшая на уровень ссылка — + самый дешёвый способ развалить документацию. """ delta = len(new_dir.parts) - len(old_dir.parts) if delta <= 0: @@ -3420,7 +3421,7 @@ def main() -> int: asub = p.add_subparsers(dest="adopt_command", required=True) s = asub.add_parser("scan", help="только карта: что найдено и как разложилось") s.add_argument("--from", dest="sources", nargs="+", required=True) - s.add_argument("--target", default="docs/tasks") + s.add_argument("--target", default="tasks") s.add_argument("--out", default="tasks-adopt-plan.json") s.add_argument("--sections", default=DEFAULT_SECTIONS) @@ -3431,7 +3432,7 @@ def main() -> int: a = ap.parse_args() if a.command == "init": - return cmd_init(Path(a.dir or "docs/tasks"), a) + return cmd_init(Path(a.dir or "tasks"), a) if a.command == "adopt": return cmd_adopt_scan(a) if a.adopt_command == "scan" else cmd_adopt_apply(a) lay = resolve_layout(a.dir)