@rt-tools/agent-kit 0.11.0 → 0.12.0

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 (115) hide show
  1. package/assets/checks/check-file-size.mjs +19 -4
  2. package/assets/checks/check-state-next.mjs +10 -2
  3. package/assets/checks/rt-kit-checks.config.mjs +16 -2
  4. package/assets/defaults/project.sh +9 -1
  5. package/assets/defaults/turn-map.md +8 -6
  6. package/assets/hooks/browser-guard-device-id.sh +3 -1
  7. package/assets/hooks/browser-guard-no-asking.sh +3 -1
  8. package/assets/hooks/browser-guard-no-other-drivers.sh +5 -3
  9. package/assets/hooks/browser-guard-require-select.sh +4 -2
  10. package/assets/hooks/claim-guard.sh +3 -1
  11. package/assets/hooks/conscience-guard.sh +3 -1
  12. package/assets/hooks/dev-server-guard.sh +5 -3
  13. package/assets/hooks/dispatch.sh +69 -0
  14. package/assets/hooks/docs-guard.sh +6 -4
  15. package/assets/hooks/exam-guard.sh +5 -3
  16. package/assets/hooks/git-guard-delivery.sh +37 -5
  17. package/assets/hooks/git-guard-main.sh +6 -4
  18. package/assets/hooks/git-guard-push-tests.sh +6 -4
  19. package/assets/hooks/grill-gate.sh +4 -2
  20. package/assets/hooks/handoff-entry-guard.sh +4 -2
  21. package/assets/hooks/handoff-write.sh +27 -6
  22. package/assets/hooks/hook-input.sh +54 -0
  23. package/assets/hooks/lint-after-edit.sh +5 -3
  24. package/assets/hooks/postmortem-guard.sh +3 -1
  25. package/assets/hooks/proposal-guard.sh +3 -1
  26. package/assets/hooks/prose-style-guard.sh +5 -3
  27. package/assets/hooks/qa-dataid-guard.sh +4 -2
  28. package/assets/hooks/rerun-guard.sh +5 -3
  29. package/assets/hooks/reuse-first-guard.sh +5 -3
  30. package/assets/hooks/rule-article.sh +99 -0
  31. package/assets/hooks/skill-gate-rearm.sh +3 -1
  32. package/assets/hooks/skill-gate.sh +23 -2
  33. package/assets/hooks/skill-loaded.sh +3 -1
  34. package/assets/hooks/sql-guard-request.sh +2 -1
  35. package/assets/hooks/sql-guard.sh +4 -2
  36. package/assets/hooks/task-flow-guard.sh +6 -4
  37. package/assets/hooks/turn-exit-guard.sh +42 -17
  38. package/assets/hooks/waiting-turn-guard.sh +3 -1
  39. package/assets/hooks/window-fill-guard.sh +6 -4
  40. package/assets/laws/work-conduct.md +5 -9
  41. package/assets/patterns/dependencies-upgrade.md +1 -1
  42. package/assets/patterns/doc-style-write.md +3 -3
  43. package/assets/patterns/git-workflow-commit.azure.md +2 -202
  44. package/assets/patterns/git-workflow-commit.github.md +2 -258
  45. package/assets/patterns/git-workflow-commit.gitlab.md +1 -217
  46. package/assets/patterns/git-workflow-docker.md +3 -3
  47. package/assets/patterns/git-workflow-merge.md +3 -2
  48. package/assets/patterns/git-workflow-migration.md +3 -3
  49. package/assets/patterns/git-workflow-pr.azure.md +224 -0
  50. package/assets/patterns/git-workflow-pr.github.md +280 -0
  51. package/assets/patterns/git-workflow-pr.gitlab.md +240 -0
  52. package/assets/patterns/git-workflow-restart.md +3 -3
  53. package/assets/patterns/git-workflow-secrets.md +3 -3
  54. package/assets/patterns/task-flow-archive.md +193 -0
  55. package/assets/patterns/task-flow-close.md +3 -173
  56. package/assets/patterns/task-flow-handoff.md +4 -4
  57. package/assets/pitfalls/doc-style.md +80 -0
  58. package/assets/pitfalls/git-workflow.azure.md +50 -0
  59. package/assets/pitfalls/git-workflow.github.md +78 -0
  60. package/assets/pitfalls/git-workflow.gitlab.md +49 -0
  61. package/assets/pitfalls/spec-driven.md +36 -0
  62. package/assets/pitfalls/styling-bem.md +45 -0
  63. package/assets/pitfalls/task-flow.md +62 -0
  64. package/assets/pitfalls/testing.md +70 -0
  65. package/assets/rules/deploy-flow.azure.md +106 -0
  66. package/assets/rules/deploy-flow.github.md +113 -0
  67. package/assets/rules/deploy-flow.gitlab.md +108 -0
  68. package/assets/rules/doc-style.md +25 -76
  69. package/assets/rules/git-workflow.azure.md +6 -92
  70. package/assets/rules/git-workflow.github.md +14 -127
  71. package/assets/rules/git-workflow.gitlab.md +6 -93
  72. package/assets/rules/spec-driven.md +39 -30
  73. package/assets/rules/styling-bem.md +20 -39
  74. package/assets/rules/task-flow.md +17 -199
  75. package/assets/rules/testing.md +3 -64
  76. package/assets/rules/turn-conduct.md +206 -0
  77. package/assets/rules/typescript-conventions.md +15 -0
  78. package/assets/skills/agent-kit.md +35 -12
  79. package/assets/templates/pitfalls.md +10 -0
  80. package/assets/templates/rule.md +5 -3
  81. package/bin/agent-kit.d.ts.map +1 -1
  82. package/bin/agent-kit.js +1 -42
  83. package/bin/agent-kit.js.map +1 -1
  84. package/lib/assets.d.ts.map +1 -1
  85. package/lib/assets.js +6 -1
  86. package/lib/assets.js.map +1 -1
  87. package/lib/cascade.d.ts.map +1 -1
  88. package/lib/cascade.js +19 -1
  89. package/lib/cascade.js.map +1 -1
  90. package/lib/commands.d.ts.map +1 -1
  91. package/lib/commands.js +1 -0
  92. package/lib/commands.js.map +1 -1
  93. package/lib/config.d.ts +16 -1
  94. package/lib/config.d.ts.map +1 -1
  95. package/lib/config.js +8 -0
  96. package/lib/config.js.map +1 -1
  97. package/lib/hooks-map.d.ts +13 -0
  98. package/lib/hooks-map.d.ts.map +1 -1
  99. package/lib/hooks-map.js +33 -1
  100. package/lib/hooks-map.js.map +1 -1
  101. package/lib/ship.d.ts +1 -2
  102. package/lib/ship.d.ts.map +1 -1
  103. package/lib/ship.js +0 -54
  104. package/lib/ship.js.map +1 -1
  105. package/package.json +1 -1
  106. package/rt-tools-agent-kit-0.12.0.tgz +0 -0
  107. package/assets/commands/agent-kit-digest.md +0 -89
  108. package/assets/commands/rules-review.md +0 -98
  109. package/assets/patterns/cargo-triage-mark.md +0 -119
  110. package/assets/rules/cargo-triage.md +0 -126
  111. package/lib/cargo-state.d.ts +0 -62
  112. package/lib/cargo-state.d.ts.map +0 -1
  113. package/lib/cargo-state.js +0 -118
  114. package/lib/cargo-state.js.map +0 -1
  115. package/rt-tools-agent-kit-0.11.0.tgz +0 -0
