@rt-tools/agent-kit 0.8.1 → 0.8.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (129) hide show
  1. package/README.md +25 -19
  2. package/assets/agents/qa-engineer.md +1 -1
  3. package/assets/agents/rules-reviewer.md +83 -0
  4. package/assets/checks/board.github.mjs +56 -17
  5. package/assets/checks/check-board.github.mjs +49 -5
  6. package/assets/checks/check-dupes.mjs +66 -6
  7. package/assets/checks/check-specs.mjs +100 -15
  8. package/assets/checks/rt-kit-checks.config.mjs +9 -0
  9. package/assets/checks/task-new.github.mjs +33 -5
  10. package/assets/commands/agent-kit-digest.md +10 -5
  11. package/assets/commands/feedback.md +95 -0
  12. package/assets/commands/next-session.md +4 -4
  13. package/assets/commands/rules-review.md +98 -0
  14. package/assets/commands/skill-curator.md +44 -25
  15. package/assets/defaults/gate-map.sh +11 -4
  16. package/assets/defaults/project.sh +46 -0
  17. package/assets/docs/GLOSSARY.md +49 -46
  18. package/assets/hooks/docs-guard.sh +19 -3
  19. package/assets/hooks/git-guard-delivery.sh +106 -13
  20. package/assets/hooks/proposal-guard.sh +93 -0
  21. package/assets/hooks/reuse-first-guard.sh +83 -15
  22. package/assets/hooks/skill-gate-layers.sh +1 -1
  23. package/assets/hooks/skill-gate.sh +27 -1
  24. package/assets/hooks/task-flow-guard.sh +59 -19
  25. package/assets/hooks/waiting-turn-guard.sh +87 -0
  26. package/assets/hooks/window-fill-guard.sh +1 -1
  27. package/assets/laws/delivery.md +41 -7
  28. package/assets/laws/project-documentation.md +39 -0
  29. package/assets/laws/verifiability.md +6 -1
  30. package/assets/laws/work-conduct.md +83 -3
  31. package/assets/patterns/git-workflow-commit.azure.md +84 -4
  32. package/assets/patterns/git-workflow-commit.github.md +90 -4
  33. package/assets/patterns/git-workflow-commit.gitlab.md +84 -5
  34. package/assets/patterns/git-workflow-docker.md +30 -0
  35. package/assets/patterns/git-workflow-merge.md +1 -1
  36. package/assets/patterns/spec-driven-domain.md +7 -1
  37. package/assets/patterns/spec-driven-rule.md +6 -0
  38. package/assets/patterns/task-flow-close.md +197 -21
  39. package/assets/patterns/task-flow-handoff.md +28 -5
  40. package/assets/patterns/task-flow-resume.md +25 -7
  41. package/assets/patterns/task-flow-start.md +37 -6
  42. package/assets/rules/angular-patterns.md +22 -0
  43. package/assets/rules/api-layer.md +25 -0
  44. package/assets/rules/browser-verification.md +42 -1
  45. package/assets/rules/component-structure.md +21 -0
  46. package/assets/rules/dependencies.md +22 -0
  47. package/assets/rules/doc-style.md +37 -5
  48. package/assets/rules/{entity-conventions.md → entity-conventions.needs-admin.md} +21 -0
  49. package/assets/rules/entity-models.md +21 -0
  50. package/assets/rules/git-workflow.azure.md +62 -8
  51. package/assets/rules/git-workflow.github.md +78 -14
  52. package/assets/rules/git-workflow.gitlab.md +61 -8
  53. package/assets/rules/lib-layers.md +25 -0
  54. package/assets/rules/lists.md +27 -0
  55. package/assets/rules/navigation.md +21 -0
  56. package/assets/rules/{observability.md → observability.needs-app.md} +23 -0
  57. package/assets/rules/permissions.md +23 -0
  58. package/assets/rules/platform-access.md +21 -0
  59. package/assets/rules/reuse-first.md +20 -0
  60. package/assets/rules/seo.md +19 -0
  61. package/assets/rules/shared-code.md +19 -0
  62. package/assets/rules/spec-driven.md +36 -0
  63. package/assets/rules/styling-bem.md +19 -0
  64. package/assets/rules/task-flow.md +150 -35
  65. package/assets/rules/testing.md +50 -0
  66. package/assets/rules/translations.md +21 -0
  67. package/assets/rules/typescript-conventions.md +33 -0
  68. package/assets/samples/specs/_template/spec.md +83 -0
  69. package/assets/samples/tasks/_template/grill.md +28 -0
  70. package/assets/samples/tasks/_template/plan.md +39 -0
  71. package/assets/samples/tasks/_template/progress.md +23 -0
  72. package/assets/skills/agent-kit-extend.md +24 -0
  73. package/assets/skills/agent-kit.md +69 -7
  74. package/assets/templates/rule.md +31 -2
  75. package/assets/traits.json +14 -0
  76. package/bin/agent-kit.d.ts.map +1 -1
  77. package/bin/agent-kit.js +31 -16
  78. package/bin/agent-kit.js.map +1 -1
  79. package/index.d.ts +1 -0
  80. package/index.d.ts.map +1 -1
  81. package/index.js +1 -0
  82. package/index.js.map +1 -1
  83. package/lib/argv.d.ts +17 -0
  84. package/lib/argv.d.ts.map +1 -0
  85. package/lib/argv.js +44 -0
  86. package/lib/argv.js.map +1 -0
  87. package/lib/cargo.d.ts +88 -0
  88. package/lib/cargo.d.ts.map +1 -0
  89. package/lib/cargo.js +16 -0
  90. package/lib/cargo.js.map +1 -0
  91. package/lib/catalog.d.ts +18 -1
  92. package/lib/catalog.d.ts.map +1 -1
  93. package/lib/catalog.js +12 -2
  94. package/lib/catalog.js.map +1 -1
  95. package/lib/commands.d.ts +0 -26
  96. package/lib/commands.d.ts.map +1 -1
  97. package/lib/commands.js +79 -122
  98. package/lib/commands.js.map +1 -1
  99. package/lib/companion.d.ts +37 -0
  100. package/lib/companion.d.ts.map +1 -1
  101. package/lib/companion.js +42 -1
  102. package/lib/companion.js.map +1 -1
  103. package/lib/config.d.ts +38 -1
  104. package/lib/config.d.ts.map +1 -1
  105. package/lib/config.js +24 -0
  106. package/lib/config.js.map +1 -1
  107. package/lib/ship.d.ts +39 -0
  108. package/lib/ship.d.ts.map +1 -0
  109. package/lib/ship.js +87 -0
  110. package/lib/ship.js.map +1 -0
  111. package/lib/shipment.d.ts +60 -0
  112. package/lib/shipment.d.ts.map +1 -0
  113. package/lib/shipment.js +247 -0
  114. package/lib/shipment.js.map +1 -0
  115. package/lib/snapshot.d.ts +30 -0
  116. package/lib/snapshot.d.ts.map +1 -0
  117. package/lib/snapshot.js +73 -0
  118. package/lib/snapshot.js.map +1 -0
  119. package/lib/traits.d.ts +32 -0
  120. package/lib/traits.d.ts.map +1 -0
  121. package/lib/traits.js +82 -0
  122. package/lib/traits.js.map +1 -0
  123. package/package.json +6 -2
  124. package/rt-tools-agent-kit-0.8.3.tgz +0 -0
  125. package/lib/submit.d.ts +0 -24
  126. package/lib/submit.d.ts.map +0 -1
  127. package/lib/submit.js +0 -26
  128. package/lib/submit.js.map +0 -1
  129. package/rt-tools-agent-kit-0.8.1.tgz +0 -0
