Reconcile the feature-container convention with the depends validator #14

Closed
opened 2026-08-10 10:07:06 +00:00 by claude · 0 comments
Collaborator

Summary

references/format.md велит дочерним issue ссылаться на контейнер через
depends:, а валидатор требует обратного ребра — контейнер обязан перечислять
детей в своём depends:. Выполнить оба правила разом нельзя: получается цикл, и
он ловится как ERROR. Любой type/feature с непустой секцией ## Issues
заканчивается либо варнингами, либо ошибкой.

Spec

skills/issue/references/format.md

Steps to reproduce

  1. Создать контейнер и ребёнка ровно по документации
    (skills/issue/references/format.md:261-262 — «Child issues carry their own
    type/* … and link back via their depends:»):
    python3 skills/issue/scripts/issue_new.py --type feature --id feat-x  --title "Container x"
    python3 skills/issue/scripts/issue_new.py --type task    --id child-y --title "Child y" --depends feat-x
    
  2. Вписать - [ ] child-y — часть в секцию ## Issues контейнера, как требует
    шаблон type/feature (format.md:277-279).
  3. python3 skills/issue/scripts/issue_check.py
  4. Попробовать убрать варнинг тем способом, который он предлагает, — добавить
    child-y в depends: контейнера — и прогнать проверку снова.

Expected

Канонический контейнер из шаблона проходит проверку без варнингов и с кодом
выхода 0.

Actual

Шаг 3 — варнинг на контейнере, и он называет секцию, которой в файле нет:

warn  feat-x: ## Depends on mentions 'child-y' but `depends:` does not list it

Шаг 4 — жёсткая ошибка, выход 1:

ERROR cycle: child-y -> feat-x -> child-y
2 issue(s) checked, 1 with errors

Третьего варианта нет: либо варнинги, либо ошибка. Побочно ломается и картинка —
при ребре «ребёнок → контейнер» issue_tree.py рисует контейнер листом под
каждым ребёнком, повторяя его столько раз, сколько детей
(skills/issue/scripts/issue_tree.py:38-47). Это читается ровно наоборот
относительно вложенности.

Environment

Причина в трёх местах:

  • skills/issue/scripts/issue.py:270-285body_dep_refs() считает
    ## Issues таким же источником рёбер, как ## Depends on:
    active = line.strip() in (DEPENDS_SECTION, "## Issues"). Для контейнера это
    разворачивает направление графа.
  • skills/issue/scripts/issue.py:347-350 — текст варнинга всегда подставляет
    DEPENDS_SECTION, даже когда ссылка пришла из ## Issues. Отсюда упоминание
    несуществующей секции.
  • skills/issue/scripts/issue_check.py:58-61 — детектор циклов, который делает
    «выполнить оба правила» невозможным.

Развилка, которую надо решить до правки (в этой issue она не выбрана):

Вариант Что меняется Цена
A. ## Issues перестаёт быть источником рёбер состав контейнера остаётся прозой, depends: детей смотрит на контейнер — как и написано в format.md сейчас дёшево, но контейнер не блокируется детьми, и в дереве он корень только формально
B. направление меняется в документации контейнер перечисляет детей в depends:, дети про контейнер не знают; граф читается как вложенность, чек зелёный надо переписать format.md:260-263

Аргумент за B: «контейнер закрыт, когда закрыты дети» — это и есть отношение
зависимости, а «ребёнок принадлежит фиче» — отношение принадлежности, ему в
графе зависимостей делать нечего.

Acceptance criteria

  • контейнер из шаблона type/feature с заполненной ## Issues и ребёнок,
    оформленный по format.md, дают issue_check.py без варнингов и с кодом 0
  • format.md и валидатор называют одно направление ребра
    контейнер↔ребёнок, и оно проверяется тестом
  • варнинг о рассинхроне называет ту секцию, из которой реально взята ссылка,
    а не ## Depends on всегда
  • issue_tree.py рисует контейнер корнем, дети под ним
## Summary `references/format.md` велит дочерним issue ссылаться на контейнер через `depends:`, а валидатор требует обратного ребра — контейнер обязан перечислять детей в своём `depends:`. Выполнить оба правила разом нельзя: получается цикл, и он ловится как ERROR. Любой `type/feature` с непустой секцией `## Issues` заканчивается либо варнингами, либо ошибкой. ## Spec `skills/issue/references/format.md` ## Steps to reproduce 1. Создать контейнер и ребёнка ровно по документации (`skills/issue/references/format.md:261-262` — «Child issues carry their own `type/*` … and link back via their `depends:`»): ```sh python3 skills/issue/scripts/issue_new.py --type feature --id feat-x --title "Container x" python3 skills/issue/scripts/issue_new.py --type task --id child-y --title "Child y" --depends feat-x ``` 2. Вписать `- [ ] child-y — часть` в секцию `## Issues` контейнера, как требует шаблон `type/feature` (`format.md:277-279`). 3. `python3 skills/issue/scripts/issue_check.py` 4. Попробовать убрать варнинг тем способом, который он предлагает, — добавить `child-y` в `depends:` контейнера — и прогнать проверку снова. ## Expected Канонический контейнер из шаблона проходит проверку без варнингов и с кодом выхода 0. ## Actual Шаг 3 — варнинг на контейнере, и он называет секцию, которой в файле нет: ``` warn feat-x: ## Depends on mentions 'child-y' but `depends:` does not list it ``` Шаг 4 — жёсткая ошибка, выход 1: ``` ERROR cycle: child-y -> feat-x -> child-y 2 issue(s) checked, 1 with errors ``` Третьего варианта нет: либо варнинги, либо ошибка. Побочно ломается и картинка — при ребре «ребёнок → контейнер» `issue_tree.py` рисует контейнер листом под каждым ребёнком, повторяя его столько раз, сколько детей (`skills/issue/scripts/issue_tree.py:38-47`). Это читается ровно наоборот относительно вложенности. ## Environment Причина в трёх местах: - `skills/issue/scripts/issue.py:270-285` — `body_dep_refs()` считает `## Issues` таким же источником рёбер, как `## Depends on`: `active = line.strip() in (DEPENDS_SECTION, "## Issues")`. Для контейнера это разворачивает направление графа. - `skills/issue/scripts/issue.py:347-350` — текст варнинга всегда подставляет `DEPENDS_SECTION`, даже когда ссылка пришла из `## Issues`. Отсюда упоминание несуществующей секции. - `skills/issue/scripts/issue_check.py:58-61` — детектор циклов, который делает «выполнить оба правила» невозможным. Развилка, которую надо решить до правки (в этой issue она не выбрана): | Вариант | Что меняется | Цена | |---|---|---| | A. `## Issues` перестаёт быть источником рёбер | состав контейнера остаётся прозой, `depends:` детей смотрит на контейнер — как и написано в `format.md` сейчас | дёшево, но контейнер не блокируется детьми, и в дереве он корень только формально | | B. направление меняется в документации | контейнер перечисляет детей в `depends:`, дети про контейнер не знают; граф читается как вложенность, чек зелёный | надо переписать `format.md:260-263` | Аргумент за B: «контейнер закрыт, когда закрыты дети» — это и есть отношение зависимости, а «ребёнок принадлежит фиче» — отношение принадлежности, ему в графе зависимостей делать нечего. ## Acceptance criteria - [x] контейнер из шаблона `type/feature` с заполненной `## Issues` и ребёнок, оформленный по `format.md`, дают `issue_check.py` без варнингов и с кодом 0 - [x] `format.md` и валидатор называют **одно** направление ребра контейнер↔ребёнок, и оно проверяется тестом - [x] варнинг о рассинхроне называет ту секцию, из которой реально взята ссылка, а не `## Depends on` всегда - [x] `issue_tree.py` рисует контейнер корнем, дети под ним
claude added the
severity
medium
comp/issue
type
bug
labels 2026-08-10 10:07:06 +00:00
claude changed reference from feat/issue-cache-scripts to feat/issue-cache-scripts 2026-08-10 10:58:15 +00:00
Sign in to join this conversation.