@@ -12,6 +12,9 @@ description: Правило под «Закон о документации пр
12
12
  Устройство спеков и слоёв документации — правило `spec-driven` под тем же законом; здесь
13
13
  только формулировки.
14
14
 
15
+ **Холодная часть:** `pitfalls.md` рядом — ловушки, грабли, на которые уже наступали.
16
+ Грузится по требованию, а не вместе с правилом.
17
+
15
18
  ## Как это называется здесь
16
19
 
17
20
  | В законе | Здесь |
@@ -58,42 +61,62 @@ flowchart TD
58
61
  которые едут в репозиторий: личный черновик, закрытый `.gitignore` или
59
62
  `.git/info/exclude`, проверка не читает — мёртвая ссылка в нём держала гейт пуша, хотя ни
60
63
  в одну ветку этот файл не попадёт.
64
+ <!-- rt-when: *.md -->
65
+
61
66
  - **Голое имя и каталог судятся наравне с полным путём.** Имя без каталога ищется по всему
62
67
  дереву, каталог — среди каталогов; дерево спрашивается у системы контроля версий, иначе
63
68
  каталоги, начинающиеся с точки, не видны и всё, что в них лежит, читалось бы как
64
69
  несуществующее. Половина строк в таблицах «Где это лежит» — как раз каталоги.
70
+ <!-- rt-when: *.md -->
71
+
65
72
  - **Описание прошлого из проверки путей выведено целиком.** Архив по устройству называет
66
73
  файлы, которых уже нет, и правкой это не лечится. Папка задачи выведена по той же причине:
67
74
  раздел находок в ходе работы перечисляет ровно то, чего в дереве нет.
75
+ <!-- rt-when: *.md -->
76
+
68
77
  - **Переносимый текст из сверки адресов выведен, как архив.** Закон, правило и паттерн написаны
69
78
  для любого дерева этого класса, и адреса в них принадлежат тому дереву, куда текст ложится:
70
79
  `libs/common/util` там, где корни зовутся иначе, — пример, а не мёртвая ссылка. Разложенную
71
80
  копию проверка узнаёт по шапке раскладки, исходник — по каталогу, названному в настройке; без
72
81
  этого сверка краснеет на полторы сотни строк, ни одна из которых не чинится здесь.
82
+ <!-- rt-when: *.md -->
83
+
73
84
  - **Указатель каталога сверяется с его содержимым обеими сторонами.** Записи каталог набирает
74
85
  быстрее, чем читают его указатель, и промах не виден ни в сборке, ни в браузере: запись,
75
86
  приехавшая слиянием соседней ветки, просто не попадает в таблицу. Сверенный руками указатель
76
87
  расходится снова через сутки.
88
+ <!-- rt-when: *.md -->
89
+
77
90
  - **Имя, названное затем, чтобы сказать «его нет», стоит в списке исключений поимённо.**
78
91
  Отличить такое упоминание от ссылки машине нечем, а текст без него теряет смысл: правило и
79
92
  замысел предупреждают именно о снятом. Туда же — то, что появляется только после сборки,
80
93
  имена веток и правила линтеров: выглядят адресом, адресом не являются.
94
+ <!-- rt-when: *.md -->
95
+
81
96
  - **Документ едет в том же коммите, что и правка, которую он описывает.** Обход — строка
82
97
  `Docs-skip: <причина>` в теле коммита; пустая причина не принимается.
98
+ <!-- rt-when: *.md -->
99
+
83
100
  - **Документ не длиннее предела длины.** Текст, который не влезает на экран целиком, дописывают
84
101
  в конец, не перечитав начала, — так в одном документе и оказываются два ответа на один вопрос.
85
- Предел тот же, что у кода, и считается так же — все строки; выросший спек делится на
86
- поддомены, а не переносит границу. Описание прошлого из счёта выведено: архив по устройству
102
+ Предел у текста свой, ниже, чем у кода, и считается так же — все строки; выросший спек
103
+ делится на поддомены, а не переносит границу. Два числа вместо одного заведены потому, что
104
+ тексту порог нужен раньше: у кода длину стережёт ещё и линтер, а у прозы — только это число. Описание прошлого из счёта выведено: архив по устройству
87
105
  перечисляет то, чего в дереве уже нет, а папка задачи умирает со слиянием.
106
+ <!-- rt-when: *.md -->
107
+
88
108
  - **Файл, уезжающий в описание прошлого, называет в шапке свой прежний адрес.** Записи архива
89
109
  ссылались на него, пока он был живым, и после переезда эти ссылки ведут в пустоту: проверка
90
110
  путей архив не читает вовсе, поэтому промах не краснеет никогда. Найти переехавшее нечем —
91
111
  имя записи архива с прежним адресом не совпадает, и поиск по нему её не показывает. Одна
92
112
  строка в шапке дешевле правки всех ссылающихся записей и прошлого не трогает.
113
+ <!-- rt-when: *.md -->
114
+
93
115
  - **Текст, называющий состояние машины, устаревает без единой правки в дереве.** Ловушка о том,
94
116
  что на машине установлено, верна в день, когда её пишут, и становится неправдой сама собой —
95
117
  ни одна сверка этого не видит: они читают дерево, а состарилась машина. Утверждение о машине
96
118
  пишется способом её спросить: команда и то, с чем сверять ответ, вместо снимка ответа.