@@ -26,11 +26,45 @@ description: Правило под «Закон о проверяемости».
26
26
  переносится между репозиториями, раскладка — нет, и путь, названный в правиле, врёт в первом
27
27
  же дереве, которое держит код иначе.
28
28
 
29
+ ## Ход
30
+
31
+ Ход проверки через браузер: что выясняется до первого запроса, где развилка между поднятым и
32
+ не поднятым приложением и чем подтверждается вывод.
33
+
34
+ ```mermaid
35
+ flowchart TD
36
+ A[Нужна проверка в браузере] --> B{Порт занят}
37
+ B -->|Да| C[Выясняется, что именно отвечает]
38
+ B -->|Нет| D{Кто поднимает стенд}
39
+ C --> E{Отвечает то самое приложение}
40
+ E -->|Да| F[Второй экземпляр не поднимается]
41
+ E -->|Нет| T[Занявшее порт останавливается по идентификатору процесса, а не по имени команды]
42
+ T --> D
43
+ D -->|Владелец| U[Ему говорят сразу, а не в конце захода: агента отбивает гард]
44
+ D -->|Агент| V[Стенд поднимается]
45
+ U --> G[Драйвер водит браузер на закреплённом профиле]
46
+ V --> G
47
+ F --> G
48
+ G --> W{Экран публичный и это первый заход}
49
+ W -->|Да| X[Заход метится признаком служебного посещения: иначе он попадёт в счёт гостей]
50
+ W -->|Нет| H{Что проверяется}
51
+ X --> H
52
+ H -->|Путь запроса и отдача страницы| I[Проверка идёт за настоящим прокси]
53
+ H -->|Вёрстка| J[Замер вычисленных значений, а не взгляд]
54
+ I --> K{Расхождение объясняется кэшем}
55
+ J --> K
56
+ K -->|Да| L[Кэш чистится и замер повторяется: объяснение не подтверждение]
57
+ K -->|Нет| M[Вывод называет число и способ его получить]
58
+ L --> M
59
+ ```
60
+
29
61
  ## Как закон применяется здесь
30
62
 
31
63
  - **Второй экземпляр уже поднятого приложения не поднимается.** До первого запроса выясняется,
32
64
  кто отвечает на порту: поднятый заново экземпляр отвечает своей сборкой, а не той, которую
33
- проверяют. Кто поднимает стенд — владелец или агент, — сказано в именах дерева.
65
+ проверяют. Кто поднимает стенд — владелец или агент, — сказано в именах дерева. Занятый порт,
66
+ обнаруженный поздно, означает, что стенд уже есть: запущенное поверх него останавливается по
67
+ идентификатору процесса, а не по имени команды — у обоих экземпляров оно одно.
34
68
  - **Браузер водится одним драйвером на закреплённом профиле.** Остальные двери — второй
35
69
  драйвер, `open`, `osascript`, запуск бинарника — закреплённый профиль не спрашивают вовсе.
36
70
  - **Выбор браузера протухает и требует повторного вызова.** Выбор, сделанный в начале
@@ -66,6 +100,13 @@ description: Правило под «Закон о проверяемости».
66
100
  отвечает 200 старым кодом, а заведённого в ветке обработчика у него нет вовсе, и 404 читается
67
101
  как дефект регистрации. Таких процессов бывает несколько; снятие по шаблону команды не
68
102
  попадает ни в один — убивать по PID из `lsof`, каждый.
103
+ - **Шаблон имени команды не годится ни чтобы попасть, ни чтобы не задеть.** Два экземпляра
104
+ одного стенда — уже поднятый и только что запущенный — по тексту команды неотличимы: она у них
105
+ одна и та же. Снятие по шаблону уносит оба, и разобрать это потом нечем: снятым оказывается
106
+ ровно тот стенд, ради которого всё затевалось. Своё и чужое различает только идентификатор
107
+ процесса, снятый разбором порта; им и останавливают, поштучно. Обратный промах тот же по
108
+ природе — оболочка запускает процесс под именем, которого в шаблоне нет, и снятие не попадает
109
+ ни во что.
69
110
  - Инкрементальная сборка протухает поштучно: разметка бывает уже новая, а клиентский чанк — от
70
111
  компиляции до правки. Признак дев-сборки — имена бандла без хеша (`main.js`). Расхождение
71
112
  между `curl` и страницей после гидратации — повод пересобрать, а не искать дефект в коде.
@@ -27,6 +27,27 @@ description: Правило под «Закон о фронтовом прило
27
27
  переносится между репозиториями, раскладка — нет, и путь, названный в правиле, врёт в первом
28
28
  же дереве, которое держит код иначе.
29
29
 
