@rt-tools/agent-kit 0.3.0 → 0.5.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 (202) hide show
  1. package/README.md +194 -30
  2. package/assets/agents/business-analyst.md +74 -0
  3. package/assets/agents/project-manager.md +70 -0
  4. package/assets/agents/qa-engineer.md +72 -0
  5. package/assets/agents/skill-curator.md +110 -0
  6. package/assets/agents/spec-critic.md +44 -0
  7. package/assets/agents/spec-writer.md +50 -0
  8. package/assets/checks/board.github.mjs +329 -0
  9. package/assets/checks/check-board.github.mjs +181 -0
  10. package/assets/checks/check-doc-paths.mjs +163 -0
  11. package/assets/checks/check-dupes.mjs +277 -0
  12. package/assets/checks/check-lib-layers.mjs +573 -0
  13. package/assets/checks/check-reuse.mjs +208 -0
  14. package/assets/checks/check-schema-drift.mjs +186 -0
  15. package/assets/checks/check-specs.mjs +1086 -0
  16. package/assets/checks/check-styles.mjs +109 -0
  17. package/assets/checks/rt-kit-checks.config.mjs +134 -0
  18. package/assets/checks/task-new.github.mjs +198 -0
  19. package/assets/commands/skill-curator.md +70 -0
  20. package/assets/defaults/gate-map.sh +106 -0
  21. package/assets/defaults/project.sh +204 -0
  22. package/assets/hooks/browser-device-id.sh +0 -0
  23. package/assets/hooks/browser-guard-device-id.sh +2 -1
  24. package/assets/hooks/browser-guard-no-asking.sh +27 -0
  25. package/assets/hooks/browser-guard-no-listing.sh +2 -1
  26. package/assets/hooks/browser-guard-no-other-drivers.sh +2 -1
  27. package/assets/hooks/browser-guard-require-select.sh +2 -1
  28. package/assets/hooks/commit-msg.sh +1 -1
  29. package/assets/hooks/constitution-index.sh +5 -4
  30. package/assets/hooks/dev-server-guard.sh +8 -6
  31. package/assets/hooks/docs-guard.sh +223 -37
  32. package/assets/hooks/git-guard-delivery.sh +171 -31
  33. package/assets/hooks/git-guard-main.sh +1 -0
  34. package/assets/hooks/git-guard-push-tests.sh +34 -13
  35. package/assets/hooks/glossary-load.sh +23 -0
  36. package/assets/hooks/grill-gate.sh +96 -0
  37. package/assets/hooks/lint-after-edit.sh +155 -30
  38. package/assets/hooks/qa-dataid-guard.sh +72 -32
  39. package/assets/hooks/reuse-first-guard.sh +105 -34
  40. package/assets/hooks/skill-gate-rearm.sh +1 -0
  41. package/assets/hooks/skill-gate.sh +75 -15
  42. package/assets/hooks/skill-loaded.sh +1 -0
  43. package/assets/hooks/sql-guard.sh +606 -56
  44. package/assets/hooks/task-context-load.sh +100 -0
  45. package/assets/hooks/task-flow-guard.sh +118 -0
  46. package/assets/laws/{access.md → application/access.md} +1 -4
  47. package/assets/laws/{locales.md → application/locales.md} +1 -3
  48. package/assets/laws/application/money.md +41 -0
  49. package/assets/laws/application/ownership.md +32 -0
  50. package/assets/laws/{search-visibility.md → application/search-visibility.md} +1 -1
  51. package/assets/laws/code-structure.md +7 -6
  52. package/assets/laws/delivery.md +53 -3
  53. package/assets/laws/entity-editing.md +49 -55
  54. package/assets/laws/entity-models.md +4 -14
  55. package/assets/laws/frontend-application.md +5 -5
  56. package/assets/laws/lib-imports.md +14 -1
  57. package/assets/laws/lists.md +33 -0
  58. package/assets/laws/navigation.md +40 -0
  59. package/assets/laws/project-documentation.md +27 -8
  60. package/assets/laws/reuse-first.md +26 -21
  61. package/assets/laws/shared-code.md +13 -1
  62. package/assets/laws/verifiability.md +30 -1
  63. package/assets/laws/work-conduct.md +59 -0
  64. package/assets/patterns/admin-lists-screen.md +131 -0
  65. package/assets/patterns/admin-nav-item.md +71 -0
  66. package/assets/patterns/angular-patterns-state.md +29 -22
  67. package/assets/patterns/api-layer-pair.md +40 -30
  68. package/assets/patterns/browser-verification-measure.md +41 -38
  69. package/assets/patterns/browser-verification-stand.md +106 -42
  70. package/assets/patterns/component-structure-new.md +33 -32
  71. package/assets/patterns/dependencies-upgrade.md +65 -0
  72. package/assets/patterns/doc-style-sweep.md +65 -28
  73. package/assets/patterns/doc-style-write.md +36 -33
  74. package/assets/patterns/entity-aside.md +136 -0
  75. package/assets/patterns/entity-models-new.md +124 -0
  76. package/assets/patterns/entity-store.md +91 -0
  77. package/assets/patterns/git-workflow-commit.azure.md +259 -0
  78. package/assets/patterns/git-workflow-commit.github.md +337 -0
  79. package/assets/patterns/git-workflow-commit.gitlab.md +283 -0
  80. package/assets/patterns/git-workflow-merge.md +42 -25
  81. package/assets/patterns/git-workflow-migration.md +61 -31
  82. package/assets/patterns/git-workflow-restart.md +20 -20
  83. package/assets/patterns/lib-layers-move.md +50 -32
  84. package/assets/patterns/lib-layers-new.md +41 -29
  85. package/assets/patterns/ownership-scope-resolve.md +69 -0
  86. package/assets/patterns/permissions-procedure.md +35 -33
  87. package/assets/patterns/platform-access-di.md +39 -25
  88. package/assets/patterns/pricing-quote.md +71 -0
  89. package/assets/patterns/reuse-first-extend.md +22 -22
  90. package/assets/patterns/seo-page.md +52 -40
  91. package/assets/patterns/seo-verify.md +48 -29
  92. package/assets/patterns/shared-code-new.md +37 -31
  93. package/assets/patterns/spec-driven-domain.md +60 -37
  94. package/assets/patterns/spec-driven-rule.md +55 -40
  95. package/assets/patterns/styling-bem-component.md +43 -32
  96. package/assets/patterns/styling-bem-layout.md +30 -24
  97. package/assets/patterns/task-flow-close.md +154 -0
  98. package/assets/patterns/task-flow-resume.md +94 -0
  99. package/assets/patterns/task-flow-start.md +129 -0
  100. package/assets/patterns/testing-e2e.md +53 -51
  101. package/assets/patterns/testing-unit.md +70 -46
  102. package/assets/patterns/translations-key.md +32 -19
  103. package/assets/patterns/ts-procedure.md +24 -25
  104. package/assets/rules/angular-patterns.md +50 -27
  105. package/assets/rules/api-layer.md +46 -28
  106. package/assets/rules/browser-verification.md +67 -48
  107. package/assets/rules/component-structure.md +43 -27
  108. package/assets/rules/dependencies.md +66 -0
  109. package/assets/rules/doc-style.md +95 -39
  110. package/assets/rules/entity-conventions.md +78 -0
  111. package/assets/rules/entity-models.md +70 -0
  112. package/assets/rules/git-workflow.azure.md +116 -0
  113. package/assets/rules/git-workflow.github.md +123 -0
  114. package/assets/rules/git-workflow.gitlab.md +113 -0
  115. package/assets/rules/lib-layers.md +56 -30
  116. package/assets/rules/lists.md +73 -0
  117. package/assets/rules/navigation.md +78 -0
  118. package/assets/rules/ownership-scope.md +63 -0
  119. package/assets/rules/permissions.md +43 -25
  120. package/assets/rules/platform-access.md +57 -29
  121. package/assets/rules/pricing.md +64 -0
  122. package/assets/rules/reuse-first.md +57 -43
  123. package/assets/rules/seo.md +51 -30
  124. package/assets/rules/shared-code.md +51 -26
  125. package/assets/rules/spec-driven.md +107 -51
  126. package/assets/rules/styling-bem.md +54 -39
  127. package/assets/rules/task-flow.md +150 -0
  128. package/assets/rules/testing.md +78 -47
  129. package/assets/rules/translations.md +48 -31
  130. package/assets/rules/typescript-conventions.md +57 -27
  131. package/assets/skills/agent-kit.md +85 -0
  132. package/assets/skills/write-a-skill.md +108 -0
  133. package/assets/templates/gate-map.sh +23 -15
  134. package/assets/templates/implementation.md +14 -8
  135. package/assets/templates/pattern.md +1 -1
  136. package/assets/templates/project.sh +32 -19
  137. package/assets/templates/rule.md +2 -2
  138. package/assets/variants.json +20 -0
  139. package/assets/workflows/feature.js +134 -0
  140. package/assets/workflows/plan.js +150 -0
  141. package/bin/agent-kit.d.ts.map +1 -1
  142. package/bin/agent-kit.js +78 -5
  143. package/bin/agent-kit.js.map +1 -1
  144. package/bin/prompt.d.ts +5 -0
  145. package/bin/prompt.d.ts.map +1 -1
  146. package/bin/prompt.js +19 -7
  147. package/bin/prompt.js.map +1 -1
  148. package/index.d.ts +1 -0
  149. package/index.d.ts.map +1 -1
  150. package/index.js +1 -0
  151. package/index.js.map +1 -1
  152. package/lib/assets.d.ts +8 -3
  153. package/lib/assets.d.ts.map +1 -1
  154. package/lib/assets.js +13 -3
  155. package/lib/assets.js.map +1 -1
  156. package/lib/catalog.d.ts +52 -5
  157. package/lib/catalog.d.ts.map +1 -1
  158. package/lib/catalog.js +104 -16
  159. package/lib/catalog.js.map +1 -1
  160. package/lib/commands.d.ts +22 -1
  161. package/lib/commands.d.ts.map +1 -1
  162. package/lib/commands.js +202 -14
  163. package/lib/commands.js.map +1 -1
  164. package/lib/companion.d.ts +5 -1
  165. package/lib/companion.d.ts.map +1 -1
  166. package/lib/companion.js +29 -2
  167. package/lib/companion.js.map +1 -1
  168. package/lib/config.d.ts +26 -9
  169. package/lib/config.d.ts.map +1 -1
  170. package/lib/config.js +41 -15
  171. package/lib/config.js.map +1 -1
  172. package/lib/freshness.d.ts +14 -0
  173. package/lib/freshness.d.ts.map +1 -0
  174. package/lib/freshness.js +116 -0
  175. package/lib/freshness.js.map +1 -0
  176. package/lib/hooks-map.d.ts +27 -0
  177. package/lib/hooks-map.d.ts.map +1 -0
  178. package/lib/hooks-map.js +77 -0
  179. package/lib/hooks-map.js.map +1 -0
  180. package/lib/integrity.d.ts +36 -0
  181. package/lib/integrity.d.ts.map +1 -0
  182. package/lib/integrity.js +44 -0
  183. package/lib/integrity.js.map +1 -0
  184. package/lib/picker.d.ts +11 -1
  185. package/lib/picker.d.ts.map +1 -1
  186. package/lib/picker.js +44 -6
  187. package/lib/picker.js.map +1 -1
  188. package/lib/sync.d.ts +26 -0
  189. package/lib/sync.d.ts.map +1 -1
  190. package/lib/sync.js +59 -4
  191. package/lib/sync.js.map +1 -1
  192. package/lib/variants.d.ts +44 -0
  193. package/lib/variants.d.ts.map +1 -0
  194. package/lib/variants.js +82 -0
  195. package/lib/variants.js.map +1 -0
  196. package/package.json +1 -1
  197. package/rt-tools-agent-kit-0.5.0.tgz +0 -0
  198. package/assets/laws/admin-lists.md +0 -35
  199. package/assets/laws/admin-navigation.md +0 -38
  200. package/assets/patterns/git-workflow-commit.md +0 -175
  201. package/assets/rules/git-workflow.md +0 -106
  202. package/rt-tools-agent-kit-0.3.0.tgz +0 -0
