канон 13: файл версии зовётся по владельцу, у задач появилась своя версия формата
Имя `.pm.json` пережило плагин `av-dev-pm` на два месяца и указывало в пустоту. Правило, которое из этого вынуто: имя служебного файла — имя плагина, который его завёл, и по нему же владельца узнают. - `docs/.pm.json` → `docs/.docs.json`, запись 13 журнала. Прежнее имя docs.py не читает намеренно: по этому числу upgrade решает, какие записи применять, и два дома разъехались бы молча ровно там, где это дороже всего. Вместо совместимости — узнавание: check видит старый файл и печатает готовую git mv - у каталога задач появилась своя версия формата — ключ `tasks` в `.tasks.json`, свой журнал версий и своё повышение. До сих пор её не было вовсе, хотя docs.py в комментарии уверенно на неё ссылался: описание опережало механику ровно так, как сказано в решении 195 - число своё, а не копия канонического: плагин ставится в одиночку, и у проекта без docs/ версии канона нет — сверять было бы не с чем - конфиг задач стал обязательным (init и adopt apply пишут его всегда), check сверяет число, `check --fix` его не приписывает: приписанное объявляло бы каталог приведённым к формату, шагов которого никто не делал - переезды 11 и 12 в новый журнал задним числом не переписаны — версия 1 велит догнать формат по журналу канона, называя признаки отставания поимённо (каталог в docs/tasks/, живой SPRINT.md) - запись 60 в DECISIONS со следствиями 200–203; отдельно разведено с решением F, где `.docs.json` отвергался как указатель путей: отвергнут был указатель, а не имя
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: tasks
|
||||
description: Ведение задач и целей как каталога markdown-файлов (одна запись = один файл в items/ + строка в одном из индексов). У каждой записи есть тип (goal, feature, fix, chore, research), и тип решает, каких разделов она требует и что с ней можно делать. Заведение записи из диалога, разбор находок аудита/ревью, декомпозиция на независимо полезные части, штурм сырья, гигиена полей и проверка согласованности индексов. Использовать, когда просят добавить задачу/идею/цель, превратить находки ревью в задачи, разбить задачу, проработать идею, поправить формат или проверить беклог. Расстановка приоритетов и разбор накопившегося — скилл groom. Не реализует задачи — этим занимается скилл решения задачи.
|
||||
description: Ведение задач и целей как каталога markdown-файлов (одна запись = один файл в items/ + строка в одном из индексов). У каждой записи есть тип (goal, feature, fix, chore, research), и тип решает, каких разделов она требует и что с ней можно делать. Заведение записи из диалога, разбор находок аудита/ревью, декомпозиция на независимо полезные части, штурм сырья, гигиена полей и проверка согласованности индексов. Использовать, когда просят добавить задачу/идею/цель, превратить находки ревью в задачи, разбить задачу, проработать идею, поправить формат или проверить беклог. Он же повышает каталог до текущей версии формата по своему журналу версий, когда tasks.py check говорит, что каталог отстал. Расстановка приоритетов и разбор накопившегося — скилл groom. Не реализует задачи — этим занимается скилл решения задачи.
|
||||
---
|
||||
|
||||
# Задачи
|
||||
@@ -399,7 +399,7 @@ python3 $tk adopt scan --from … | apply --plan … # разовая адап
|
||||
| 0 | сошлось / сделано | дальше по сценарию |
|
||||
| 1 | **только `check`:** найден дрейф индексов и файлов | `check --fix`, остаток разобрать |
|
||||
| 2 | ошибка употребления: аргументы или нарушенное правило | читать сообщение, это отказ по существу |
|
||||
| 3 | окружение: каталог не найден, конфиг битый или мимо диска | чинится путём или `docs/.pm.json`, повтор не поможет |
|
||||
| 3 | окружение: каталог не найден, конфиг битый или мимо диска | чинится путём или `<каталог задач>/.tasks.json`, повтор не поможет |
|
||||
| 4 | внутренний сбой | дефект скрипта, доложить |
|
||||
|
||||
Различать 1 и 3 обязательно: «дрейф в беклоге» — рабочая ситуация, «каталога
|
||||
@@ -486,6 +486,39 @@ python3 $tk adopt scan --from … | apply --plan … # разовая адап
|
||||
[fix](references/task-fix.md) · [chore](references/task-chore.md) ·
|
||||
[research](references/task-research.md).
|
||||
|
||||
## Версия формата
|
||||
|
||||
Формат каталога задач меняется, и проект должен знать, к какой его версии
|
||||
приведён. Число живёт ключом `tasks` в `<каталог задач>/.tasks.json`, журнал
|
||||
версий — [references/changelog.md](references/changelog.md), сверяет их
|
||||
`tasks.py check`: отстало — строка расхождения, ушло вперёд — устарел плагин.
|
||||
|
||||
**Версия своя, а не канона документов.** Плагин ставится в одиночку: проект,
|
||||
взявший учёт работ без `av-dev-docs`, каталога `docs/` не имеет вовсе, а значит
|
||||
не имеет и версии канона — сверять было бы не с чем. Обратной совместимости у
|
||||
формата нет: есть «приведён» и «не приведён».
|
||||
|
||||
**`upgrade` — повысить каталог до текущего формата:**
|
||||
|
||||
1. `python3 $tk check --dir D` — первая же строка расхождений называет версию
|
||||
проекта и версию скрипта. Проект новее скрипта — **обнови маркетплейс**, а не
|
||||
проект: это отстал плагин.
|
||||
2. Иди по [журналу](references/changelog.md) снизу вверх от версии проекта до
|
||||
текущей и делай названное в каждой записи. Записи независимы и применяются по
|
||||
порядку.
|
||||
3. Подними `tasks` в `.tasks.json` до текущей — руками, последним шагом. Раньше
|
||||
времени поднятое число объявляет каталог приведённым к формату, шагов
|
||||
которого никто не делал; `check --fix` этого не пишет намеренно.
|
||||
4. `check --dir D` ещё раз — до отсутствия расхождений.
|
||||
|
||||
Записи журнала описывают **что сделать проекту**. Если запись этого не говорит —
|
||||
это дефект журнала, и о нём надо сказать, а не догадываться.
|
||||
|
||||
**Канон документов сюда не вмешивается.** Его журнал двигает своё число в
|
||||
`docs/.docs.json` и вправе сказать «позови этот скилл», но не двигать версию
|
||||
формата задач: две версии, ходящие по одному журналу, разъедутся на первом же
|
||||
проекте, где стоит один плагин без другого.
|
||||
|
||||
## Сценарии
|
||||
|
||||
### Завести запись из диалога
|
||||
@@ -647,17 +680,19 @@ python3 $tk adopt scan --from … | apply --plan … # разовая адап
|
||||
действительно новый, а перевод чужой раскладки делает `av-dev-docs:canon`.
|
||||
У скрипта поиск вверх по дереву ещё жив — он для непереведённых проектов, и
|
||||
полагаться на него скилл не должен: молча найденный чужой каталог это дрейф.
|
||||
- **Настройки живут в `<каталог задач>/.tasks.json`** — свой файл у своего
|
||||
плагина: **имена** файлов и заголовков, и только если они отличаются от
|
||||
умолчания. Неизвестный ключ — код 3 на любой команде, так что лишнее слово в
|
||||
этом объекте останавливает работу с задачами целиком.
|
||||
- **Версия формата и настройки живут в `<каталог задач>/.tasks.json`** — свой
|
||||
файл у своего плагина: ключ `tasks` с версией формата плюс **имена** файлов и
|
||||
заголовков, и последние — только если отличаются от умолчания. Неизвестный
|
||||
ключ — код 3 на любой команде, так что лишнее слово в этом объекте
|
||||
останавливает работу с задачами целиком.
|
||||
|
||||
Дом именно свой, а не `docs/.pm.json`, потому что `docs/` принадлежит плагину
|
||||
канона: проект, поставивший учёт работ без него, каталога `docs/` не имеет
|
||||
вовсе. Прежний ключ `tasks` в `docs/.pm.json` читается, **только когда своего
|
||||
файла нет** — для проектов, заведённых до раскола плагинов; скрипт при этом
|
||||
говорит замечанием, куда его перенести. Есть оба — побеждает свой, и об этом
|
||||
тоже говорится вслух: молча выбранный из двух конфиг это дрейф.
|
||||
Дом именно свой, а не `docs/.docs.json`, потому что `docs/` принадлежит
|
||||
плагину канона: проект, поставивший учёт работ без него, каталога `docs/` не
|
||||
имеет вовсе. Прежний ключ `tasks` в `docs/.pm.json` читается, **только когда
|
||||
своего файла нет** — для проектов, заведённых до раскола плагинов; скрипт при
|
||||
этом говорит замечанием, куда его перенести. Есть оба — побеждает свой, и об
|
||||
этом тоже говорится вслух: молча выбранный из двух конфиг это дрейф. Версию
|
||||
прежний дом не знает и знать не может — она читается только из своего файла.
|
||||
- **Секции беклога** берутся из заголовков `##` индекса как есть; их количество
|
||||
и названия — дело проекта (умолчание `Ядро` / `Инфра`). **В конфиге их нет** —
|
||||
второй список разошёлся бы с заголовками молча.
|
||||
|
||||
@@ -66,7 +66,9 @@ python3 $tk adopt apply --plan tasks-adopt-plan.json \
|
||||
всегда `tasks`. Секции беклога (`--sections`) — по умолчанию
|
||||
`Ядро,Инфра`; если у проекта деление другое по существу, оно называется
|
||||
здесь, а не подгоняется под умолчание, и становится **заголовками `##`
|
||||
индекса** — их единственным домом. В `docs/.pm.json` секции не пишутся.
|
||||
индекса** — их единственным домом. В `.tasks.json` секции не пишутся: там
|
||||
версия формата и имена частей, а второй список секций разошёлся бы с
|
||||
заголовками молча.
|
||||
2. **`adopt scan`** по всем источникам разом. Один прогон, одна карта: два
|
||||
прохода дадут два несогласованных состояния.
|
||||
3. **Заполни карту**: `slug` (английский), `section`, `goal` у каждой записи;
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
# Журнал версий формата задач
|
||||
|
||||
Одна запись на версию. Проект знает свою версию из ключа `tasks` в `<каталог
|
||||
задач>/.tasks.json`; повышение (`upgrade` в [SKILL.md](../SKILL.md), раздел
|
||||
«Версия формата») идёт по записям снизу вверх от версии проекта до текущей и
|
||||
делает то, что в них названо.
|
||||
|
||||
Правило записи: **что добавилось, что переехало, что удалено, что сделать
|
||||
проекту**. Без последнего пункта запись бесполезна — по ней и работает
|
||||
повышение.
|
||||
|
||||
Версия — целое число. Обратной совместимости у формата нет: есть «приведён» и «не
|
||||
приведён».
|
||||
|
||||
**Это журнал формата задач, а не канона документов.** Числа у них разные и
|
||||
двигаются порознь: плагин `av-dev-tasks` ставится в одиночку, и у проекта без
|
||||
`av-dev-docs` версии канона нет вовсе. Журнал канона —
|
||||
`references/changelog.md` скилла `av-dev-docs:canon`.
|
||||
|
||||
---
|
||||
|
||||
## Версия 1 — 2026-08-11
|
||||
|
||||
Первая объявленная версия формата. До неё каталог задач версии не имел вовсе:
|
||||
формат менялся, а сказать, к какому его состоянию приведён конкретный проект,
|
||||
было нечем — `tasks.py` о расхождении молчал, и отставший каталог выглядел
|
||||
здоровым ровно до первой команды, которая об него спотыкалась.
|
||||
|
||||
**Что появилось.** Ключ `tasks` в `<каталог задач>/.tasks.json` — целое число,
|
||||
версия формата. Сам файл стал **обязательным**: до сих пор он заводился только
|
||||
ради имён, отличных от умолчания, и проект с умолчаниями жил без него. Версия —
|
||||
не настройка, от которой можно отказаться, поэтому `init` и `adopt apply` теперь
|
||||
пишут файл всегда, а `check` требует числа и сверяет его со своим.
|
||||
|
||||
**Что версия значит, а что нет.** Она отвечает на один вопрос — «по какой записи
|
||||
журнала повышать каталог». Что записи применены **по существу**, из числа не
|
||||
следует: двигают его руками, и соврать им так же легко, как любой другой
|
||||
строкой. `check --fix` недостающее число не приписывает намеренно — это было бы
|
||||
объявлением каталога приведённым к формату, шагов которого никто не делал.
|
||||
|
||||
**Чего в этой записи нет.** Переезды, случившиеся до появления числа, — каталог
|
||||
из `docs/` в корень (канон 11) и отмена спринтов (канон 12) — задним числом сюда
|
||||
не переписаны. Они уже названы журналом канона, и второй перечень тех же шагов
|
||||
разошёлся бы с первым. Версия 1 — это формат на день её появления, что бы
|
||||
проекту ни пришлось пройти до неё.
|
||||
|
||||
**Что сделать проекту.**
|
||||
|
||||
1. **Догнать формат по журналу канона, если каталог отстал.** Признаки известны
|
||||
поимённо: каталог лежит в `docs/tasks/` (канон 11 велит `git mv docs/tasks
|
||||
tasks` и починку относительных ссылок внутри записей), в нём есть `SPRINT.md`
|
||||
или теги `sprint:<слаг>` (канон 12 велит снести файл, вернуть строки в беклог
|
||||
через `check --fix` и расставить порядок грумингом). Ничего из этого нет —
|
||||
каталог уже в сегодняшнем формате, и шаг пропускается.
|
||||
2. **Завести `<каталог задач>/.tasks.json`**, если его нет. Имена частей в него
|
||||
не переписываются: там только то, что отличается от умолчания.
|
||||
3. **Записать версию**: `"tasks": 1` первым ключом.
|
||||
4. `tasks.py check --dir <каталог задач>` — до отсутствия расхождений.
|
||||
|
||||
**Что при этом не трогается.** Записи в `items/`, индексы и `REJECTED.md` не
|
||||
меняются ни строкой: версия 1 объявляет то, что уже есть, а не переделывает его.
|
||||
@@ -10,7 +10,8 @@
|
||||
Раскладка. Путь каталога — `tasks/` в корне репозитория, жёстко. Каталог
|
||||
принадлежит этому плагину, а не канону документов: `docs/` ведёт другой плагин, и
|
||||
проект, поставивший учёт работ без него, каталога `docs/` не имеет вовсе. Имена
|
||||
внутри настраиваются через `tasks/.tasks.json`.
|
||||
внутри и **версия формата** живут в `tasks/.tasks.json`; журнал версий —
|
||||
references/changelog.md рядом со скриптом.
|
||||
|
||||
tasks/
|
||||
items/ задачи и цели файлами, <slug>.md
|
||||
@@ -107,8 +108,26 @@ import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
CONFIG_NAME = ".tasks.json" # дом настроек: свой файл в каталоге задач
|
||||
PM_CONFIG_REL = "../.pm.json" # прежний дом: docs/.pm.json, ключ "tasks"
|
||||
CONFIG_NAME = ".tasks.json" # дом настроек и версии: свой файл в каталоге
|
||||
PM_CONFIG_REL = "../.pm.json" # прежний дом настроек: docs/.pm.json, ключ "tasks"
|
||||
|
||||
# Версия формата задач — **своя, а не канона документов**. Число живёт ключом
|
||||
# `tasks` в `.tasks.json`, журнал версий — references/changelog.md рядом со
|
||||
# скриптом, повышает его операция `upgrade` скилла `av-dev-tasks:tasks`.
|
||||
#
|
||||
# Число именно своё, потому что плагин ставится в одиночку: проект, взявший учёт
|
||||
# работ без канона документов, каталога `docs/` не имеет вовсе, а значит не имеет
|
||||
# и версии канона — сверять было бы не с чем. Копия чужого числа в этом скрипте
|
||||
# была бы вторым домом для одной версии и разъехалась бы молча при обновлении
|
||||
# одного плагина без другого.
|
||||
#
|
||||
# Переезды каталога задач, случившиеся до появления этого числа (в корень —
|
||||
# канон 11, отмена спринтов — канон 12), задним числом сюда не переписаны: они
|
||||
# уже названы журналом канона, и второй перечень тех же шагов разошёлся бы с
|
||||
# первым. Версия 1 — формат на день её появления, что бы проекту ни пришлось
|
||||
# пройти до неё.
|
||||
FORMAT_VERSION = 1
|
||||
VERSION_KEY = "tasks"
|
||||
|
||||
EXIT_OK = 0
|
||||
EXIT_DRIFT = 1
|
||||
@@ -433,7 +452,7 @@ class Layout:
|
||||
|
||||
|
||||
def load_config(root: Path) -> dict:
|
||||
"""Настройки каталога задач.
|
||||
"""Настройки каталога задач и версия его формата.
|
||||
|
||||
Дом — `<каталог задач>/.tasks.json`: **свой файл у своего плагина**. Ключ
|
||||
`tasks` в `docs/.pm.json` читается, пока живы проекты, заведённые до раскола
|
||||
@@ -445,6 +464,10 @@ def load_config(root: Path) -> dict:
|
||||
плагину. Проект, поставивший учёт задач без канона документов, каталога
|
||||
`docs/` не имеет вовсе, и дом настроек, лежащий в чужом дереве, был бы домом,
|
||||
которого у половины проектов нет.
|
||||
|
||||
Версия формата (ключ `tasks`) читается **только из своего файла**: прежний
|
||||
дом её не знал и знать не может, и молча выведенная из его отсутствия версия
|
||||
была бы догадкой о том, что чинится одной строкой.
|
||||
"""
|
||||
path = root / CONFIG_NAME
|
||||
pm = (root / PM_CONFIG_REL).resolve()
|
||||
@@ -485,7 +508,7 @@ def _read_json(path: Path) -> dict:
|
||||
|
||||
|
||||
def _validate_config(data: dict, path: Path) -> dict:
|
||||
unknown = set(data) - set(DEFAULTS)
|
||||
unknown = set(data) - set(DEFAULTS) - {VERSION_KEY}
|
||||
# Ключ «plan» был домом оглавления целей до того, как файл стал ROADMAP.md.
|
||||
# Без этой ветки проект со старым конфигом получал бы «неизвестный ключ» и
|
||||
# искал опечатку там, где на самом деле переименование канона.
|
||||
@@ -495,9 +518,19 @@ def _validate_config(data: dict, path: Path) -> dict:
|
||||
f" av-dev-docs:canon (upgrade), а не правь ключ в одиночку:"
|
||||
f" файл и ссылки на него переезжают вместе с ним")
|
||||
if unknown:
|
||||
known = sorted({*DEFAULTS, VERSION_KEY})
|
||||
raise Env(f"{path}: неизвестные ключи: {', '.join(sorted(unknown))}"
|
||||
f" (известны: {', '.join(sorted(DEFAULTS))})")
|
||||
f" (известны: {', '.join(known)})")
|
||||
# Версия — единственный ключ-число: остальные это имена файлов и заголовков.
|
||||
# Битое число тут останавливает работу целиком (код 3), а не идёт дрейфом,
|
||||
# потому что «на какой версии формата каталог» решает, чему верить дальше.
|
||||
got = data.get(VERSION_KEY)
|
||||
if VERSION_KEY in data and (isinstance(got, bool) or not isinstance(got, int)):
|
||||
raise Env(f"{path}: ключ «{VERSION_KEY}» — версия формата задач,"
|
||||
f" ожидалось целое число, а не {got!r}")
|
||||
for key, value in data.items():
|
||||
if key == VERSION_KEY:
|
||||
continue
|
||||
if not isinstance(value, str) or not value.strip():
|
||||
raise Env(f"{path}: ключ «{key}» — ожидалась непустая строка")
|
||||
if key in PATH_KEYS and (value.startswith("/") or ".." in Path(value).parts):
|
||||
@@ -536,10 +569,46 @@ def config_problems(lay: Layout) -> list[str]:
|
||||
return out
|
||||
|
||||
|
||||
def version_problems(lay: Layout) -> list[str]:
|
||||
"""Версия формата задач: объявлена ли и та ли, которую знает скрипт.
|
||||
|
||||
Отвечает на один вопрос — «по какой записи журнала повышать каталог», — и
|
||||
ни на какой другой. Что запись оформлена по правилам своей версии, отсюда не
|
||||
следует: число двигает тот, кто прошёл шаги, и соврать им так же легко, как
|
||||
любой другой строкой. Цена вранья при этом низкая, а польза от вопроса есть
|
||||
ровно там, где формат поменялся, а каталог остался прежним.
|
||||
|
||||
`check --fix` этого не чинит намеренно: приписать недостающее число значило
|
||||
бы объявить каталог приведённым к формату, шагов которого никто не делал.
|
||||
Заводит число `init`, двигает — операция `upgrade` скилла.
|
||||
"""
|
||||
path = lay.root / CONFIG_NAME
|
||||
# Прежний дом (`docs/.pm.json`) версии не знает, поэтому спрашиваем строго
|
||||
# свой файл: «конфиг нашёлся» и «версия объявлена» это разные события.
|
||||
if not path.is_file():
|
||||
return [f"нет {path} — версия формата задач не объявлена."
|
||||
f" Заведи файл с «{VERSION_KEY}»: {FORMAT_VERSION} (журнал версий —"
|
||||
f" references/changelog.md скилла av-dev-tasks:tasks)"]
|
||||
# Что число целое, уже проверил `_validate_config` — иначе сюда не дошли бы
|
||||
# вовсе (код 3). Здесь `isinstance` значит ровно «ключ есть».
|
||||
got = lay.cfg.get(VERSION_KEY)
|
||||
if not isinstance(got, int):
|
||||
return [f"{path}: нет ключа «{VERSION_KEY}» — версия формата не объявлена,"
|
||||
f" текущая {FORMAT_VERSION}"]
|
||||
if got < FORMAT_VERSION:
|
||||
return [f"каталог приведён к формату версии {got}, текущая —"
|
||||
f" {FORMAT_VERSION}: нужно повышение по журналу"
|
||||
f" (скилл av-dev-tasks:tasks, операция upgrade)"]
|
||||
if got > FORMAT_VERSION:
|
||||
return [f"каталог приведён к формату версии {got}, а скрипт знает"
|
||||
f" {FORMAT_VERSION}: устарел плагин, обнови маркетплейс"]
|
||||
return []
|
||||
|
||||
|
||||
def looks_like_tasks(p: Path) -> bool:
|
||||
if (p / CONFIG_NAME).is_file():
|
||||
return True
|
||||
try: # индекс мог быть переименован через docs/.pm.json
|
||||
try: # индекс мог быть переименован через конфиг
|
||||
name = load_config(p).get("backlog", DEFAULTS["backlog"])
|
||||
except Env:
|
||||
name = DEFAULTS["backlog"]
|
||||
@@ -1027,7 +1096,10 @@ def check(lay: Layout, fix: bool = False) -> int:
|
||||
entries = {k: v[0] for k, v in idx.items()}
|
||||
sections = {k: v[1] for k, v in idx.items()}
|
||||
tasks = tasks_of(lay)
|
||||
errors: list[str] = []
|
||||
# Версия формата идёт первой строкой расхождений: остальные находки читаются
|
||||
# иначе, когда каталог отстал от формата, — часть из них тогда не дрейф, а
|
||||
# непройденный шаг журнала.
|
||||
errors: list[str] = version_problems(lay)
|
||||
notes: list[str] = []
|
||||
label = {k: lay.name(k) for k in lay.indexes}
|
||||
|
||||
@@ -2524,13 +2596,15 @@ def apply_fixes(lay: Layout) -> tuple[list[str], list[str]]:
|
||||
def init_files(lay: Layout, sections: list[str], roadmap_sections: list[str],
|
||||
cfg: dict) -> dict[Path, str]:
|
||||
out: dict[Path, str] = {}
|
||||
if cfg:
|
||||
# Пишем всегда в свой `.tasks.json`, даже когда рядом живёт
|
||||
# `docs/.pm.json`: дом настроек принадлежит этому плагину, а `docs/` —
|
||||
# другому, и его в проекте может не быть. load_config читает свой файл
|
||||
# первым, так что записанное сюда и прочитается отсюда.
|
||||
out[lay.root / CONFIG_NAME] = json.dumps(cfg, ensure_ascii=False,
|
||||
indent=2) + "\n"
|
||||
# Файл заводится всегда, даже когда все имена умолчательные: в нём живёт
|
||||
# версия формата, а версия — не настройка, от которой можно отказаться.
|
||||
#
|
||||
# Пишем всегда в свой `.tasks.json`, даже когда рядом живёт `docs/.pm.json`:
|
||||
# дом настроек принадлежит этому плагину, а `docs/` — другому, и его в
|
||||
# проекте может не быть. load_config читает свой файл первым, так что
|
||||
# записанное сюда и прочитается отсюда.
|
||||
out[lay.root / CONFIG_NAME] = json.dumps(cfg, ensure_ascii=False,
|
||||
indent=2) + "\n"
|
||||
out[lay.index("backlog")] = (
|
||||
"# Беклог\n\n"
|
||||
f"Что **можно взять**. Одна задача = один файл `{lay.cfg['items']}/<slug>.md`\n"
|
||||
@@ -2589,9 +2663,15 @@ def uniq_sections(raw: str) -> list[str]:
|
||||
def cmd_init(root: Path, a: argparse.Namespace) -> int:
|
||||
if not dir_within_cwd(root):
|
||||
raise Usage(f"--dir вне рабочего каталога: {root}")
|
||||
cfg = {k: v for k, v in (("items", a.items), ("backlog", a.backlog), ("roadmap", a.roadmap),
|
||||
("rejected", a.rejected)) if v}
|
||||
lay = Layout(root, cfg)
|
||||
names = {k: v for k, v in (("items", a.items), ("backlog", a.backlog),
|
||||
("roadmap", a.roadmap), ("rejected", a.rejected)) if v}
|
||||
# Версия формата — первым ключом и всегда: каталог, заведённый сегодня,
|
||||
# приведён к сегодняшнему формату, и объявить это должен тот, кто его завёл.
|
||||
# Имена частей — следом и только те, что названы явно: умолчание, записанное
|
||||
# в файл, стало бы вторым домом для того же имени. В раскладку версия не
|
||||
# идёт — `Layout` про имена, и число среди имён там ничего не значит.
|
||||
cfg = {VERSION_KEY: FORMAT_VERSION, **names}
|
||||
lay = Layout(root, names)
|
||||
if lay.index("backlog").exists():
|
||||
raise Usage(f"{lay.index('backlog')} уже есть — каталог задач заведён")
|
||||
|
||||
@@ -2615,8 +2695,9 @@ def cmd_init(root: Path, a: argparse.Namespace) -> int:
|
||||
print(f"каталог задач заведён: {root}")
|
||||
print(f" секции беклога: {', '.join(sections)};"
|
||||
f" секции роадмапа канонические: {', '.join(roadmap_sections)}")
|
||||
if cfg:
|
||||
print(f" имена частей записаны в {root / CONFIG_NAME}")
|
||||
what = ("версия формата и имена частей записаны" if names
|
||||
else "версия формата записана")
|
||||
print(f" {what} в {root / CONFIG_NAME}: формат {FORMAT_VERSION}")
|
||||
return EXIT_OK
|
||||
|
||||
|
||||
@@ -2974,7 +3055,11 @@ def cmd_adopt_apply(a: argparse.Namespace) -> int:
|
||||
|
||||
# --- план записи ---
|
||||
wr = Plan()
|
||||
for path, text in init_files(lay, sections, roadmap_sections, {}).items():
|
||||
# Версия та же, что у `init`: каталог выводится из чужой раскладки сегодня и
|
||||
# сегодняшним форматом, сколько бы лет ни было тому, из чего он выведен.
|
||||
# Имён частей здесь нет — адаптация раскладывает всё по умолчаниям.
|
||||
for path, text in init_files(lay, sections, roadmap_sections,
|
||||
{VERSION_KEY: FORMAT_VERSION}).items():
|
||||
wr.file(path, text)
|
||||
backlog_lines = init_files(lay, sections, roadmap_sections, {})[lay.index("backlog")].splitlines()
|
||||
roadmap_lines = init_files(lay, sections, roadmap_sections, {})[lay.index("roadmap")].splitlines()
|
||||
|
||||
Reference in New Issue
Block a user