30
+ ## Ход
31
+
32
+ Ход правки компонента: где решается разметка, где — привязка, и что ставится на каждый
33
+ интерактивный элемент.
34
+
35
+ ```mermaid
36
+ flowchart TD
37
+ A[Правится компонент] --> B[Селектор элементный, класс блока на хосте]
38
+ B --> C{В шаблоне нужен вычисленный вид}
39
+ C -->|Да| D[Значение считается в классе, шаблон методов не зовёт]
40
+ C -->|Нет| E[Разметка пишется как есть]
41
+ D --> F{Есть интерактивный элемент}
42
+ E --> F
43
+ F -->|Да| G[Ставится якорь для спек, а доступность — отдельными атрибутами]
44
+ F -->|Нет| H[Готово]
45
+ G --> I{Один компонент в обеих ветках условия}
46
+ I -->|Да| J[Это условная привязка: ветки сводятся в одну]
47
+ I -->|Нет| H
48
+ J --> H
49
+ ```
50
+
30
51
  ## Как закон применяется здесь
31
52
 
32
53
  - **Шаблон не зовёт методов.** Правило линтера банит `{{ getTotal() }}` и `@if
@@ -27,6 +27,28 @@ description: Правило под «Закон о поставке». Брат
27
27
  переносится между репозиториями, раскладка — нет, и путь, названный в правиле, врёт в первом
28
28
  же дереве, которое держит код иначе.
29
29
 
30
+ ## Ход
31
+
32
+ Ход подъёма версии: где решается сама версия, где — граница переформатирования, и чем работа
33
+ кончается.
34
+
35
+ ```mermaid
36
+ flowchart TD
37
+ A[Нужна новая версия пакета] --> B{Верхняя граница чем задана}
38
+ B -->|Есть peer-диапазоны у зависимых| C[Граница берётся из них, а не из последнего номера в реестре]
39
+ B -->|Нет| D[Берётся последняя выпущенная]
40
+ C --> E{Версия выпущена недавно}
41
+ D --> E
42
+ E -->|Да, и она нужна прямо сейчас| F[Выписывается отдельно, с названной причиной]
43
+ E -->|Да, но не срочно| G[Выдерживается: свежая версия отзывается чаще старой]
44
+ E -->|Нет| H[Записывается точным номером, без диапазона]
45
+ F --> H
46
+ H --> I{Обновился форматтер или линтер}
47
+ I -->|Да| J[Переформатируется только то, что линтер и проверяет; новые правила разбираются поимённо]
48
+ I -->|Нет| K[Снимок дерева пересобирается, набор гоняется целиком]
49
+ J --> K
50
+ ```
51
+
30
52
  ## Как закон применяется здесь
31
53
 
32
54
  - **Версия пакета записана точным номером.** Из диапазона сегодня и через неделю поставится
@@ -27,6 +27,30 @@ description: Правило под «Закон о документации пр
27
27
  переносится между репозиториями, раскладка — нет, и путь, названный в правиле, врёт в первом
28
28
  же дереве, которое держит код иначе.
29
29
 
30
+ ## Ход
31
+
32
+ Ход правки текста: что проверяется до записи, где развилка между новым словом и уже занятым, и
33
+ что делается со снятым именем.
34
+
35
+ ```mermaid
36
+ flowchart TD
37
+ A[Пишется текст проекта] --> B{Нужно слово для нового понятия}
38
+ B -->|Да| C{Оно есть в словаре}
39
+ C -->|Да| D[Берётся оттуда в том же значении]
40
+ C -->|Нет| E[Заводится в словаре той же правкой либо заменяется простым]
41
+ B -->|Нет| F{В тексте называется путь или имя файла}
42
+ D --> F
43
+ E --> F
44
+ F -->|Да| G[Путь проверяется командой: названный, но несуществующий читается как указание]
45
+ F -->|Нет| H{В тексте стоит число или утверждение о проверке}
46
+ G --> H
47
+ H -->|Число| I[Пересчитывается командой в том же изменении либо не пишется вовсе]
48
+ H -->|Проверка| J[Запускается, а не пересказывается]
49
+ H -->|Ни то ни другое| K[Документ едет тем же коммитом, что и правка, которую описывает]
50
+ I --> K
51
+ J --> K
52
+ ```
53
+
30
54
  ## Как закон применяется здесь
31
55
 
32
56
  - **Путь, названный в документе, существует.** Ссылка на переехавший файл читается как
@@ -115,7 +139,7 @@ description: Правило под «Закон о документации пр
115
139
  паттерн `doc-style-sweep`.
116
140
  - **Словарь действует и на разговор с владельцем, не только на файлы.** Он приходит в контекст
117
141
  на запуске сессии, поэтому «не читал» основанием не бывает. Слово из левой колонки «Так не
118
- пишем» всплывало именно в ответах: в дереве его уже вычистили, а в отчёте о сделанном оно
142
+ пишем» всплывало именно в ответах: в дереве его уже вычистили, а в PR о сделанном оно
119
143
  оставалось, и владелец читал ровно то слово, от которого отказались.
120
144
  - **Термин берётся из `docs/GLOSSARY.md`, а не придумывается на месте.** Слова, которого там
121
145
  нет, у читателя нет тоже: «журнал приложения» простоял в спеке почты, пока владелец не
@@ -131,19 +155,27 @@ description: Правило под «Закон о документации пр
131
155
  - **Снятое имя вычищается одним грепом по всему дереву:** правила, их зеркала в скилах,
132
156
  документы и комментарии. Описание того, чего в коде уже нет, читается как действующее
133
157
  указание.
158
+ - **У снятого слова второе значение возвращается после сплошной замены, а не обходится до
159
+ неё.** Слово снимают ровно потому, что оно стояло над двумя вещами, и второе значение при
160
+ этом остаётся законным. Отобрать его заранее нечем: какое из двух значений в строке, видно
161
+ только по соседнему тексту, а строк бывают сотни. Порядок обратный — сплошная замена, затем
162
+ сплошной просмотр самой правки, и найденное второе значение возвращается поимённо. Из 353
163
+ замен так вернулись шесть, и две первые были поломкой: сверка печатала новое имя дважды
164
+ подряд, а комментарий обещал «поломку вместо PR о том, что долгов нет». Просматривается
165
+ правка, а не дерево после неё: в дереве обе стороны выглядят одинаково верными.
134
166
  - **Поиск по дереву не покрывает того, что уже уехало наружу.** Заголовок задачи и её тело,
135
- заголовок отчёта и его тело, заголовки коммитов лежат вне файлов, и проверки текстов их не
167
+ заголовок PR и его тело, заголовки коммитов лежат вне файлов, и проверки текстов их не
136
168
  читают вовсе. Вычистив слово в дереве, обходят те же места в очереди работ и в истории:
137
169
 
138
170
  ```bash
