@rt-tools/agent-kit 0.2.0 → 0.4.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 (203) hide show
  1. package/README.md +235 -18
  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 +286 -0
  9. package/assets/checks/check-board.github.mjs +188 -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 +1007 -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 +100 -0
  21. package/assets/defaults/project.sh +179 -0
  22. package/assets/hooks/browser-device-id.sh +20 -0
  23. package/assets/hooks/browser-guard-device-id.sh +28 -0
  24. package/assets/hooks/browser-guard-no-asking.sh +27 -0
  25. package/assets/hooks/browser-guard-no-listing.sh +18 -0
  26. package/assets/hooks/browser-guard-no-other-drivers.sh +79 -0
  27. package/assets/hooks/browser-guard-require-select.sh +54 -0
  28. package/assets/hooks/commit-msg.sh +26 -0
  29. package/assets/hooks/constitution-index.sh +43 -0
  30. package/assets/hooks/dev-server-guard.sh +115 -0
  31. package/assets/hooks/docs-guard.sh +282 -0
  32. package/assets/hooks/git-guard-delivery.sh +167 -0
  33. package/assets/hooks/git-guard-main.sh +73 -0
  34. package/assets/hooks/git-guard-push-tests.sh +94 -0
  35. package/assets/hooks/glossary-load.sh +23 -0
  36. package/assets/hooks/lint-after-edit.sh +219 -0
  37. package/assets/hooks/qa-dataid-guard.sh +121 -0
  38. package/assets/hooks/reuse-first-guard.sh +154 -0
  39. package/assets/hooks/skill-gate-rearm.sh +23 -0
  40. package/assets/hooks/skill-gate.sh +128 -0
  41. package/assets/hooks/skill-loaded.sh +21 -0
  42. package/assets/hooks/sql-guard.sh +679 -0
  43. package/assets/hooks/task-context-load.sh +100 -0
  44. package/assets/hooks/task-flow-guard.sh +107 -0
  45. package/assets/laws/{access.md → application/access.md} +1 -4
  46. package/assets/laws/{locales.md → application/locales.md} +1 -3
  47. package/assets/laws/application/money.md +41 -0
  48. package/assets/laws/application/ownership.md +32 -0
  49. package/assets/laws/{search-visibility.md → application/search-visibility.md} +1 -1
  50. package/assets/laws/code-structure.md +7 -6
  51. package/assets/laws/delivery.md +53 -3
  52. package/assets/laws/entity-editing.md +49 -55
  53. package/assets/laws/entity-models.md +4 -14
  54. package/assets/laws/frontend-application.md +5 -5
  55. package/assets/laws/lib-imports.md +14 -1
  56. package/assets/laws/lists.md +33 -0
  57. package/assets/laws/navigation.md +40 -0
  58. package/assets/laws/project-documentation.md +17 -8
  59. package/assets/laws/reuse-first.md +26 -21
  60. package/assets/laws/shared-code.md +13 -1
  61. package/assets/laws/verifiability.md +17 -1
  62. package/assets/laws/work-conduct.md +48 -0
  63. package/assets/patterns/admin-lists-screen.md +131 -0
  64. package/assets/patterns/admin-nav-item.md +71 -0
  65. package/assets/patterns/angular-patterns-state.md +101 -0
  66. package/assets/patterns/api-layer-pair.md +88 -0
  67. package/assets/patterns/browser-verification-measure.md +86 -0
  68. package/assets/patterns/browser-verification-stand.md +143 -0
  69. package/assets/patterns/component-structure-new.md +99 -0
  70. package/assets/patterns/dependencies-upgrade.md +65 -0
  71. package/assets/patterns/doc-style-sweep.md +137 -0
  72. package/assets/patterns/doc-style-write.md +109 -0
  73. package/assets/patterns/entity-aside.md +136 -0
  74. package/assets/patterns/entity-models-new.md +124 -0
  75. package/assets/patterns/entity-store.md +91 -0
  76. package/assets/patterns/git-workflow-commit.azure.md +259 -0
  77. package/assets/patterns/git-workflow-commit.github.md +333 -0
  78. package/assets/patterns/git-workflow-commit.gitlab.md +283 -0
  79. package/assets/patterns/git-workflow-merge.md +99 -0
  80. package/assets/patterns/git-workflow-migration.md +88 -0
  81. package/assets/patterns/git-workflow-restart.md +49 -0
  82. package/assets/patterns/lib-layers-move.md +95 -0
  83. package/assets/patterns/lib-layers-new.md +82 -0
  84. package/assets/patterns/ownership-scope-resolve.md +69 -0
  85. package/assets/patterns/permissions-procedure.md +71 -0
  86. package/assets/patterns/platform-access-di.md +84 -0
  87. package/assets/patterns/pricing-quote.md +71 -0
  88. package/assets/patterns/reuse-first-extend.md +73 -0
  89. package/assets/patterns/seo-page.md +104 -0
  90. package/assets/patterns/seo-verify.md +83 -0
  91. package/assets/patterns/shared-code-new.md +86 -0
  92. package/assets/patterns/spec-driven-domain.md +107 -0
  93. package/assets/patterns/spec-driven-rule.md +127 -0
  94. package/assets/patterns/styling-bem-component.md +88 -0
  95. package/assets/patterns/styling-bem-layout.md +73 -0
  96. package/assets/patterns/task-flow-close.md +90 -0
  97. package/assets/patterns/task-flow-resume.md +94 -0
  98. package/assets/patterns/task-flow-start.md +117 -0
  99. package/assets/patterns/testing-e2e.md +92 -0
  100. package/assets/patterns/testing-unit.md +117 -0
  101. package/assets/patterns/translations-key.md +64 -0
  102. package/assets/patterns/ts-procedure.md +65 -0
  103. package/assets/rules/angular-patterns.md +71 -0
  104. package/assets/rules/api-layer.md +71 -0
  105. package/assets/rules/browser-verification.md +87 -0
  106. package/assets/rules/component-structure.md +64 -0
  107. package/assets/rules/dependencies.md +66 -0
  108. package/assets/rules/doc-style.md +103 -0
  109. package/assets/rules/entity-conventions.md +78 -0
  110. package/assets/rules/entity-models.md +70 -0
  111. package/assets/rules/git-workflow.azure.md +116 -0
  112. package/assets/rules/git-workflow.github.md +123 -0
  113. package/assets/rules/git-workflow.gitlab.md +113 -0
  114. package/assets/rules/lib-layers.md +80 -0
  115. package/assets/rules/lists.md +73 -0
  116. package/assets/rules/navigation.md +78 -0
  117. package/assets/rules/ownership-scope.md +63 -0
  118. package/assets/rules/permissions.md +70 -0
  119. package/assets/rules/platform-access.md +77 -0
  120. package/assets/rules/pricing.md +64 -0
  121. package/assets/rules/reuse-first.md +83 -0
  122. package/assets/rules/seo.md +71 -0
  123. package/assets/rules/shared-code.md +70 -0
  124. package/assets/rules/spec-driven.md +135 -0
  125. package/assets/rules/styling-bem.md +74 -0
  126. package/assets/rules/task-flow.md +110 -0
  127. package/assets/rules/testing.md +100 -0
  128. package/assets/rules/translations.md +69 -0
  129. package/assets/rules/typescript-conventions.md +76 -0
  130. package/assets/skills/agent-kit.md +81 -0
  131. package/assets/skills/write-a-skill.md +108 -0
  132. package/assets/templates/gate-map.sh +45 -0
  133. package/assets/templates/implementation.md +44 -0
  134. package/assets/templates/pattern.md +5 -1
  135. package/assets/templates/project.sh +54 -0
  136. package/assets/templates/rule.md +12 -23
  137. package/assets/variants.json +20 -0
  138. package/assets/workflows/feature.js +134 -0
  139. package/assets/workflows/plan.js +150 -0
  140. package/bin/agent-kit.d.ts.map +1 -1
  141. package/bin/agent-kit.js +78 -5
  142. package/bin/agent-kit.js.map +1 -1
  143. package/bin/prompt.d.ts +5 -0
  144. package/bin/prompt.d.ts.map +1 -1
  145. package/bin/prompt.js +19 -7
  146. package/bin/prompt.js.map +1 -1
  147. package/index.d.ts +1 -0
  148. package/index.d.ts.map +1 -1
  149. package/index.js +1 -0
  150. package/index.js.map +1 -1
  151. package/lib/assets.d.ts +14 -1
  152. package/lib/assets.d.ts.map +1 -1
  153. package/lib/assets.js +23 -2
  154. package/lib/assets.js.map +1 -1
  155. package/lib/catalog.d.ts +52 -5
  156. package/lib/catalog.d.ts.map +1 -1
  157. package/lib/catalog.js +104 -16
  158. package/lib/catalog.js.map +1 -1
  159. package/lib/commands.d.ts +22 -1
  160. package/lib/commands.d.ts.map +1 -1
  161. package/lib/commands.js +218 -11
  162. package/lib/commands.js.map +1 -1
  163. package/lib/companion.d.ts +57 -0
  164. package/lib/companion.d.ts.map +1 -0
  165. package/lib/companion.js +60 -0
  166. package/lib/companion.js.map +1 -0
  167. package/lib/config.d.ts +42 -2
  168. package/lib/config.d.ts.map +1 -1
  169. package/lib/config.js +60 -2
  170. package/lib/config.js.map +1 -1
  171. package/lib/freshness.d.ts +14 -0
  172. package/lib/freshness.d.ts.map +1 -0
  173. package/lib/freshness.js +116 -0
  174. package/lib/freshness.js.map +1 -0
  175. package/lib/hooks-map.d.ts +24 -0
  176. package/lib/hooks-map.d.ts.map +1 -0
  177. package/lib/hooks-map.js +72 -0
  178. package/lib/hooks-map.js.map +1 -0
  179. package/lib/integrity.d.ts +36 -0
  180. package/lib/integrity.d.ts.map +1 -0
  181. package/lib/integrity.js +44 -0
  182. package/lib/integrity.js.map +1 -0
  183. package/lib/picker.d.ts +11 -1
  184. package/lib/picker.d.ts.map +1 -1
  185. package/lib/picker.js +44 -6
  186. package/lib/picker.js.map +1 -1
  187. package/lib/stamp.d.ts +2 -5
  188. package/lib/stamp.d.ts.map +1 -1
  189. package/lib/stamp.js +25 -10
  190. package/lib/stamp.js.map +1 -1
  191. package/lib/sync.d.ts +29 -0
  192. package/lib/sync.d.ts.map +1 -1
  193. package/lib/sync.js +78 -4
  194. package/lib/sync.js.map +1 -1
  195. package/lib/variants.d.ts +44 -0
  196. package/lib/variants.d.ts.map +1 -0
  197. package/lib/variants.js +82 -0
  198. package/lib/variants.js.map +1 -0
  199. package/package.json +1 -1
  200. package/rt-tools-agent-kit-0.4.0.tgz +0 -0
  201. package/assets/laws/admin-lists.md +0 -35
  202. package/assets/laws/admin-navigation.md +0 -38
  203. package/rt-tools-agent-kit-0.2.0.tgz +0 -0