119
+ <!-- rt-when: *.md -->
97
120
 
98
121
  ## Чего из закона здесь нет
99
122
 
@@ -127,77 +150,3 @@ flowchart TD
127
150
 
128
151
  Читается этот список раньше остального: правило говорит, как формулировать, а скил дерева — что
129
152
  у документа этого рода обязательно есть, вплоть до второго файла рядом.
130
-
131
- ## Ловушки
132
-
133
- - **Оставшаяся работа не записывается в документ, а заводится задачей.** `docs/BACKLOG.md`
134
- держит только то, что задачей не бывает: договорённости и решения, которые решено не
135
- править. Признак — утверждение остаётся, если править его никто не собирается. «Сделать
136
- потом» в плане, README или спеке — второй список работ: он расходится с бордой молча, а
137
- разбирать его потом дороже, чем завести задачу сразу. Из 1411 строк документа действующими
138
- оказались 71, и на разбор остальных ушла отдельная задача. Как разбирать накопившееся —
139
- паттерн `doc-style-sweep`.
140
- - **Словарь действует и на разговор с владельцем, не только на файлы.** Он приходит в контекст
141
- на запуске сессии, поэтому «не читал» основанием не бывает. Слово из левой колонки «Так не
142
- пишем» всплывало именно в ответах: в дереве его уже вычистили, а в PR о сделанном оно
143
- оставалось, и владелец читал ровно то слово, от которого отказались.
144
- - **Термин берётся из `docs/GLOSSARY.md`, а не придумывается на месте.** Слова, которого там
145
- нет, у читателя нет тоже: «журнал приложения» простоял в спеке почты, пока владелец не
146
- спросил, что это, — оказалось, логи бэкенда, а слово «журнал» здесь уже занято журналом
147
- событий. Новое слово либо заводится в словаре вместе с правкой, либо заменяется тем, что
148
- уже есть.
149
- - **Проход по словарю глазами слово не находит.** «Формулировки приведены к словарю» означает
150
- ровно те строки, которые в тот момент читали: «спека» пережила такой проход и осталась в
151
- соседней строке того же файла. Слово из левой колонки таблицы «Так не пишем» вычищается
152
- грепом по всему дереву, а не вычиткой. Форма задаётся точно: «спек» — документ — склоняется
153
- в «спека» и «спеки» тоже, и совпадений по корню законных больше, чем нарушений; ищутся
154
- сочетания («спеки на … нет», «спека проверяет»), а не корень.
155
- - **Снятое имя вычищается одним грепом по всему дереву:** правила, их зеркала в скилах,
156
- документы и комментарии. Описание того, чего в коде уже нет, читается как действующее
157
- указание.
158
- - **У снятого слова второе значение возвращается после сплошной замены, а не обходится до
159
- неё.** Слово снимают ровно потому, что оно стояло над двумя вещами, и второе значение при
160
- этом остаётся законным. Отобрать его заранее нечем: какое из двух значений в строке, видно
161
- только по соседнему тексту, а строк бывают сотни. Порядок обратный — сплошная замена, затем
162
- сплошной просмотр самой правки, и найденное второе значение возвращается поимённо. Из 353
163
- замен так вернулись шесть, и две первые были поломкой: сверка печатала новое имя дважды
164
- подряд, а комментарий обещал «поломку вместо PR о том, что долгов нет». Просматривается
165
- правка, а не дерево после неё: в дереве обе стороны выглядят одинаково верными.
166
- - **Поиск по дереву не покрывает того, что уже уехало наружу.** Заголовок задачи и её тело,
167
- заголовок PR и его тело, заголовки коммитов лежат вне файлов, и проверки текстов их не
168
- читают вовсе. Вычистив слово в дереве, обходят те же места в очереди работ и в истории:
169
-
170
- ```bash
171
- <клиент хостинга> api "<путь к PR>" --jq '.title, .body' | grep -i '<слово>'
172
- <клиент хостинга> api "<путь к задаче>" --jq '.title, .body' | grep -i '<слово>'
173
- git log --format='%s%n%b' <база>..HEAD | grep -i '<слово>'
174
- ```
175
-
176
- Заголовок PR правится вызовом хостинга, заголовок коммита — только переписыванием ветки,
177
- поэтому его проверяют до пуша. Выдуманное слово было вычищено из трёх файлов и объявлено
178
- снятым, а в заголовке PR и в заголовке коммита осталось — владелец прочитал именно его.
179
-
180
- - **Число в тексте пересчитывается командой в том же коммите, где пишется.** Оно стареет
181
- внутри одной ветки: «шестнадцать пар» стало неправдой через два коммита после того, как
182
- было написано, и нашёл это владелец, а не проверка. Число, которое придётся пересчитывать
183
- при каждой правке, лучше не писать вовсе. Число, полученное разбором текста, сверяется на
184
- выборке руками до того, как его называют: разбор, не знающий второй формы записи, ошибается
185
- молча — «51 пункт без задачи» оказался шестью, потому что номер стоял и отдельной строкой, и
186
- в заголовке подраздела.
187
- - **Названная в тексте проверка запускается, а не пересказывается.** «Проверка есть» и
188
- «проверка проходит» — разные утверждения, и второго в тексте обычно нет вовсе. Из четырёх
189
- проверок, названных правилом, три оказались не в том состоянии, в каком текст их описывает:
190
- одна отдавала полтора десятка замечаний, вторая переписывала файлы самим запуском, третья
191
- была красной и роняла общую сводку вместе с собой. Ни одна из трёх не входила в выкатку,
192
- поэтому молчание было полным. Проверку, которая переписывает файлы, запускают на чистом
193
- дереве: иначе её правки уедут чужим коммитом.
194
- - **Сделанность читается по дереву, а не по тексту, который о ней написан.** Это верно в обе
195
- стороны: строка про README обеих либ была вычеркнута как сделанная, а README остался с
196
- прежним числом импортёров; задача, названная владельцу несделанной, оказалась наполовину
197
- закрытой и покрытой сценариями хука. Пункт плана и тело задачи описывают день, когда их
198
- написали, и с тех пор не менялись.
199
- - **Комментарий в файле — такое же утверждение, как строка в документе.** Обоснование «строки
200
- идут во всю ширину панели, иначе подсветка обрывается» было выдумано, прожило три сессии и
201
- каждый раз читалось как основание вёрстку не трогать.
202
- - **Чужие проекты не упоминаются нигде** — ни имени репозитория, ни «портировано из», ни
203
- ссылок на его файлы. Описывается то, что код делает здесь, в терминах этого проекта.
@@ -2,7 +2,7 @@
2
2
  name: git-workflow