139
- <клиент хостинга> api "<путь к отчёту>" --jq '.title, .body' | grep -i '<слово>'
171
+ <клиент хостинга> api "<путь к PR>" --jq '.title, .body' | grep -i '<слово>'
140
172
  <клиент хостинга> api "<путь к задаче>" --jq '.title, .body' | grep -i '<слово>'
141
173
  git log --format='%s%n%b' <база>..HEAD | grep -i '<слово>'
142
174
  ```
143
175
 
144
- Заголовок отчёта правится вызовом хостинга, заголовок коммита — только переписыванием ветки,
176
+ Заголовок PR правится вызовом хостинга, заголовок коммита — только переписыванием ветки,
145
177
  поэтому его проверяют до пуша. Выдуманное слово было вычищено из трёх файлов и объявлено
146
- снятым, а в заголовке отчёта и в заголовке коммита осталось — владелец прочитал именно его.
178
+ снятым, а в заголовке PR и в заголовке коммита осталось — владелец прочитал именно его.
147
179
 
148
180
  - **Число в тексте пересчитывается командой в том же коммите, где пишется.** Оно стареет
149
181
  внутри одной ветки: «шестнадцать пар» стало неправдой через два коммита после того, как
@@ -28,6 +28,27 @@ description: Правило под «Закон о правке сущности
28
28
  переносится между репозиториями, раскладка — нет, и путь, названный в правиле, врёт в первом
29
29
  же дереве, которое держит код иначе.
30
30
 
31
+ ## Ход
32
+
33
+ Ход правки записи из панели: чем панель открывается, что происходит с мутацией и чем кончается
34
+ уход с панели.
35
+
36
+ ```mermaid
37
+ flowchart TD
38
+ A[Нужна панель создания или правки] --> B[Открывается маршрутом во вспомогательном аутлете]
39
+ B --> C[Запись читается по признаку из адреса полной моделью, а не берётся из списка]
40
+ C --> D[Панель наследует общую основу и отдаёт ей поток мутации]
41
+ D --> E{Мутация чем кончилась}
42
+ E -->|Значение| F[Список перечитывается, и только после этого работа считается сделанной]
43
+ E -->|Ошибка потока| G[Панель показывает отказ и остаётся открытой]
44
+ E -->|Пусто| H[Так не бывает: пустой поток гасит панель навсегда]
45
+ F --> I{Уход с панели}
46
+ G --> I
47
+ I -->|Есть несохранённые правки| J[Гард спрашивает — на всех путях закрытия сразу]
48
+ I -->|Нет| K[Уход идёт общим переходом к связанной записи, а не своим маршрутом]
49
+ J --> K
50
+ ```
51
+
31
52
  ## Как закон применяется здесь
32
53
 
33
54
  - **Асайд открывается маршрутом в аутлете `ro`, а не вызовом сервиса.** Программного открытия
@@ -28,6 +28,27 @@ description: Правило под «Закон о моделях сущност
28
28
  переносится между репозиториями, раскладка — нет, и путь, названный в правиле, врёт в первом
29
29
  же дереве, которое держит код иначе.
30
30
 
31
+ ## Ход
32
+
33
+ Ход заведения модели: две стороны сущности, кто из них пишется руками и где стоит перевод между
34
+ ними.
35
+
36
+ ```mermaid
37
+ flowchart TD
38
+ A[Заводится модель записи] --> B[Обе стороны живут в одном неймспейсе имени сущности]
39
+ B --> C{Сторона контракта}
40
+ C -->|Объявляется псевдонимом сгенерированного типа| D[Руками не пишется: своя копия разойдётся молча]
41
+ D --> E[Между сторонами встаёт маппер-наследник общей основы]
42
+ E --> F{Значение пришло не в том виде}
43
+ F -->|Да| G[Приведение идёт способом маппера, а не подстановкой по умолчанию]
44
+ F -->|Нет| H[Экраны читают только сторону состояния]
45
+ G --> H
46
+ H --> I{Нужны страница, порядок или отбор}
47
+ I -->|Да| J[Типы берутся из общего набора: второго такого не заводится]
48
+ I -->|Нет| K[Пустое выражается пустой строкой или нулём, а не отсутствием поля]
49
+ J --> K
50
+ ```
51
+
31
52
  ## Как закон применяется здесь
32
53
 
33
54
  - **У сущности две стороны, и обе лежат в неймспейсе `I<Сущность>`.** `Api` повторяет
@@ -21,7 +21,7 @@ description: Правило под «Закон о поставке» для д
21
21
  | задача | рабочий элемент (work item) рода `Task` или `Bug`, заголовок `[<номер>] <Что не так>`, исполнитель — учётная запись машинной работы; PR прикрепляется к нему при создании флагом `--work-items`, а коммит — строкой `AB#<номер>` |
22
22
  | очередь работ | Azure Boards проекта. Рабочий элемент попадает на доску тем, что заведён: доска показывает элементы своей области и итерации |
23
23
  | состояние задачи в очереди работ | поле `State` рабочего элемента: `New` у заведённого, `Active` у взятого в работу, `Resolved` у ждущего разбора. Набор состояний зависит от процесса проекта и назван в `implementation.md`; закрытая задача уходит из очереди слиянием |
24
- | отчёт о задаче | заголовок PR `[<номер>] <Что сделано>` — тот же номер, что у рабочего элемента, и его название, переведённое в сделанное; тип и область коммита сюда не идут |
24
+ | PR о задаче | заголовок PR `[<номер>] <Что сделано>` — тот же номер, что у рабочего элемента, и его название, переведённое в сделанное; тип и область коммита сюда не идут |
25
25
  | обсуждение правки | разбор PR: ревьювер — владелец репозитория, исполнитель — учётная запись машинной работы, метки — те же, что у рабочего элемента |
26
26
  | попадание правки в главную ветку | слияние PR; оно же запускает выкатку — `azure-pipelines.yml` |
27
27
  | образ того коммита | `IMAGE_TAG=<sha>` в командах `docker compose` на сервере |
@@ -35,6 +35,30 @@ description: Правило под «Закон о поставке» для д
35
35
  переносится между репозиториями, раскладка — нет, и путь, названный в правиле, врёт в первом
36
36
  же дереве, которое держит код иначе.
37
37
 
