docs: канон поднят с версии 7 до 12
- каталог задач переехал в tasks/ в корне, спринт упразднён — приоритет теперь порядок строк в BACKLOG.md, четыре задачи набора вернулись в беклог - гейт: путь docs.py переведён на av-dev-docs вместо снесённого av-dev-pm, добавлены шаги tasks.py check и openspec.py check - относительные ссылки внутри задач и ссылки из docs/ на задачи починены
This commit is contained in:
+64
-26
@@ -26,16 +26,20 @@ from pathlib import Path
|
||||
|
||||
OUT_DIR = Path("tmp/gate")
|
||||
|
||||
# docs.py скилла av-dev-pm:canon — проверка раскладки docs/ против канона.
|
||||
# Скрипты трёх плагинов av-dev. Каждый проверяет своё и ни один не тянет
|
||||
# остальных: без своего шага дрейф соответствующего каталога перестаёт ловиться
|
||||
# молча.
|
||||
# docs.py — раскладка docs/ против канона (av-dev-docs:canon)
|
||||
# tasks.py — согласованность индексов каталога задач (av-dev-tasks:tasks)
|
||||
# openspec.py — форма openspec/config.yaml (av-dev-code:openspec)
|
||||
# Путь берётся из каталога маркетплейса (он без хеша версии, в отличие от
|
||||
# cache/), переопределяется переменной DOCS_PY: переустановка плагина не должна
|
||||
# cache/), переопределяется переменной: переустановка плагина не должна
|
||||
# требовать правки гейта. Если скрипта нет — шаг краснеет, а не пропускается:
|
||||
# молча пропущенная проверка даёт ложное ощущение проверенности.
|
||||
DOCS_PY_DEFAULT = (
|
||||
Path.home()
|
||||
/ ".claude/plugins/marketplaces/av-dev-skills"
|
||||
/ "av-dev-pm/skills/canon/scripts/docs.py"
|
||||
)
|
||||
MARKETPLACE = Path.home() / ".claude/plugins/marketplaces/av-dev-skills"
|
||||
DOCS_PY_DEFAULT = MARKETPLACE / "av-dev-docs/skills/canon/scripts/docs.py"
|
||||
TASKS_PY_DEFAULT = MARKETPLACE / "av-dev-tasks/skills/tasks/scripts/tasks.py"
|
||||
OPENSPEC_PY_DEFAULT = MARKETPLACE / "av-dev-code/skills/openspec/scripts/openspec.py"
|
||||
|
||||
OK, FAIL, WARN, SKIP = "OK", "FAIL", "WARN", "SKIP"
|
||||
|
||||
@@ -92,6 +96,34 @@ def step(name: str, cmd: list[str], hint: str = "", env: dict[str, str] | None =
|
||||
return ok
|
||||
|
||||
|
||||
def plugin_step(
|
||||
name: str, var: str, default: Path, plugin: str, args: list[str], hint: str
|
||||
) -> None:
|
||||
"""Шаг, который гоняет скрипт плагина av-dev.
|
||||
|
||||
Отсутствие скрипта краснеет, а не пропускается: молча пропущенная проверка
|
||||
неотличима от пройденной. Коды выхода у всех трёх скриптов общие: 0 сошлось,
|
||||
1 дрейф, 2 ошибка употребления, 3 окружение, 4 внутренний сбой. Различать 1
|
||||
и остальные обязательно — дрейф чинится правкой проекта, прочее нет.
|
||||
"""
|
||||
script = Path(os.environ.get(var) or default)
|
||||
if not script.is_file():
|
||||
record(FAIL, name, f"{script.name} не найден: {script} — переустанови"
|
||||
f" плагин {plugin} или задай путь переменной {var}")
|
||||
return
|
||||
log = OUT_DIR / f"{name}.log"
|
||||
proc = subprocess.run(
|
||||
["python3", str(script), *args], capture_output=True, text=True,
|
||||
)
|
||||
log.write_text(proc.stdout + proc.stderr, encoding="utf-8")
|
||||
if proc.returncode == 0:
|
||||
record(OK, name)
|
||||
elif proc.returncode == 1:
|
||||
record(FAIL, name, f"{hint} → {log}")
|
||||
else:
|
||||
record(FAIL, name, f"{script.name} не отработал (код {proc.returncode}) → {log}")
|
||||
|
||||
|
||||
def main() -> int:
|
||||
OUT_DIR.mkdir(parents=True, exist_ok=True)
|
||||
base = base_rev(sys.argv)
|
||||
@@ -186,25 +218,31 @@ def main() -> int:
|
||||
# нетронутые плейсхолдеры и две сверки с кодом — включая «миграция изменена,
|
||||
# а database.md нет» (отдельного шага er-schema поэтому больше нет).
|
||||
# База диффа обязательна: без неё сверка миграций со схемой не гоняется.
|
||||
docs_py = Path(os.environ.get("DOCS_PY") or DOCS_PY_DEFAULT)
|
||||
if not docs_py.is_file():
|
||||
record(FAIL, "canon", f"docs.py не найден: {docs_py} — переустанови плагин"
|
||||
" av-dev-pm или задай путь переменной DOCS_PY")
|
||||
else:
|
||||
log = OUT_DIR / "canon.log"
|
||||
proc = subprocess.run(
|
||||
["python3", str(docs_py), "check", "--dir", ".", "--base", base],
|
||||
capture_output=True, text=True,
|
||||
)
|
||||
log.write_text(proc.stdout + proc.stderr, encoding="utf-8")
|
||||
if proc.returncode == 0:
|
||||
record(OK, "canon")
|
||||
elif proc.returncode == 1:
|
||||
record(FAIL, "canon", f"раскладка docs/ разошлась с каноном → {log}")
|
||||
else:
|
||||
# 2 — ошибка употребления, 3 — окружение, 4 — внутренний сбой.
|
||||
# Это не дрейф документации, и чинится оно не правкой docs/.
|
||||
record(FAIL, "canon", f"docs.py не отработал (код {proc.returncode}) → {log}")
|
||||
plugin_step(
|
||||
"canon", "DOCS_PY", DOCS_PY_DEFAULT, "av-dev-docs",
|
||||
["check", "--dir", ".", "--base", base],
|
||||
"раскладка docs/ разошлась с каноном",
|
||||
)
|
||||
|
||||
# --- Каталог задач ---
|
||||
# Согласованность индексов с файлами items/: бездомная запись, строка без
|
||||
# файла, порядок секций роадмапа, сырьё не в конце секции. docs.py этого не
|
||||
# смотрит — каталог принадлежит другому плагину.
|
||||
plugin_step(
|
||||
"tasks", "TASKS_PY", TASKS_PY_DEFAULT, "av-dev-tasks",
|
||||
["check", "--dir", "tasks"],
|
||||
"индексы задач разошлись с items/",
|
||||
)
|
||||
|
||||
# --- Форма настройки OpenSpec ---
|
||||
# Незаменённый пример в openspec/config.yaml читается как настроенный файл,
|
||||
# а работает как пустой. Ловит это только свой шаг: docs.py про config.yaml
|
||||
# молчит с канона 10.
|
||||
plugin_step(
|
||||
"openspec", "OPENSPEC_PY", OPENSPEC_PY_DEFAULT, "av-dev-code",
|
||||
["check", "--dir", "."],
|
||||
"openspec/config.yaml разошёлся с канонической формой",
|
||||
)
|
||||
|
||||
# --- Секреты ---
|
||||
# Гоняем всегда: секрет утекает из любого файла, не только из кода.
|
||||
|
||||
Reference in New Issue
Block a user