3
3
  kind: rule
4
4
  law: delivery
5
- description: Правило под «Закон о поставке» для дерева в Azure DevOps. Брать на заведение задачи, ветки, коммит, пуш, создание PR, слияние, а также на правку схемы хранилища, её миграций и вызовы миграций. Называет рабочий элемент как начало работы, его состояние как ход работы, соответствие задачи и ветки один к одному, имя ветки, формат коммита, учётную запись машинной работы, обязательный состав PR, гарды поставки и сверку очереди работ. Готовый код — в паттернах git-workflow-commit, git-workflow-merge, git-workflow-migration, git-workflow-restart, git-workflow-docker и git-workflow-secrets.
5
+ description: Правило под «Закон о поставке» для дерева в Azure DevOps. Брать на заведение задачи, ветки, коммит, пуш, создание PR, слияние, а также на правку схемы хранилища, её миграций и вызовы миграций. Называет рабочий элемент как начало работы, его состояние как ход работы, соответствие задачи и ветки один к одному, имя ветки, формат коммита, учётную запись машинной работы, обязательный состав PR, гарды поставки и сверку очереди работ. Готовый код — в паттернах git-workflow-commit, git-workflow-pr, git-workflow-merge. Выкатка, образы и миграции — правило deploy-flow.
6
6
  ---
7
7
 
8
8
  # Поставка — как это устроено здесь
@@ -12,6 +12,9 @@ description: Правило под «Закон о поставке» для д
12
12
  учётная запись машинной работы и области коммита — при этом дереве, в `implementation.md`
13
13
  рядом: их не угадать, и общими они не бывают.
14
14
 
15
+ **Холодная часть:** `pitfalls.md` рядом — ловушки, грабли, на которые уже наступали.
16
+ Грузится по требованию, а не вместе с правилом.
17
+
15
18
  ## Как это называется здесь
16
19
 
17
20
  | В законе | Здесь |
@@ -23,9 +26,6 @@ description: Правило под «Закон о поставке» для д
23
26
  | состояние задачи в очереди работ | поле `State` рабочего элемента: `New` у заведённого, `Active` у взятого в работу, `Resolved` у ждущего разбора. Набор состояний зависит от процесса проекта и назван в `implementation.md`; закрытая задача уходит из очереди слиянием |
24
27
  | PR о задаче | заголовок PR `[<номер>] <Что сделано>` — тот же номер, что у рабочего элемента, и его название, переведённое в сделанное; тип и область коммита сюда не идут |
25
28
  | обсуждение правки | разбор PR: ревьювер — владелец репозитория, исполнитель — учётная запись машинной работы, метки — те же, что у рабочего элемента |
26
- | попадание правки в главную ветку | слияние PR; оно же запускает выкатку — `azure-pipelines.yml` |
27
- | образ того коммита | `IMAGE_TAG=<sha>` в командах `docker compose` на сервере |
28
- | изменение хранилища | миграция в `prisma/migrations/<метка>_<имя>/` |
29
29
  | запись о правке | коммит формата `type(scope): description` — типы `feat`, `fix`, `refactor`, `docs`, `style`, `test`, `chore`, `perf`; области — в `implementation.md` |
30
30
  | автор машинной работы | отдельная учётная запись; её имя и место токена — в `implementation.md`. Токен лежит вне репозитория |
31
31
 
@@ -128,11 +128,6 @@ flowchart TD
128
128
  не нужна вовсе, а заведённая живёт своей жизнью: состояния под неё нет, из очереди она не
129
129
  уходит и остаётся в ней после слияния навсегда. Находит такие сверка очереди — строкой на
130
130
  каждую.
131
- - **Конвейер судит по составу правки, а не гоняет всё подряд.** Шаги, которым нечего проверять,
132
- пропускаются по признаку, посчитанному от главной ветки: ветка, не тронувшая ни строки кода,
133
- не поднимает стенда, не снимает кадров и не собирает образов. Признак объявляется переменной
134
- задания и считается один раз, а не переспрашивается в каждом условии. Пропущенный шаг виден в
135
- прогоне пропущенным — молча выпавший читается как пройденный.
136
131
  - **Отставшее состояние находится сверкой очереди, а не глазами.** Сверка судит состояние по
137
132
  PR в обе стороны: открытый PR при элементе не в разборе и разбор без открытого PR — оба
138
133
  расхождения. Момента, когда задачу берут в работу, ей не видно: ветки на доске нет.
@@ -144,36 +139,6 @@ flowchart TD
144
139
  исполнитель открывает карточку раньше, чем замысел эпика, а планирует по замыслу. Одна пометка без
145
140
  другой лжёт молча, поэтому сверка очереди судит пару в обе стороны. Помечается только то, что
146
141
  законно не делится: пометка объёма правом делить не становится.
147
- - **Слияние в главную ветку выкатывает прод.** Фильтры путей конвейера покрывают документы
148
- отдельно, поэтому переменные окружения, секреты и записи имён ставятся до слияния, а не
149
- после.
150
- - **Признак режима объявлен в образе, а не только в составе прода.** Значение, заданное
151
- составом, действует лишь на контейнер, поднятый этим составом; ручной прогон того же образа
152
- идёт с пустым значением, а пусто здесь означает локалхост — со всеми отладочными
153
- умолчаниями, которые он разрешает. Умолчание образа задаётся в самом образе.
154
- - **Образы выкатываются по sha коммита, а не по метке «последний».** Метка в реестре отстаёт
155
- от главной ветки, и прод молча возвращается к прежней версии, продолжая отвечать.
156
- - **Выкатка убирает за собой старые образы, оставляя три последних sha.** Помеченный sha образ
157
- висячим не бывает никогда, и чистка висячего его не касается: за полгода они съедают диск
158
- сервера целиком. Три sha — это глубина отката, и меньше брать нельзя: поломка, замеченная
159
- через две выкатки, откатывается уже некуда.
160
- - **Описание прода правится вместе с составом прода.** Устройство, путь запроса, гейты и
161
- бэкапы описаны текстами вне слоёв правил, и ни линтер, ни сборка их не читают: расхождение
162
- копится молча, а читают эти тексты как действующие. Пару стережёт гард документов.
163
- - **Правка конвейера прогоняется до слияния ручным запуском.** Конвейер запускается на любой
164
- ветке, а задание выкатки прибито условием к главной: прогон ради проверки доходит до сборок и
165
- там кончается. Прогон команд задания на своей машине его не покрывает: он проверяет команды,
166
- а не файл конвейера, — верность самого файла читается только по списку прогонов после пуша.
167
- - **PR проверяется до слияния тем же конвейером, что и главная ветка.** Проверки и сборки
168
- образов идут на конвейере проверки PR, выкатка — нет: её держит условие по главной ветке у
169
- своего задания, а образ PR в реестр не уезжает.
170
- - **Расхождение прода с главной веткой видно сверкой очереди работ.** Рабочий элемент уходит из
171
- очереди слиянием, но слияние — ещё не прод: отказавшая выкатка не трогает ни элемент, ни его
172
- состояние, и заметить её неоткуда. Сверка спрашивает последний прогон главной ветки и судит
173
- только завершённый: идущий ещё может кончиться выкаткой.
174
- - **Цепочка миграций прогоняется с пустого хранилища до слияния.** Порядок применения
175
- лексикографический по имени каталога, а метку времени ставит момент создания: миграция из
176
- ветки, начатой раньше, встаёт перед той, от которой зависит.
177
142
  - **Документ едет в том же коммите, что и правка.** Обход — строка `Docs-skip: <причина>` в