38
+ ## Ход
39
+
40
+ Ход работы от задачи до слияния: где стоит гард, что двигает колонку очереди работ и чем
41
+ работа кончается.
42
+
43
+ ```mermaid
44
+ flowchart TD
45
+ A[Работа начинается] --> B[Задача заводится командой и встаёт в очередь работ]
46
+ B --> C[Ветка называется номером задачи; колонка двигается тем же движением]
47
+ C --> D{Правка готова}
48
+ D -->|Нет| C
49
+ D -->|Да| E[Главная ветка влита, набор гейта прогнан целиком]
50
+ E --> F{Ветка несёт номер задачи}
51
+ F -->|Нет| G[Гард поставки отбивает открытие: за правкой ничего не стоит в очереди]
52
+ F -->|Да| H[PR открывается; не готовое к слиянию идёт черновиком]
53
+ H --> I[Колонка задачи переходит в разбор, исполнитель берёт следующую задачу]
54
+ I --> J{Прогон и разбор кончились}
55
+ J -->|Прогон красный| K[Чинится в той же ветке]
56
+ K --> J
57
+ J -->|Зелено| L[Папка задачи разбирается последним коммитом, черновик снимается]
58
+ L --> M[Слияние нажимает человек: исполнитель просит и называет номер]
59
+ G --> B
60
+ ```
61
+
38
62
  ## Как закон применяется здесь
39
63
 
40
64
  - **Коммит в главную ветку отбивается гардом.** Гард ищет вызов коммита в любом месте команды
@@ -48,6 +72,25 @@ description: Правило под «Закон о поставке» для д
48
72
  открытие, пока вершина главной ветки не стала предком текущей, и называет расхождение числом
49
73
  коммитов. PR с разошедшейся ветки показывает ревьюверу свою правку вперемешку с чужой, а всё,
50
74
  что автор проверил до публикации, он проверил от основания, которого в главной ветке уже нет.
75
+ - **Правка кода отдаётся человеку открытым PR, а не запушенной веткой.** Ветка в списке ветвей
76
+ ему не показывается, в его дела не приходит и обсуждения не имеет: до открытия PR правки для
77
+ человека нет. Открывается он тем же ходом, которым исполнитель говорит, что работу отдаёт, и
78
+ в ответе называется номером.
79
+ - **Не готовое к слиянию открывается черновиком — `az repos pr create --draft true`.** Кнопка
80
+ завершения у черновика заблокирована самим хостингом, поэтому состояние «выложено на
81
+ обозрение» и состояние «можно вливать» перестают выглядеть одинаково. Черновиком идёт всё,
82
+ что ждёт прогона конвейера, доработки или ответа на вопрос; вопрос задаётся в самом PR.
83
+ Конвейер проверок черновика по умолчанию не запускает — до снятия черновика он молчит, и это
84
+ молчание за зелёный прогон не принимается.
85
+ - **Черновик снимается отдельным вызовом — `az repos pr update --id <номер> --draft false`.**
86
+ Им исполнитель отвечает за готовность: проверки пройдены, доработок не осталось, работа
87
+ сходится с рабочим элементом. Снятие черновика и просьба влить — один ход, а не два разных
88
+ дня.
89
+ - **Заведённый рабочий элемент подтверждается ответом очереди работ, а не выводом команды
90
+ заведения.** Команда отвечает за свои вызовы: она может завести элемент и не довести его до
91
+ доски, и её собственный разбор ошибок этот случай называет. Напечатанный номер значит «вызов
92
+ прошёл», а не «работа видна тому, кто по ней придёт». Спрашивается очередь — по номеру, одним
93
+ вызовом, — и ответ читается присутствием элемента на доске, его состоянием и исполнителем.
51
94
  - **Номер ветки и номер в заголовке PR сверяются на месте, а состояние — по доске.** Формат
52
95
  читается из текста команды и работает без сети; существование рабочего элемента, его
53
96
  состояние, исполнитель и то, что он ещё открыт, — только когда есть чем спросить. Нет сети
@@ -57,15 +100,26 @@ description: Правило под «Закон о поставке» для д
57
100
  элемент переводится в `Active`, PR открыт — в `Resolved`; делает это команда перевода, а не
58
101
  набор вызовов по памяти. Перевод идёт сразу за шагом, который его вызвал: очередь работ
59
102
  читают между шагами, а не после них.
103
+ - **На доске стоят рабочие элементы, а не PR о них.** Доска показывает, что сделано и что
104
+ осталось; PR отвечает на другой вопрос — как именно сделано, — и открывается из элемента, где
105
+ связь с ним стоит сама. Здесь эта связь ставится при создании PR, поэтому отдельная карточка
106
+ не нужна вовсе, а заведённая живёт своей жизнью: состояния под неё нет, из очереди она не
107
+ уходит и остаётся в ней после слияния навсегда. Находит такие сверка очереди — строкой на
108
+ каждую.
109
+ - **Конвейер судит по составу правки, а не гоняет всё подряд.** Шаги, которым нечего проверять,
110
+ пропускаются по признаку, посчитанному от главной ветки: ветка, не тронувшая ни строки кода,
111
+ не поднимает стенда, не снимает кадров и не собирает образов. Признак объявляется переменной
112
+ задания и считается один раз, а не переспрашивается в каждом условии. Пропущенный шаг виден в
113
+ прогоне пропущенным — молча выпавший читается как пройденный.
60
114
  - **Отставшее состояние находится сверкой очереди, а не глазами.** Сверка судит состояние по
61
- отчёту в обе стороны: открытый PR при элементе не в разборе и разбор без открытого PR — оба
115
+ PR в обе стороны: открытый PR при элементе не в разборе и разбор без открытого PR — оба
62
116
  расхождения. Момента, когда задачу берут в работу, ей не видно: ветки на доске нет.
63
117
  - **Задачи, чинящиеся одной правкой, сливаются до слияния ветки.** Вторая закрывается как
64
118
  дубликат, а недостающее из неё дописывается в первую. После слияния слить уже нельзя: ветка
65
119
  въехала, и откатывается она целиком.
66
120
  - **Работа, которую одним заходом не закрыть, помечена в двух местах, и они сверяются.** Метка
67
- на доске и строка о заходах с передачей в линии работ говорят одно и то же двум читателям:
68
- исполнитель открывает карточку раньше, чем линию, а планирует по линии. Одна пометка без
121
+ на доске и строка о заходах с передачей в замысле эпика говорят одно и то же двум читателям:
122
+ исполнитель открывает карточку раньше, чем замысел эпика, а планирует по замыслу. Одна пометка без
69
123
  другой лжёт молча, поэтому сверка очереди судит пару в обе стороны. Помечается только то, что