@@ -2,33 +2,55 @@
2
2
  name: doc-style
3
3
  kind: rule
4
4
  law: project-documentation
5
- description: Правило под закон «Документация проекта». Брать при правке любого документа, включая спеки, а также комментариев в коде, тел коммитов и описаний PR. Пути, которые существуют, пара «правка и её документ», словарь проекта, запрет упоминать чужие проекты. Готовые формулировки — в паттерне doc-style-write. Чем это названо здесь — в implementation.md рядом.
5
+ description: Правило под «Закон о документации проекта». Брать при правке любого .md, включая спеки, а также комментариев в коде, тел коммитов и описаний PR. Называет проверку путей, пары «правка и её документ» и то, что в этом дереве не проверяет ничто. Готовые формулировки — в паттерне doc-style-write.
6
6
  ---
7
7
 
8
- # Тексты проекта — каким приёмом
8
+ # Тексты проекта — как это устроено здесь
9
9
 
10
- Правило под закон `{{lawsDir}}/project-documentation.md`. Закон говорит, что должно быть верно
11
- про тексты; здесь — каким приёмом это держится и что остаётся за автором. Где лежит словарь,
12
- чем проверяются пути и какие пары требует гейт `implementation.md` рядом. Устройство спеков и
13
- слоёв документации — правило `spec-driven` под тем же законом.
10
+ Правило под закон `docs/constitution/project-documentation.md`. Закон говорит, что должно
11
+ быть верно про тексты; здесь — чем это проверяется в этом дереве и что остаётся за автором.
12
+ Устройство спеков и слоёв документации правило `spec-driven` под тем же законом; здесь
13
+ только формулировки.
14
14
 
