av-dev-pipeline: бриф удалён, проходы читают документы канона напрямую
- удалены скилл project-brief и контракт брифа; вместо них references/ project-facts.md — карта «что нужно проходу → где лежит» и таблица поразрядной деградации по документам - девять charter'ов, review-pipeline, task-pipeline и task-batch переписаны на пути канона; OpenSpec стал объявленной предпосылкой без ветки деградации - шаг синка документации переписан в построчный доклад, закрытие задачи — вызовом скилла av-dev-pm:tasks вместо строки-слота из CLAUDE.md - по находкам ревью: docs.py звал tasks.py из чужого каталога и выдавал его отказ окружения за дрейф; сверка миграций не видела рабочее дерево; плейсхолдер краснел вместо замечания; сверка capability проходила по совпадению с именем пакета; tasks.py не читал docs/.pm.json; скилл docs пересказывал канон в пяти местах
This commit is contained in:
@@ -83,7 +83,8 @@ import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
CONFIG_NAME = ".tasks.json"
|
||||
CONFIG_NAME = ".tasks.json" # прежний дом настроек, читается для совместимости
|
||||
PM_CONFIG_REL = "../.pm.json" # текущий дом: docs/.pm.json, ключ "tasks"
|
||||
|
||||
EXIT_OK = 0
|
||||
EXIT_DRIFT = 1
|
||||
@@ -278,15 +279,42 @@ class Layout:
|
||||
|
||||
|
||||
def load_config(root: Path) -> dict:
|
||||
"""Настройки каталога задач.
|
||||
|
||||
Дом один — `docs/.pm.json`, ключ `tasks`: один конфиг на весь канон, а не по
|
||||
одному на каталог. Прежний `<tasks>/.tasks.json` читается, пока живы проекты,
|
||||
которые ещё не переехали; когда есть оба, побеждает `.pm.json`, и об этом
|
||||
говорится вслух, потому что молча выбранный из двух конфиг — это дрейф,
|
||||
который потом никто не объяснит.
|
||||
"""
|
||||
pm = (root / PM_CONFIG_REL).resolve()
|
||||
if pm.is_file():
|
||||
data = _read_json(pm)
|
||||
section = data.get("tasks", {})
|
||||
if not isinstance(section, dict):
|
||||
raise Env(f"{pm}: ключ «tasks» — ожидался объект с настройками")
|
||||
if (root / CONFIG_NAME).is_file():
|
||||
print(f"ЗАМЕЧАНИЕ настройки взяты из {pm}; {root / CONFIG_NAME}"
|
||||
f" остался от прежней раскладки и не читается — удали его",
|
||||
file=sys.stderr)
|
||||
return _validate_config(section, pm)
|
||||
path = root / CONFIG_NAME
|
||||
if not path.is_file():
|
||||
return {}
|
||||
return _validate_config(_read_json(path), path)
|
||||
|
||||
|
||||
def _read_json(path: Path) -> dict:
|
||||
try:
|
||||
data = json.loads(path.read_text(encoding="utf-8"))
|
||||
except json.JSONDecodeError as e:
|
||||
raise Env(f"{path}: не разбирается как JSON — {e}")
|
||||
if not isinstance(data, dict):
|
||||
raise Env(f"{path}: ожидался объект с настройками")
|
||||
return data
|
||||
|
||||
|
||||
def _validate_config(data: dict, path: Path) -> dict:
|
||||
unknown = set(data) - set(DEFAULTS)
|
||||
if unknown:
|
||||
raise Env(f"{path}: неизвестные ключи: {', '.join(sorted(unknown))}"
|
||||
@@ -351,10 +379,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, ни .tasks.json вверх от"
|
||||
f" {here}, ни умолчания (docs/tasks, tasks, doc/tasks)."
|
||||
" Путь каталога называет CLAUDE.md проекта; новый проект —"
|
||||
" tasks.py init --dir <путь>")
|
||||
raise Env("каталог задач не найден: ни --dir, ни docs/tasks вверх от"
|
||||
f" {here}. По канону путь всегда docs/tasks; чужую раскладку"
|
||||
" переводит скилл av-dev-pm:canon, новый проект —"
|
||||
" tasks.py init --dir docs/tasks")
|
||||
|
||||
|
||||
# --- Чтение индексов ---
|
||||
|
||||
Reference in New Issue
Block a user