178
143
  теле коммита; пустая причина не принимается.
179
144
  - **Заголовок коммита сверяется с форматом на месте.** Разобранный по типу и области
@@ -219,10 +184,6 @@ flowchart TD
219
184
  репозитории сценария наследует общий конфиг: если включена подпись, git идёт в агент ключей,
220
185
  а заблокированный агент роняет весь набор — со стороны это выглядит сломанным гардом. Автор,
221
186
  почта и подпись передаются флагами `-c` прямо в команду.
222
- - **Расхождение миграций со схемой меряется на теневом хранилище, а не на том, где работает
223
- тот, кто пушит.** Оно законно несёт след любой недоделанной ветки, и сверка с ним держала бы
224
- чужую правку. Гейт и выкатка зовут одну и ту же проверку — иначе «сошлось» станет значить в
225
- двух местах разное.
226
187
 
227
188
  ## Чего из закона здесь нет
228
189
 
@@ -249,54 +210,7 @@ flowchart TD
249
210
 
250
211
  ## Паттерны
251
212
 
252
- - `git-workflow-commit` — рабочий элемент, ветка, коммит, пуш и PR от учётной записи машинной
213
+ - `git-workflow-commit` — рабочий элемент, ветка, коммит и пуш от учётной записи машинной работы.
214
+ - `git-workflow-pr` — открытие PR, черновик и его снятие, тело, ревьювер, привязка к элементу.
253
215
  работы.
254
216
  - `git-workflow-merge` — главная ветка влита в ветку задачи, конфликт разобран.
255
- - `git-workflow-migration` — правка схемы хранилища и её миграций.
256
- - `git-workflow-restart` — ручной перезапуск прода.
257
- - `git-workflow-docker` — образы на своей машине: демон, реестр, сборка под платформу сервера.
258
- - `git-workflow-secrets` — ключи внешних служб: где лежат, как заводятся, что говорит их состояние.
259
-
260
- ## Ловушки
261
-
262
- - **Одна работа — одна задача, сколько бы файлов она ни задела.** Числа, за которым правка
263
- становится вторым рабочим элементом, здесь нет: делится то, что придётся откатывать порознь.
264
- Сплошная правка текстов дерева была заведена тремя задачами «по объёму» — пришлось стирать
265
- два рабочих элемента, закрывать два PR и переносить коммиты по одному с двумя конфликтами.
266
- Одна из трёх не дала коммита вовсе: правка тел уже заведённых задач веткой не бывает и задачей
267
- под ветку тоже.
268
- - **Рабочий элемент заводится командой, а не вызовами подряд.** Доска показывает элементы своей
269
- области и итерации, и заведённый мимо них в очереди работ не виден: со стороны это выглядит
270
- так же, как незаведённый. Команда заведения ставит все поля разом — род, состояние,
271
- исполнителя, область и итерацию, — и печатает готовую строку заведения ветки. Замеченный по
272
- ходу дефект проходит тот же путь.
273
- - **Ветка заводится вторым вызовом, а не тем же.** Гард главной ветки отклоняет составную
274
- «создать ветку и сразу коммитить» целиком: ветки в момент разбора ещё нет.
275
- - **Сторона конфликта бывает удалением, и «сохранить обе стороны» заводит второе объявление.**
276
- Главная ветка снимает объявление, потому что символ переехал, — в конфликте это выглядит как
277
- сторона, которая ничего не дописала. Разбирается чтением версии главной ветки целиком, а не по
278
- хунку, и сверяется проверкой повторов: обе копии сами по себе исправны, сборка и линт зелёные.
279
- - **Учётная запись для пуша и автор PR выбираются отдельно.** Если пушить пришлось из-под другой
280
- записи, на следующий вызов это не переносится: PR открывают токеном учётной записи машинной
281
- работы, и от того, чьей записью он открыт, зависит, кого можно назначить ревьювером. Однажды
282
- смена записи ради пуша утекла в публикацию — PR вышел от владельца.
283
- - **Невалидный файл конвейера виден прогоном нулевой длительности сразу после пуша.** Прогон
284
- заводится и кончается на разборе файла, не начав ни одного задания: в списке он стоит
285
- отказом, а внутри нет ни задания, ни лога — читается только длительность. Поэтому список
286
- прогонов ветки смотрится тем же движением, что и пуш: `az pipelines runs list` по своей
287
- ветке.
288
- - **`online` у агента на своей машине означает запущенный процесс, а не работающий конвейер.**
289
- Две стороны сходятся отдельно: требования заданий и возможности самого агента в его пуле.
290
- Пока пересечения нет, агент стоит `online` и не берёт ничего, а задания ждут размещённого
291
- пула — по состоянию это выглядит настроенным. Владельцу называют выполненное задание с его
292
- номером, а не строку состояния.
293
- - **Вход в реестр образов из агента, запущенного службой, отказывает молча.** Служба идёт без
294
- сеанса пользователя, а клиент реестра уходит в системный помощник хранения ключей и получает
295
- отказ во взаимодействии — задание падает до сборки. Свой каталог настроек с пустым помощником
296
- не спасает: клиент переписывает пустое значение обратно сам. Готовые команды — паттерн
297
- `git-workflow-docker`.
298
- - **Новое рабочее дерево получает только то, что лежит в индексе.** `git worktree add`
299
- разворачивает коммит, а настройки, ключи, локальные разрешения и зависимости в коммит не
300
- входят: свежее дерево выглядит готовым и упирается в нехватку не сразу, а на первом гарде,
301
- которому нужен ключ. Что именно переносится руками, названо списком в компаньоне правила, и
302
- список пополняется тем же движением, которым заводится новый файл вне индекса.
@@ -2,7 +2,7 @@
2
2
  name: git-workflow