70
124
  законно не делится: пометка объёма правом делить не становится.
71
125
  - **Слияние в главную ветку выкатывает прод.** Фильтры путей конвейера покрывают документы
@@ -88,9 +142,9 @@ description: Правило под «Закон о поставке» для д
88
142
  ветке, а задание выкатки прибито условием к главной: прогон ради проверки доходит до сборок и
89
143
  там кончается. Прогон команд задания на своей машине его не покрывает: он проверяет команды,
90
144
  а не файл конвейера, — верность самого файла читается только по списку прогонов после пуша.
91
- - **Отчёт проверяется до слияния тем же конвейером, что и главная ветка.** Проверки и сборки
145
+ - **PR проверяется до слияния тем же конвейером, что и главная ветка.** Проверки и сборки
92
146
  образов идут на конвейере проверки PR, выкатка — нет: её держит условие по главной ветке у
93
- своего задания, а образ отчёта в реестр не уезжает.
147
+ своего задания, а образ PR в реестр не уезжает.
94
148
  - **Расхождение прода с главной веткой видно сверкой очереди работ.** Рабочий элемент уходит из
95
149
  очереди слиянием, но слияние — ещё не прод: отказавшая выкатка не трогает ни элемент, ни его
96
150
  состояние, и заметить её неоткуда. Сверка спрашивает последний прогон главной ветки и судит
@@ -145,7 +199,7 @@ description: Правило под «Закон о поставке» для д
145
199
 
146
200
  Гард поставки стоит на командах агента, поэтому ветку, заведённую руками в редакторе, он не
147
201
  видит: имя такой ветки держится памятью. Требование от этого не слабеет — просто отдельной
148
- проверки под него не заводится: работа опознаётся заголовком рабочего элемента и отчёта, а это
202
+ проверки под него не заводится: работа опознаётся заголовком рабочего элемента и PR, а это
149
203
  сверяется у всех. Сверка очереди имя ветки не судит вовсе: у открытого PR его не переименовать.
150
204
 
151
205
  Взятие задачи в работу не стережёт ничто: доска ветки не видит, а гард поставки её видит, но
@@ -191,7 +245,7 @@ description: Правило под «Закон о поставке» для д
191
245
  - **Учётная запись для пуша и автор PR выбираются отдельно.** Если пушить пришлось из-под другой
192
246
  записи, на следующий вызов это не переносится: PR открывают токеном учётной записи машинной
193
247
  работы, и от того, чьей записью он открыт, зависит, кого можно назначить ревьювером. Однажды
194
- смена записи ради пуша утекла в публикацию — отчёт вышел от владельца.
248
+ смена записи ради пуша утекла в публикацию — PR вышел от владельца.
195
249
  - **Невалидный файл конвейера виден прогоном нулевой длительности сразу после пуша.** Прогон
196
250
  заводится и кончается на разборе файла, не начав ни одного задания: в списке он стоит
197
251
  отказом, а внутри нет ни задания, ни лога — читается только длительность. Поэтому список
@@ -22,7 +22,7 @@ description: Правило под «Закон о поставке» для д
22
22
  | задача | issue репозитория, заголовок `[<КЛЮЧ>-<номер>] <Что не так>`, исполнитель — учётная запись машинной работы; PR прикрепляется к нему строкой `Closes #<номер>` в теле |
23
23
  | очередь работ | борда GitHub Projects. К репозиторию она не привязана: `projectsV2` у него пуст, и задача попадает на борду только явным добавлением |
24
24
  | состояние задачи в очереди работ | колонка борды — поле «Status»: заведённая, взятая в работу, ждущая разбора. Имена колонок — в `implementation.md`; закрытая задача уходит из очереди мержем, а не переводом в последнюю колонку |
25
- | отчёт о задаче | заголовок PR `[<КЛЮЧ>-<номер>] <Что сделано>` — тот же номер, что у задачи, и её название, переведённое в сделанное; тип и область коммита сюда не идут |
25
+ | PR о задаче | заголовок PR `[<КЛЮЧ>-<номер>] <Что сделано>` — тот же номер, что у задачи, и её название, переведённое в сделанное; тип и область коммита сюда не идут |
26
26
  | обсуждение правки | разбор PR: ревьювер — владелец репозитория, исполнитель — учётная запись машинной работы, метки — те же, что у задачи |
27
27
  | попадание правки в главную ветку | мерж PR; он же запускает выкатку — `.github/workflows/deploy.yml` |
28
28
  | образ того коммита | `IMAGE_TAG=<sha>` в командах `docker compose` на сервере |
@@ -36,6 +36,30 @@ description: Правило под «Закон о поставке» для д
36
36
  переносится между репозиториями, раскладка — нет, и путь, названный в правиле, врёт в первом
37
37
  же дереве, которое держит код иначе.
38
38
 
39
+ ## Ход
40
+
41
+ Ход работы от задачи до слияния: где стоит гард, что двигает колонку очереди работ и чем
42
+ работа кончается.
43
+
44
+ ```mermaid
45
+ flowchart TD
46
+ A[Работа начинается] --> B[Задача заводится командой и встаёт в очередь работ]
47
+ B --> C[Ветка называется номером задачи; колонка двигается тем же движением]
48
+ C --> D{Правка готова}
49
+ D -->|Нет| C
50
+ D -->|Да| E[Главная ветка влита, набор гейта прогнан целиком]
51
+ E --> F{Ветка несёт номер задачи}
52
+ F -->|Нет| G[Гард поставки отбивает открытие: за правкой ничего не стоит в очереди]
53
+ F -->|Да| H[PR открывается; не готовое к слиянию идёт черновиком]
54
+ H --> I[Колонка задачи переходит в разбор, исполнитель берёт следующую задачу]
55
+ I --> J{Прогон и разбор кончились}
56
+ J -->|Прогон красный| K[Чинится в той же ветке]
57
+ K --> J
58
+ J -->|Зелено| L[Папка задачи разбирается последним коммитом, черновик снимается]
59
+ L --> M[Слияние нажимает человек: исполнитель просит и называет номер]
60
+ G --> B
61
+ ```
62
+
39
63
  ## Как закон применяется здесь
40
64
 
41
65
  - **Коммит в главную ветку отбивается гардом.** Гард ищет вызов коммита в любом месте команды