15
- ## Когда берётся
15
+ ## Как это называется здесь
16
16
 
17
- Правка любого документа, комментария в коде, тела коммита, описания PR.
17
+ | В законе | Здесь |
18
+ | ---------------------------------- | ------------------------------------------------------------------------------------------------------------------ |
19
+ | документ | любой `.md` вне `docs/archive/`, плюс комментарии в коде, тела коммитов и описания PR |
20
+ | путь, названный в документе | строка с расширением в обратных кавычках — её и ищет проверка |
21
+ | правка, которую документ описывает | пара из `docs-guard`: правило и его зеркало, `.proto` и спек, хук и его сценарии, переезд файла и README обеих либ |
22
+ | описание прошлого | `docs/archive/` — из проверки путей выведено целиком |
18
23
 
19
- ## Что здесь действует
24
+ ## Где это лежит
25
+
26
+ В этом дереве — таблица в `implementation.md` рядом. Пути живут там, а не здесь: правило
27
+ переносится между репозиториями, раскладка — нет, и путь, названный в правиле, врёт в первом
28
+ же дереве, которое держит код иначе.
29
+
30
+ ## Как закон применяется здесь
20
31
 
21
32
  - **Путь, названный в документе, существует.** Ссылка на переехавший файл читается как
22
- действующее указание, и следующий читатель заводит снятое заново.
23
- - **Описание прошлого из проверки путей выведено целиком.** Архив по устройству называет файлы,
24
- которых уже нет, и правкой это не лечится.
25
- - **Документ едет в том же коммите, что и правка, которую он описывает.** Обход — отметка с
26
- причиной в теле коммита; пустая причина не принимается.
27
- - **Термин берётся из словаря проекта, а не придумывается на месте.** Слова, которого там нет,
28
- у читателя нет тоже. Новое слово либо заводится в словаре вместе с правкой, либо заменяется
29
- тем, что уже есть.
30
- - **Чужие проекты не упоминаются нигде** — ни имени репозитория, ни «портировано из», ни ссылок
31
- на его файлы. Описывается то, что код делает здесь, в терминах этого проекта.
33
+ действующее указание, и следующий читатель заводит снятое заново. Судятся документы,
34
+ которые едут в репозиторий: личный черновик, закрытый `.gitignore` или
35
+ `.git/info/exclude`, проверка не читает мёртвая ссылка в нём держала гейт пуша, хотя ни
36
+ в одну ветку этот файл не попадёт.
37
+ - **Описание прошлого из проверки путей выведено целиком.** Архив по устройству называет
38
+ файлы, которых уже нет, и правкой это не лечится.
39
+ - **Документ едет в том же коммите, что и правка, которую он описывает.** Обход — строка
40
+ `Docs-skip: <причина>` в теле коммита; пустая причина не принимается.
41
+
42
+ ## Чего из закона здесь нет
43
+
44
+ Ни одна из формулировочных договорённостей не проверяется: одна фраза на правило, простые
45
+ слова, отсутствие утверждений о будущем, свежесть числа в тексте. Две последние закон прямо
46
+ оставляет автору: открытый вопрос пишется теми же словами, что и обещание, а дата и номер —
47
+ такие же числа, как то, что пересчитывают.
48
+
49
+ На комментарии в коде правило распространяется, но гейтом не требуется: он зовёт его только
50
+ на `.md`. Расширять требование на каждый `.ts` значило бы шуметь на каждой правке, поэтому
51
+ здесь оно держится памятью автора — и цена этого видна: слова из левой колонки словаря живут
52
+ в комментариях хуков и `tools/*.mjs` десятками строк, включая текст отказа, который гард
53
+ печатает агенту.
32
54
 
33
55
  ## Паттерны
34
56
 
@@ -37,25 +59,59 @@ description: Правило под закон «Документация про
37
59
 
38
60
  ## Ловушки
39
61
 
40
- - **Оставшаяся работа не записывается в документ, а заводится задачей.** «Сделать потом» в
41
- плане, README или спеке второй список работ: он расходится с очередью задач молча, а
42
- разбирать его потом дороже, чем завести задачу сразу. Документ держит только то, что задачей
43
- не бывает: договорённости и решения, которые решено не править.
44
- - **Словарь действует и на разговор с владельцем, не только на файлы.** Слово, от которого в
45
- дереве отказались, всплывает именно в отчёте о сделанном и владелец читает ровно то слово,
46
- которое просил не употреблять.
62
+ - **Оставшаяся работа не записывается в документ, а заводится задачей.** `docs/BACKLOG.md`
63
+ держит только то, что задачей не бывает: договорённости и решения, которые решено не
64
+ править. Признак утверждение остаётся, если править его никто не собирается. «Сделать
65
+ потом» в плане, README или спеке второй список работ: он расходится с бордой молча, а
66
+ разбирать его потом дороже, чем завести задачу сразу. Из 1411 строк документа действующими
67
+ оказались 71, и на разбор остальных ушла отдельная задача. Как разбирать накопившееся
68
+ паттерн `doc-style-sweep`.
69
+ - **Словарь действует и на разговор с владельцем, не только на файлы.** Он приходит в контекст
70
+ на запуске сессии, поэтому «не читал» основанием не бывает. Слово из левой колонки «Так не
71
+ пишем» всплывало именно в ответах: в дереве его уже вычистили, а в отчёте о сделанном оно
72
+ оставалось, и владелец читал ровно то слово, от которого отказались.
73
+ - **Термин берётся из `docs/GLOSSARY.md`, а не придумывается на месте.** Слова, которого там
74
+ нет, у читателя нет тоже: «журнал приложения» простоял в спеке почты, пока владелец не
75
+ спросил, что это, — оказалось, логи бэкенда, а слово «журнал» здесь уже занято журналом
76
+ событий. Новое слово либо заводится в словаре вместе с правкой, либо заменяется тем, что
77
+ уже есть.
47
78
  - **Проход по словарю глазами слово не находит.** «Формулировки приведены к словарю» означает