3
3
  kind: rule
4
4
  law: delivery
5
- description: Правило под «Закон о поставке» для дерева на GitHub. Брать на заведение задачи, ветки, коммит, пуш, создание PR, мерж, а также на правку схемы хранилища, её миграций и вызовы миграций. Называет задачу на борде как начало работы, колонку задачи как ход работы, соответствие задачи и ветки один к одному, имя ветки, формат коммита, учётную запись машинной работы, обязательный состав PR, гарды поставки и сверку очереди работ. Готовый код — в паттернах git-workflow-commit, git-workflow-merge, git-workflow-migration, git-workflow-restart, git-workflow-docker и git-workflow-secrets.
5
+ description: Правило под «Закон о поставке» для дерева на GitHub. Брать на заведение задачи, ветки, коммит, пуш, создание PR, мерж, а также на правку схемы хранилища, её миграций и вызовы миграций. Называет задачу на борде как начало работы, колонку задачи как ход работы, соответствие задачи и ветки один к одному, имя ветки, формат коммита, учётную запись машинной работы, обязательный состав PR, гарды поставки и сверку очереди работ. Готовый код — в паттернах git-workflow-commit, git-workflow-pr, git-workflow-merge. Выкатка, образы и миграции — правило deploy-flow.
6
6
  ---
7
7
 
8
8
  # Поставка — как это устроено здесь
@@ -12,6 +12,9 @@ description: Правило под «Закон о поставке» для д
12
12
  учётная запись машинной работы и области коммита — при этом дереве, в `implementation.md`
13
13
  рядом: их не угадать, и общими они не бывают.
14
14
 
15
+ **Холодная часть:** `pitfalls.md` рядом — ловушки, грабли, на которые уже наступали.
16
+ Грузится по требованию, а не вместе с правилом.
17
+
15
18
  ## Как это называется здесь
16
19
 
17
20
  | В законе | Здесь |
@@ -24,9 +27,6 @@ description: Правило под «Закон о поставке» для д
24
27
  | состояние задачи в очереди работ | колонка борды — поле «Status»: заведённая, взятая в работу, ждущая разбора. Имена колонок — в `implementation.md`; закрытая задача уходит из очереди мержем, а не переводом в последнюю колонку |
25
28
  | PR о задаче | заголовок PR `[<КЛЮЧ>-<номер>] <Что сделано>` — тот же номер, что у задачи, и её название, переведённое в сделанное; тип и область коммита сюда не идут |
26
29
  | обсуждение правки | разбор PR: ревьювер — владелец репозитория, исполнитель — учётная запись машинной работы, метки — те же, что у задачи |
27
- | попадание правки в главную ветку | мерж PR; он же запускает выкатку — `.github/workflows/deploy.yml` |
28
- | образ того коммита | `IMAGE_TAG=<sha>` в командах `docker compose` на сервере |
29
- | изменение хранилища | миграция в `prisma/migrations/<метка>_<имя>/` |
30
30
  | запись о правке | коммит формата `type(scope): description` — типы `feat`, `fix`, `refactor`, `docs`, `style`, `test`, `chore`, `perf`; области — в `implementation.md` |
31
31
  | автор машинной работы | отдельная учётная запись; её имя и место токена — в `implementation.md`. Токен лежит вне репозитория |
32
32
 
@@ -123,11 +123,6 @@ flowchart TD
123
123
  под неё нет, из очереди она не уходит и остаётся в ней после слияния навсегда. Заводится она
124
124
  либо встроенным правилом борды, либо рукой; и то и другое выключается, а накопившееся
125
125
  снимается. Находит их сверка очереди — строкой на каждую.
126
- - **Конвейер судит по составу правки, а не гоняет всё подряд.** Шаги, которым нечего проверять,
127
- пропускаются по признаку, посчитанному от главной ветки: ветка, не тронувшая ни строки кода,
128
- не поднимает стенда, не снимает кадров и не собирает образов. Признак считается один раз и
129
- объявляется выводом шага, а не переспрашивается в каждом условии. Пропущенный шаг виден в
130
- прогоне пропущенным — молча выпавший читается как пройденный.
131
126
  - **Отставшая колонка находится сверкой очереди, а не глазами.** Сверка судит колонку по
132
127
  PR в обе стороны: открытый PR при задаче не в разборе и разбор без открытого PR — оба
133
128
  расхождения. Момента, когда задачу берут в работу, ей не видно: ветки на борде нет.
@@ -139,36 +134,6 @@ flowchart TD
139
134
  исполнитель открывает карточку раньше, чем замысел эпика, а планирует по замыслу. Одна пометка без
140
135
  другой лжёт молча, поэтому сверка очереди судит пару в обе стороны. Помечается только то, что
141
136
  законно не делится: пометка объёма правом делить не становится.