@@ -49,7 +73,7 @@ description: Правило под «Закон о поставке» для д
49
73
  разошедшейся ветки показывает ревьюверу свою правку вперемешку с чужой, а всё, что автор
50
74
  проверил до публикации, он проверил от основания, которого в главной ветке уже нет.
51
75
  - **Ключ задач задаётся один раз, и все три формы имени выводятся из него.** Заголовок задачи,
52
- имя ветки и заголовок отчёта строит один и тот же ключ: команда заведения задачи собирает по
76
+ имя ветки и заголовок PR строит один и тот же ключ: команда заведения задачи собирает по
53
77
  нему заголовок, гард поставки достаёт по нему номер из имени ветки, сверка очереди — из
54
78
  заголовка. Форма ветки в профиле дерева пишется той же парой `<КЛЮЧ>-<номер>`, а не своей
55
79
  похожей: разойдясь, они не отказывают, а перестают узнавать номер, и проверка, искавшая
@@ -58,6 +82,11 @@ description: Правило под «Закон о поставке» для д
58
82
  же вызове и называет, где ключ задаётся. Умолчания у ключа нет намеренно: собранный из
59
83
  пустого значения заголовок `[-317]` не совпадает ни с чем, и сверка очереди помечает
60
84
  неправильно названной каждую задачу — настоящее расхождение тонет среди этих строк.
85
+ - **Заведённая задача подтверждается ответом очереди работ, а не выводом команды заведения.**
86
+ Команда отвечает за свои вызовы: она может завести задачу и не довести её до борды, и её
87
+ собственный разбор ошибок этот случай называет. Напечатанный номер значит «вызов прошёл», а не
88
+ «задача видна тому, кто по ней работает». Спрашивается очередь — по номеру, одним вызовом, — и
89
+ ответ читается присутствием задачи на борде, её колонкой и исполнителем.
61
90
  - **Номер ветки и номер в заголовке PR сверяются на месте, а состояние задачи — по борде.**
62
91
  Формат читается из текста команды и работает без сети; существование задачи, её присутствие
63
92
  на борде, исполнитель и то, что она ещё открыта, — только когда есть чем спросить. Нет сети
@@ -67,15 +96,26 @@ description: Правило под «Закон о поставке» для д
67
96
  переставляется во взятые в работу, PR открыт — в ждущие разбора; делает это команда
68
97
  перевода, а не набор вызовов GraphQL по памяти. Перевод идёт сразу за шагом, который его
69
98
  вызвал: очередь работ читают между шагами, а не после них.
99
+ - **На борде стоят задачи, а не PR о них.** Борда показывает, что сделано и что осталось; PR
100
+ отвечает на другой вопрос — как именно сделано, — и открывается из карточки задачи, где связь
101
+ с ним заполняется сама полем «Linked pull requests». Карточка PR живёт своей жизнью: колонки
102
+ под неё нет, из очереди она не уходит и остаётся в ней после слияния навсегда. Заводится она
103
+ либо встроенным правилом борды, либо рукой; и то и другое выключается, а накопившееся
104
+ снимается. Находит их сверка очереди — строкой на каждую.
105
+ - **Конвейер судит по составу правки, а не гоняет всё подряд.** Шаги, которым нечего проверять,
106
+ пропускаются по признаку, посчитанному от главной ветки: ветка, не тронувшая ни строки кода,
107
+ не поднимает стенда, не снимает кадров и не собирает образов. Признак считается один раз и
108
+ объявляется выводом шага, а не переспрашивается в каждом условии. Пропущенный шаг виден в
109
+ прогоне пропущенным — молча выпавший читается как пройденный.
70
110
  - **Отставшая колонка находится сверкой очереди, а не глазами.** Сверка судит колонку по
71
- отчёту в обе стороны: открытый PR при задаче не в разборе и разбор без открытого PR — оба
111
+ PR в обе стороны: открытый PR при задаче не в разборе и разбор без открытого PR — оба
72
112
  расхождения. Момента, когда задачу берут в работу, ей не видно: ветки на борде нет.
73
113
  - **Задачи, чинящиеся одной правкой, сливаются до мержа.** Вторая стирается вместе с номером,
74
114
  а недостающее из неё дописывается в первую. После мержа слить уже нельзя: ветка въехала, и
75
115
  откатывается она целиком.
76
116
  - **Работа, которую одним заходом не закрыть, помечена в двух местах, и они сверяются.** Метка
77
- на борде и строка о заходах с передачей в линии работ говорят одно и то же двум читателям:
78
- исполнитель открывает карточку раньше, чем линию, а планирует по линии. Одна пометка без
117
+ на борде и строка о заходах с передачей в замысле эпика говорят одно и то же двум читателям:
118
+ исполнитель открывает карточку раньше, чем замысел эпика, а планирует по замыслу. Одна пометка без
79
119
  другой лжёт молча, поэтому сверка очереди судит пару в обе стороны. Помечается только то, что
80
120
  законно не делится: пометка объёма правом делить не становится.
81
121
  - **Мерж в главную ветку выкатывает прод.** Исключения по путям покрывают только документы,
@@ -99,9 +139,9 @@ description: Правило под «Закон о поставке» для д
99
139
  которая его заводит или переносит, ручной запуск не покрывает. Прогон команд задания на своей
100
140
  машине не покрывает её тоже: он проверяет команды, а не файл конвейера, — верность самого
101
141
  файла читается только по списку прогонов после пуша.
102
- - **Отчёт проверяется до мержа тем же конвейером, что и главная ветка.** Проверки и сборки
142
+ - **PR проверяется до мержа тем же конвейером, что и главная ветка.** Проверки и сборки
103
143
  образов идут на событии `pull_request`, выкатка — нет: её держит условие по главной ветке у
104
- своего задания, а образ отчёта в реестр не уезжает.
144
+ своего задания, а образ PR в реестр не уезжает.
105
145
  - **Расхождение прода с главной веткой видно сверкой очереди работ.** Задача уходит из очереди
106
146
  мержем, но мерж — ещё не прод: отказавшая выкатка не трогает ни задачу, ни её колонку, и
107
147
  заметить её неоткуда. Сверка спрашивает последний прогон главной ветки и судит только
@@ -133,6 +173,21 @@ description: Правило под «Закон о поставке» для д
133
173
  сломана, она описывает вчерашний день. Сравнение веток пишется от `origin/main` целиком —
134
174
  смешав в одной команде удалённую ссылку для одной стороны и локальную для другой, промах
