@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,135 @@
1
+ ---
2
+ name: spec-driven
3
+ kind: rule
4
+ law: project-documentation
5
+ description: Правило под «Закон о документации проекта». Брать при правке docs/specs/**, docs/constitution/** и любого скила в .claude/skills. Называет три слоя — закон, правило, паттерн, — обязательные разделы, привязку к коду и связь сценариев с тестами. Готовый порядок действий — в паттернах spec-driven-domain и spec-driven-rule.
6
+ ---
7
+
8
+ # Документация проекта — как это устроено здесь
9
+
10
+ Правило под закон `docs/constitution/project-documentation.md`. Закон говорит, что должно
11
+ быть верно про тексты; здесь — из каких слоёв они сложены в этом дереве и что сверяет машина.
12
+ Формулировки — правило `doc-style` под тем же законом.
13
+
14
+ ## Как это называется здесь
15
+
16
+ ```
17
+ ЗАКОН docs/constitution/<закон>.md — верен для любого приложения этого класса
18
+ docs/constitution/application/<закон>.md — закон приложения: деньги, локали, доступ
19
+ о проекте не знает ничего: ни путей, ни имён файлов, ни привязок
20
+
21
+ ├─ ПРАВИЛО .claude/skills/<правило>/SKILL.md (kind: rule, law: <закон>)
22
+ │ привязывает закон к этому проекту; несколько правил на закон
23
+ │ .claude/skills/<правило>/implementation.md — привязка к коду
24
+
25
+ │ └─ ПАТТЕРН .claude/skills/<правило>-<что>/SKILL.md (kind: pattern, rule: <правило>)
26
+ │ готовый код и конкретные приёмы; минимум один на правило
27
+
28
+ └─ СПЕК ДОМЕНА docs/specs/<домен>/
29
+ как работает домен; объявляет законы, которые применяет
30
+
31
+ СКИЛ БЕЗ ЗАКОНА .claude/skills/<имя>/SKILL.md (ни kind: rule, ни kind: pattern)
32
+ стоит рядом с лестницей, а не в ней: он не про то, что должно быть
33
+ верно в продукте, а про то, как здесь делается работа
34
+ ```
35
+
36
+ Ссылки идут только снизу вверх: закон не ссылается ни на правило, ни на спек, ни на файл.
37
+
38
+ Скил без закона — третий случай, и он законный. Витрина, генератор, работа с чужим сервисом,
39
+ заведение самого скила: над таким нет утверждения о продукте, а значит нет и закона. Выдумывать
40
+ ему закон, чтобы уложить в лестницу, нельзя — закон, у которого одно правило и ни одной статьи о
41
+ продукте, разъезжается с остальными при первой же правке. Как такой скил заводится — скил
42
+ `write-a-skill`.
43
+
44
+ | В законе | Здесь |
45
+ | ------------------------------- | ---------------------------------------------------------------------------------------------------- |
46
+ | набор разделов | `REQUIRED_HEADINGS` для спека, «Статьи» для закона |
47
+ | утверждение документа | пункт `## Правила` в спеке, `## Статьи` в законе, `## Как закон применяется здесь` в правиле |
48
+ | место, где оно исполняется | строка в `implementation.md` рядом: `` `файл:символ` `` |
49
+ | обещанное поведение | сценарий `SC-<ПРЕФИКС>-<НОМЕР>` в `docs/specs/<домен>/scenarios.md` |
50
+ | открытый вопрос | `Q-N` — на него ссылаются из задачи на борде и из коммитов; номер после закрытия не переиспользуется |
51
+ | законы, которые применяет домен | строка `**Законы:**` в шапке спека, именами в кавычках |
52
+
53
+ ## Где это лежит
54
+
55
+ В этом дереве — таблица в `implementation.md` рядом. Пути живут там, а не здесь: правило
56
+ переносится между репозиториями, раскладка — нет, и путь, названный в правиле, врёт в первом
57
+ же дереве, которое держит код иначе.
58
+
59
+ ## Как закон применяется здесь
60
+
61
+ - **Набор разделов спека задан заранее, и отсутствие раздела — отказ.** «Не применимо» —
62
+ законный ответ, отсутствие раздела — нет: сквозные требования вспоминаются постфактум
63
+ именно тогда, когда для них не заведено места.
64
+ - **Каждое утверждение привязано к месту в коде, и связь сверяется в обе стороны.** Ключ
65
+ связи — сам текст утверждения, поэтому переформулировать его, забыв про привязку, нельзя.
66
+ - **Привязка не ведёт в код, который никто не зовёт.** Символ, объявленный в своём файле и
67
+ больше нигде не встречающийся, местом исполнения не считается.
68
+ - **Таблица процедур сверяется с декораторами в обе стороны.** Иначе процедура, которую домен
69
+ обслуживает, но забыл описать, видна только в декораторе.
70
+ - **Код отказа принимается, только если он в домене бросается.** Коды выписывались по
71
+ замыслу, и на одном пути обещанный отказ не бросал никто.
72
+ - **Префикс сценариев в домене один.** Второй префикс означает, что домен описан дважды.
73
+ - **У закона обязателен раздел «Статьи», а кроме них он держит только открытые вопросы.**
74
+ Истории правок и доводов о выбранном когда-то варианте в законе нет: историю держит система
75
+ контроля версий, а довод с отвергнутой альтернативой — свойство работы, и место ему в
76
+ «Ловушках» правила. Закрытый вопрос из закона уходит, а пустой раздел ради заголовка
77
+ проверку всё равно проходил.
78
+ - **Закон, назвавший файл проекта, — отказ.** Путям и привязкам место в правиле: иначе закон
79
+ нельзя ни прочитать без знания дерева, ни применить на другом приложении.
80
+ - **Правило объявляет закон, под который написано.** Правило без закона — набор приёмов, из
81
+ которого не видно, что именно должно быть верно.
82
+ - **Слоёв законов два, а имя закона одно на оба.** Общий лежит в корне конституции, закон
83
+ приложения — в `application/`; ни `law:`, ни `**Законы:**` слоя не называют, поэтому имена
84
+ законов уникальны по всему дереву конституции.
85
+ - **Спек объявляет законы, которые применяет, и связь сверяется в обе стороны.** Закон,
86
+ названный в тексте спека, обязан стоять в шапке: иначе по закону не узнать, какие домены
87
+ на нём стоят.
88
+
89
+ ## Чего из закона здесь нет
90
+
91
+ Закон, которого не применяет ни один спек, отказом не считается: законы про устройство кода,
92
+ поставку и проверяемость доменов не касаются вовсе. Порядок «сначала описание, потом код»
93
+ держится договорённостью — это `Q-PD-3` в законе.
94
+
95
+ Таблицы состояний экрана не сверяются ничем. `check:specs` знает сценарии против заголовков
96
+ тестов, правила против якорей, процедуры против декораторов и коды отказа против бросков —
97
+ строка таблицы состояний не привязана ни к чему и проходит зелёной, даже когда код в
98
+ названное состояние не попадает. Состояние «проверка не загрузилась» стояло в таблицах двух
99
+ доменов раньше, чем код научился в него приходить, и всё это время читалось описанием
100
+ работающего.
101
+
102
+ ## Паттерны
103
+
104
+ - `spec-driven-domain` — заведение и правка спека домена, сценарии, привязка.
105
+ - `spec-driven-rule` — заведение закона, правила и паттерна.
106
+
107
+ ## Ловушки
108
+
109
+ - **`tasks.md` в спеке не заводить.** Шаги — артефакт сессии, им место в ветке или в описании
110
+ PR. Как только в директории появляются «шаги», спек снова становится планом и умирает после
111
+ мержа.
112
+ - **Спек описывает установившееся, а не предстоящее.** Единственное место, где он говорит о
113
+ будущем, — `proposed/<фича>/`. После выкатки его текст вливается в спек домена, директория
114
+ удаляется, идентификаторы сценариев не меняются.
115
+ - **Семантику полей не сверяет ничто.** Проверка знает имена процедур, коды отказа и связь
116
+ сценариев с тестами; что означает пустое поле — не знает. Правка `.proto` поэтому тянет
117
+ спеки всех доменов, чьи процедуры она задела, в той же ветке.
118
+ - **Живость символа считается совпадением имени по всему дереву, а не вызовом.** Символу
119
+ хватает второго упоминания где угодно — в чужом поле с тем же именем, в атрибуте разметки.
120
+ Место, где правило исполняется на самом деле, подтверждается только чтением кода.
121
+ - **Якорь в `tools/*.mjs` сверяется почти ничем:** живость считается только для `.ts`, а
122
+ исходники обходятся по `apps`, `libs` и `prisma`. Правило, привязанное к проверке, поэтому
123
+ читается вместе с её телом.
124
+ - **Зелёная проверка не значит, что структура верна.** Спутники с привязкой сначала лежали
125
+ рядом с законами, и проверка была зелёной именно потому, что структура совпадала с тем,
126
+ чего проверка сама и ждала.
127
+ - **Конфликт мержа в спеке разрешается сохранением обеих сторон, а не выбором одной.** Две
128
+ ветки дописывают в конец одних и тех же списков — сценариев, правил, строк привязки, — и
129
+ обе стороны верны: конфликт здесь не спор, а две дописи в одно место. Номера сценариев при
130
+ разрешении не пересчитываются: идентификатор — ключ связи с тестами, и сдвиг номеров рвёт
131
+ сверку у соседей, которых правка не касалась. Порядок сохранённых сторон держится
132
+ одинаковым в `spec.md`, `scenarios.md` и `implementation.md`: иначе правило, его сценарий и
133
+ его привязка перестают находиться друг по другу. После разрешения гоняется
134
+ `npm run check:specs` — конфликт в спеке кода не задевает, и ни сборка, ни линтеры его не
135
+ увидят.
@@ -0,0 +1,74 @@
1
+ ---
2
+ name: styling-bem
3
+ kind: rule
4
+ law: frontend-application
5
+ description: Правило под «Закон о фронтовом приложении». Брать при правке любого *.scss и шаблона компонента. Называет директивы BEM, токены оформления, общий слой раскладки приложения и проверку класса без правила. Готовый код — в паттернах styling-bem-layout и styling-bem-component.
6
+ ---
7
+
8
+ # Оформление — как это устроено здесь
9
+
10
+ Правило под закон `docs/constitution/frontend-application.md`. Закон говорит, что должно быть
11
+ верно; здесь — чем это названо в этом дереве и где лежит. Раскладка файла компонента —
12
+ `component-structure`, состояние — `angular-patterns`, окружение браузера —
13
+ `platform-access`, слой обращения к серверу — `api-layer`. Все пять под одним законом.
14
+
15
+ ## Как это называется здесь
16
+
17
+ | В законе | Здесь |
18
+ | ------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
19
+ | общий набор значений оформления | шкалы кита `--rt-*`; своё поверх них — `--vm-*` в `styles.scss` приложения |
20
+ | класс в разметке | директивы `rtBlock` и `rtElem` из `@rt-tools`, а не строка в атрибуте |
21
+ | правило стилей | объявление `&__<элемент>` в `.scss` — своём или в общем слое приложения |
22
+ | общий слой раскладки | `apps/<app>/src/styles/`: `<префикс>-page`, `<префикс>-form`, `<префикс>-panel`, `<префикс>-window` у админки, `<префикс>-site-page` у сайта |
23
+
24
+ ## Где это лежит
25
+
26
+ В этом дереве — таблица в `implementation.md` рядом. Пути живут там, а не здесь: правило
27
+ переносится между репозиториями, раскладка — нет, и путь, названный в правиле, врёт в первом
28
+ же дереве, которое держит код иначе.
29
+
30
+ ## Как закон применяется здесь
31
+
32
+ - **Оформление берётся токеном `--rt-*`, а не пишется значением на месте.** Составные
33
+ значения — `box-shadow`, `text-shadow` — берутся готовым токеном целиком, а не собираются
34
+ из частей.
35
+ - **У каждого класса элемента есть своё правило стилей.** Класс без правила выглядит рабочим
36
+ и молча ничего не делает.
37
+ - **Класс ставится директивой, а не строкой в атрибуте.** Имя блока `rtElem` получает
38
+ инъекцией от ближайшего предка с `rtBlock`, и повторить этот разбор по тексту шаблона нечем.
39
+ - **Раскладка объявлена в общем слое приложения, а не в стилях экрана.** У компонента экрана
40
+ вне кита файл стилей по умолчанию пустой.
41
+ - **Предупреждение stylelint роняет прогон наравне с ошибкой.** `!important` объявлен
42
+ предупреждением, а прогон идёт с `--max-warnings 0`: иначе запрет читается как пожелание —
43
+ два таких предупреждения лежали в дереве, а `npm run stylelint` возвращал ноль и гейтом не был.
44
+
45
+ ## Чего из закона здесь нет
46
+
47
+ Проверка «класс без правила» считает совпадение по имени элемента, а не по паре «блок —
48
+ элемент»: класс, у которого правило есть, но у чужого блока, она пропускает. Обратное
49
+ направление — снятие правила у живого класса — не проверяется вовсе и ловится чтением шаблона.
50
+
51
+ ## Паттерны
52
+
53
+ - `styling-bem-layout` — экран на общем слое раскладки, блоки приложения.
54
+ - `styling-bem-component` — стили компонента кита, `:host`, модификаторы, язык оформления сайта.
55
+
56
+ ## Ловушки
57
+
58
+ - **`rtElem` без предка с `rtBlock` роняет отрисовку в рантайме** — сборка и линт молчат.
59
+ - **`rtBlock` на `<ng-container>` класса не ставит вовсе:** узел это комментарий, и имя блока
60
+ он только объявляет потомкам. Класс блока экрана вешает хост через `host: { class: … }`.
61
+ - **`justify-content: center` во flex-контейнере с `overflow-x` уводит первые элементы за
62
+ нулевой скролл** — доскроллить до них невозможно. В прокручиваемых лентах —
63
+ `justify-content: safe center`.
64
+ - **`scrollbar-gutter: stable` на корне не заводить:** резерв под полосу прокрутки сужает
65
+ содержащий блок для `position: fixed`, и попап, выровненный по правому краю, встаёт на
66
+ ширину резерва левее своей кнопки.
67
+ - **`& + :host` невалиден:** изнутри компонента до соседнего хоста не дотянуться. Разделитель
68
+ между повторяющимися хостами — `:host(:not(:first-of-type))`.
69
+ - **`[attr.aria-disabled]` визуального состояния не даёт:** браузер стилизует `:disabled`, но
70
+ атрибуты `aria-*` — нет. К каждому `aria-disabled` заводится правило `[aria-disabled='true']`.
71
+ - **Гарнитуру с `body` элементы формы не наследуют:** браузер задаёт `button`, `input`,
72
+ `select` и `textarea` свой шрифт. Наследование включено глобально — сбрасывать его нельзя.
73
+ - **Комментарии-выключатели stylelint не ставятся.** Селекторы объединяются вложенностью.
74
+ - **При переносе стилей новых объявлений не появляется** — только перемещение существующих.
@@ -0,0 +1,110 @@
1
+ ---
2
+ name: task-flow
3
+ kind: rule
4
+ law: work-conduct
5
+ description: Правило под «Закон о ведении работы». Брать в начале любой работы от владельца, при правке docs/tasks/**, docs/specs/*/proposed/** и при возвращении к незаконченной задаче. Называет разбор просьбы до первой правки, папку задачи по имени ветки, договорённость о продукте до кода, шесть обязательных вопросов и разбор папки при закрытии. Готовый порядок — в паттернах task-flow-start, task-flow-resume и task-flow-close.
6
+ ---
7
+
8
+ # Ведение работы — как это устроено здесь
9
+
10
+ Правило под закон `docs/constitution/work-conduct.md`. Закон говорит, что должно быть верно
11
+ про ход работы; здесь — чем это названо в этом дереве, где лежит и что из закона у нас не
12
+ проверяется.
13
+
14
+ ## Как это называется здесь
15
+
16
+ | В законе | Здесь |
17
+ | --------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
18
+ | просьба владельца | то, с чего начинается работа; разбирается командой `/grill-me` до первой правки |
19
+ | понимание, записанное там, где идёт работа | `docs/tasks/<ветка>/grill.md` — просьба дословно, ответы владельца его словами, решения с доводами |
20
+ | замысел | `docs/tasks/<ветка>/plan.md` — след задачи и этапы с признаками готовности; после написания не правится |
21
+ | ход работы | `docs/tasks/<ветка>/progress.md` — «Где стоим», решения по ходу, записи заходов; единственное место, где отмечается сделанное |
22
+ | договорённость о продукте, записанная до кода | `docs/specs/<домен>/proposed/<фича>/` — спек фичи; переживает мерж и вливается в спек домена |
23
+ | работа шире одной ветки | файл в `docs/plans/`, один на линию работ: порядок задач и зависимости между ними |
24
+ | папка задачи до заведения задачи | `docs/tasks/_draft-<slug>/` — вне истории, пока номера нет |
25
+ | разведка | заход `Explore` или `general-purpose` до первого вопроса владельцу |
26
+ | разбор замысла ролями | `.claude/workflows/plan.js` — нужность, договорённость, критика, замысел |
27
+
28
+ ## Где это лежит
29
+
30
+ В этом дереве — таблица в `implementation.md` рядом. Пути живут там, а не здесь: правило
31
+ переносится между репозиториями, раскладка — нет, и путь, названный в правиле, врёт в первом
32
+ же дереве, которое держит код иначе.
33
+
34
+ ## Как закон применяется здесь
35
+
36
+ - **Правка кода приложения отбивается, пока на диске нет замысла.** Гард требует папку задачи
37
+ по имени ветки, `plan.md` в ней и названную в его шапке договорённость о продукте.
38
+ - **Договорённость требуется по путям правки, а не по оценке задачи.** `apps/**` и `libs/**`
39
+ — признак; правила, тексты, обвязка и зависимости под него не подпадают. Обход — строка
40
+ `**Поведение:** не меняется — <причина владельца>` в замысле; пустая причина не
41
+ принимается.
42
+ - **Состояние незаконченной работы приходит в контекст на запуске сессии.** Замысел и ход
43
+ работы отдаются целиком, разбор просьбы — путём. Ветка вида `<КЛЮЧ>-*` без папки даёт
44
+ предупреждение с готовой командой, но сессию не рвёт.
45
+ - **Сделанное отмечается только в ходе работы.** «Где стоим» перезаписывается каждым заходом,
46
+ а не дописывается: это первое, что читает следующий заход.
47
+ - **Папка задачи заводится черновиком и получает номер командой.** До конца разбора
48
+ неизвестно, сколько задач из него выйдет, поэтому номер не может быть первым;
49
+ `npm run task:new` переименовывает черновик и проставляет шапку замысла.
50
+ - **Брошенный разбор виден.** Черновик старше недели перечисляет сверка очереди работ —
51
+ задачи за ним ещё нет, и спросить о нём некого.
52
+ - **Договорённость вливается в спек домена последним коммитом отчёта.** К этому моменту код
53
+ написан, привязки известны, и в главной ветке директория `proposed/` не появляется вовсе.
54
+ Готовые к вливанию перечисляет `npm run check:specs`.
55
+ - **Папка закрытой задачи разбирается, а не переносится целиком.** В `docs/archive/` уезжает
56
+ то, что объясняет состоявшееся решение; остальное удаляется. Неразобранную ловит сверка
57
+ очереди работ.
58
+
59
+ ## Чего из закона здесь нет
60
+
61
+ Полноту записанного понимания не проверяет ничто, и проверки на неё не будет: машине видно
62
+ наличие записи, но не то, что в ней закрыты все пробелы. Разбор из одной строки проходит гард
63
+ так же, как разбор на сто. То же с вопросом, который стоило задать и не задали, — он не
64
+ оставляет следа. Оба разобраны решениями в законе: судит владелец.
65
+
66
+ Гард судит по путям правки, а не по тому, меняет ли работа поведение на самом деле.
67
+ Рефакторинг, снаружи не видный, упирается в требование договорённости и проходит обходом с
68
+ причиной. Своего признака рефакторингу не заводится: оценку «поведение не меняется»
69
+ назначал бы тот, кому она мешает.
70
+
71
+ Ничто из самого разбора не проверяется, и всё это держится памятью того, кто его ведёт.
72
+ Разговор с владельцем инструментом не является — гард видит правку файла и ничего не знает
73
+ ни о том, была ли разведка до первого вопроса, ни о том, задан ли каждый из шести
74
+ обязательных вопросов, ни о том, ответил ли на них владелец. Образец разбора перечисляет их
75
+ таблицей, но пустая таблица проходит так же, как заполненная.
76
+
77
+ Неизменность замысла не стережёт ничто: `plan.md` правится тем же инструментом, что и
78
+ остальные тексты, и правка по ходу отличима от первоначальной записи только по истории.
79
+ Держится это тем же, чем и порядок разбора.
80
+
81
+ Разбор папки при закрытии не проверяется по существу: сверка очереди работ видит, что папка
82
+ закрытой задачи лежит на месте, но не судит, что из неё стоило увезти в архив.
83
+
84
+ ## Паттерны
85
+
86
+ - `task-flow-start` — разведка, разбор, договорённость, замысел, задача и ветка.
87
+ - `task-flow-resume` — возвращение к незаконченной работе новым заходом.
88
+ - `task-flow-close` — вливание договорённости, разбор папки, переезд в архив.
89
+
90
+ ## Ловушки
91
+
92
+ - **Папка называется именем ветки, один в один.** Хук запуска ищет её по
93
+ `git branch --show-current`, и папка, названная иначе, не находится ничем: работа идёт с
94
+ пустым контекстом, а владельца просят пересказать то, что уже записано.
95
+ - **Разбор просьбы задним числом не переписывается.** Пересказ незаметно подгоняется под уже
96
+ сделанное, и сверять результат становится не с чем. Решение, изменённое по ходу, дописывается
97
+ в ход работы, а не правится в разборе.
98
+ - **Договорённость о продукте не кладётся в папку задачи.** Папка умирает с мержем, а
99
+ договорённость обязана его пережить: её сценарии получают номера в общей нумерации домена,
100
+ и на них ссылаются заголовки тестов. Обратное тоже верно — ход работы не кладётся в
101
+ `proposed/`: спек, в котором завелись шаги, снова становится планом и умирает после мержа.
102
+ - **Меню вариантов на разборе годится только для выбора значения из закрытого набора.** Пока
103
+ постановка вопроса не подтверждена, спрашивается прозой: у меню нет строки «вопрос не тот».
104
+ Выбор слова, имени и термина узким вопросом не является никогда.
105
+ - **Субагент вопросов владельцу не задаёт.** Ни роли, ни конвейер до него не достучатся —
106
+ они возвращают текст главному агенту. Поэтому разбор ведёт главный агент, а роли стоят по
107
+ обе стороны от него.
108
+ - **Слово для нового понятия берётся из `docs/GLOSSARY.md` или заводится там же.** Третий файл
109
+ папки задачи называется `progress.md`, а не `journal.md`, ровно поэтому: журнал в этом
110
+ дереве один, и он другой.
@@ -0,0 +1,100 @@
1
+ ---
2
+ name: testing
3
+ kind: rule
4
+ law: verifiability
5
+ description: Правило под «Закон о проверяемости». Брать при правке любого *.spec.ts и всего, что лежит в apps/site-e2e и apps/admin-e2e. Называет Vitest и Playwright, идентификатор сценария в заголовке, вынос решения в чистую функцию и то, что закрывается сквозной спекой. Готовый код — в паттернах testing-unit и testing-e2e.
6
+ ---
7
+
8
+ # Проверяемость — как это устроено здесь
9
+
10
+ Правило под закон `docs/constitution/verifiability.md`. Закон говорит, что считается
11
+ подтверждением; здесь — чем это названо в этом дереве, где лежит и что из закона у нас не
12
+ применяется. Проверка работающего приложения глазами и замером — правило
13
+ `browser-verification` под тем же законом.
14
+
15
+ ## Как это называется здесь
16
+
17
+ | В законе | Здесь |
18
+ | -------------------------- | -------------------------------------------------------------------------------- |
19
+ | сценарий | `SC-<ПРЕФИКС>-<НОМЕР>` в `docs/specs/<домен>/scenarios.md` |
20
+ | тест | `it(...)` в `*.spec.ts` рядом с исходником — Vitest; сквозная спека — Playwright |
21
+ | сводка покрытия | вывод `npm run check:specs`: покрыто, частично, без тестов |
22
+ | отметка непокрытого | строка `Не покрыто: <причина>` внутри блока сценария |
23
+ | отметка неполного покрытия | строка `Покрытие: частичное — <чего не хватает>` |
24
+
25
+ ## Где это лежит
26
+
27
+ В этом дереве — таблица в `implementation.md` рядом. Пути живут там, а не здесь: правило
28
+ переносится между репозиториями, раскладка — нет, и путь, названный в правиле, врёт в первом
29
+ же дереве, которое держит код иначе.
30
+
31
+ ## Как закон применяется здесь
32
+
33
+ - **Идентификатор сценария стоит в начале заголовка теста, через тире.** Один сценарий
34
+ проверяется несколькими тестами, один тест закрывает несколько сценариев.
35
+ - **Сценарий без теста несёт отметку с причиной.** Пустая отметка не принимается, а отметка
36
+ при существующем тесте — отказ: она означает, что долг закрыли, а отметку не сняли.
37
+ - **Тест, идущий не тем путём, что пользователь, помечается частичным покрытием.** В сводку
38
+ он попадает долгом, а не покрытием.
39
+ - **Сценарий, чьё «Тогда» называет человека и то, что он видит, закрывается сквозным тестом.**
40
+ Юнит на тот же расчёт остаётся долгом: между верным решением и тем, что человек его видит,
41
+ лежит всё, чего юнит не касался.
42
+ - **Сквозной тест, погашенный переменной окружения, покрытием не считается.** Выключатель по
43
+ состоянию стенда — пропуск случая, выключатель по переменной — невыполненный тест.
44
+ - **Упоминание в тесте сценария, которого в спеках нет, роняет проверку.** Так ловится
45
+ переименованный или выкинутый сценарий: тесты при этом остаются зелёными.
46
+ - **Решение выносится в чистую функцию и проверяется вызовом.** Компонент и сервис остаются
47
+ тонкой обёрткой и отдельно не проверяются, пока своего ветвления у них нет.
48
+ - **Процедура Connect проверяется вызовом своего метода с рукописным двойником базы.**
49
+ Контейнер и роутер поднимать не надо: спека проверяет решение, а не раскладку полей.
50
+ - **Спека, необратимо меняющая данные стенда, выключена по умолчанию.** `BASE_URL` уводит
51
+ прогон одной переменной, и без выключателя такая спека правила бы данные чужого стенда.
52
+ - **Спеки, которым нужен nginx перед приложением, просыпаются вместе с `BASE_URL`.** Голый
53
+ сервер отдачи страниц их не проходит: перенаправления живут в конфиге прокси.
54
+ - **Правило линтера, которое запрещает принятый здесь приём, выключают в конфиге, а не
55
+ обходят в каждом тесте.** Выключатель теста — приём этого дерева, а
56
+ `playwright/no-skipped-test` запретил бы его сразу в шестидесяти пяти местах. Рядом со
57
+ строкой отключения пишут причину, а точечный `eslint-disable` остаётся для того, что
58
+ запрещено по делу.
59
+
60
+ ## Чего из закона здесь нет
61
+
62
+ Полнота теста не проверяется: сверка судит путь — сквозной он или юнит, — но не то, сколько
63
+ из обещанного тест на этом пути закрыл. Признак пути читается из слов «Тогда» и нарочно
64
+ молчалив: сценарий, чьё обещание человека не называет, под него не подпадает вовсе, и
65
+ отметку неполноты там по-прежнему ставит рука. Договорённостей о подмене модулей тоже нет:
66
+ `vi.mock` в дереве не встречается ни разу, и двойник пишется руками.
67
+
68
+ ## Паттерны
69
+
70
+ - `testing-unit` — тест на чистую функцию, на процедуру Connect и разовый тест-доказательство,
71
+ который не коммитится.
72
+ - `testing-e2e` — прогон сквозных тестов, стенд под настоящим nginx, выключатели.
73
+
74
+ ## Ловушки
75
+
76
+ - **Зелёный `nx test <проект>` не значит, что хоть один файл исполнялся.** Либа без своего
77
+ `vitest.config.mts` не запускает ничего — так тесты домена броней не запускались ни разу.
78
+ Либа с конфигом, но без единого `*.spec.ts`, проходит зелёной из-за `passWithNoTests: true`,
79
+ который стоит во всех 203 конфигах дерева, и на глаз эти два случая неотличимы: в обоих
80
+ прогон успешен. Без единого теста живут 131 либа из 203 — почти две трети. Перед правкой в
81
+ незнакомой либе проверяется, есть ли в ней хоть один `*.spec.ts`; если нет — первый
82
+ заводится этой же правкой, а не откладывается: откладывать здесь не с чего, долг уже
83
+ накоплен. Пересчёт: `for d in $(find libs -name vitest.config.mts -exec dirname {} \;); do
84
+ [ -z "$(find "$d" -name '*.spec.ts')" ] && echo "$d"; done | wc -l`.
85
+ - **«Executable doesn't exist» — состояние машины, а не дефект правки.** Установлен только
86
+ chromium, `firefox` и `webkit` падают всегда: гонять `--project=chromium`, узкий экран —
87
+ `--project=mobile-chrome`. Та же ошибка приходит после смены версии Playwright: браузер
88
+ ставится под конкретную версию, и после подъёма нужен повторный
89
+ `npx playwright install chromium`. Девять тестов так и упали, и это выглядело регрессией
90
+ обновления.
91
+ - **Первому прогону сразу после установки браузера верить нельзя.** Два падения `admin-e2e`
92
+ не повторились ни при отдельном прогоне тех же тестов, ни при втором полном. Такой прогон
93
+ повторяют, а выводы делают по второму.
94
+ - Сквозные тесты админки без `E2E_ADMIN_EMAIL` и `E2E_ADMIN_PASSWORD` пропускаются молча — в
95
+ отчёте они значатся `skipped`, и прогон выглядит успешным.
96
+ - **Справочник флоу вторых сценариев не заводит.** В `docs/E2E_<ДОМЕН>_FLOWS.md` кладут то,
97
+ чего в спеке домена нет и быть не должно: `qa-dataid` элементов, состояния разметки, ловушки
98
+ стенда. Обещанное поведение остаётся сценарием в `scenarios.md`: если списать его во второе
99
+ место, копии разойдутся молча — `npm run check:specs` этого не увидит.
100
+ - `npx nx serve` проверкой не является: это шаг из правила `browser-verification`, а не тест.
@@ -0,0 +1,69 @@
1
+ ---
2
+ name: translations
3
+ kind: rule
4
+ law: locales
5
+ description: Правило под «Закон о локалях и переводах». Брать при заведении любого видимого текста, правке словарей libs/common/i18n, префиксов локалей сайта и перевода контента объекта. Называет восемь локалей, Transloco, производные переводы и начальную валюту локали. Готовый код — в паттерне translations-key.
6
+ ---
7
+
8
+ # Локали и переводы — как это устроено здесь
9
+
10
+ Правило под закон `docs/constitution/application/locales.md`. Закон говорит, что должно быть верно;
11
+ здесь — чем это названо в этом дереве и где лежит. Разметка сайта для поиска — `seo` под
12
+ своим законом.
13
+
14
+ ## Как это называется здесь
15
+
16
+ | В законе | Здесь |
17
+ | -------------------- | --------------------------------------------------------------------------------------------------- |
18
+ | локаль сайта | одна из восьми: `en`, `ru`, `de`, `zh-Hans`, `zh-Hant`, `ko`, `th`, `hi` |
19
+ | локаль по умолчанию | `en` — отдаётся из корня, без префикса в адресе |
20
+ | локаль ввода | `source_locale` в запросе сохранения объекта; берётся из языка админки |
21
+ | словарь | JSON-словари Transloco в `libs/common/i18n/src/lib/dictionaries/<локаль>/`, разложенные по разделам |
22
+ | перевод контента | jsonb по локалям в базе, заполняется бэкендом |
23
+ | подстановка в шаблон | `\| transloco` в разметке, `translateSignal` в классе |
24
+
25
+ ## Где это лежит
26
+
27
+ В этом дереве — таблица в `implementation.md` рядом. Пути живут там, а не здесь: правило
28
+ переносится между репозиториями, раскладка — нет, и путь, названный в правиле, врёт в первом
29
+ же дереве, которое держит код иначе.
30
+
31
+ ## Как закон применяется здесь
32
+
33
+ - **Локаль по умолчанию отдаётся из корня, остальные семь — из-под префикса пути.**
34
+ - **Видимый человеку текст берётся из словаря и заводится во всех локалях перевода.** Ключ,
35
+ потерянный в одной локали, гость видит на кнопке как есть.
36
+ - **Пустой перевод считается пропуском, а не переводом.** Запасной словарь подставляется
37
+ только на отсутствующий ключ, а пустую строку отдаёт как есть.
38
+ - **Переводы контента производные: владелец пишет на своём языке, остальные семь локалей
39
+ заполняет бэкенд при сохранении.** Табов локалей в формах админки нет — правка вручную всё
40
+ равно была бы перезаписана.
41
+ - **Отказ перевода сохранение не срывает.** Текст на локали ввода записывается, прежние
42
+ переводы остаются, и владелец видит предупреждение, а не сообщение об успехе.
43
+ - **Локаль, для которой перевода не пришло, сохраняет прежнее значение.**
44
+ - **Язык админки выбирает владелец, и выбор живёт в его профиле.** Он же уходит в локаль
45
+ ввода при сохранении объекта.
46
+ - **У локали есть начальная валюта показа.** Дальше валюту выбирает гость, и его выбор
47
+ сильнее умолчания локали.
48
+
49
+ ## Чего из закона здесь нет
50
+
51
+ Страница не показывает, что перевод устарел: владелец правку сохранил, провайдер отказал —
52
+ и гость по-прежнему видит старый текст. Это `Q-L-1` в законе.
53
+
54
+ ## Паттерны
55
+
56
+ - `translations-key` — заведение ключа во всех локалях перевода и подстановка в разметку.
57
+
58
+ ## Ловушки
59
+
60
+ - **Полноту словарей держит не рантайм, а тест.** `nx test common-i18n` роняет сборку на
61
+ недостающем или пустом ключе; дозаполнить недостающее — `npm run i18n:fill`.
62
+ - **Наборы ключей сверяются внутри раздела**, а не по всему словарю сразу: словари разложены
63
+ на общий, сайт, письма и админку.
64
+ - **Перевод контента идёт до транзакции сохранения:** страница объекта не должна оказаться
65
+ наполовину переведённой. Кэш сбрасывается после записи и один раз.
66
+ - **Без ключа `ANTHROPIC_API_KEY` сохранение проходит,** но переводы остаются прежними, и
67
+ владелец видит предупреждение `propertySaveTranslationFailed`.
68
+ - Новый маршрут сайта без ветки под каждую локаль существует только в локали по умолчанию:
69
+ `/de/<путь>` отдаст 404 и поисковику, и гостю.
@@ -0,0 +1,76 @@
1
+ ---
2
+ name: typescript-conventions
3
+ kind: rule
4
+ law: code-structure
5
+ description: Правило под «Закон об устройстве кода». Брать при правке любого .ts, кроме компонента, сервиса, директивы, пайпа, гарда и интерцептора — строгая типизация, модификаторы доступа, приватные поля, имена файлов, префиксы, перечисления, запрет приведения в маппере, процедуры Connect на бэкенде. Готовый код процедуры — в паттерне ts-procedure.
6
+ ---
7
+
8
+ # Устройство кода — как это устроено здесь
9
+
10
+ Правило под закон `docs/constitution/code-structure.md`. Закон говорит, что должно быть верно
11
+ про объявления; здесь — как это записано в этом дереве.
12
+
13
+ ## Как это называется здесь
14
+
15
+ | В законе | Здесь |
16
+ | --------------------------- | --------------------------------------------------------- |
17
+ | признак рода в имени | префикс: `I` у интерфейса, `E` у перечисления, `T` у типа |
18
+ | источник, за которым следят | суффикс `$` у наблюдаемого и `Source` у субъекта |
19
+ | приватное поле | `#field`, а не `private field` |
20
+ | процедура бэкенда | класс с меткой `@ConnectProcedure()`, один на процедуру |
21
+
22
+ ## Где это лежит
23
+
24
+ В этом дереве — таблица в `implementation.md` рядом. Пути живут там, а не здесь: правило
25
+ переносится между репозиториями, раскладка — нет, и путь, названный в правиле, врёт в первом
26
+ же дереве, которое держит код иначе.
27
+
28
+ ## Как закон применяется здесь
29
+
30
+ - **Род объявления виден по префиксу имени, и это держат три правила линтера.** У интерфейса,
31
+ у типа и у перечисления свои; все три подняты для всех `.ts`.
32
+ - **Источник, за которым следят, назван суффиксом.** Субъект и наблюдаемое, поднятое из него,
33
+ различаются в месте использования, а не переходом к объявлению.
34
+ - **Суффикс имени файла находит в нём обещанное объявление.** Список суффиксов закрыт: слово,
35
+ которого в нём нет, суффиксом не считается, и такой файл правило не судит.
36
+ - **Тип берётся из того пакета, где объявлен.** Своя копия чужого типа расходится с оригиналом
37
+ молча, а компилируется из них только одна.
38
+ - **Двухступенчатое приведение `as unknown as` запрещено правилом линтера.** Вместо него —
39
+ честный тип, сужение проверкой или чтение поля формой (`Reflect.get`); место, где иначе
40
+ нельзя, помечается точечным отключением с причиной в той же строке.
41
+ - **Отметка об устаревании ставится вместе с обходом потребителей.** Все правила
42
+ `eslint-plugin-sonarjs` подняты до отказа разом, и пометка на типе красит каждое место, где
43
+ его ещё зовут: один `@deprecated` на файл дал семнадцать замечаний в чужих доменах.
44
+
45
+ ## Чего из закона здесь нет
46
+
47
+ Одноступенчатое приведение остаётся непроверенным — это `Q-CS-4` в законе: из семидесяти
48
+ шести приведений восемь обязательны, и общий запрет отбивал бы их.
49
+
50
+ Тесты из-под запрета выведены целиком: рукописный двойник базы — принятый здесь приём, и
51
+ запрет пришлось бы обходить в каждом из тридцати пяти.
52
+
53
+ Правило имён файлов судит обещание, а не его отсутствие: `menu.items.ts` и `sign-in.ts` под
54
+ него не подпадают вовсе — это `Q-CS-3` в законе. Из двух принятых здесь форм перевода
55
+ сущности — класс на фронте и чистые функции на бэкенде — правило принимает обе: оно судит
56
+ имя, а не устройство, и что форм две, остаётся вопросом `Q-S-1` в законе об общем коде.
57
+
58
+ На `libs/api/**` и `apps/api/**` правило действует целиком, а вот сигнальный API и `inject`
59
+ туда не относятся: там NestJS с конструкторным DI.
60
+
61
+ ## Паттерны
62
+
63
+ - `ts-procedure` — завести процедуру Connect на бэкенде: класс, метка, право, регистрация.
64
+
65
+ ## Ловушки
66
+
67
+ - Приведение через `as Type` в маппере запрещено, но не стережётся ничем: оно принимает любое
68
+ значение и компилируется. Вместо него — `this.typeCast`.
69
+ - Неиспользуемый параметр убирается, а не переименовывается: подчёркивание перед именем прячет
70
+ замечание, но параметр остаётся в сигнатуре.
71
+ - Агрегат Prisma своим типом не аннотируется: сгенерированный тип у него шире, чем результат
72
+ выборки, и аннотация врёт.
73
+ - Своё правило линтера включается вместе с переводом всех, кого оно ловит: включённое поверх
74
+ накопленного даёт красный прогон на файлах, которых правка не касалась.
75
+ - `#field` виден только внутри класса и не принимается `viewChild` — там поле объявляется
76
+ `protected`.