142
- - **Мерж в главную ветку выкатывает прод.** Исключения по путям покрывают только документы,
143
- поэтому переменные окружения, секреты и записи имён ставятся до мержа, а не после.
144
- - **Признак режима объявлен в образе, а не только в составе прода.** Значение, заданное
145
- составом, действует лишь на контейнер, поднятый этим составом; ручной прогон того же образа
146
- идёт с пустым значением, а пусто здесь означает локалхост — со всеми отладочными
147
- умолчаниями, которые он разрешает. Умолчание образа задаётся в самом образе.
148
- - **Образы выкатываются по sha коммита, а не по метке «последний».** Метка в реестре отстаёт
149
- от главной ветки, и прод молча возвращается к прежней версии, продолжая отвечать.
150
- - **Убирает за собой и та машина, которая образы собирает.** Отбор у обеих один — своё имя
151
- реестра, три последних sha, поднятые контейнеры остаются, — и зовётся он одним сценарием:
152
- разойдясь, две чистки начали бы оставлять разное, а заметить это нечем. Отличаются они
153
- хвостом: сервер снимает следом висячие слои и кэш сборки, машина сборки оставляет их себе,
154
- иначе каждая сборка идёт как первая. Чистка на сборке не ждёт мержа: образ ветки занимает
155
- столько же места, в реестр не уезжает вовсе и точкой отката не бывает.
156
- - **Выкатка убирает за собой старые образы, оставляя три последних sha.** Помеченный sha образ
157
- висячим не бывает никогда, и чистка висячего его не касается: за полгода они съедают диск
158
- сервера целиком. Три sha — это глубина отката, и меньше брать нельзя: поломка, замеченная
159
- через две выкатки, откатывается уже некуда.
160
- - **Описание прода правится вместе с составом прода.** Устройство, путь запроса, гейты и
161
- бэкапы описаны текстами вне слоёв правил, и ни линтер, ни сборка их не читают: расхождение
162
- копится молча, а читают эти тексты как действующие. Пару стережёт гард документов.
163
- - **Правка конвейера прогоняется до мержа ручным запуском.** `workflow_dispatch` у выкатки
164
- запускает её на любой ветке, а сама выкатка прибита условием к главной: прогон ради проверки
165
- доходит до сборок и там кончается. Триггер регистрируется по главной ветке, поэтому правку,
166
- которая его заводит или переносит, ручной запуск не покрывает. Прогон команд задания на своей
167
- машине не покрывает её тоже: он проверяет команды, а не файл конвейера, — верность самого
168
- файла читается только по списку прогонов после пуша.
169
- - **PR проверяется до мержа тем же конвейером, что и главная ветка.** Проверки и сборки
170
- образов идут на событии `pull_request`, выкатка — нет: её держит условие по главной ветке у
171
- своего задания, а образ PR в реестр не уезжает.
172
137
  - **Вершина открытого PR без прогона видна сверкой очереди работ.** Страница PR без прогона
173
138
  выглядит так же, как страница с зелёным: цвета у неё нет ни там, ни там, — и вершину, за
174
139
  которой прогон не встал, замечали только тем, что открывали список прогонов руками. Сверка
@@ -185,13 +150,6 @@ flowchart TD
185
150
  метку хостинг показывает только внутри самого PR. Непосчитанная сливаемость расхождением не
186
151
  считается: хостинг считает её заново после каждой правки главной ветки, и строка на
187
152
  «ещё не посчитано» краснела бы на каждой свежей вершине.
188
- - **Расхождение прода с главной веткой видно сверкой очереди работ.** Задача уходит из очереди
189
- мержем, но мерж — ещё не прод: отказавшая выкатка не трогает ни задачу, ни её колонку, и
190
- заметить её неоткуда. Сверка спрашивает последний прогон главной ветки и судит только
191
- завершённый: идущий ещё может кончиться выкаткой.
192
- - **Цепочка миграций прогоняется с пустого хранилища до мержа.** Порядок применения
193
- лексикографический по имени каталога, а метку времени ставит момент создания: миграция из
194
- ветки, начатой раньше, встаёт перед той, от которой зависит.
195
153
  - **Документ едет в том же коммите, что и правка.** Обход — строка `Docs-skip: <причина>` в
196
154
  теле коммита; пустая причина не принимается.
197
155
  - **Заголовок коммита сверяется с форматом на месте.** Разобранный по типу и области
@@ -269,6 +227,14 @@ flowchart TD
269
227
  на них один. Исполнитель сливает свой PR только тогда, когда человек сказал это прямо и про
270
228
  этот PR; сказанное об одном PR на следующий не переносится, а молчание разрешением не
271
229
  бывает. Работа кончается PR, с которого снят черновик, и в ответе называется его номер.
230
+ - **Личность вызова, открывающего заявку, стережёт гард поставки, а не память исполнителя.**
231
+ Она приходит окружением, и из текста команды видна только явной подстановкой токена — её гард
232
+ и требует, называя переменную и готовую строку; дерево, не назвавшее машинной записи,
233
+ требования не получает. Ответ хостинга об авторе он спрашивает позже, на снятии черновика:
234
+ это последний ход, где промах ещё исправим — влитую заявку не переоткрыть, а автора у неё не
235
+ сменить. Прежде обе стороны держались статьёй и разбором происшествия, и промах повторился на
236
+ третий день.
237
+
272
238
  - **Автор PR не может быть его ревьювером.** Запрос разбора на самого себя GitHub принимает и
273
239
  молча не создаёт — разбор при этом выглядит запрошенным. Поэтому в разборе считаются только
274
240
  запрос и отзыв не от автора: гард снятия черновика читает состояние PR именно так.
@@ -291,10 +257,6 @@ flowchart TD
291
257
  репозитории сценария наследует общий конфиг: если включена подпись, git идёт в агент ключей,
292
258
  а заблокированный агент роняет весь набор — со стороны это выглядит сломанным гардом. Автор,
293
259
  почта и подпись передаются флагами `-c` прямо в команду.
294
- - **Расхождение миграций со схемой меряется на теневом хранилище, а не на том, где работает
295
- тот, кто пушит.** Оно законно несёт след любой недоделанной ветки, и сверка с ним держала бы
296
- чужую правку. Гейт и выкатка зовут одну и ту же проверку — иначе «сошлось» станет значить в
297
- двух местах разное.
298
260
 
299
261
  ## Чего из закона здесь нет
300
262
 
@@ -317,81 +279,6 @@ flowchart TD
317
279
 
318
280
  ## Паттерны
319
281
 
320
- - `git-workflow-commit` — задача, ветка, коммит, пуш и PR от учётной записи машинной работы.
282
+ - `git-workflow-commit` — задача, ветка, коммит и пуш от учётной записи машинной работы.
283
+ - `git-workflow-pr` — открытие PR, черновик и его снятие, тело, ревьювер, метки, состояние.
321
284
  - `git-workflow-merge` — главная ветка влита в ветку задачи, конфликт разобран.