48
- ровно те строки, которые в тот момент читали. Снятое слово вычищается поиском по всему дереву,
49
- а не вычиткой; ищутся сочетания, а не корень совпадений по корню законных обычно больше,
50
- чем нарушений.
51
- - **Снятое имя вычищается одним проходом по всему дереву:** правила, их зеркала, документы и
52
- комментарии. Описание того, чего в коде уже нет, читается как действующее указание.
53
- - **Число в тексте пересчитывается командой в том же коммите, где пишется.** Оно стареет внутри
54
- одной ветки. Число, которое придётся пересчитывать при каждой правке, лучше не писать вовсе;
55
- число, полученное разбором текста, сверяется на выборке руками — разбор, не знающий второй
56
- формы записи, ошибается молча.
79
+ ровно те строки, которые в тот момент читали: «спека» пережила такой проход и осталась в
80
+ соседней строке того же файла. Слово из левой колонки таблицы «Так не пишем» вычищается
81
+ грепом по всему дереву, а не вычиткой. Форма задаётся точно: «спек» — документ — склоняется
82
+ в «спека» и «спеки» тоже, и совпадений по корню законных больше, чем нарушений; ищутся
83
+ сочетания («спеки на нет», «спека проверяет»), а не корень.
84
+ - **Снятое имя вычищается одним грепом по всему дереву:** правила, их зеркала в скилах,
85
+ документы и комментарии. Описание того, чего в коде уже нет, читается как действующее
86
+ указание.
87
+ - **Поиск по дереву не покрывает того, что уже уехало наружу.** Заголовок задачи и её тело,
88
+ заголовок отчёта и его тело, заголовки коммитов лежат вне файлов, и проверки текстов их не
89
+ читают вовсе. Вычистив слово в дереве, обходят те же места в очереди работ и в истории:
90
+
91
+ ```bash
92
+ <клиент хостинга> api "<путь к отчёту>" --jq '.title, .body' | grep -i '<слово>'
93
+ <клиент хостинга> api "<путь к задаче>" --jq '.title, .body' | grep -i '<слово>'
94
+ git log --format='%s%n%b' <база>..HEAD | grep -i '<слово>'
95
+ ```
96
+
97
+ Заголовок отчёта правится вызовом хостинга, заголовок коммита — только переписыванием ветки,
98
+ поэтому его проверяют до пуша. Выдуманное слово было вычищено из трёх файлов и объявлено
99
+ снятым, а в заголовке отчёта и в заголовке коммита осталось — владелец прочитал именно его.
100
+
101
+ - **Число в тексте пересчитывается командой в том же коммите, где пишется.** Оно стареет
102
+ внутри одной ветки: «шестнадцать пар» стало неправдой через два коммита после того, как
103
+ было написано, и нашёл это владелец, а не проверка. Число, которое придётся пересчитывать
104
+ при каждой правке, лучше не писать вовсе. Число, полученное разбором текста, сверяется на
105
+ выборке руками до того, как его называют: разбор, не знающий второй формы записи, ошибается
106
+ молча — «51 пункт без задачи» оказался шестью, потому что номер стоял и отдельной строкой, и
107
+ в заголовке подраздела.
57
108
  - **Сделанность читается по дереву, а не по тексту, который о ней написан.** Это верно в обе