@@ -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` — ручной перезапуск прода.
@@ -0,0 +1,113 @@
1
+ ---
2
+ name: git-workflow
3
+ kind: rule
4
+ law: delivery
5
+ description: Правило под «Закон о поставке» для дерева на GitLab. Брать на заведение задачи, ветки, коммит, пуш, создание MR, слияние, а также на правку схемы хранилища, её миграций и вызовы миграций. Называет задачу на борде как начало работы, колонку задачи как ход работы, соответствие задачи и ветки один к одному, имя ветки, формат коммита, учётную запись машинной работы, обязательный состав MR, гарды поставки и сверку очереди работ. Готовый код — в паттернах git-workflow-commit, git-workflow-merge, git-workflow-migration и git-workflow-restart.
6
+ ---
7
+
8
+ # Поставка — как это устроено здесь
9
+
10
+ Правило под закон `docs/constitution/delivery.md`. Закон говорит, что должно быть верно;
11
+ здесь — каким приёмом это держится в дереве, лежащем на GitLab. Ключ задач, адрес борды,
12
+ учётная запись машинной работы и области коммита — при этом дереве, в `implementation.md`
13
+ рядом: их не угадать, и общими они не бывают.
14
+
15
+ ## Как это называется здесь
16
+
17
+ | В законе | Здесь |
18
+ | -------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
19
+ | главная ветка | `main` |
20
+ | отдельная ветка | `<КЛЮЧ>-<номер задачи>-<короткий-slug>`; ключ задач — в `implementation.md`. Имя без номера (`feat/…`, `fix/…`) законно, пока ветка живёт локально: MR с неё не откроется |
21
+ | задача | issue проекта, заголовок `[<КЛЮЧ>-<номер>] <Что не так>`, исполнитель — учётная запись машинной работы; MR прикрепляется к нему строкой `Closes #<номер>` в описании |
22
+ | очередь работ | доска задач проекта — Issue Board. Задача попадает на неё меткой списка, а не самим фактом заведения: доска показывает те issue, чью метку знает |
23
+ | состояние задачи в очереди работ | список доски, за которым стоит метка: заведённая, взятая в работу, ждущая разбора. Имена меток — в `implementation.md`; закрытая задача уходит из очереди слиянием, а не переносом в последний список |
24
+ | отчёт о задаче | заголовок MR `[<КЛЮЧ>-<номер>] <Что сделано>` — тот же номер, что у задачи, и её название, переведённое в сделанное; тип и область коммита сюда не идут |
25
+ | обсуждение правки | разбор MR: ревьювер — владелец проекта, исполнитель — учётная запись машинной работы, метки — те же, что у задачи |
26
+ | попадание правки в главную ветку | слияние MR; оно же запускает выкатку — `.gitlab-ci.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
+ - **Ветка без номера задачи MR не открывает.** Гард поставки отбивает `glab mr create` с такой
44
+ ветки: локально она законна, но правка из неё — это выкатка, за которой в очереди работ
45
+ ничего не стоит. Заводится задача, и работа переносится в ветку с её номером.
46
+ - **Номер ветки и номер в заголовке MR сверяются на месте, а состояние задачи — по доске.**
47
+ Формат читается из текста команды и работает без сети; существование задачи, её метка
48
+ списка, исполнитель и то, что она ещё открыта, — только когда есть чем спросить. Нет сети
49
+ или нет токена — второй ярус молча пропускается: проверка, падающая в самолёте, перестаёт
50
+ что-либо значить.
51
+ - **Список задачи двигается тем же движением, что и работа.** Ветка заведена — задача
52
+ переставляется во взятые в работу, MR открыт — в ждущие разбора; делает это команда
53
+ перевода, а не набор вызовов по памяти. Перевод идёт сразу за шагом, который его вызвал:
54
+ очередь работ читают между шагами, а не после них.
55
+ - **Отставший список находится сверкой очереди, а не глазами.** Сверка судит список по отчёту
56
+ в обе стороны: открытый MR при задаче не в разборе и разбор без открытого MR — оба
57
+ расхождения. Момента, когда задачу берут в работу, ей не видно: ветки на доске нет.
58
+ - **Задачи, чинящиеся одной правкой, сливаются до слияния ветки.** Вторая стирается вместе с
59
+ номером, а недостающее из неё дописывается в первую. После слияния слить уже нельзя: ветка
60
+ въехала, и откатывается она целиком.
61
+ - **Слияние в главную ветку выкатывает прод.** Правила `only`/`rules` конвейера покрывают
62
+ документы отдельно, поэтому переменные окружения, секреты и записи имён ставятся до слияния,
63
+ а не после.
64
+ - **Признак режима объявлен в образе, а не только в составе прода.** Значение, заданное
65
+ составом, действует лишь на контейнер, поднятый этим составом; ручной прогон того же образа
66
+ идёт с пустым значением, а пусто здесь означает локалхост — со всеми отладочными
67
+ умолчаниями, которые он разрешает. Умолчание образа задаётся в самом образе.
68
+ - **Образы выкатываются по sha коммита, а не по метке «последний».** Метка в реестре отстаёт
69
+ от главной ветки, и прод молча возвращается к прежней версии, продолжая отвечать.
70
+ - **Цепочка миграций прогоняется с пустого хранилища до слияния.** Порядок применения
71
+ лексикографический по имени каталога, а метку времени ставит момент создания: миграция из
72
+ ветки, начатой раньше, встаёт перед той, от которой зависит.
73
+ - **Документ едет в том же коммите, что и правка.** Обход — строка `Docs-skip: <причина>` в
74
+ теле коммита; пустая причина не принимается.
75
+ - **Заголовок коммита сверяется с форматом на месте.** Разобранный по типу и области
76
+ заголовок читается списком, а свободный текст — только целиком.
77
+ - **Перед пушем прогоняются все линтеры, а не один.** Линтер кода обычно не читает файлы
78
+ стилей вовсе, и правила оформления без второго прогона не проверяет ничто.
79
+ - **Слияние по кнопке «Merge when pipeline succeeds» не заменяет проверок до пуша.** Конвейер
80
+ видит только то, что уже отправлено, а отправленная красная ветка занимает очередь работ и
81
+ выглядит готовой к разбору.
82
+ - **Метки и исполнитель MR ставятся при создании, а не правкой после.** `glab mr create`
83
+ принимает их флагами; правка открытого MR второй командой обходится молча, когда токен
84
+ учётной записи машинной работы не видит проект целиком.
85
+ - **Задача переводится по списку правкой её меток.** Списки доски — это метки: перевод, не
86
+ снявший прежнюю метку, оставляет задачу в двух списках сразу, и очередь читается неверно.
87
+ - **Сценарии гардов задают настройки git сами, а не берут их с машины.** Коммит во временном
88
+ репозитории сценария наследует общий конфиг: если включена подпись, git идёт в агент ключей,
89
+ а заблокированный агент роняет весь набор — со стороны это выглядит сломанным гардом. Автор,
90
+ почта и подпись передаются флагами `-c` прямо в команду.
91
+ - **Расхождение миграций со схемой меряется на теневом хранилище, а не на том, где работает
92
+ тот, кто пушит.** Оно законно несёт след любой недоделанной ветки, и сверка с ним держала бы
93
+ чужую правку. Гейт и выкатка зовут одну и ту же проверку — иначе «сошлось» станет значить в
94
+ двух местах разное.
95
+
96
+ ## Чего из закона здесь нет
97
+
98
+ Гард поставки стоит на командах агента, поэтому ветку, заведённую руками в редакторе, он не
99
+ видит: имя такой ветки держится памятью. Требование от этого не слабеет — просто отдельной
100
+ проверки под него не заводится: работа опознаётся заголовком задачи и отчёта, а это сверяется
101
+ у всех. Сверка очереди имя ветки не судит вовсе: у открытого MR его не переименовать.
102
+
103
+ Взятие задачи в работу не стережёт ничто: доска ветки не видит, а гард поставки её видит, но
104
+ доску не правит — сетевой вызов в разборе команды падал бы вместе со связью и отбивал бы
105
+ работу вместо промаха. Держится это памятью и подсказкой, которую печатает команда заведения
106
+ задачи. Отставший список находит сверка очереди — но уже после того, как MR открыт.
107
+
108
+ ## Паттерны
109
+
110
+ - `git-workflow-commit` — задача, ветка, коммит, пуш и MR от учётной записи машинной работы.
111
+ - `git-workflow-merge` — главная ветка влита в ветку задачи, конфликт разобран.
112
+ - `git-workflow-migration` — правка схемы хранилища и её миграций.
113
+ - `git-workflow-restart` — ручной перезапуск прода.
@@ -0,0 +1,80 @@
1
+ ---
2
+ name: lib-layers
3
+ kind: rule
4
+ law: lib-imports
5
+ description: Правило под «Закон об импортах между либами». Брать при правке project.json, tsconfig.base.json, eslint/boundaries/**, любого src/index.ts и проверок раскладки, а также когда решается, где живёт общий символ. Называет семьи, слои, теги и границы этого дерева. Готовый порядок действий — в паттернах lib-layers-new и lib-layers-move.
6
+ ---
7
+
8
+ # Импорты между либами — как это устроено здесь
9
+
10
+ Правило под закон `docs/constitution/lib-imports.md`. Закон говорит, кто кого видит; здесь —
11
+ как это нарезано в этом дереве, чем названо и чего у нас нет.
12
+
13
+ ## Как это называется здесь
14
+
15
+ | В законе | Здесь |
16
+ | -------------------------------- | ------------------------------------------------------ |
17
+ | семья либ | `libs/site`, `libs/admin`, `libs/api` |
18
+ | слой | `api`, `data-access`, `feature`, `shell`, `ui`, `util` |
19
+ | право видеть либу | тег в `eslint/boundaries/domains/<семья>.config.mjs` |
20
+ | общая всем трём приложениям либа | `libs/common/util`, тег `scope:common-util` |
21
+ | основание семейства | `<семья>/core`; его тег входит в `ADMIN_UNIVERSAL` |
22
+ | барель | `src/index.ts` либы и `index.ts` каталога компонента |
23
+
24
+ Фичевый домен фронта — шесть слоёв (`api`, `data-access`, `feature/<экран>`, `shell`, `ui`,
25
+ `util`), общий домен — те же без `shell`. У бэкенда `ui` и `shell` нет: отдавать разметку и
26
+ роутиться ему нечем.
27
+
28
+ ## Где это лежит
29
+
30
+ В этом дереве — таблица в `implementation.md` рядом. Пути живут там, а не здесь: правило
31
+ переносится между репозиториями, раскладка — нет, и путь, названный в правиле, врёт в первом
32
+ же дереве, которое держит код иначе.
33
+
34
+ ## Как закон применяется здесь
35
+
36
+ - **Чужой символ не реэкспортируется ни одной из двух форм.** Запрещены и
37
+ `export { X } from '@<область>/…'`, и пара «импорт плюс `export { X };`»: вторая
38
+ выглядит как собственное объявление и глазами в ревью проходила.
39
+ - **Строка с алиасом чужой либы в бареле — тот же реэкспорт.** Относительный путь в бареле
40
+ законен: он собирает наружу собственные файлы либы.
41
+ - **Не хватает права — оно дописывается строкой в конфиге домена с комментарием.** Импорт,
42
+ который «просто заработал», означает, что тег ещё не сужен.
43
+ - **У `libs/common/util` список зависимостей пуст, и Angular туда не попадает.** Либу
44
+ импортирует бэкенд, и фреймворк уехал бы в его бандл; токен DI, общий двум фронтовым
45
+ семьям, живёт в `common/platform`.
46
+ - **Основание семейства видит только `util`.** Его зовут все домены семьи, и любая его
47
+ зависимость становится общей для всех сразу.
48
+ - **Домен заводится под предмет, а не под механику.** Механика, общая нескольким доменам,
49
+ едет в либу, которой она уже видна: у фронта это основание семейства, у бэкенда — слой
50
+ `util`, перечисленный у каждого домена.
51
+ - **Домен, у которого непуст один слой, значится строкой с причиной.** Иначе он неотличим от
52
+ слота: пустые слои есть и у того, и у другого, а барель лежит в обоих.
53
+
54
+ ## Чего из закона здесь нет
55
+
56
+ Отклонение основания семейства от лесенки сегодня одно и названо в `CORE_EXCEPTIONS`
57
+ проверки: `common/proto` и `common/connect` у `admin/core` ради транспорта Connect.
58
+
59
+ Полный набор слоёв требуется у всех, и пустой слой дефектом не считается: `api` пуст у
60
+ домена, который ни с кем чужим не говорит. Судится только крайний случай — непуст ровно один
61
+ слой; такой домен обычно один, и он стоит в списке исключений с
62
+ причиной.
63
+
64
+ ## Паттерны
65
+
66
+ - `lib-layers-new` — завести или удалить либу: генератор, теги, алиас, README.
67
+ - `lib-layers-move` — перенести код между либами: порядок, границы, импорты, README обеих.
68
+
69
+ ## Ловушки
70
+
71
+ - **Либа, которую никто не импортирует, не проверена ничем.** `nx lint` и `nx test` проверяют
72
+ её саму, а не договор с потребителем: потерянное поле в `*.State` ошибкой не считается, пока
73
+ нет вызывающего кода. Первый импортёр и есть первая проверка — слой моделей принимается
74
+ после `nx build` и живого прогона сценария, а не по зелёному `lint test`.
75
+ - **Проверка принимается на нарушении, а не на зелёном прогоне.** Нарушение вносится руками,
76
+ прогон краснеет, правка снимается. У проверок в `tools/` тестов нет, и это единственная
77
+ приёмка.
78
+ - `git rm -r` оставляет `node_modules/.vite` внутри удалённого каталога, и проверка продолжает
79
+ видеть его как домен без слоёв. Добивать `rm -rf`.
80
+ - Образец, написанный второй раз, ловит `npm run check:dupes` — правило `shared-code`.
@@ -0,0 +1,73 @@
1
+ ---
2
+ name: lists
3
+ kind: rule
4
+ law: lists
5
+ description: Правило под «Закон о списке записей». Брать при правке списочного экрана (libs/admin/*/feature/list), таблицы и пагинации кита. Называет порядок блоков, чем собирается список, где живёт выборка и что уже есть в ките. Готовый код экрана — в паттерне admin-lists-screen.
6
+ ---
7
+
8
+ # Списочный экран — как это устроено здесь
9
+
10
+ Правило под закон `docs/constitution/lists.md`. Закон говорит, что пользователь видит и
11
+ делает; здесь — из чего этот экран собирается в этом дереве и как он выглядит. Про вид
12
+ говорит правило: закон о нём молчит намеренно.
13
+
14
+ ## Как это называется здесь
15
+
16
+ | В законе | Здесь |
17
+ | ------------------------- | ------------------------------------------------------------------------- |
18
+ | таблица записей | `rt-table` из `@rt-tools/ui-kit-v2`, вход `[dataSource]` |
19
+ | состав и порядок столбцов | `[columnsConfig]`, хранятся по ключу `tableId` |
20
+ | карточка на узком экране | ветка `<префикс>-table`, а не своя разметка |
21
+ | тулбар | `<префикс>-toolbar` со слотами `vmToolbarLeft` и `vmToolbarRight` |
22
+ | выборка | `IList.Query.State` — страница, сортировка, условия отбора, строка поиска |
23
+ | панель настройки столбцов | асайд по маршруту `path: 'table-settings'` |
24
+
25
+ ## Где это лежит
26
+
27
+ В этом дереве — таблица в `implementation.md` рядом. Пути живут там, а не здесь: правило
28
+ переносится между репозиториями, раскладка — нет, и путь, названный в правиле, врёт в первом
29
+ же дереве, которое держит код иначе.
30
+
31
+ ## Как закон применяется здесь
32
+
33
+ - **Список собирается `<префикс>-table`, а не своей разметкой.** Скелетоны, пустое состояние,
34
+ карточки на узком экране и настройка столбцов — входы таблицы; свой
35
+ `@if (rows().length === 0)` означает, что экран собран мимо неё.
36
+ - **Строки объявляются на `rowsTable.displayedColumns()`, а не на своём списке.** Столбец с
37
+ меню таблица добавляет сама.
38
+ - **Клик по строке открывает запись, а меню — для действий над ней.** Вид нажимаемой строки
39
+ даёт `clickable`, активацию мышью и с клавиатуры — `vmTableRow`; клик по кнопке внутри строки
40
+ активацией не считается.
41
+ - **Доступность действия лежит полем строки, а не вызовом метода компонента.** Метод из шаблона
42
+ пересчитывался бы на каждой проверке.
43
+ - **Недоступное сейчас действие в меню строки не рисуется вовсе.** Пункт заводится под `@if` по
44
+ полю строки, а не выключенным: выключенный пункт перечисляет владельцу запреты вместо того,
45
+ что он может сделать, и набор их меняется от строки к строке.
46
+ - **Кнопка меню не показывается, если у строки не осталось доступных действий.** За это
47
+ отвечает вход `[rowHasActions]` — предикат по строке; считать по содержимому меню нельзя,
48
+ спроецированный шаблон известен только после отрисовки.
49
+ - **Отказ загрузки подаётся тостом, а не строкой над таблицей.** Ключ отказа читается сразу
50
+ после запроса, а не подпиской на сигнал стора: стор делят список и панель правки.
51
+ - **Экран берёт сортировку и условия отбора из ответа, а не из своего запроса.** Сервер мог
52
+ применить умолчание домена или отбросить условие.
53
+ - **Строка списка получает короткую модель сущности, а не полную.**
54
+
55
+ ## Чего из закона здесь нет
56
+
57
+ Страницу отдаёт только та процедура, в чьём ответе есть `page_model`; заявки и объекты
58
+ приходят целиком — долги `Q-L-5`, `Q-L-7` и `Q-M-2`. Выборка живёт в адресе не у каждого
59
+ списка — долг `Q-L-4`.
60
+
61
+ ## Паттерны
62
+
63
+ - `admin-lists-screen` — собрать экран: порядок блоков, таблица, меню строки, сортируемый
64
+ заголовок, тулбар.
65
+
66
+ ## Ловушки
67
+
68
+ - Без `[vmTableRowActionsRowType]` тип `let-row` выводится как `unknown`, и падает только
69
+ продовая сборка — юниты и дев-сервер проходят.
70
+ - Прокрутке нужны оба правила вместе: контейнер с `overflow-x`, таблица с
71
+ `min-width: max-content`. С одним столбцы сжимаются вместо сдвига.
72
+ - Тулбар и пагинация своих классов не носят: промежуток задаёт `<префикс>-page`.
73
+ - Заголовок стоит в своём `<header>`, а не внутри тулбара.