322
- - `git-workflow-migration` — правка схемы хранилища и её миграций.
323
- - `git-workflow-restart` — ручной перезапуск прода.
324
- - `git-workflow-docker` — образы на своей машине: демон, реестр, сборка под платформу сервера.
325
- - `git-workflow-secrets` — ключи внешних служб: где лежат, как заводятся, что говорит их состояние.
326
-
327
- ## Ловушки
328
-
329
- - **Одна работа — одна задача, сколько бы файлов она ни задела.** Числа, за которым правка
330
- становится второй задачей, здесь нет: делится то, что придётся откатывать порознь. Сплошная
331
- правка текстов дерева была заведена тремя задачами «по объёму» — пришлось стирать две,
332
- закрывать два PR и переносить коммиты по одному с двумя конфликтами. Одна из трёх не дала
333
- коммита вовсе: правка тел уже заведённых задач веткой не бывает и задачей под ветку тоже.
334
- - **Задача заводится командой, а не четырьмя вызовами подряд.** Борда к репозиторию не
335
- привязана, и задача попадает на неё только явным добавлением: две задачи так и простояли вне
336
- очереди работ, потому что шаг переписывали руками. Команда заведения делает все четыре — issue,
337
- номер в его заголовке, добавление на борду, начальную колонку, — и печатает готовую строку
338
- заведения ветки. Замеченный по ходу дефект проходит тот же путь.
339
- - **Ветка заводится вторым вызовом, а не тем же.** Гард главной ветки отклоняет составную
340
- «создать ветку и сразу коммитить» целиком: ветки в момент разбора ещё нет.
341
- - **Сторона конфликта бывает удалением, и «сохранить обе стороны» заводит второе объявление.**
342
- Главная ветка снимает объявление, потому что символ переехал, — в конфликте это выглядит как
343
- сторона, которая ничего не дописала. Разбирается чтением версии главной ветки целиком, а не по
344
- хунку, и сверяется проверкой повторов: обе копии сами по себе исправны, сборка и линт зелёные.
345
- - **Учётная запись для пуша и автор PR выбираются отдельно.** Если пушить пришлось из-под другой
346
- записи, на следующий вызов это не переносится: PR открывают токеном учётной записи машинной
347
- работы, и от того, чьей записью он открыт, зависит, кого можно назначить ревьювером. Однажды
348
- смена записи ради пуша утекла в публикацию — PR вышел от владельца. Разница между читающим и
349
- пишущим вызовом в самом тексте команды не видна: личность приходит окружением, поэтому у
350
- вызова на запись токен называется явно, а открытая заявка проверяется ответом хостинга о её
351
- авторе — напечатанная ссылка говорит, что заявка создана, и молчит о том, кем. Чинится это
352
- только переоткрытием: автора у заявки не сменить.
353
- - **Невалидный файл конвейера виден прогоном нулевой длительности сразу после пуша.** GitHub
354
- заводит такой прогон и на ветке, на которую ни один триггер не подписан: в списке он стоит
355
- отказом, а внутри у него нет ни задания, ни лога — читается только длительность. Поэтому
356
- список прогонов ветки смотрится тем же движением, что и пуш — `gh run list --branch <ветка>`.
357
- Один такой отказ простоял в списке до мержа, и на него никто не посмотрел: выкатка после
358
- мержа отказала ровно тем же.
359
- - **Прогон, не вставший на пуш, возвращается повтором события, а не разбором ветки.** Замером
360
- проверены обе законные дороги: и открытие PR, и пуш в уже открытый PR прогон заводят —
361
- текстовый коммит и учётная запись, которой пушат, тут ни при чём. Пропавшие события пришлись
362
- на час, когда хостинг отвечал `429` на загрузке действия и `503` на API, а списком прогонов
363
- «не завёлся» от «не создан» не отличить. Поэтому вершину без прогона называет сверка очереди
364
- работ, а событие возвращается новым коммитом либо перезакрытием PR.
365
- - **Красное на шаге подготовки задания — отказ хостинга, а не дефект ветки.** Раннер не смог
366
- скачать действие чекаута и получил `429 Too Many Requests`; до кода прогон при этом не дошёл
367
- вовсе. Лечится перезапуском прогона, и от красного по существу отличается тем, на каком шаге
368
- оно встало: три прогона одного дня упали именно так.
369
- - **Контекст `runner` в `env` задания отбивает весь файл конвейера.** Там доступны только
370
- `github`, `needs`, `strategy`, `matrix`, `vars`, `secrets` и `inputs`; `runner` появляется на
371
- уровне шага, где то же значение приходит переменной окружения. Такой файл не принимается
372
- вовсе: прогон кончается за ноль секунд, не начав ни одного задания. Разбор YAML этого не
373
- ловит — синтаксис верный, а доступность контекстов синтаксисом не является.
374
- - **`online` у раннера на своей машине означает запущенный процесс, а не работающий
375
- конвейер.** Две стороны сходятся отдельно: `runs-on` у заданий и метки самого раннера. Пока
376
- пересечения нет, раннер стоит `online` и не берёт ничего, а задания уходят в облако — по
377
- состоянию это выглядит настроенным. Владельцу называют выполненное задание с его номером, а
378
- не строку состояния.
379
- - **Раннер на своей машине делает прогон общим ресурсом, и стенд у прогонов один.** Порт,
380
- имя базы и каталог сборки зашиты в дереве одним значением на всех: два прогона разом
381
- поднимают два стенда на один порт, и второй падает целиком. Дороже всего не падение, а его
382
- вид — в отчёте оно выглядит десятком красных спек про экраны, то есть дефектом правки,
383
- которого нет; три прогона подряд так и упали на трёх ветках, не тронувших кода. Лечится с
384
- двух сторон сразу: конвейеру объявляется группа очереди на всё дерево, а имена стенда
385
- читаются из окружения с нынешними значениями в умолчании — иначе прогон и гейт пуша, зовущий
386
- ту же команду, столкнутся и при очереди. Одной очереди мало, одних имён — тоже: прогоны делят
387
- ещё диск, кэш сборщика и демон образов.
388
- - **Вход в реестр образов из раннера, запущенного службой, отказывает молча.** Служба идёт без
389
- сеанса пользователя, а клиент реестра уходит в системный помощник хранения ключей и получает
390
- отказ во взаимодействии — задание падает до сборки. Свой каталог настроек с пустым помощником
391
- не спасает: клиент переписывает пустое значение обратно сам. Готовые команды — паттерн
392
- `git-workflow-docker`.
393
- - **Новое рабочее дерево получает только то, что лежит в индексе.** `git worktree add`
394
- разворачивает коммит, а настройки, ключи, локальные разрешения и зависимости в коммит не
395
- входят: свежее дерево выглядит готовым и упирается в нехватку не сразу, а на первом гарде,
396
- которому нужен ключ. Что именно переносится руками, названо списком в компаньоне правила, и
397
- список пополняется тем же движением, которым заводится новый файл вне индекса.