58
- стороны: вычеркнутый пункт при несделанной работе и несделанным названная задача, закрытая
59
- наполовину, встречаются одинаково часто. Пункт плана описывает день, когда его написали.
60
- - **Комментарий в файле такое же утверждение, как строка в документе.** Выдуманное
61
- обоснование живёт в коде годами и каждый раз читается как основание ничего не трогать.
109
+ стороны: строка про README обеих либ была вычеркнута как сделанная, а README остался с
110
+ прежним числом импортёров; задача, названная владельцу несделанной, оказалась наполовину
111
+ закрытой и покрытой сценариями хука. Пункт плана и тело задачи описывают день, когда их
112
+ написали, и с тех пор не менялись.
113
+ - **Комментарий в файле — такое же утверждение, как строка в документе.** Обоснование «строки
114
+ идут во всю ширину панели, иначе подсветка обрывается» было выдумано, прожило три сессии и
115
+ каждый раз читалось как основание вёрстку не трогать.
116
+ - **Чужие проекты не упоминаются нигде** — ни имени репозитория, ни «портировано из», ни
117
+ ссылок на его файлы. Описывается то, что код делает здесь, в терминах этого проекта.
@@ -0,0 +1,78 @@
1
+ ---
2
+ name: entity-conventions
3
+ kind: rule
4
+ law: entity-editing
5
+ description: Правило под «Закон о правке сущности». Брать при правке любого стора админки (*.store.ts) и любой панели создания или правки записи. Называет общую основу асайда, общую основу списочного стора, устройство панели и то, что асайд открывается маршрутом в аутлете ro. Готовый код — в паттернах entity-aside и entity-store.
6
+ ---
7
+
8
+ # Правка сущности — как это устроено здесь
9
+
10
+ Правило под закон `docs/constitution/entity-editing.md`. Закон говорит, как ведёт себя
11
+ приложение при создании и правке записи; здесь — из чего это собрано в этом дереве и как
12
+ выглядит. Про вид говорит правило: закон о нём молчит намеренно.
13
+
14
+ ## Как это называется здесь
15
+
16
+ | В законе | Здесь |
17
+ | ----------------------------- | ------------------------------------------------------------------------ |
18
+ | панель правки записи | асайд; открывается маршрутом с `outlet: 'ro'` |
19
+ | общая основа асайда | `RtRouteAsideComponent<T>` — директива без селектора |
20
+ | общая основа списочного стора | `BaseListStoreService` |
21
+ | запись | `entity`, `entityId`, `isCreateMode` — имена от сущности, а не от домена |
22
+ | обвязка записи | `runMutation` в панели, `mutate` в сторе |
23
+ | нетронутость формы | `pristineSignal(control)` |
24
+
25
+ ## Где это лежит
26
+
27
+ В этом дереве — таблица в `implementation.md` рядом. Пути живут там, а не здесь: правило
28
+ переносится между репозиториями, раскладка — нет, и путь, названный в правиле, врёт в первом
29
+ же дереве, которое держит код иначе.
30
+
31
+ ## Как закон применяется здесь
32
+
33
+ - **Асайд открывается маршрутом в аутлете `ro`, а не вызовом сервиса.** Программного открытия
34
+ через `RtAsideService.open()` в админке нет: так панель переживает перезагрузку, передаётся
35
+ ссылкой и попадает в историю браузера.
36
+ - **Запись идёт через `runMutation`, и панель отдаёт основе поток мутации и ключи.** Занятость,
37
+ гашение прежней ошибки, тост об успехе и закрытие держит основа.
38
+ - **Поток мутации обязан отдать значение или ошибку.** Пустой поток гасит панель навсегда:
39
+ она ждёт того или другого, а закрыть её владелец не может, пока идёт запись.
40
+ - **Мутация завершается перечитанным списком, а не отправленным запросом.** Список,
41
+ перечитанный после закрытия, показал бы прежнее значение.
42
+ - **Стор отвечает потоком: успех — значение, отказ — ошибка потока.** Булева ответа у сторов
43
+ админки не осталось: он терял и записанную запись, и причину отказа.
44
+ - **Имена берутся от сущности, а не от домена.** `save`, `remove`, `load` — не
45
+ `createBooking`, `loadBookings`: имя домена уже в имени стора.
46
+ - **Гард несохранённых правок ставит сама панель и на все четыре пути закрытия.** Проверять
47
+ один путь бессмысленно — Esc обойдёт то, что ловит кнопка. Четыре пути — кнопка в шапке,
48
+ кнопка в футере, нажатие мимо панели и Esc.
49
+ - **Уход из панели идёт через `openRelated`, а не своим `router.navigate`.** Абсолютные
50
+ команды меняют только первичную ветку, аутлет `ro` остаётся в адресе, и роутер отклоняет
51
+ навигацию молча.
52
+ - **Запись читается по идентификатору из адреса полной моделью, а не берётся из списка.**
53
+ Список отдаёт короткую.
54
+
55
+ ## Чего из закона здесь нет
56
+
57
+ Чтение записи отдельной процедурой заведено не везде — долг `Q-M-3`. Файл стора называется
58
+ `<сущность>.store.ts`; имя `<сущность>-store.service.ts` выводит его и из правила линтера, и
59
+ из гейта скилов, и общие сторы заявок и объектов правятся без правил сущностей вовсе.
60
+
61
+ ## Паттерны
62
+
63
+ - `entity-aside` — собрать панель правки: маршрут, основа, `runMutation`, шапка и футер, гард.
64
+ - `entity-store` — собрать стор сущности: наследник общей основы, `mutate`, ключи отказа.
65
+
66
+ ## Ловушки
67
+
68
+ - Действие со своей занятостью через основу не идёт: опрос подписки держит свой `pollingId`,
69
+ потому что панель на минуту опроса не гасится.
70
+ - Хвост с `EMPTY`, приклеенный к мутации, гасится `defaultIfEmpty`: иначе отказ приклеенного
71
+ потока превращает удачную запись в вечный спиннер.
72
+ - `routerLink` в панели не годится: директива навигирует сама, `preventDefault` её не
73
+ останавливает, и вопрос о несохранённых правках она обходит.
74
+ - `viewChild` на поле с `#` Angular не принимает — поле объявляется `protected`.
75
+ - Скелетоны полей идут по `resolving()`, не по `busy()`: `busy` включает и запись, и чтение.
76
+ - Панель, которая после успеха остаётся открытой, сбрасывает нетронутость сама.
77
+ - Ветка, куда забыли подмешать константу ro-маршрута, отличается только тем, что кнопка в
78
+ шапке на ней ничего не открывает: сборка, линт и маршруты остальных веток при этом целы.
@@ -0,0 +1,70 @@
1
+ ---
2
+ name: entity-models
3
+ kind: rule
4
+ law: entity-models
5
+ description: Правило под «Закон о моделях сущностей». Брать при объявлении или правке модели записи и её маппера в админке, при правке моделей и мапперов в libs/common/util и при правке .proto. Называет неймспейс I<Сущность>, уровни модели и что берётся из @rt-tools/utils. Готовый код — в паттерне entity-models-new.
6
+ ---
7
+
8
+ # Модели сущностей — как это устроено здесь
9
+
10
+ Правило под закон `docs/constitution/entity-models.md`. Закон говорит, сколько данных
11
+ приложение запрашивает на каждом экране; здесь — как эта модель объявляется в этом дереве.
12
+
13
+ ## Как это называется здесь
14
+
15
+ | В законе | Здесь |
16
+ | ------------------------ | -------------------------------------------------------------------- |
17
+ | сторона контракта | `Api` — псевдоним сгенерированного типа из `@<область>/common/proto` |
18
+ | то, чем пользуется экран | `State`, все поля `readonly` |
19
+ | то, что уходит на запись | `Draft` |
20
+ | короткий уровень | вложенный неймспейс `Short` с собственными `Api` и `State` |
21
+ | перевод | маппер-наследник `BaseMapper`, свой на каждый уровень |
22
+
23
+ Все три стороны лежат в одном неймспейсе `I<Сущность>` и спутать их в импортах нечем.
24
+
25
+ ## Где это лежит
26
+
27
+ В этом дереве — таблица в `implementation.md` рядом. Пути живут там, а не здесь: правило
28
+ переносится между репозиториями, раскладка — нет, и путь, названный в правиле, врёт в первом
29
+ же дереве, которое держит код иначе.
30
+
31
+ ## Как закон применяется здесь
32
+
33
+ - **У сущности две стороны, и обе лежат в неймспейсе `I<Сущность>`.** `Api` повторяет
34
+ контракт, `State` нормализован и от смены контракта не зависит.
35
+ - **Сторона контракта руками не пишется — она объявляется псевдонимом.** Своя копия разойдётся
36
+ с контрактом молча, а компилируется из них только одна.
37
+ - **Между сторонами стоит маппер-наследник `BaseMapper`, и экраны читают только `State`.** Тип
38
+ из контракта в шаблон не попадает.
39
+ - **Пустое выражается пустой строкой или нулём, а не отсутствием поля.** Необязательных
40
+ скаляров в контракте нет, поэтому `null` и `undefined` в `State` не заводятся; смысл нуля
41
+ объясняется комментарием рядом с полем.
42
+ - **Приведение идёт через `this.typeCast`, а не через `??`.** Контракт отдаёт значения по
43
+ умолчанию, а не пустоту, и проверка на `undefined` здесь не ловит ничего.
44
+ - **Типы страницы, порядка и отбора берутся из `@rt-tools/utils`.** Второго набора этих типов
45
+ в дереве нет: `rt-pagination` принимает `IPageModel` оттуда же.
46
+
47
+ ## Чего из закона здесь нет
48
+
49
+ Уровней нет ни у одной сущности, и контракт короткого сообщения не отдаёт — долги `Q-M-1` и
50
+ `Q-M-2`. Новая сущность заводится сразу с уровнями.
51
+
52
+ Правка `.proto` не проверяется ничем: `buf lint` и `buf breaking` настроены, но не входят ни в
53
+ `check:all`, ни в CI.
54
+
55
+ ## Паттерны
56
+
57
+ - `entity-models-new` — объявить модель и маппер: неймспейс, уровни, `typeCast`, перегенерация
58
+ контракта.
59
+
60
+ ## Ловушки
61
+
62
+ - `getAsType` умолчания не принимает: значение вне набора он пишет в консоль и возвращает
63
+ строкой `'unknown'`. Строковое поле с конечным набором значений сверяется с набором явно.
64
+ - `as Type` в маппере запрещено — правило `typescript-conventions`.
65
+ - Поле-сообщение необязательно всегда; обязательное поле модели им не заполнить без запасного
66
+ значения. Обратно, в запрос, `readonly`-массив не проходит: init-тип требует изменяемый.
67
+ - Модель админки и модель сайта — разные. Общий тип на два приложения означал бы, что сайт
68
+ тянет поля админки.
69
+ - Снятое поле контракта помечается `reserved` с номером и именем: номер, отданный новому полю,
70
+ ломает уже выкаченного клиента молча.
@@ -0,0 +1,116 @@
1
+ ---
2
+ name: git-workflow
3
+ kind: rule
4
+ law: delivery
5
+ description: Правило под «Закон о поставке» для дерева в Azure DevOps. Брать на заведение задачи, ветки, коммит, пуш, создание PR, слияние, а также на правку схемы хранилища, её миграций и вызовы миграций. Называет рабочий элемент как начало работы, его состояние как ход работы, соответствие задачи и ветки один к одному, имя ветки, формат коммита, учётную запись машинной работы, обязательный состав PR, гарды поставки и сверку очереди работ. Готовый код — в паттернах git-workflow-commit, git-workflow-merge, git-workflow-migration и git-workflow-restart.
6
+ ---
7
+
8
+ # Поставка — как это устроено здесь
9
+
10
+ Правило под закон `docs/constitution/delivery.md`. Закон говорит, что должно быть верно;
11
+ здесь — каким приёмом это держится в дереве, лежащем в Azure DevOps. Организация, проект,
12
+ учётная запись машинной работы и области коммита — при этом дереве, в `implementation.md`
13
+ рядом: их не угадать, и общими они не бывают.
14
+
15
+ ## Как это называется здесь
16
+
17
+ | В законе | Здесь |
18
+ | -------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
19
+ | главная ветка | `main` |
20
+ | отдельная ветка | `<номер рабочего элемента>-<короткий-slug>`; форма — в `implementation.md`. Имя без номера (`feat/…`, `fix/…`) законно, пока ветка живёт локально: PR с неё не откроется |
21
+ | задача | рабочий элемент (work item) рода `Task` или `Bug`, заголовок `[<номер>] <Что не так>`, исполнитель — учётная запись машинной работы; PR прикрепляется к нему при создании флагом `--work-items`, а коммит — строкой `AB#<номер>` |
22
+ | очередь работ | Azure Boards проекта. Рабочий элемент попадает на доску тем, что заведён: доска показывает элементы своей области и итерации |
23
+ | состояние задачи в очереди работ | поле `State` рабочего элемента: `New` у заведённого, `Active` у взятого в работу, `Resolved` у ждущего разбора. Набор состояний зависит от процесса проекта и назван в `implementation.md`; закрытая задача уходит из очереди слиянием |
24
+ | отчёт о задаче | заголовок PR `[<номер>] <Что сделано>` — тот же номер, что у рабочего элемента, и его название, переведённое в сделанное; тип и область коммита сюда не идут |
25
+ | обсуждение правки | разбор PR: ревьювер — владелец репозитория, исполнитель — учётная запись машинной работы, метки — те же, что у рабочего элемента |
26
+ | попадание правки в главную ветку | слияние PR; оно же запускает выкатку — `azure-pipelines.yml` |
27
+ | образ того коммита | `IMAGE_TAG=<sha>` в командах `docker compose` на сервере |
28
+ | изменение хранилища | миграция в `prisma/migrations/<метка>_<имя>/` |
29
+ | запись о правке | коммит формата `type(scope): description` — типы `feat`, `fix`, `refactor`, `docs`, `style`, `test`, `chore`, `perf`; области — в `implementation.md` |
30
+ | автор машинной работы | отдельная учётная запись; её имя и место токена — в `implementation.md`. Токен лежит вне репозитория |
31
+
32
+ ## Где это лежит
33
+
34
+ В этом дереве — таблица в `implementation.md` рядом. Пути живут там, а не здесь: правило
35
+ переносится между репозиториями, раскладка — нет, и путь, названный в правиле, врёт в первом
36
+ же дереве, которое держит код иначе.
37
+
38
+ ## Как закон применяется здесь
39
+
40
+ - **Коммит в главную ветку отбивается гардом.** Гард ищет вызов коммита в любом месте команды
41
+ и смотрит текущую ветку на момент запуска, поэтому составная «создать ветку и сразу
42
+ коммитить» отклоняется целиком: ветки в момент разбора ещё нет.
43
+ - **Ветка без номера рабочего элемента PR не открывает.** Гард поставки отбивает
44
+ `az repos pr create` с такой ветки: локально она законна, но правка из неё — это выкатка, за
45
+ которой в очереди работ ничего не стоит. Заводится рабочий элемент, и работа переносится в
46
+ ветку с его номером.
47
+ - **Номер ветки и номер в заголовке PR сверяются на месте, а состояние — по доске.** Формат
48
+ читается из текста команды и работает без сети; существование рабочего элемента, его
49
+ состояние, исполнитель и то, что он ещё открыт, — только когда есть чем спросить. Нет сети
50
+ или нет токена — второй ярус молча пропускается: проверка, падающая в самолёте, перестаёт
51
+ что-либо значить.
52
+ - **Состояние рабочего элемента двигается тем же движением, что и работа.** Ветка заведена —
53
+ элемент переводится в `Active`, PR открыт — в `Resolved`; делает это команда перевода, а не
54
+ набор вызовов по памяти. Перевод идёт сразу за шагом, который его вызвал: очередь работ
55
+ читают между шагами, а не после них.
56
+ - **Отставшее состояние находится сверкой очереди, а не глазами.** Сверка судит состояние по
57
+ отчёту в обе стороны: открытый PR при элементе не в разборе и разбор без открытого PR — оба
58
+ расхождения. Момента, когда задачу берут в работу, ей не видно: ветки на доске нет.
59
+ - **Задачи, чинящиеся одной правкой, сливаются до слияния ветки.** Вторая закрывается как
60
+ дубликат, а недостающее из неё дописывается в первую. После слияния слить уже нельзя: ветка
61
+ въехала, и откатывается она целиком.
62
+ - **Слияние в главную ветку выкатывает прод.** Фильтры путей конвейера покрывают документы
63
+ отдельно, поэтому переменные окружения, секреты и записи имён ставятся до слияния, а не
64
+ после.
65
+ - **Признак режима объявлен в образе, а не только в составе прода.** Значение, заданное
66
+ составом, действует лишь на контейнер, поднятый этим составом; ручной прогон того же образа
67
+ идёт с пустым значением, а пусто здесь означает локалхост — со всеми отладочными
68
+ умолчаниями, которые он разрешает. Умолчание образа задаётся в самом образе.
69
+ - **Образы выкатываются по sha коммита, а не по метке «последний».** Метка в реестре отстаёт
70
+ от главной ветки, и прод молча возвращается к прежней версии, продолжая отвечать.
71
+ - **Цепочка миграций прогоняется с пустого хранилища до слияния.** Порядок применения
72
+ лексикографический по имени каталога, а метку времени ставит момент создания: миграция из
73
+ ветки, начатой раньше, встаёт перед той, от которой зависит.
74
+ - **Документ едет в том же коммите, что и правка.** Обход — строка `Docs-skip: <причина>` в
75
+ теле коммита; пустая причина не принимается.
76
+ - **Заголовок коммита сверяется с форматом на месте.** Разобранный по типу и области
77
+ заголовок читается списком, а свободный текст — только целиком.
78
+ - **Перед пушем прогоняются все линтеры, а не один.** Линтер кода обычно не читает файлы
79
+ стилей вовсе, и правила оформления без второго прогона не проверяет ничто.
80
+ - **Рабочий элемент привязывается к PR при создании, а не после.** `az repos pr create`
81
+ принимает `--work-items`; привязка второй командой обходится молча, когда у токена нет права
82
+ править чужой элемент, и PR остаётся ни с чем не связанным.
83
+ - **Слияние с автозавершением не заменяет проверок до пуша.** Автозавершение видит только
84
+ конвейер, а конвейер видит только отправленное: красная ветка занимает очередь работ и
85
+ выглядит готовой к разбору.
86
+ - **Набор состояний берётся у процесса проекта, а не назначается правилом.** Agile, Scrum и
87
+ Basic называют одни и те же три шага по-разному, и перевод в состояние, которого в процессе
88
+ нет, отвечает отказом на каждой задаче подряд.
89
+ - **Сценарии гардов задают настройки git сами, а не берут их с машины.** Коммит во временном
90
+ репозитории сценария наследует общий конфиг: если включена подпись, git идёт в агент ключей,
91
+ а заблокированный агент роняет весь набор — со стороны это выглядит сломанным гардом. Автор,
92
+ почта и подпись передаются флагами `-c` прямо в команду.
93
+ - **Расхождение миграций со схемой меряется на теневом хранилище, а не на том, где работает
94
+ тот, кто пушит.** Оно законно несёт след любой недоделанной ветки, и сверка с ним держала бы
95
+ чужую правку. Гейт и выкатка зовут одну и ту же проверку — иначе «сошлось» станет значить в
96
+ двух местах разное.
97
+
98
+ ## Чего из закона здесь нет
99
+
100
+ Гард поставки стоит на командах агента, поэтому ветку, заведённую руками в редакторе, он не
101
+ видит: имя такой ветки держится памятью. Требование от этого не слабеет — просто отдельной
102
+ проверки под него не заводится: работа опознаётся заголовком рабочего элемента и отчёта, а это
103
+ сверяется у всех. Сверка очереди имя ветки не судит вовсе: у открытого PR его не переименовать.
104
+
105
+ Взятие задачи в работу не стережёт ничто: доска ветки не видит, а гард поставки её видит, но
106
+ доску не правит — сетевой вызов в разборе команды падал бы вместе со связью и отбивал бы
107
+ работу вместо промаха. Держится это памятью и подсказкой, которую печатает команда заведения
108
+ задачи. Отставшее состояние находит сверка очереди — но уже после того, как PR открыт.
109
+
110
+ ## Паттерны
111
+
112
+ - `git-workflow-commit` — рабочий элемент, ветка, коммит, пуш и PR от учётной записи машинной
113
+ работы.
114
+ - `git-workflow-merge` — главная ветка влита в ветку задачи, конфликт разобран.
115
+ - `git-workflow-migration` — правка схемы хранилища и её миграций.
116
+ - `git-workflow-restart` — ручной перезапуск прода.
@@ -0,0 +1,123 @@
1
+ ---
2
+ name: git-workflow
3
+ kind: rule
4
+ law: delivery
5
+ description: Правило под «Закон о поставке» для дерева на GitHub. Брать на заведение задачи, ветки, коммит, пуш, создание PR, мерж, а также на правку схемы хранилища, её миграций и вызовы миграций. Называет задачу на борде как начало работы, колонку задачи как ход работы, соответствие задачи и ветки один к одному, имя ветки, формат коммита, учётную запись машинной работы, обязательный состав PR, гарды поставки и сверку очереди работ. Готовый код — в паттернах git-workflow-commit, git-workflow-merge, git-workflow-migration и git-workflow-restart.
6
+ ---
7
+
8
+ # Поставка — как это устроено здесь
9
+
10
+ Правило под закон `docs/constitution/delivery.md`. Закон говорит, что должно быть верно;
11
+ здесь — каким приёмом это держится в дереве, лежащем на GitHub. Ключ задач, адрес борды,
12
+ учётная запись машинной работы и области коммита — при этом дереве, в `implementation.md`
13
+ рядом: их не угадать, и общими они не бывают.
14
+
15
+ ## Как это называется здесь
16
+
17
+ | В законе | Здесь |
18
+ | -------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
19
+ | главная ветка | `main` |
20
+ | ключ задач | короткое слово, которым дерево зовёт свои задачи; задаётся ключом `board.taskKey` в `.claude/rt-kit/checks.json`, а в `implementation.md` называется для читателя |
21
+ | отдельная ветка | `<КЛЮЧ>-<номер задачи>-<короткий-slug>`. Имя без номера (`feat/…`, `fix/…`) законно, пока ветка живёт локально: PR с неё не откроется |
22
+ | задача | issue репозитория, заголовок `[<КЛЮЧ>-<номер>] <Что не так>`, исполнитель — учётная запись машинной работы; PR прикрепляется к нему строкой `Closes #<номер>` в теле |
23
+ | очередь работ | борда GitHub Projects. К репозиторию она не привязана: `projectsV2` у него пуст, и задача попадает на борду только явным добавлением |
24
+ | состояние задачи в очереди работ | колонка борды — поле «Status»: заведённая, взятая в работу, ждущая разбора. Имена колонок — в `implementation.md`; закрытая задача уходит из очереди мержем, а не переводом в последнюю колонку |
25
+ | отчёт о задаче | заголовок PR `[<КЛЮЧ>-<номер>] <Что сделано>` — тот же номер, что у задачи, и её название, переведённое в сделанное; тип и область коммита сюда не идут |
26
+ | обсуждение правки | разбор PR: ревьювер — владелец репозитория, исполнитель — учётная запись машинной работы, метки — те же, что у задачи |
27
+ | попадание правки в главную ветку | мерж PR; он же запускает выкатку — `.github/workflows/deploy.yml` |
28
+ | образ того коммита | `IMAGE_TAG=<sha>` в командах `docker compose` на сервере |
29
+ | изменение хранилища | миграция в `prisma/migrations/<метка>_<имя>/` |
30
+ | запись о правке | коммит формата `type(scope): description` — типы `feat`, `fix`, `refactor`, `docs`, `style`, `test`, `chore`, `perf`; области — в `implementation.md` |
31
+ | автор машинной работы | отдельная учётная запись; её имя и место токена — в `implementation.md`. Токен лежит вне репозитория |
32
+
33
+ ## Где это лежит
34
+
35
+ В этом дереве — таблица в `implementation.md` рядом. Пути живут там, а не здесь: правило
36
+ переносится между репозиториями, раскладка — нет, и путь, названный в правиле, врёт в первом
37
+ же дереве, которое держит код иначе.
38
+
39
+ ## Как закон применяется здесь
40
+
41
+ - **Коммит в главную ветку отбивается гардом.** Гард ищет вызов коммита в любом месте команды
42
+ и смотрит текущую ветку на момент запуска, поэтому составная «создать ветку и сразу
43
+ коммитить» отклоняется целиком: ветки в момент разбора ещё нет.
44
+ - **Ветка без номера задачи PR не открывает.** Гард поставки отбивает `gh pr create` с такой
45
+ ветки: локально она законна, но правка из неё — это выкатка, за которой в очереди работ
46
+ ничего не стоит. Заводится задача, и работа переносится в ветку с её номером.
47
+ - **Ключ задач задаётся один раз, и все три формы имени выводятся из него.** Заголовок задачи,
48
+ имя ветки и заголовок отчёта строит один и тот же ключ: команда заведения задачи собирает по
49
+ нему заголовок, гард поставки достаёт по нему номер из имени ветки, сверка очереди — из
50
+ заголовка. Форма ветки в профиле дерева пишется той же парой `<КЛЮЧ>-<номер>`, а не своей
51
+ похожей: разойдясь, они не отказывают, а перестают узнавать номер, и проверка, искавшая
52
+ работу без задачи, пропускает всё подряд.
53
+ - **Незаданный ключ отбивает работу с очередью на месте.** Модуль борды отказывает при первом
54
+ же вызове и называет, где ключ задаётся. Умолчания у ключа нет намеренно: собранный из
55
+ пустого значения заголовок `[-317]` не совпадает ни с чем, и сверка очереди помечает
56
+ неправильно названной каждую задачу — настоящее расхождение тонет среди этих строк.
57
+ - **Номер ветки и номер в заголовке PR сверяются на месте, а состояние задачи — по борде.**
58
+ Формат читается из текста команды и работает без сети; существование задачи, её присутствие
59
+ на борде, исполнитель и то, что она ещё открыта, — только когда есть чем спросить. Нет сети
60
+ или нет токена — второй ярус молча пропускается: проверка, падающая в самолёте, перестаёт
61
+ что-либо значить.
62
+ - **Колонка задачи двигается тем же движением, что и работа.** Ветка заведена — задача
63
+ переставляется во взятые в работу, PR открыт — в ждущие разбора; делает это команда
64
+ перевода, а не набор вызовов GraphQL по памяти. Перевод идёт сразу за шагом, который его
65
+ вызвал: очередь работ читают между шагами, а не после них.
66
+ - **Отставшая колонка находится сверкой очереди, а не глазами.** Сверка судит колонку по
67
+ отчёту в обе стороны: открытый PR при задаче не в разборе и разбор без открытого PR — оба
68
+ расхождения. Момента, когда задачу берут в работу, ей не видно: ветки на борде нет.
69
+ - **Задачи, чинящиеся одной правкой, сливаются до мержа.** Вторая стирается вместе с номером,
70
+ а недостающее из неё дописывается в первую. После мержа слить уже нельзя: ветка въехала, и
71
+ откатывается она целиком.
72
+ - **Мерж в главную ветку выкатывает прод.** Исключения по путям покрывают только документы,
73
+ поэтому переменные окружения, секреты и записи имён ставятся до мержа, а не после.
74
+ - **Признак режима объявлен в образе, а не только в составе прода.** Значение, заданное
75
+ составом, действует лишь на контейнер, поднятый этим составом; ручной прогон того же образа
76
+ идёт с пустым значением, а пусто здесь означает локалхост — со всеми отладочными
77
+ умолчаниями, которые он разрешает. Умолчание образа задаётся в самом образе.
78
+ - **Образы выкатываются по sha коммита, а не по метке «последний».** Метка в реестре отстаёт
79
+ от главной ветки, и прод молча возвращается к прежней версии, продолжая отвечать.
80
+ - **Цепочка миграций прогоняется с пустого хранилища до мержа.** Порядок применения
81
+ лексикографический по имени каталога, а метку времени ставит момент создания: миграция из
82
+ ветки, начатой раньше, встаёт перед той, от которой зависит.
83
+ - **Документ едет в том же коммите, что и правка.** Обход — строка `Docs-skip: <причина>` в
84
+ теле коммита; пустая причина не принимается.
85
+ - **Заголовок коммита сверяется с форматом на месте.** Разобранный по типу и области
86
+ заголовок читается списком, а свободный текст — только целиком.
87
+ - **Перед пушем прогоняются все линтеры, а не один.** Линтер кода обычно не читает файлы
88
+ стилей вовсе, и правила оформления без второго прогона не проверяет ничто.
89
+ - **Автор PR не может быть его ревьювером.** Запрос разбора на самого себя GitHub принимает и
90
+ молча не создаёт — разбор при этом выглядит запрошенным.
91
+ - **Метки, исполнитель и ревьювер PR ставятся вызовами `gh api`, а не `gh pr edit`.** На
92
+ репозитории со старой бордой `gh pr edit` отвечает отказом про Projects (classic) и до
93
+ правки не доходит вовсе.
94
+ - **Борда правится запросом GraphQL по идентификатору проекта.** `gh project` с `--owner`
95
+ отвечает `unknown owner type`, когда владелец борды — не та учётная запись, под которой
96
+ идёт вызов.
97
+ - **Сценарии гардов задают настройки git сами, а не берут их с машины.** Коммит во временном
98
+ репозитории сценария наследует общий конфиг: если включена подпись, git идёт в агент ключей,
99
+ а заблокированный агент роняет весь набор — со стороны это выглядит сломанным гардом. Автор,
100
+ почта и подпись передаются флагами `-c` прямо в команду.
101
+ - **Расхождение миграций со схемой меряется на теневом хранилище, а не на том, где работает
102
+ тот, кто пушит.** Оно законно несёт след любой недоделанной ветки, и сверка с ним держала бы
103
+ чужую правку. Гейт и выкатка зовут одну и ту же проверку — иначе «сошлось» станет значить в
104
+ двух местах разное.
105
+
106
+ ## Чего из закона здесь нет
107
+
108
+ Гард поставки стоит на командах агента, поэтому ветку, заведённую руками в редакторе, он не
109
+ видит: имя такой ветки держится памятью. Требование от этого не слабеет — просто отдельной
110
+ проверки под него не заводится: работа опознаётся заголовком задачи и отчёта, а это сверяется
111
+ у всех. Сверка очереди имя ветки не судит вовсе: у открытого PR его не переименовать.
112
+
113
+ Взятие задачи в работу не стережёт ничто: борда ветки не видит, а гард поставки её видит, но
114
+ борду не правит — сетевой вызов в разборе команды падал бы вместе со связью и отбивал бы
115
+ работу вместо промаха. Держится это памятью и подсказкой, которую печатает команда заведения
116
+ задачи. Отставшую колонку находит сверка очереди — но уже после того, как PR открыт.
117
+
118
+ ## Паттерны
119
+
120
+ - `git-workflow-commit` — задача, ветка, коммит, пуш и PR от учётной записи машинной работы.
121
+ - `git-workflow-merge` — главная ветка влита в ветку задачи, конфликт разобран.
122
+ - `git-workflow-migration` — правка схемы хранилища и её миграций.
123
+ - `git-workflow-restart` — ручной перезапуск прода.