135
175
  изнутри выглядит правильным.
176
+ - **Правка кода отдаётся человеку открытым PR, а не запушенной веткой.** Ветка в списке ветвей
177
+ ему не показывается, во входящие не приходит и обсуждения не имеет: до открытия PR правки
178
+ для человека нет. Открывается он тем же ходом, которым исполнитель говорит, что работу
179
+ отдаёт, и в ответе называется номером.
180
+ - **Не готовое к слиянию открывается черновиком — `gh pr create --draft`.** Кнопка слияния у
181
+ черновика заблокирована самим хостингом, поэтому состояние «выложено на обозрение» и
182
+ состояние «можно вливать» перестают выглядеть одинаково. Черновиком идёт всё, что ждёт
183
+ прогона конвейера, доработки или ответа на вопрос; вопрос задаётся в самом PR.
184
+ - **Черновик снимается отдельным вызовом — `gh pr ready <номер>`.** Им исполнитель отвечает за
185
+ готовность: проверки пройдены, доработок не осталось, работа сходится с задачей. Снятие
186
+ черновика и просьба влить — один ход, а не два разных дня.
187
+ - **Мерж PR нажимает человек, а не исполнитель работы.** Кнопка и вызов слияния равны: запрет
188
+ на них один. Исполнитель сливает свой PR только тогда, когда человек сказал это прямо и про
189
+ этот PR; сказанное об одном PR на следующий не переносится, а молчание разрешением не
190
+ бывает. Работа кончается PR, с которого снят черновик, и в ответе называется его номер.
136
191
  - **Автор PR не может быть его ревьювером.** Запрос разбора на самого себя GitHub принимает и
137
192
  молча не создаёт — разбор при этом выглядит запрошенным.
138
193
  - **Метки, исполнитель и ревьювер PR ставятся вызовами `gh api`, а не `gh pr edit`.** На
@@ -141,6 +196,15 @@ description: Правило под «Закон о поставке» для д
141
196
  - **Борда правится запросом GraphQL по идентификатору проекта.** `gh project` с `--owner`
142
197
  отвечает `unknown owner type`, когда владелец борды — не та учётная запись, под которой
143
198
  идёт вызов.
199
+ - **Почта машинного коммита копируется из компаньона, а не набирается по памяти.** Служебный
200
+ адрес GitHub — `<число>+<логин>@users.noreply.github.com`, и сопоставляется он по числу: логин
201
+ рядом с ним не сверяет никто. Коммит с чужим числом уезжает подписанным посторонним человеком,
202
+ а изнутри промах не виден — имя учётной записи в истории то самое. Ту же строку держит профиль
203
+ дерева, и гард поставки отбивает пуш при расхождении.
204
+ - **Личность машинной записи подтверждается ответом хостинга, а не узнаванием строки.** Знакомый
205
+ вид строки подтверждением не бывает — промах выглядел знакомо. Спрашивается токеном самой
206
+ записи, ответом о себе: логин и число приходят вместе. Поиск по числу для ограниченной записи
207
+ отвечает «не найдено» и её собственным токеном, поэтому годится только ответ о себе.
144
208
  - **Сценарии гардов задают настройки git сами, а не берут их с машины.** Коммит во временном
145
209
  репозитории сценария наследует общий конфиг: если включена подпись, git идёт в агент ключей,
146
210
  а заблокированный агент роняет весь набор — со стороны это выглядит сломанным гардом. Автор,
@@ -154,7 +218,7 @@ description: Правило под «Закон о поставке» для д
154
218
 
155
219
  Гард поставки стоит на командах агента, поэтому ветку, заведённую руками в редакторе, он не
156
220
  видит: имя такой ветки держится памятью. Требование от этого не слабеет — просто отдельной
157
- проверки под него не заводится: работа опознаётся заголовком задачи и отчёта, а это сверяется
221
+ проверки под него не заводится: работа опознаётся заголовком задачи и PR, а это сверяется
158
222
  у всех. Сверка очереди имя ветки не судит вовсе: у открытого PR его не переименовать.
159
223
 
160
224
  Взятие задачи в работу не стережёт ничто: борда ветки не видит, а гард поставки её видит, но
@@ -162,11 +226,11 @@ description: Правило под «Закон о поставке» для д
162
226
  работу вместо промаха. Держится это памятью и подсказкой, которую печатает команда заведения
163
227
  задачи. Отставшую колонку находит сверка очереди — но уже после того, как PR открыт.
164
228
 
165
- Свежесть самой вершины главной ветки гард не проверяет: он судит по тому, что лежит в дереве, и
166
- вершину недельной давности от сегодняшней не отличает. Сетевой вызов сюда не заводится по той же
167
- причине, что и выше, он падал бы вместе со связью. Поэтому гард ловит ветку, отставшую
168
- заведомо, а `git fetch` перед вливанием стоит первым пунктом чеклиста и держится тем же, чем
169
- держатся остальные его пункты.
229
+ Свежесть самой вершины главной ветки гард спрашивает вторым ярусом тем же приёмом, что и
230
+ состояние задачи: есть чем спросить, спрашивает; нет сети или доступа пропускает молча.
231
+ Первый ярус при этом остаётся, и работает он без сети: локальная ссылка отвечает на вопрос
232
+ «отстала ли ветка от того, что уже лежит в дереве», удалённая на вопрос «не протухла ли сама
233
+ ссылка». Без второго яруса молчание гарда значило лишь первое, а читалось как второе.
170
234
 
171
235
  ## Паттерны
172
236
 
@@ -198,7 +262,7 @@ description: Правило под «Закон о поставке» для д
198
262
  - **Учётная запись для пуша и автор PR выбираются отдельно.** Если пушить пришлось из-под другой
199
263
  записи, на следующий вызов это не переносится: PR открывают токеном учётной записи машинной
200
264
  работы, и от того, чьей записью он открыт, зависит, кого можно назначить ревьювером. Однажды
201
- смена записи ради пуша утекла в публикацию — отчёт вышел от владельца.
265
+ смена записи ради пуша утекла в публикацию — PR вышел от владельца.
202
266
  - **Невалидный файл конвейера виден прогоном нулевой длительности сразу после пуша.** GitHub
203
267
  заводит такой прогон и на ветке, на которую ни один триггер не подписан: в списке он стоит
204
268
  отказом, а внутри у него нет ни задания, ни лога — читается только длительность. Поэтому