секции роадмапа названы и закреплены линтером
готово | запланировано | направления | разработка, англ. done | planned | directions | tooling. Из четырёх предложенных имён отвергнуто одно, и по проверяемой причине: «окружение» уже занято — в architecture.md это боевое окружение приложения, «где работает, что рядом, кто перезапускает», и одно слово в двух смыслах развело бы документы канона. Секции роадмапа стали каноническими, в отличие от секций беклога, и разница выведена, а не назначена: у каждой секции роадмапа своя семантика, в первую пишет сам close, и роадмап, названный по-своему, читался бы только своим автором. Секции беклога — полки, смысла не несут, остаются делом проекта. check проверяет три вещи: состав закреплён (чужая секция — ошибка), все четыре обязаны быть, язык один на весь индекс. Проверено на том случае, ради которого правило и заводилось: «Что уже пройдено», которую healthlog вёл руками, теперь называется ошибкой поимённо. Оба языка прогнаны вживую, включая close в английский роадмап. Ключа tasks.achieved_section не появилось — секция достигнутого опознаётся по каноническому имени в любом из языков; --roadmap-sections у init упразднён, выбирать больше нечего. Названные вслух компромиссы: «готово» слегка тянет в трекерную рамку «состояние работы», тогда как секция про возможность — перевесила читаемость; цель в «запланировано» может быть уже наполовину построена, это очередь, а не «не начато», «в работе» живёт в SPRINT.md. DECISIONS 19: ГГГ переписан, добавлен ДДД, следствие 78 заменено. TODO 7 закрыт. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
+27
-7
@@ -1407,16 +1407,36 @@ SSS: рубрика на узел без нового понятия порож
|
|||||||
словаре, тесте готовности, автомате переходов, `split.md` и трёх местах
|
словаре, тесте готовности, автомате переходов, `split.md` и трёх местах
|
||||||
`tasks.py`.
|
`tasks.py`.
|
||||||
|
|
||||||
**ГГГ. Имена секций роадмапа временные.** `умеет` / `строим` / `направления` /
|
**ГГГ. Имена секций роадмапа — `готово` / `запланировано` / `направления` /
|
||||||
`станок` приняты как рабочие и признаны неудачными на месте — особенно `станок`
|
`разработка`.** Первый набор (`умеет` / `строим` / `станок`) прожил один заход и
|
||||||
(слово пришло из `CLAUDE.md`, где «общий станок» уже значит инструмент,
|
был признан неудачным. Из четырёх предложенных имён отвергнуто одно, и по
|
||||||
врывающийся в спринт). Вопрос запаркован до отдельного захода.
|
проверяемой причине: **`окружение` уже занято** — в `architecture.md` это боевое
|
||||||
|
окружение приложения, «где работает, что рядом, кто перезапускает», и одно слово
|
||||||
|
в двух смыслах развело бы документы канона. Взято `разработка`.
|
||||||
|
|
||||||
|
Принятый компромисс назван вслух: `готово` слегка тянет обратно в трекерную рамку
|
||||||
|
«состояние работы», тогда как секция про **возможность**. Перевесила читаемость с
|
||||||
|
первого взгляда, а смысл несут заголовки целей внутри секции. Так же принято, что
|
||||||
|
цель в `запланировано` может быть уже наполовину построена: это очередь, а не
|
||||||
|
«не начато», а «в работе» живёт в `SPRINT.md`.
|
||||||
|
|
||||||
|
**ДДД. Секции роадмапа канонические, секции беклога — нет.** Разница выведена, а
|
||||||
|
не назначена: у секций роадмапа есть **семантика** (достигнутое, очередь, долгое,
|
||||||
|
не про продукт), в первую пишет сам `close`, и роадмап, названный по-своему,
|
||||||
|
читался бы только своим автором. Секции беклога (`ядро`, `инфра`) семантики не
|
||||||
|
несут — это полки. Поэтому `check` проверяет у роадмапа три вещи: состав закреплён
|
||||||
|
(чужая секция — ошибка), все четыре обязаны быть, язык один на весь индекс;
|
||||||
|
`--roadmap-sections` у `init` упразднён. Английский набор — `done` | `planned` |
|
||||||
|
`directions` | `tooling`.
|
||||||
|
|
||||||
|
Проверено на том самом случае, ради которого правило и заводилось: секция «Что
|
||||||
|
уже пройдено», которую healthlog вёл руками, теперь называется ошибкой поимённо.
|
||||||
|
|
||||||
### Что из этого следует
|
### Что из этого следует
|
||||||
|
|
||||||
78. **Секция достигнутого — единственная, чьё имя знает скрипт.** Остальные
|
78. **Ключа `tasks.achieved_section` не появилось.** Секция достигнутого
|
||||||
берутся из заголовков индекса как есть; в эту `close` пишет сам, поэтому её
|
опознаётся по каноническому имени в любом из двух языков, и лишний knob не
|
||||||
имя живёт в `docs/.pm.json`, ключ `tasks.achieved_section`.
|
заводится: канонический состав отвечает на тот же вопрос надёжнее конфига.
|
||||||
79. **`reopen` цели снимает строку достигнутого.** Иначе роадмап продолжает
|
79. **`reopen` цели снимает строку достигнутого.** Иначе роадмап продолжает
|
||||||
утверждать, что приложение умеет то, что вернулось в работу.
|
утверждать, что приложение умеет то, что вернулось в работу.
|
||||||
80. **Прозаический раздел в индексе — дрейф.** Любой `##` проверка считает
|
80. **Прозаический раздел в индексе — дрейф.** Любой `##` проверка считает
|
||||||
|
|||||||
@@ -167,15 +167,11 @@ jellybit 43. Шаги повышения — [changelog.md](av-dev-pm/skills/can
|
|||||||
- [ ] род работы и раздел «Затрагивает» — **не задним числом**: сперва то, что
|
- [ ] род работы и раздел «Затрагивает» — **не задним числом**: сперва то, что
|
||||||
идёт в ближайший набор (`sprint take` без них откажет), остальное по ходу
|
идёт в ближайший набор (`sprint take` без них откажет), остальное по ходу
|
||||||
переоценки (PPP)
|
переоценки (PPP)
|
||||||
- [ ] секции роадмапа: `порядок` → `строим`, `темы` → `направления`, завести
|
- [ ] секции роадмапа: `порядок` → `запланировано`, `темы` → `направления`,
|
||||||
`умеет` и `станок`; прозаические разделы healthlog («Что уже пройдено»,
|
завести `готово` и `разработка`; прозаические разделы healthlog («Что уже
|
||||||
«Почему в таком порядке») разложить — звенья строками в `умеет`,
|
пройдено», «Почему в таком порядке») разложить — звенья строками в
|
||||||
обоснование очереди прозой внутри `строим` (тема 19, 80)
|
`готово`, обоснование очереди прозой внутри `запланировано` (тема 19, 80).
|
||||||
|
`check` теперь называет чужую секцию ошибкой, так что шаг обязателен
|
||||||
- [ ] переформулировать цели ответом на «что приложение будет уметь»; цели не
|
- [ ] переформулировать цели ответом на «что приложение будет уметь»; цели не
|
||||||
про приложение («Процесс и качество разработки» в jellybit) — в `станок`
|
про приложение («Процесс и качество разработки» в jellybit) — в
|
||||||
|
`разработка`
|
||||||
## 7. Имена секций роадмапа — выбрать хорошие (тема 19, ГГГ)
|
|
||||||
|
|
||||||
- [ ] `умеет` / `строим` / `направления` / `станок` приняты как временные.
|
|
||||||
Особенно плох `станок`; заодно проверить, не плохо ли то же слово в
|
|
||||||
`CLAUDE.md`, откуда оно взято («общий станок, врывающийся в спринт»)
|
|
||||||
|
|||||||
@@ -31,11 +31,13 @@ upgrade` идёт по записям снизу вверх от версии п
|
|||||||
2. **Раздел «Затрагивает»** в теле задачи — перечень границ, которых изменение
|
2. **Раздел «Затрагивает»** в теле задачи — перечень границ, которых изменение
|
||||||
касается (эндпоинт, таблица и миграция, формат на диске, публичный тип). Как
|
касается (эндпоинт, таблица и миграция, формат на диске, публичный тип). Как
|
||||||
и критерии приёмки, требуется к взятию в спринт, а не к заведению.
|
и критерии приёмки, требуется к взятию в спринт, а не к заведению.
|
||||||
3. **Секции роадмапа** — четыре вместо двух: `умеет` (достигнутые цели строкой
|
3. **Секции роадмапа** — четыре вместо двух и **канонические**, в отличие от
|
||||||
с датой, без ссылки на файл), `строим` (очередь значима), `направления`
|
секций беклога: `готово` (достигнутые цели строкой с датой, без ссылки на
|
||||||
(очереди нет), `станок` (инструмент и процесс, не возможности приложения).
|
файл), `запланировано` (очередь значима), `направления` (очереди нет),
|
||||||
Имя секции достигнутого скрипт знает по конфигу — `tasks.achieved_section`.
|
`разработка` (инструмент и процесс, не возможности приложения). Английский
|
||||||
Имена **временные** и будут пересмотрены (DECISIONS, тема 19).
|
вариант — `done` | `planned` | `directions` | `tooling`, один язык на весь
|
||||||
|
индекс. Переименованию проектом не подлежат: у каждой свой смысл, и в первую
|
||||||
|
пишет сам `close`; `tasks.py check` проверяет состав.
|
||||||
4. **Умолчание профиля ревью сменилось** — это не раскладка, но проектный текст
|
4. **Умолчание профиля ревью сменилось** — это не раскладка, но проектный текст
|
||||||
под него уже написан. `standard` стал рабочим умолчанием: миграция схемы,
|
под него уже написан. `standard` стал рабочим умолчанием: миграция схемы,
|
||||||
публичный контракт и инвариант ступень больше **не** поднимают, `wide`
|
публичный контракт и инвариант ступень больше **не** поднимают, `wide`
|
||||||
@@ -43,7 +45,7 @@ upgrade` идёт по записям снизу вверх от версии п
|
|||||||
в `docs/review.md` остаётся на месте, но его содержимое надо перечитать.
|
в `docs/review.md` остаётся на месте, но его содержимое надо перечитать.
|
||||||
|
|
||||||
**Что переехало:** `docs/tasks/PLAN.md` → `docs/tasks/ROADMAP.md`; достигнутая
|
**Что переехало:** `docs/tasks/PLAN.md` → `docs/tasks/ROADMAP.md`; достигнутая
|
||||||
цель — из небытия в секцию `умеет`: `close <цель> --implemented` удаляет файл, но
|
цель — из небытия в секцию `готово`: `close <цель> --implemented` удаляет файл, но
|
||||||
**оставляет строку с датой**. Прежде роадмап отвечал только «что осталось», и
|
**оставляет строку с датой**. Прежде роадмап отвечал только «что осталось», и
|
||||||
половину его вопроса вели прозой руками. Вместе с
|
половину его вопроса вели прозой руками. Вместе с
|
||||||
файлом переименован ключ конфига `tasks.plan` → `tasks.roadmap` и токены
|
файлом переименован ключ конфига `tasks.plan` → `tasks.roadmap` и токены
|
||||||
@@ -74,16 +76,16 @@ upgrade` идёт по записям снизу вверх от версии п
|
|||||||
что для этого проекта считается **новым понятием** и **правилом
|
что для этого проекта считается **новым понятием** и **правилом
|
||||||
идентичности**, — и убрать остальное, иначе проект возвращает себе прежнюю
|
идентичности**, — и убрать остальное, иначе проект возвращает себе прежнюю
|
||||||
частоту полного набора уточнением.
|
частоту полного набора уточнением.
|
||||||
7. Переименовать секции роадмапа: `порядок` → `строим`, `темы` →
|
7. Переименовать секции роадмапа: `порядок` → `запланировано`, `темы` →
|
||||||
`направления`; завести `умеет` **первой** и `станок` последней. Прозаические
|
`направления`; завести `готово` **первой** и `разработка` последней.
|
||||||
разделы вроде «Что уже пройдено», которые велись руками, разложить: звенья —
|
Прозаические разделы вроде «Что уже пройдено», которые велись руками,
|
||||||
строками в `умеет` (дата, слаг, что стало возможно), обоснование очереди
|
разложить: звенья — строками в `готово` (дата, слаг, что стало возможно),
|
||||||
оставить прозой в `строим`. Любой `##` в индексе проверка считает секцией,
|
обоснование очереди оставить прозой в `запланировано`. Любой `##` в индексе
|
||||||
поэтому прозаический заголовок здесь — дрейф.
|
проверка считает секцией, и теперь `check` называет чужую секцию ошибкой.
|
||||||
8. Переформулировать цели ответом на **«что приложение будет уметь»**: не
|
8. Переформулировать цели ответом на **«что приложение будет уметь»**: не
|
||||||
«Работа со слиянием», а «Исход слияния не зависит от порядка доставки».
|
«Работа со слиянием», а «Исход слияния не зависит от порядка доставки».
|
||||||
Свойство поведения — законная цель. Цель, которая не про приложение
|
Свойство поведения — законная цель. Цель, которая не про приложение
|
||||||
(процесс, инструмент), переезжает в `станок`.
|
(процесс, инструмент), переезжает в `разработка`.
|
||||||
9. `[epic]`, если он в проекте заводился: это либо цель, либо набор задач под
|
9. `[epic]`, если он в проекте заводился: это либо цель, либо набор задач под
|
||||||
общей целью. `check` назовёт его неизвестным типом.
|
общей целью. `check` назовёт его неизвестным типом.
|
||||||
10. `docs/.pm.json`: `"canon": 3`.
|
10. `docs/.pm.json`: `"canon": 3`.
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ description: "Завести новый проект — сессия вопро
|
|||||||
5. **Чем краснеет гейт.** Какие проверки обязательны; что красит безусловно;
|
5. **Чем краснеет гейт.** Какие проверки обязательны; что красит безусловно;
|
||||||
чего в гейте намеренно не будет и кто тогда это гоняет.
|
чего в гейте намеренно не будет и кто тогда это гоняет.
|
||||||
6. **Первые цели.** Возможности приложения, а не задачи: три-пять целей в
|
6. **Первые цели.** Возможности приложения, а не задачи: три-пять целей в
|
||||||
`строим`, каждая — ответ на «что приложение будет уметь», с
|
`запланировано`, каждая — ответ на «что приложение будет уметь», с
|
||||||
обоснованием очереди прозой.
|
обоснованием очереди прозой.
|
||||||
|
|
||||||
### Как вести
|
### Как вести
|
||||||
|
|||||||
@@ -175,8 +175,9 @@
|
|||||||
|
|
||||||
## Шаг 4. Выбор цели и набор спринта
|
## Шаг 4. Выбор цели и набор спринта
|
||||||
|
|
||||||
1. **Покажи состояние проекта**: секцию `умеет` (что уже сделано — это половина
|
1. **Покажи состояние проекта**: секцию `готово` (что приложение уже умеет —
|
||||||
ответа на «где мы»), затем `строим` с обоснованием очереди, `направления`, и
|
это половина ответа на «где мы»), затем `запланировано` с обоснованием
|
||||||
|
очереди, `направления`, и
|
||||||
по каждой цели-кандидату — сколько под ней задач без открытых вопросов
|
по каждой цели-кандидату — сколько под ней задач без открытых вопросов
|
||||||
(`list --goal <слаг>`). Цель без готовых задач набором не станет: её сперва
|
(`list --goal <слаг>`). Цель без готовых задач набором не станет: её сперва
|
||||||
надо декомпозировать.
|
надо декомпозировать.
|
||||||
|
|||||||
@@ -69,17 +69,26 @@ docs/tasks/
|
|||||||
|
|
||||||
**Четыре секции роадмапа, и первая отвечает на половину вопроса:**
|
**Четыре секции роадмапа, и первая отвечает на половину вопроса:**
|
||||||
|
|
||||||
| Секция | Что в ней |
|
| Секция | Англ. | Что в ней |
|
||||||
| --- | --- |
|
| --- | --- | --- |
|
||||||
| `умеет` | достигнутые цели — строкой с датой, **без ссылки на файл**: файл удалён, поведение живёт в спеках |
|
| `готово` | `done` | достигнутые цели — строкой с датой, **без ссылки на файл**: файл удалён, поведение живёт в спеках |
|
||||||
| `строим` | очередь значима и обосновывается прозой рядом |
|
| `запланировано` | `planned` | очередь значима и обосновывается прозой рядом |
|
||||||
| `направления` | очереди нет, тянутся долго |
|
| `направления` | `directions` | очереди нет, тянутся долго |
|
||||||
| `станок` | инструмент и процесс разработки — не возможности приложения, и потому отдельно |
|
| `разработка` | `tooling` | инструмент и процесс — не возможности приложения, и потому отдельно |
|
||||||
|
|
||||||
Имена секций сейчас **временные**: они читаются хуже, чем должны, и будут
|
**Секции роадмапа канонические, секции беклога — нет**, и разница не в любви к
|
||||||
пересмотрены (`DECISIONS.md`, тема 19). Проект вправе назвать свои иначе —
|
единообразию. У каждой секции роадмапа свой смысл, в первую пишет сам `close`, и
|
||||||
секции берутся из заголовков индекса, — но дом достигнутого скрипт обязан знать
|
роадмап, названный по-своему, читался бы только своим автором. Секции беклога
|
||||||
по имени, и оно живёт в `docs/.pm.json`, ключ `tasks.achieved_section`.
|
(`ядро`, `инфра`) смысла не несут — это полки, и остаются делом проекта.
|
||||||
|
|
||||||
|
Отсюда три правила, которые проверяет `tasks.py check`: **состав закреплён**
|
||||||
|
(чужая секция — ошибка, а не вольность), **все четыре обязаны быть** (нет
|
||||||
|
секции — нет ответа на её часть вопроса), **язык один на весь индекс**.
|
||||||
|
`--roadmap-sections` у `init` нет: выбирать нечего.
|
||||||
|
|
||||||
|
Оговорка про `разработка`: слово `окружение` сюда не годится — в
|
||||||
|
`architecture.md` оно уже значит боевое окружение приложения, и одно слово в двух
|
||||||
|
смыслах развело бы документы канона.
|
||||||
|
|
||||||
**Секции «блокеры» в беклоге нет.** Блокер — это *состояние* (спринт не может
|
**Секции «блокеры» в беклоге нет.** Блокер — это *состояние* (спринт не может
|
||||||
продолжаться ни одной задачей), а не полка: он живёт ровно до ответа человека, и
|
продолжаться ни одной задачей), а не полка: он живёт ровно до ответа человека, и
|
||||||
@@ -101,7 +110,7 @@ docs/tasks/
|
|||||||
всех наборов без отдельного журнала.
|
всех наборов без отдельного журнала.
|
||||||
|
|
||||||
**У достигнутой цели запись остаётся, и это единственное исключение.** Файл
|
**У достигнутой цели запись остаётся, и это единственное исключение.** Файл
|
||||||
удаляется так же, а строка переезжает в секцию `умеет` с датой. Причина в том,
|
удаляется так же, а строка переезжает в секцию `готово` с датой. Причина в том,
|
||||||
что цель — не работа, а **возможность**: «что приложение умеет» это половина
|
что цель — не работа, а **возможность**: «что приложение умеет» это половина
|
||||||
вопроса, ради которого роадмап и открывают, и стирать её вместе с файлом значит
|
вопроса, ради которого роадмап и открывают, и стирать её вместе с файлом значит
|
||||||
оставить инструмент, отвечающий только «что осталось». Вторым домом это не
|
оставить инструмент, отвечающий только «что осталось». Вторым домом это не
|
||||||
@@ -163,9 +172,9 @@ stateDiagram-v2
|
|||||||
отведена отдельная секция роадмапа, чтобы они были видны в том же экране и при
|
отведена отдельная секция роадмапа, чтобы они были видны в том же экране и при
|
||||||
этом не читались как возможности продукта.
|
этом не читались как возможности продукта.
|
||||||
|
|
||||||
Секция выбирается так: очередь значима и обоснована прозой — `строим`; тянется
|
Секция выбирается так: очередь значима и обоснована прозой — `запланировано`;
|
||||||
долго и очереди не имеет — `направления`; не про приложение — `станок`;
|
тянется долго и очереди не имеет — `направления`; не про приложение —
|
||||||
достигнутое кладёт туда сам `close`.
|
`разработка`; в `готово` кладёт сам `close`.
|
||||||
|
|
||||||
- **Список задач цели выводится, а не хранится.** В теле цели — зачем она и что
|
- **Список задач цели выводится, а не хранится.** В теле цели — зачем она и что
|
||||||
считается её завершением; перечня задач там нет. Он был бы третьим индексом и
|
считается её завершением; перечня задач там нет. Он был бы третьим индексом и
|
||||||
@@ -175,7 +184,7 @@ stateDiagram-v2
|
|||||||
- **Статус цели выводится.** Цель достигнута, когда у неё не осталось открытых
|
- **Статус цели выводится.** Цель достигнута, когда у неё не осталось открытых
|
||||||
задач; `[x]`/`[~]` руками не ведутся, а `close` цели с живыми задачами
|
задач; `[x]`/`[~]` руками не ведутся, а `close` цели с живыми задачами
|
||||||
скрипт запретит. Достижение — `close <цель> --implemented`: файл удаляется,
|
скрипт запретит. Достижение — `close <цель> --implemented`: файл удаляется,
|
||||||
строка с датой переезжает в `умеет`. Ошиблись — `reopen` вернёт файл и
|
строка с датой переезжает в `готово`. Ошиблись — `reopen` вернёт файл и
|
||||||
**снимет строку достигнутого**, чтобы роадмап не утверждал того, чего нет. Единственная оговорка: цель без задач неотличима — «ещё не
|
**снимет строку достигнутого**, чтобы роадмап не утверждал того, чего нет. Единственная оговорка: цель без задач неотличима — «ещё не
|
||||||
разобрана» или «всё закрыто». Различает **тег `decomposed`** в мете
|
разобрана» или «всё закрыто». Различает **тег `decomposed`** в мете
|
||||||
цели: он ставится, когда цель разложена на задачи. Тег, а не строка в теле —
|
цели: он ставится, когда цель разложена на задачи. Тег, а не строка в теле —
|
||||||
|
|||||||
@@ -187,7 +187,7 @@
|
|||||||
которой задачи есть, а цель с тегом и без задач — прямое приглашение закрыть.
|
которой задачи есть, а цель с тегом и без задач — прямое приглашение закрыть.
|
||||||
- Цель живёт в `ROADMAP.md` и **никогда** — в `BACKLOG.md` или `SPRINT.md`.
|
- Цель живёт в `ROADMAP.md` и **никогда** — в `BACKLOG.md` или `SPRINT.md`.
|
||||||
- **Достигнутая цель не исчезает.** `close <слаг> --implemented` удаляет файл и
|
- **Достигнутая цель не исчезает.** `close <слаг> --implemented` удаляет файл и
|
||||||
переносит строку в секцию `умеет` с датой:
|
переносит строку в секцию `готово` с датой:
|
||||||
`- 2026-08-04 \`merge-order\` — Исход слияния не зависит от порядка доставки. …`
|
`- 2026-08-04 \`merge-order\` — Исход слияния не зависит от порядка доставки. …`
|
||||||
Ссылки на файл в ней нет — файл удалён, а битая ссылка это ошибка `check`.
|
Ссылки на файл в ней нет — файл удалён, а битая ссылка это ошибка `check`.
|
||||||
Поведение живёт в спеках проекта; роадмап отвечает, **когда и в каком порядке**
|
Поведение живёт в спеках проекта; роадмап отвечает, **когда и в каком порядке**
|
||||||
@@ -219,7 +219,7 @@
|
|||||||
|
|
||||||
| Файл | Что отвечает | Секции |
|
| Файл | Что отвечает | Секции |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| `ROADMAP.md` | что приложение уже умеет и чего ещё не умеет | `умеет` (достигнутое), `строим` (очередь значима), `направления` (очереди нет), `станок` (не про приложение) |
|
| `ROADMAP.md` | что приложение уже умеет и чего ещё не умеет | канонические: `готово`, `запланировано`, `направления`, `разработка` (англ. `done`, `planned`, `directions`, `tooling`) |
|
||||||
| `BACKLOG.md` | что **можно взять** — только задачи | секции проекта (по умолчанию ядро/инфра) |
|
| `BACKLOG.md` | что **можно взять** — только задачи | секции проекта (по умолчанию ядро/инфра) |
|
||||||
| `SPRINT.md` | какая цель и какой набор под неё | одна: «Набор» |
|
| `SPRINT.md` | какая цель и какой набор под неё | одна: «Набор» |
|
||||||
| `REJECTED.md` | что ушло без реализации и почему | — |
|
| `REJECTED.md` | что ушло без реализации и почему | — |
|
||||||
@@ -239,11 +239,13 @@
|
|||||||
Постоянно пустая секция со старой семантикой «разбираются пачками» противоречила
|
Постоянно пустая секция со старой семантикой «разбираются пачками» противоречила
|
||||||
бы правилу «эскалируем немедленно», поэтому `init` её не заводит, а `check`
|
бы правилу «эскалируем немедленно», поэтому `init` её не заводит, а `check`
|
||||||
говорит о ней в чужом беклоге. Переезжаешь с такой секцией — удали её. В секции
|
говорит о ней в чужом беклоге. Переезжаешь с такой секцией — удали её. В секции
|
||||||
**`строим`** очередь значима и обосновывается прозой; двигают строку
|
**`запланировано`** очередь значима и обосновывается прозой; двигают строку
|
||||||
`move <slug> --section строим --after <другой>`. В секции **`умеет`** строки
|
`move <slug> --section запланировано --after <другой>`. В секции **`готово`**
|
||||||
не той формы, что у прочих индексов: дата, слаг, заголовок — как в
|
строки не той формы, что у прочих индексов: дата, слаг, заголовок — как в
|
||||||
`REJECTED.md`, и по той же причине (файла уже нет, ссылаться некуда). Имена
|
`REJECTED.md`, и по той же причине (файла уже нет, ссылаться некуда).
|
||||||
секций временные, см. SKILL.md.
|
|
||||||
|
**Секции роадмапа закреплены** — состав, полнота и единство языка проверяются
|
||||||
|
`check`; секции беклога проект называет сам. Почему так — SKILL.md.
|
||||||
|
|
||||||
Индексы **производны**: расходятся с файлом — правим индексы (`check --fix`).
|
Индексы **производны**: расходятся с файлом — правим индексы (`check --fix`).
|
||||||
Строку руками не пишут.
|
Строку руками не пишут.
|
||||||
|
|||||||
@@ -12,7 +12,10 @@ av-dev, и подгоняется под него проект. Имена вн
|
|||||||
|
|
||||||
docs/tasks/
|
docs/tasks/
|
||||||
items/ задачи и цели файлами, <slug>.md
|
items/ задачи и цели файлами, <slug>.md
|
||||||
ROADMAP.md состояние проекта: что уже умеет и чего ещё не умеет
|
ROADMAP.md состояние проекта: что уже умеет и чего ещё не умеет.
|
||||||
|
Секции канонические: готово | запланировано |
|
||||||
|
направления | разработка (или done | planned |
|
||||||
|
directions | tooling — один язык на весь индекс)
|
||||||
BACKLOG.md что можно взять — только задачи, целей здесь нет
|
BACKLOG.md что можно взять — только задачи, целей здесь нет
|
||||||
SPRINT.md текущий спринт: цель, набор, дата, слаг
|
SPRINT.md текущий спринт: цель, набор, дата, слаг
|
||||||
REJECTED.md ушедшее БЕЗ реализации, с причиной и датой
|
REJECTED.md ушедшее БЕЗ реализации, с причиной и датой
|
||||||
@@ -46,7 +49,7 @@ av-dev, и подгоняется под него проект. Имена вн
|
|||||||
разъедется на синонимах, и отбор по роду перестанет отвечать.
|
разъедется на синонимах, и отбор по роду перестанет отвечать.
|
||||||
|
|
||||||
Использование:
|
Использование:
|
||||||
tasks.py init [--dir DIR] [--sections …] [--roadmap-sections …] [--items …]
|
tasks.py init [--dir DIR] [--sections …] [--items …] [--backlog …] …
|
||||||
tasks.py check [--dir DIR] [--fix]
|
tasks.py check [--dir DIR] [--fix]
|
||||||
tasks.py list [--dir DIR] [--stale] [--section S] [--type T] [--tag a,b]
|
tasks.py list [--dir DIR] [--stale] [--section S] [--type T] [--tag a,b]
|
||||||
[--goal S] [--kind K] [--index backlog|sprint|roadmap|all]
|
[--goal S] [--kind K] [--index backlog|sprint|roadmap|all]
|
||||||
@@ -114,9 +117,6 @@ DEFAULTS = {
|
|||||||
"sprint": "SPRINT.md",
|
"sprint": "SPRINT.md",
|
||||||
"rejected": "REJECTED.md",
|
"rejected": "REJECTED.md",
|
||||||
"sprint_section": "Набор",
|
"sprint_section": "Набор",
|
||||||
# Секция роадмапа, куда переезжает достигнутая цель. Скрипт обязан знать её
|
|
||||||
# имя: остальные секции он берёт из заголовков как есть, а в эту пишет сам.
|
|
||||||
"achieved_section": "умеет",
|
|
||||||
"criteria_heading": "Критерии приёмки",
|
"criteria_heading": "Критерии приёмки",
|
||||||
"surface_heading": "Затрагивает",
|
"surface_heading": "Затрагивает",
|
||||||
"questions_heading": "Вопросы",
|
"questions_heading": "Вопросы",
|
||||||
@@ -128,7 +128,23 @@ DEFAULTS = {
|
|||||||
PATH_KEYS = ("items", "backlog", "roadmap", "sprint", "rejected")
|
PATH_KEYS = ("items", "backlog", "roadmap", "sprint", "rejected")
|
||||||
|
|
||||||
DEFAULT_SECTIONS = "ядро,инфра"
|
DEFAULT_SECTIONS = "ядро,инфра"
|
||||||
DEFAULT_ROADMAP_SECTIONS = "умеет,строим,направления,станок"
|
|
||||||
|
# Секции роадмапа **канонические**, в отличие от секций беклога. Причина не в
|
||||||
|
# любви к единообразию: у каждой своя семантика — достигнутое, очередь, долгие
|
||||||
|
# направления, работа над инструментом, — в первую пишет сам `close`, и роадмап,
|
||||||
|
# названный по-своему, читался бы только своим автором. Секции беклога семантики
|
||||||
|
# не несут, это полки, и остаются делом проекта.
|
||||||
|
#
|
||||||
|
# Пара на секцию: русское имя и английское. Проект держит **один язык на весь
|
||||||
|
# индекс** — вперемешку это дрейф, который check называет вслух.
|
||||||
|
ROADMAP_SECTIONS = (
|
||||||
|
("готово", "done"), # достигнутое: что приложение уже умеет
|
||||||
|
("запланировано", "planned"), # очередь значима, обоснована прозой
|
||||||
|
("направления", "directions"), # очереди нет, тянутся долго
|
||||||
|
("разработка", "tooling"), # инструмент и процесс, не про приложение
|
||||||
|
)
|
||||||
|
ACHIEVED, PLANNED = 0, 1 # индексы в ROADMAP_SECTIONS
|
||||||
|
DEFAULT_ROADMAP_SECTIONS = ",".join(ru for ru, _ in ROADMAP_SECTIONS)
|
||||||
|
|
||||||
# Мета — список под заголовком, поле на строку. Старая форма (все поля одной
|
# Мета — список под заголовком, поле на строку. Старая форма (все поля одной
|
||||||
# строкой через `·`) читается по-прежнему: у проектов на диске лежат файлы в
|
# строкой через `·`) читается по-прежнему: у проектов на диске лежат файлы в
|
||||||
@@ -934,7 +950,10 @@ def check(lay: Layout, fix: bool = False) -> int:
|
|||||||
if name not in tasks:
|
if name not in tasks:
|
||||||
errors.append(f"{label[kind]}:{entry['line']}: ссылка на несуществующий"
|
errors.append(f"{label[kind]}:{entry['line']}: ссылка на несуществующий"
|
||||||
f" {lay.cfg['items']}/{name}")
|
f" {lay.cfg['items']}/{name}")
|
||||||
errors += index_lint(read_lines(lay.index(kind)), label[kind])
|
lines = read_lines(lay.index(kind))
|
||||||
|
errors += index_lint(lines, label[kind])
|
||||||
|
if kind == "roadmap":
|
||||||
|
errors += roadmap_lint(lines, label[kind])
|
||||||
|
|
||||||
if goal_of_sprint and goal_of_sprint + ".md" not in tasks:
|
if goal_of_sprint and goal_of_sprint + ".md" not in tasks:
|
||||||
errors.append(f"{label['sprint']}: цель «{goal_of_sprint}» не найдена"
|
errors.append(f"{label['sprint']}: цель «{goal_of_sprint}» не найдена"
|
||||||
@@ -1104,6 +1123,52 @@ def section_headers(lines: list[str]) -> list[tuple[int, str]]:
|
|||||||
return [(i, m.group(1)) for i, line in enumerate(lines) if (m := SECTION.match(line))]
|
return [(i, m.group(1)) for i, line in enumerate(lines) if (m := SECTION.match(line))]
|
||||||
|
|
||||||
|
|
||||||
|
def canon_section(lines: list[str], which: int) -> str | None:
|
||||||
|
"""Имя канонической секции роадмапа **как она названа в этом индексе**.
|
||||||
|
|
||||||
|
Проект пишет либо русские имена, либо английские; скрипт принимает оба и
|
||||||
|
возвращает то, что нашёл, — иначе `insert_entry` промахнётся мимо секции,
|
||||||
|
которая есть, но названа на другом языке.
|
||||||
|
"""
|
||||||
|
names = {n.lower() for n in ROADMAP_SECTIONS[which]}
|
||||||
|
for _, section in section_headers(lines):
|
||||||
|
if section.lower() in names:
|
||||||
|
return section
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def roadmap_lint(lines: list[str], label: str) -> list[str]:
|
||||||
|
"""Секции роадмапа: все канонические, все на месте, все на одном языке."""
|
||||||
|
known = {n.lower(): i for i, pair in enumerate(ROADMAP_SECTIONS) for n in pair}
|
||||||
|
errors: list[str] = []
|
||||||
|
seen: dict[int, str] = {}
|
||||||
|
langs: set[int] = set()
|
||||||
|
for _, section in section_headers(lines):
|
||||||
|
i = known.get(section.lower())
|
||||||
|
if i is None:
|
||||||
|
ru = ", ".join(pair[0] for pair in ROADMAP_SECTIONS)
|
||||||
|
errors.append(f"{label}: секция «{section}» не из канона роадмапа"
|
||||||
|
f" ({ru}). Секции роадмапа несут смысл и потому"
|
||||||
|
f" закреплены; прозаический заголовок здесь — секция,"
|
||||||
|
f" в которую может уехать цель")
|
||||||
|
continue
|
||||||
|
langs.add(0 if section.lower() == ROADMAP_SECTIONS[i][0] else 1)
|
||||||
|
if i in seen:
|
||||||
|
errors.append(f"{label}: секция «{section}» повторяет «{seen[i]}» —"
|
||||||
|
f" это одна и та же секция на двух языках")
|
||||||
|
else:
|
||||||
|
seen[i] = section
|
||||||
|
missing = [ROADMAP_SECTIONS[i][0] for i in range(len(ROADMAP_SECTIONS))
|
||||||
|
if i not in seen]
|
||||||
|
if missing:
|
||||||
|
errors.append(f"{label}: нет секций: {', '.join(missing)}."
|
||||||
|
f" Роадмап отвечает на «что умеет и чего не умеет»"
|
||||||
|
f" целиком — отсутствующая секция это отсутствующий ответ")
|
||||||
|
if len(langs) > 1:
|
||||||
|
errors.append(f"{label}: секции вперемешку на двух языках — выбери один")
|
||||||
|
return errors
|
||||||
|
|
||||||
|
|
||||||
def find_section(lines: list[str], name: str) -> tuple[int | None, str]:
|
def find_section(lines: list[str], name: str) -> tuple[int | None, str]:
|
||||||
for i, s in section_headers(lines):
|
for i, s in section_headers(lines):
|
||||||
if s.lower() == (name or "").lower():
|
if s.lower() == (name or "").lower():
|
||||||
@@ -1474,6 +1539,17 @@ def cmd_move(lay: Layout, a: argparse.Namespace) -> int:
|
|||||||
return EXIT_OK
|
return EXIT_OK
|
||||||
|
|
||||||
|
|
||||||
|
def section_of_achieved(lines: list[str]) -> str:
|
||||||
|
"""Секция достигнутого этого роадмапа. Нет её — отказ по существу: цель
|
||||||
|
закрыть некуда, и молча удалить её значит стереть половину ответа."""
|
||||||
|
name = canon_section(lines, ACHIEVED)
|
||||||
|
if name is None:
|
||||||
|
ru, en = ROADMAP_SECTIONS[ACHIEVED]
|
||||||
|
raise Usage(f"в роадмапе нет секции достигнутого («{ru}» или «{en}») —"
|
||||||
|
f" закрывать цель некуда. Заведи секцию и повтори")
|
||||||
|
return name
|
||||||
|
|
||||||
|
|
||||||
def is_achieved(task: dict, a: argparse.Namespace) -> bool:
|
def is_achieved(task: dict, a: argparse.Namespace) -> bool:
|
||||||
"""Цель, закрытая как достигнутая: `close --implemented` без причины."""
|
"""Цель, закрытая как достигнутая: `close --implemented` без причины."""
|
||||||
return task["type"] == GOAL and not a.reason
|
return task["type"] == GOAL and not a.reason
|
||||||
@@ -1528,20 +1604,22 @@ def cmd_close(lay: Layout, a: argparse.Namespace) -> int:
|
|||||||
for kind_index, (lines, ei) in places.items():
|
for kind_index, (lines, ei) in places.items():
|
||||||
lines.pop(ei)
|
lines.pop(ei)
|
||||||
if achieved is not None and kind_index == "roadmap":
|
if achieved is not None and kind_index == "roadmap":
|
||||||
insert_entry(lines, lay.cfg["achieved_section"], achieved, first=True)
|
insert_entry(lines, section_of_achieved(lines), achieved, first=True)
|
||||||
achieved = None
|
achieved = None
|
||||||
plan.index(lay, kind_index, lines)
|
plan.index(lay, kind_index, lines)
|
||||||
if achieved is not None: # строки в роадмапе не было — чиним
|
if achieved is not None: # строки в роадмапе не было — чиним
|
||||||
lines = read_lines(lay.index("roadmap"))
|
lines = read_lines(lay.index("roadmap"))
|
||||||
insert_entry(lines, lay.cfg["achieved_section"], achieved, first=True)
|
insert_entry(lines, section_of_achieved(lines), achieved, first=True)
|
||||||
plan.index(lay, "roadmap", lines)
|
plan.index(lay, "roadmap", lines)
|
||||||
plan.delete(path)
|
plan.delete(path)
|
||||||
plan.commit()
|
plan.commit()
|
||||||
|
|
||||||
if is_achieved(task, a):
|
if is_achieved(task, a):
|
||||||
|
# Имя берём из индекса: проект мог назвать секции по-английски.
|
||||||
|
where = (canon_section(read_lines(lay.index("roadmap")), ACHIEVED)
|
||||||
|
or ROADMAP_SECTIONS[ACHIEVED][0])
|
||||||
print(f"{a.slug}: цель достигнута — строка перенесена в"
|
print(f"{a.slug}: цель достигнута — строка перенесена в"
|
||||||
f" {lay.name('roadmap')}, секция «{lay.cfg['achieved_section']}»,"
|
f" {lay.name('roadmap')}, секция «{where}», файл удалён")
|
||||||
f" файл удалён")
|
|
||||||
else:
|
else:
|
||||||
print(f"{a.slug}: {'записано в ' + lay.name('rejected') + ' + удалено' if a.reason else 'удалено (реализовано, есть коммит)'}")
|
print(f"{a.slug}: {'записано в ' + lay.name('rejected') + ' + удалено' if a.reason else 'удалено (реализовано, есть коммит)'}")
|
||||||
if "sprint" in places and a.reason:
|
if "sprint" in places and a.reason:
|
||||||
@@ -1655,7 +1733,7 @@ def cmd_reopen(lay: Layout, a: argparse.Namespace) -> int:
|
|||||||
for line in removed:
|
for line in removed:
|
||||||
print(f" снята строка {lay.name('rejected')}: {line.strip()}")
|
print(f" снята строка {lay.name('rejected')}: {line.strip()}")
|
||||||
for line in unachieved:
|
for line in unachieved:
|
||||||
print(f" снята строка «{lay.cfg['achieved_section']}» в"
|
print(f" снята строка достигнутого в"
|
||||||
f" {lay.name('roadmap')}: {line.strip()}")
|
f" {lay.name('roadmap')}: {line.strip()}")
|
||||||
print(" роадмап больше не утверждает, что приложение это умеет")
|
print(" роадмап больше не утверждает, что приложение это умеет")
|
||||||
print(" сверь тело: оно восстановлено на момент удаления, всё позднейшее"
|
print(" сверь тело: оно восстановлено на момент удаления, всё позднейшее"
|
||||||
@@ -2064,13 +2142,17 @@ def init_files(lay: Layout, sections: list[str], roadmap_sections: list[str],
|
|||||||
f"Цель — возможность приложения, файл `[goal]` в `{lay.cfg['items']}/`; её\n"
|
f"Цель — возможность приложения, файл `[goal]` в `{lay.cfg['items']}/`; её\n"
|
||||||
"задачи здесь **не перечисляются** — перечень даёт\n"
|
"задачи здесь **не перечисляются** — перечень даёт\n"
|
||||||
"`tasks.py list --goal <слаг>`.\n\n"
|
"`tasks.py list --goal <слаг>`.\n\n"
|
||||||
f"- **{lay.cfg['achieved_section']}** — достигнутое: строку пишет\n"
|
f"- **{ROADMAP_SECTIONS[ACHIEVED][0]}** — достигнутое: строку пишет\n"
|
||||||
" `tasks.py close <цель> --implemented`, ссылки на файл в ней нет —\n"
|
" `tasks.py close <цель> --implemented`, ссылки на файл в ней нет —\n"
|
||||||
" файл удаляется, поведение живёт в спеках;\n"
|
" файл удаляется, поведение живёт в спеках;\n"
|
||||||
"- **строим** — очередь значима и обосновывается прозой;\n"
|
"- **запланировано** — очередь значима и обосновывается прозой;\n"
|
||||||
"- **направления** — очереди нет, тянутся долго;\n"
|
"- **направления** — очереди нет, тянутся долго;\n"
|
||||||
"- **станок** — инструмент и процесс разработки, не возможности\n"
|
"- **разработка** — инструмент и процесс, не возможности приложения.\n"
|
||||||
" приложения. Отдельно, чтобы не смешиваться с ними.\n\n"
|
" Отдельно, чтобы не читаться как обещание продукта.\n\n"
|
||||||
|
"Секции **канонические** и переименованию проектом не подлежат:\n"
|
||||||
|
"у каждой свой смысл, и в первую пишет сам `close`. Английский\n"
|
||||||
|
"вариант — done | planned | directions | tooling, один язык на весь\n"
|
||||||
|
"индекс.\n\n"
|
||||||
+ "".join(f"## {s}\n\n" for s in roadmap_sections))
|
+ "".join(f"## {s}\n\n" for s in roadmap_sections))
|
||||||
out[lay.index("sprint")] = empty_sprint(lay)
|
out[lay.index("sprint")] = empty_sprint(lay)
|
||||||
out[lay.index("rejected")] = (
|
out[lay.index("rejected")] = (
|
||||||
@@ -2100,8 +2182,9 @@ def cmd_init(root: Path, a: argparse.Namespace) -> int:
|
|||||||
if lay.index("backlog").exists():
|
if lay.index("backlog").exists():
|
||||||
raise Usage(f"{lay.index('backlog')} уже есть — каталог задач заведён")
|
raise Usage(f"{lay.index('backlog')} уже есть — каталог задач заведён")
|
||||||
|
|
||||||
sections, roadmap_sections = uniq_sections(a.sections), uniq_sections(a.roadmap_sections)
|
sections = uniq_sections(a.sections)
|
||||||
if not sections or not roadmap_sections:
|
roadmap_sections = uniq_sections(DEFAULT_ROADMAP_SECTIONS)
|
||||||
|
if not sections:
|
||||||
raise Usage("пустой список секций")
|
raise Usage("пустой список секций")
|
||||||
blockers = [s for s in sections if s.lower() in BLOCKER_SECTIONS]
|
blockers = [s for s in sections if s.lower() in BLOCKER_SECTIONS]
|
||||||
if blockers:
|
if blockers:
|
||||||
@@ -2118,7 +2201,7 @@ def cmd_init(root: Path, a: argparse.Namespace) -> int:
|
|||||||
plan.commit()
|
plan.commit()
|
||||||
print(f"каталог задач заведён: {root}")
|
print(f"каталог задач заведён: {root}")
|
||||||
print(f" секции беклога: {', '.join(sections)};"
|
print(f" секции беклога: {', '.join(sections)};"
|
||||||
f" части роадмапа: {', '.join(roadmap_sections)}")
|
f" секции роадмапа канонические: {', '.join(roadmap_sections)}")
|
||||||
if cfg:
|
if cfg:
|
||||||
pm = (root / PM_CONFIG_REL).resolve()
|
pm = (root / PM_CONFIG_REL).resolve()
|
||||||
print(f" имена частей записаны в {pm if pm.is_file() else root / CONFIG_NAME}")
|
print(f" имена частей записаны в {pm if pm.is_file() else root / CONFIG_NAME}")
|
||||||
@@ -2260,17 +2343,6 @@ def scan_list_file(path: Path) -> dict:
|
|||||||
return found
|
return found
|
||||||
|
|
||||||
|
|
||||||
def first_open_section(raw: str) -> str:
|
|
||||||
"""Секция, куда adopt кладёт выведенные цели: первая **не** достигнутая.
|
|
||||||
|
|
||||||
Первой в роадмапе идёт секция достигнутого, и класть в неё цель, выведенную
|
|
||||||
из шага плана, значит объявить сделанным то, что ещё не начато.
|
|
||||||
"""
|
|
||||||
done = DEFAULTS["achieved_section"].lower()
|
|
||||||
names = [s.strip() for s in raw.split(",") if s.strip()]
|
|
||||||
return next((s for s in names if s.lower() != done), "строим")
|
|
||||||
|
|
||||||
|
|
||||||
def cmd_adopt_scan(a: argparse.Namespace) -> int:
|
def cmd_adopt_scan(a: argparse.Namespace) -> int:
|
||||||
sources = [Path(s) for s in a.sources]
|
sources = [Path(s) for s in a.sources]
|
||||||
for s in sources:
|
for s in sources:
|
||||||
@@ -2286,7 +2358,7 @@ def cmd_adopt_scan(a: argparse.Namespace) -> int:
|
|||||||
rejected += sc.get("rejected", [])
|
rejected += sc.get("rejected", [])
|
||||||
unclassified += sc.get("unclassified", [])
|
unclassified += sc.get("unclassified", [])
|
||||||
goals += [{"slug": "", "title": g["title"],
|
goals += [{"slug": "", "title": g["title"],
|
||||||
"section": first_open_section(a.roadmap_sections),
|
"section": ROADMAP_SECTIONS[PLANNED][0],
|
||||||
"from": g["from"], "step": g.get("step"), "done": g.get("done"),
|
"from": g["from"], "step": g.get("step"), "done": g.get("done"),
|
||||||
"body": f"Выведена из шага «{g['title']}» ({g['from']})."
|
"body": f"Выведена из шага «{g['title']}» ({g['from']})."
|
||||||
+ ("\n\nШаг помечен закрытым — цель, скорее всего,"
|
+ ("\n\nШаг помечен закрытым — цель, скорее всего,"
|
||||||
@@ -2320,7 +2392,7 @@ def cmd_adopt_scan(a: argparse.Namespace) -> int:
|
|||||||
"sources": [str(s) for s in sources],
|
"sources": [str(s) for s in sources],
|
||||||
"path_map": path_map,
|
"path_map": path_map,
|
||||||
"sections_backlog": uniq_sections(a.sections),
|
"sections_backlog": uniq_sections(a.sections),
|
||||||
"sections_roadmap": uniq_sections(a.roadmap_sections),
|
"sections_roadmap": uniq_sections(DEFAULT_ROADMAP_SECTIONS),
|
||||||
"section_map": {},
|
"section_map": {},
|
||||||
"goals": goals,
|
"goals": goals,
|
||||||
"items": items,
|
"items": items,
|
||||||
@@ -2688,7 +2760,6 @@ def main() -> int:
|
|||||||
p = sub.add_parser("init", help="завести каталог задач в новом проекте")
|
p = sub.add_parser("init", help="завести каталог задач в новом проекте")
|
||||||
p.add_argument("--dir")
|
p.add_argument("--dir")
|
||||||
p.add_argument("--sections", default=DEFAULT_SECTIONS)
|
p.add_argument("--sections", default=DEFAULT_SECTIONS)
|
||||||
p.add_argument("--roadmap-sections", dest="roadmap_sections", default=DEFAULT_ROADMAP_SECTIONS)
|
|
||||||
p.add_argument("--items")
|
p.add_argument("--items")
|
||||||
p.add_argument("--backlog")
|
p.add_argument("--backlog")
|
||||||
p.add_argument("--roadmap")
|
p.add_argument("--roadmap")
|
||||||
@@ -2702,7 +2773,7 @@ def main() -> int:
|
|||||||
s.add_argument("--target", default="docs/tasks")
|
s.add_argument("--target", default="docs/tasks")
|
||||||
s.add_argument("--out", default="tasks-adopt-plan.json")
|
s.add_argument("--out", default="tasks-adopt-plan.json")
|
||||||
s.add_argument("--sections", default=DEFAULT_SECTIONS)
|
s.add_argument("--sections", default=DEFAULT_SECTIONS)
|
||||||
s.add_argument("--roadmap-sections", dest="roadmap_sections", default=DEFAULT_ROADMAP_SECTIONS)
|
|
||||||
s = asub.add_parser("apply", help="записать каталог по подтверждённой карте")
|
s = asub.add_parser("apply", help="записать каталог по подтверждённой карте")
|
||||||
s.add_argument("--plan", required=True)
|
s.add_argument("--plan", required=True)
|
||||||
s.add_argument("--refs", nargs="*", help="файлы и каталоги, где чинить ссылки на слаги")
|
s.add_argument("--refs", nargs="*", help="файлы и каталоги, где чинить ссылки на слаги")
|
||||||
|
|||||||
Reference in New Issue
Block a user