@rt-tools/agent-kit 0.3.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (202) hide show
  1. package/README.md +194 -30
  2. package/assets/agents/business-analyst.md +74 -0
  3. package/assets/agents/project-manager.md +70 -0
  4. package/assets/agents/qa-engineer.md +72 -0
  5. package/assets/agents/skill-curator.md +110 -0
  6. package/assets/agents/spec-critic.md +44 -0
  7. package/assets/agents/spec-writer.md +50 -0
  8. package/assets/checks/board.github.mjs +329 -0
  9. package/assets/checks/check-board.github.mjs +181 -0
  10. package/assets/checks/check-doc-paths.mjs +163 -0
  11. package/assets/checks/check-dupes.mjs +277 -0
  12. package/assets/checks/check-lib-layers.mjs +573 -0
  13. package/assets/checks/check-reuse.mjs +208 -0
  14. package/assets/checks/check-schema-drift.mjs +186 -0
  15. package/assets/checks/check-specs.mjs +1086 -0
  16. package/assets/checks/check-styles.mjs +109 -0
  17. package/assets/checks/rt-kit-checks.config.mjs +134 -0
  18. package/assets/checks/task-new.github.mjs +198 -0
  19. package/assets/commands/skill-curator.md +70 -0
  20. package/assets/defaults/gate-map.sh +106 -0
  21. package/assets/defaults/project.sh +204 -0
  22. package/assets/hooks/browser-device-id.sh +0 -0
  23. package/assets/hooks/browser-guard-device-id.sh +2 -1
  24. package/assets/hooks/browser-guard-no-asking.sh +27 -0
  25. package/assets/hooks/browser-guard-no-listing.sh +2 -1
  26. package/assets/hooks/browser-guard-no-other-drivers.sh +2 -1
  27. package/assets/hooks/browser-guard-require-select.sh +2 -1
  28. package/assets/hooks/commit-msg.sh +1 -1
  29. package/assets/hooks/constitution-index.sh +5 -4
  30. package/assets/hooks/dev-server-guard.sh +8 -6
  31. package/assets/hooks/docs-guard.sh +223 -37
  32. package/assets/hooks/git-guard-delivery.sh +171 -31
  33. package/assets/hooks/git-guard-main.sh +1 -0
  34. package/assets/hooks/git-guard-push-tests.sh +34 -13
  35. package/assets/hooks/glossary-load.sh +23 -0
  36. package/assets/hooks/grill-gate.sh +96 -0
  37. package/assets/hooks/lint-after-edit.sh +155 -30
  38. package/assets/hooks/qa-dataid-guard.sh +72 -32
  39. package/assets/hooks/reuse-first-guard.sh +105 -34
  40. package/assets/hooks/skill-gate-rearm.sh +1 -0
  41. package/assets/hooks/skill-gate.sh +75 -15
  42. package/assets/hooks/skill-loaded.sh +1 -0
  43. package/assets/hooks/sql-guard.sh +606 -56
  44. package/assets/hooks/task-context-load.sh +100 -0
  45. package/assets/hooks/task-flow-guard.sh +118 -0
  46. package/assets/laws/{access.md → application/access.md} +1 -4
  47. package/assets/laws/{locales.md → application/locales.md} +1 -3
  48. package/assets/laws/application/money.md +41 -0
  49. package/assets/laws/application/ownership.md +32 -0
  50. package/assets/laws/{search-visibility.md → application/search-visibility.md} +1 -1
  51. package/assets/laws/code-structure.md +7 -6
  52. package/assets/laws/delivery.md +53 -3
  53. package/assets/laws/entity-editing.md +49 -55
  54. package/assets/laws/entity-models.md +4 -14
  55. package/assets/laws/frontend-application.md +5 -5
  56. package/assets/laws/lib-imports.md +14 -1
  57. package/assets/laws/lists.md +33 -0
  58. package/assets/laws/navigation.md +40 -0
  59. package/assets/laws/project-documentation.md +27 -8
  60. package/assets/laws/reuse-first.md +26 -21
  61. package/assets/laws/shared-code.md +13 -1
  62. package/assets/laws/verifiability.md +30 -1
  63. package/assets/laws/work-conduct.md +59 -0
  64. package/assets/patterns/admin-lists-screen.md +131 -0
  65. package/assets/patterns/admin-nav-item.md +71 -0
  66. package/assets/patterns/angular-patterns-state.md +29 -22
  67. package/assets/patterns/api-layer-pair.md +40 -30
  68. package/assets/patterns/browser-verification-measure.md +41 -38
  69. package/assets/patterns/browser-verification-stand.md +106 -42
  70. package/assets/patterns/component-structure-new.md +33 -32
  71. package/assets/patterns/dependencies-upgrade.md +65 -0
  72. package/assets/patterns/doc-style-sweep.md +65 -28
  73. package/assets/patterns/doc-style-write.md +36 -33
  74. package/assets/patterns/entity-aside.md +136 -0
  75. package/assets/patterns/entity-models-new.md +124 -0
  76. package/assets/patterns/entity-store.md +91 -0
  77. package/assets/patterns/git-workflow-commit.azure.md +259 -0
  78. package/assets/patterns/git-workflow-commit.github.md +337 -0
  79. package/assets/patterns/git-workflow-commit.gitlab.md +283 -0
  80. package/assets/patterns/git-workflow-merge.md +42 -25
  81. package/assets/patterns/git-workflow-migration.md +61 -31
  82. package/assets/patterns/git-workflow-restart.md +20 -20
  83. package/assets/patterns/lib-layers-move.md +50 -32
  84. package/assets/patterns/lib-layers-new.md +41 -29
  85. package/assets/patterns/ownership-scope-resolve.md +69 -0
  86. package/assets/patterns/permissions-procedure.md +35 -33
  87. package/assets/patterns/platform-access-di.md +39 -25
  88. package/assets/patterns/pricing-quote.md +71 -0
  89. package/assets/patterns/reuse-first-extend.md +22 -22
  90. package/assets/patterns/seo-page.md +52 -40
  91. package/assets/patterns/seo-verify.md +48 -29
  92. package/assets/patterns/shared-code-new.md +37 -31
  93. package/assets/patterns/spec-driven-domain.md +60 -37
  94. package/assets/patterns/spec-driven-rule.md +55 -40
  95. package/assets/patterns/styling-bem-component.md +43 -32
  96. package/assets/patterns/styling-bem-layout.md +30 -24
  97. package/assets/patterns/task-flow-close.md +154 -0
  98. package/assets/patterns/task-flow-resume.md +94 -0
  99. package/assets/patterns/task-flow-start.md +129 -0
  100. package/assets/patterns/testing-e2e.md +53 -51
  101. package/assets/patterns/testing-unit.md +70 -46
  102. package/assets/patterns/translations-key.md +32 -19
  103. package/assets/patterns/ts-procedure.md +24 -25
  104. package/assets/rules/angular-patterns.md +50 -27
  105. package/assets/rules/api-layer.md +46 -28
  106. package/assets/rules/browser-verification.md +67 -48
  107. package/assets/rules/component-structure.md +43 -27
  108. package/assets/rules/dependencies.md +66 -0
  109. package/assets/rules/doc-style.md +95 -39
  110. package/assets/rules/entity-conventions.md +78 -0
  111. package/assets/rules/entity-models.md +70 -0
  112. package/assets/rules/git-workflow.azure.md +116 -0
  113. package/assets/rules/git-workflow.github.md +123 -0
  114. package/assets/rules/git-workflow.gitlab.md +113 -0
  115. package/assets/rules/lib-layers.md +56 -30
  116. package/assets/rules/lists.md +73 -0
  117. package/assets/rules/navigation.md +78 -0
  118. package/assets/rules/ownership-scope.md +63 -0
  119. package/assets/rules/permissions.md +43 -25
  120. package/assets/rules/platform-access.md +57 -29
  121. package/assets/rules/pricing.md +64 -0
  122. package/assets/rules/reuse-first.md +57 -43
  123. package/assets/rules/seo.md +51 -30
  124. package/assets/rules/shared-code.md +51 -26
  125. package/assets/rules/spec-driven.md +107 -51
  126. package/assets/rules/styling-bem.md +54 -39
  127. package/assets/rules/task-flow.md +150 -0
  128. package/assets/rules/testing.md +78 -47
  129. package/assets/rules/translations.md +48 -31
  130. package/assets/rules/typescript-conventions.md +57 -27
  131. package/assets/skills/agent-kit.md +85 -0
  132. package/assets/skills/write-a-skill.md +108 -0
  133. package/assets/templates/gate-map.sh +23 -15
  134. package/assets/templates/implementation.md +14 -8
  135. package/assets/templates/pattern.md +1 -1
  136. package/assets/templates/project.sh +32 -19
  137. package/assets/templates/rule.md +2 -2
  138. package/assets/variants.json +20 -0
  139. package/assets/workflows/feature.js +134 -0
  140. package/assets/workflows/plan.js +150 -0
  141. package/bin/agent-kit.d.ts.map +1 -1
  142. package/bin/agent-kit.js +78 -5
  143. package/bin/agent-kit.js.map +1 -1
  144. package/bin/prompt.d.ts +5 -0
  145. package/bin/prompt.d.ts.map +1 -1
  146. package/bin/prompt.js +19 -7
  147. package/bin/prompt.js.map +1 -1
  148. package/index.d.ts +1 -0
  149. package/index.d.ts.map +1 -1
  150. package/index.js +1 -0
  151. package/index.js.map +1 -1
  152. package/lib/assets.d.ts +8 -3
  153. package/lib/assets.d.ts.map +1 -1
  154. package/lib/assets.js +13 -3
  155. package/lib/assets.js.map +1 -1
  156. package/lib/catalog.d.ts +52 -5
  157. package/lib/catalog.d.ts.map +1 -1
  158. package/lib/catalog.js +104 -16
  159. package/lib/catalog.js.map +1 -1
  160. package/lib/commands.d.ts +22 -1
  161. package/lib/commands.d.ts.map +1 -1
  162. package/lib/commands.js +202 -14
  163. package/lib/commands.js.map +1 -1
  164. package/lib/companion.d.ts +5 -1
  165. package/lib/companion.d.ts.map +1 -1
  166. package/lib/companion.js +29 -2
  167. package/lib/companion.js.map +1 -1
  168. package/lib/config.d.ts +26 -9
  169. package/lib/config.d.ts.map +1 -1
  170. package/lib/config.js +41 -15
  171. package/lib/config.js.map +1 -1
  172. package/lib/freshness.d.ts +14 -0
  173. package/lib/freshness.d.ts.map +1 -0
  174. package/lib/freshness.js +116 -0
  175. package/lib/freshness.js.map +1 -0
  176. package/lib/hooks-map.d.ts +27 -0
  177. package/lib/hooks-map.d.ts.map +1 -0
  178. package/lib/hooks-map.js +77 -0
  179. package/lib/hooks-map.js.map +1 -0
  180. package/lib/integrity.d.ts +36 -0
  181. package/lib/integrity.d.ts.map +1 -0
  182. package/lib/integrity.js +44 -0
  183. package/lib/integrity.js.map +1 -0
  184. package/lib/picker.d.ts +11 -1
  185. package/lib/picker.d.ts.map +1 -1
  186. package/lib/picker.js +44 -6
  187. package/lib/picker.js.map +1 -1
  188. package/lib/sync.d.ts +26 -0
  189. package/lib/sync.d.ts.map +1 -1
  190. package/lib/sync.js +59 -4
  191. package/lib/sync.js.map +1 -1
  192. package/lib/variants.d.ts +44 -0
  193. package/lib/variants.d.ts.map +1 -0
  194. package/lib/variants.js +82 -0
  195. package/lib/variants.js.map +1 -0
  196. package/package.json +1 -1
  197. package/rt-tools-agent-kit-0.5.0.tgz +0 -0
  198. package/assets/laws/admin-lists.md +0 -35
  199. package/assets/laws/admin-navigation.md +0 -38
  200. package/assets/patterns/git-workflow-commit.md +0 -175
  201. package/assets/rules/git-workflow.md +0 -106
  202. package/rt-tools-agent-kit-0.3.0.tgz +0 -0
@@ -2,48 +2,76 @@
2
2
  name: platform-access
3
3
  kind: rule
4
4
  law: frontend-application
5
- description: Правило под закон «Фронтовое приложение». Брать, когда правка задевает глобальный объект или среду исполнения — окно, глобальную область, признак браузера, хранилище, наблюдатели. Глобальное приходит внедрением, среда проверяется службой, а не наличием глобала. Не действует на серверной стороне. Готовый код — в паттерне platform-access-di. Чем это названо здесь — в implementation.md рядом.
5
+ description: Правило под «Закон о фронтовом приложении». Брать, когда правка задевает глобальный объект или среду исполнения — window, globalThis, PLATFORM_ID, isPlatformBrowser, document.defaultView, localStorage, IntersectionObserver. Называет токены DI, приведение типа и подводные камни отдачи страницы сервером. Не действует под libs/api и apps/api. Готовый код — в паттерне platform-access-di.
6
6
  ---
7
7
 
8
- # Окружение браузера — каким приёмом
8
+ # Окружение браузера — как это устроено здесь
9
9
 
10
- Правило под закон `{{lawsDir}}/frontend-application.md`. Закон говорит, что должно быть верно;
11
- здесь — каким приёмом прямое обращение к глобальному объекту заменяется. Какими токенами и
12
- службами это названо и откуда они приходят — `implementation.md` рядом. Состояние —
13
- `angular-patterns`, файл компонента — `component-structure`, оформление — `styling-bem`, слой
10
+ Правило под закон `docs/constitution/frontend-application.md`. Закон говорит, что должно быть
11
+ верно; здесь — чем в этом дереве заменяется прямое обращение к глобальному объекту. Состояние
12
+ `angular-patterns`, файл компонента `component-structure`, стили — `styling-bem`, слой
14
13
  обращения к серверу — `api-layer`. Все пять под одним законом.
15
14
 
16
- Правило про фронт: на серверной стороне своя среда, и ничего из перечисленного к ней не
17
- относится.
15
+ Правило про фронт: `libs/api/**` и `apps/api/**` — это NestJS, там своя среда, и ничего из
16
+ перечисленного не применяется.
18
17
 
19
- ## Когда берётся
18
+ ## Как это называется здесь
20
19
 
21
- Правка задевает глобальный объект, признак среды, хранилище браузера или наблюдателя за
22
- разметкой.
20
+ | Прямое обращение | Здесь |
21
+ | ---------------------------------------- | -------------------------------------------------- |
22
+ | `globalThis.open(...)`, `window.open()` | `inject(WINDOW).open(...)` |
23
+ | `globalThis.crypto.randomUUID()` | `inject(WINDOW).crypto.randomUUID()` |
24
+ | `isPlatformBrowser(inject(PLATFORM_ID))` | `inject(PlatformService).isPlatformBrowser` |
25
+ | `document.defaultView` | `inject(WINDOW)` |
26
+ | прямой `document` | `inject(DOCUMENT)` из `@angular/common` — как было |
27
+ | инициализация DOM после отрисовки | `afterNextRender()` |
23
28
 
24
- ## Что здесь действует
29
+ `WINDOW`, `PlatformService` и `StorageService` приходят из `@rt-tools/core`.
25
30
 
26
- - **Глобальный объект приходит внедрением, а не берётся напрямую.** Тип уточняется приведением
27
- к глобальной области: конструкторы наблюдателей объявлены на ней, а не на интерфейсе окна.
28
- - **Среда проверяется службой, а не наличием глобала.** Проверка по наличию верна случайно и
29
- ломается на первой же среде, где глобал подставлен.
30
- - **Место прямого доступа заводится только с согласия владельца.** Их немного, и каждое
31
- осознанно: скрипт, работающий до подъёма приложения, обработчик отказа подъёма и код,
32
- исполняемый внутри страницы в сквозной спеке. Новое в этот список не добавляется молча.
31
+ ## Где это лежит
32
+
33
+ В этом дереве таблица в `implementation.md` рядом. Пути живут там, а не здесь: правило
34
+ переносится между репозиториями, раскладка нет, и путь, названный в правиле, врёт в первом
35
+ же дереве, которое держит код иначе.
36
+
37
+ ## Как закон применяется здесь
38
+
39
+ - **Глобальный объект приходит токеном `WINDOW`, а не берётся напрямую.** Тип уточняется
40
+ приведением к `Window & typeof globalThis`: конструкторы вроде `IntersectionObserver`
41
+ объявлены на глобальном объекте, а не на интерфейсе `Window`.
42
+ - **Среда проверяется через `PlatformService`, а не через `typeof window`.** Проверка по
43
+ наличию глобала верна случайно и ломается на первой же среде, где глобал подставлен.
44
+ - **Прямое обращение к глобальному объекту отбивает линтер.** Шестнадцать имён окружения
45
+ браузера запрещены на фронтовых деревьях; три места, где прямой доступ осознан, выведены
46
+ из-под запрета списком в конфиге, а не отключением в строке.
47
+
48
+ ## Чего из закона здесь нет
49
+
50
+ Запрет действует на имена, а не на пути к ним: `this.#document.defaultView` линтер пропускает,
51
+ потому что глобального имени в такой строке нет. Такое обращение законно — окно там уже пришло
52
+ внедрением, — но и настоящий обход выглядел бы так же, и увидеть его можно только чтением.
53
+
54
+ Прямой доступ остаётся в трёх местах, и это осознанно: встроенный скрипт против мелькания темы
55
+ в `apps/site/src/index.html` (работает до подъёма приложения), обработчик отказа подъёма в
56
+ `apps/*/src/main.ts`, и код внутри `page.evaluate` в сквозных спеках — он исполняется в
57
+ странице, вне DI. Новое место в этот список не добавляется без явного согласования владельца.
33
58
 
34
59
  ## Паттерны
35
60
 
36
- - `platform-access-di` — готовые внедрения, приведение типа, чистые функции, проверка среды.
61
+ - `platform-access-di` — готовые инжекты, приведение типа, чистые функции, проверка среды.
37
62
 
38
63
  ## Ловушки
39
64
 
40
- - **Фабрика токена окна бросает отказ, когда у документа нет представления.** Под отдачей
41
- страницы сервером представление есть, и внедрение полем класса безопасно, но служба,
42
- обязанная работать без разметки вовсе, берёт окно внутри метода под проверкой среды.
43
- - **После правки, добавляющей окно в службу, которая создаётся на подъёме, нужна не только
44
- сборка, но и поднятый сервер отдачи страниц.** Падение видно только там.
45
- - **Вокруг хранилища проверка среды не нужна.** Служба хранилища и так уходит в память вне
46
- браузера, и лишняя проверка вокруг чтения и записи — мёртвый код.
47
- - **В чистых функциях внедрения нет** окно принимается параметром, а внедряет его вызывающий.
48
- - **Подготавливать состояние сквозной спеки записью в хранилище нельзя**спека проходит те же
65
+ - **Фабрика токена бросает `Window is not available`, если у документа нет `defaultView`.**
66
+ Под отдачей страницы сервером `defaultView` есть, и инжект полем класса в root-сервисе
67
+ безопасен, но сервис, обязанный работать без DOM вообще, берёт окно внутри метода под
68
+ проверкой среды.
69
+ - **После правки, добавляющей `WINDOW` в сервис, который создаётся на подъёме, нужна не только
70
+ сборка, но и поднятый сервер отдачи страниц.** Сайт настоящий SSR, и падение видно только
71
+ там.
72
+ - **Вокруг хранилища проверка среды не нужна:** `StorageService` и так уходит в память вне
73
+ браузера, и лишний `if (!isBrowser)` вокруг чтения и записимёртвый код.
74
+ - **В `*.logic.ts` и `*.util.ts` нет DI:** окно принимается параметром, а инжектит его
75
+ вызывающий компонент.
76
+ - **Подготавливать состояние сквозной спеки записью в хранилище нельзя:** спека проходит те же
49
77
  шаги, что и пользователь.
@@ -0,0 +1,64 @@
1
+ ---
2
+ name: pricing
3
+ kind: rule
4
+ law: money
5
+ description: Правило под «Закон о деньгах». Брать при правке расчёта цены, скидок, курсов валют, сумм в заказах, письмах и сводках. Называет хранение в целых единицах валюты хранения, единственное округление, выбор одной наибольшей скидки и справочный пересчёт. Готовый код — в паттерне pricing-quote. Чем это названо здесь — в implementation.md рядом.
6
+ ---
7
+
8
+ # Суммы — как это устроено здесь
9
+
10
+ Правило под закон `docs/constitution/application/money.md`. Закон говорит, что должно быть
11
+ верно; здесь — каким приёмом это держится. Валюта хранения, набор справочных валют и имена
12
+ колонок — при этом дереве, в `implementation.md` рядом.
13
+
14
+ ## Как это называется здесь
15
+
16
+ | В законе | Здесь |
17
+ | ------------- | ---------------------------------------------------------------------------------------------- |
18
+ | сумма | целое число единиц валюты хранения; код валюты стоит в имени поля — `total<Код>`, `price<Код>` |
19
+ | валюта показа | поле заказа с кодом валюты, в которой пользователь смотрел цену |
20
+ | расчёт цены | одна чистая функция: на входе — что заказано, на выходе — подытог, скидка и итог |
21
+ | скидка | механики перечислены в `implementation.md`; правило про выбор одно на все |
22
+ | курс | кэш в хранилище, обновляется по расписанию |
23
+
24
+ ## Где это лежит
25
+
26
+ В этом дереве — таблица в `implementation.md` рядом. Пути живут там, а не здесь: правило
27
+ переносится между репозиториями, раскладка — нет, и путь, названный в правиле, врёт в первом
28
+ же дереве, которое держит код иначе.
29
+
30
+ ## Как закон применяется здесь
31
+
32
+ - **Сумма хранится в целых единицах валюты хранения.** Дробная часть даёт расхождение между
33
+ подытогом и итогом: показанное построчно перестаёт складываться в показанное внизу. Если
34
+ дробная часть в предметной области значима, единицей хранения становится она сама.
35
+ - **Код валюты стоит в имени поля, а не подразумевается.** Поле `total` без кода читается как
36
+ «сумма вообще», и первый же пересчёт кладёт в него чужую валюту.
37
+ - **Округление происходит один раз — при расчёте цены.** Сумма на экране, сумма в письме и
38
+ сумма в хранилище — одно и то же число, а не три результата одного пересчёта.
39
+ - **Из подходящих скидок применяется одна — наибольшая.** Сравниваются они в валюте хранения:
40
+ доля и сумма иначе несравнимы.
41
+ - **При равной выгоде побеждает та скидка, которую пользователь ввёл руками.** Он ждёт
42
+ подтверждения своему действию, а «код не сработал» при той же итоговой цене читается как
43
+ поломка.
44
+ - **Скидка суммой ограничена подытогом.** Иначе она увела бы цену ниже нуля.
45
+ - **Пересчёт в чужую валюту не хранится.** В заказе лежат сумма в валюте хранения и код валюты
46
+ показа; само число вычисляется на показ.
47
+ - **Оплата идёт в валюте хранения, остальные валюты — справка.** Ни подтверждение, ни документ
48
+ не называют справочное число суммой к оплате.
49
+ - **Валюта показа следует за пользователем.** Он выбрал её на экране — в письме сумма
50
+ пересчитана в неё же.
51
+ - **Курс недоступен — сумма показывается в валюте хранения без пересчёта.** Пересчёт по
52
+ неизвестно какому курсу хуже отсутствующего: отсутствие видно, а неверный курс — нет.
53
+
54
+ ## Паттерны
55
+
56
+ - `pricing-quote` — расчёт цены, выбор скидки, работа с курсом.
57
+
58
+ ## Ловушки
59
+
60
+ - **Курс тянется по расписанию и кэшируется.** Само число в чужой валюте нигде не сохраняется.
61
+ - **Дробная сумма в контракте — признак того, что округление уехало на показ.** Округление
62
+ одно, и живёт оно в расчёте.
63
+ - **Справочная сумма рядом с суммой к оплате читается как цена.** В письме и в документе
64
+ справочное число подписывается как справка.
@@ -2,68 +2,82 @@
2
2
  name: reuse-first
3
3
  kind: rule
4
4
  law: reuse-first
5
- description: Правило под закон «Единообразие приложения». Брать перед заведением любого нового экрана, компонента, поля, стора, сервиса, переводчика моделей или обработчика — на что опираться, по каким признакам видно, что готовое обошли. Что делать, когда готового не хватило, — в паттерне reuse-first-extend. Чем это названо здесь — в implementation.md рядом.
5
+ description: Правило под «Закон о единообразии приложения». Брать перед заведением любого нового экрана, компонента, поля, стора, сервиса, маппера или процедуры — на что опираться, что уже готово в ките и базовых классах, по каким признакам видно, что готовое обошли. Что делать, когда готового не хватило, — в паттерне reuse-first-extend.
6
6
  ---
7
7
 
8
- # Единообразие — каким приёмом
8
+ # Единообразие — как это устроено здесь
9
9
 
10
- Правило под закон `{{lawsDir}}/reuse-first.md`. Закон говорит, что одинаковые вещи ведут себя
11
- одинаково; здесь — каким приёмом это держится. На какие именно источники вида и основы это
12
- дерево опирается — `implementation.md` рядом.
10
+ Правило под закон `docs/constitution/reuse-first.md`. Закон говорит, что одинаковые вещи ведут
11
+ себя одинаково; здесь — на чём это стоит в этом дереве и чем названо.
13
12
 
14
- ## Когда берётся
13
+ ## Как это называется здесь
15
14
 
16
- Заведение нового файла: экрана, компонента, поля, стора, сервиса, переводчика моделей,
17
- обработчика серверной стороны. Правило берётся **до** первой строки, а не после.
15
+ | В законе | Здесь |
16
+ | -------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
17
+ | готовое | компоненты кита `@rt-tools/ui-kit-v2` с префиксом `rt-` — сегодня их больше семидесяти — и базовые классы без селектора, наследуемые в `@Component` экрана |
18
+ | раскладка страниц, форм и окон | `apps/<app>/src/styles/`, применяется директивами BEM |
19
+ | оформление части приложения | файл `.scss` рядом с компонентом |
20
+ | отступление, решённое владельцем | маркер `native-ok` в той же строке с объяснением |
18
21
 
19
- ## Что здесь действует
22
+ ## Где это лежит
23
+
24
+ В этом дереве — таблица в `implementation.md` рядом. Пути живут там, а не здесь: правило
25
+ переносится между репозиториями, раскладка — нет, и путь, названный в правиле, врёт в первом
26
+ же дереве, которое держит код иначе.
27
+
28
+ ## Как закон применяется здесь
20
29
 
21
- - **Источник вида выбирается по приложению, а не по привычке.** У дерева может быть больше
22
- одного источника — свой у каждого приложения; перепутанный приносит на экран форму, которой в
23
- этом приложении больше нигде нет.
24
30
  - **Работа начинается с чтения готового, а не с чистого файла.** Сначала находится опора —
25
- готовый компонент, базовый класс, образец в соседнем домене, — потом пишется своё поверх неё.
26
- Соседний домен читается целиком: приём, который кажется новым, обычно уже написан, а
27
- переименованный при переносе он перестаёт узнаваться.
31
+ компонент кита, базовый класс, образец в соседнем домене, — потом пишется своё поверх неё.
28
32
  - **Свой примитив и своя основа заводятся только с явного одобрения владельца.** Спрашивается
29
- это до того, как написан первый файл, а не после.
33
+ это до того, как написан первый файл.
30
34
  - **Панель правки записи наследует общую основу, а не собирается своей разметкой.** Тогда она
31
35
  открывается, закрывается и спрашивает про несохранённое одинаково во всех разделах.
32
36
  - **Об удаче и об отказе сообщает общая шина, а не своя разметка на экране.** Своё сообщение
33
37
  расходится с соседним видом, местом и временем показа.
34
- - **Компонент объявляется отдельными файлами разметки и стилей.** Шаблон и стили внутри
35
- декоратора, атрибут стиля в разметке и правка размеров привязкой к стилю — это оформление, до
36
- которого не дотянется ни источник вида, ни линтер стилей.
37
- - **У компонента экрана файл стилей по умолчанию пустой.** Раскладка объявлена один раз в общем
38
- слое приложения, а экран её только применяет.
39
- - **Готовое расширяется, а не клонируется рядом.** Недостающий вариант заводится в источнике
40
- вида или в базовом классе, и его видят остальные экраны. Клон, написанный рядом, забирает
41
- правки на себя и расходится с оригиналом с первой же.
38
+ - **Компонент объявляется тремя файлами: `.ts`, `.html`, `.scss`.** `template:` и `styles:` в
39
+ декораторе, атрибут `style=` и правка размеров через `[ngStyle]` — это стили, до которых не
40
+ дотянется ни кит, ни `stylelint`.
41
+ - **У компонента экрана вне кита файл стилей по умолчанию пустой.** Раскладка объявлена один
42
+ раз в общем слое приложения, а экран её только применяет теми же директивами BEM.
43
+ - **Готовое расширяется, а не клонируется рядом.** Недостающий вариант заводится в ките или в
44
+ базовом классе, и его видят остальные экраны.
45
+ - **Накопленное до гарда сосчитано сплошной проверкой и в список только не растёт.** Признаки
46
+ у неё те же, что у гарда, а смотрит она файл целиком: гейт падает на новом месте, старое
47
+ остаётся числом в сводке.
48
+
49
+ ## Чего из закона здесь нет
50
+
51
+ Одинаковый ответ поля ввода на ошибку держит кит: `RtFormControlBase` в этом дереве не
52
+ наследует никто, потому что поля берутся его готовыми компонентами. Привязать эту статью
53
+ закона здесь не к чему.
54
+
55
+ Инлайновый шаблон стоит у одного компонента, и у него же единственный `styles:` — это шапка
56
+ админки. `role="alert"` написан руками в шестнадцати шаблонах, а `rt-message` зовёт один
57
+ потребитель. Это долг, а не разрешённое отступление.
42
58
 
43
59
  ## Паттерны
44
60
 
45
- - `reuse-first-extend` — что делать, когда готового не хватило: расширить готовое, объявить
46
- разовое отступление маркером, снять его.
61
+ - `reuse-first-extend` — что делать, когда готового не хватило: расширить кит, объявить
62
+ отступление маркером, снять его.
47
63
 
48
64
  ## Признаки, по которым видно, что готовое обошли
49
65
 
50
- - в шаблоне фичи стоит нативный элемент ввода, кнопки, выбора, таблицы или диалога;
51
- - отказ или предупреждение собраны руками — своя область оповещения вместо готового сообщения;
52
- - в стилях фичи появились перекрытие всего экрана, своя вуаль, слой поверх всего, свои кадры
53
- вращения или мерцания;
54
- - в файле стилей экрана объявлена раскладка, а не только его собственные отличия;
55
- - компонент сам реализует договор поля формы, вместо того чтобы наследовать основу;
56
- - имя файла кончается на род готового компонента кнопку, поле, диалог, таблицу — и лежит вне
57
- источника вида;
58
- - переводчик моделей переводит поля вручную, минуя общую основу;
59
- - обработчик серверной стороны объявлен без общей метки.
66
+ - в шаблоне фичи стоит нативный `<input>`, `<button>`, `<select>`, `<table>` или `<dialog>`;
67
+ - отказ или предупреждение собраны руками — свой `role="alert"` вместо `rt-message`;
68
+ - в `.scss` фичи появились `position: fixed` на весь экран, свой backdrop, `z-index` от тысячи,
69
+ `@keyframes` вращения или мерцания;
70
+ - в файле стилей экрана объявлена раскладка `display: flex` с `gap` и `padding` на `:host`;
71
+ - компонент реализует `ControlValueAccessor` сам, а не наследует `RtFormControlBase`;
72
+ - имя файла кончается на `-button`, `-input`, `-dialog`, `-spinner` или `-table` вне
73
+ кита;
74
+ - маппер переводит поля вручную, без `BaseMapper`;
75
+ - процедура бэкенда объявлена без `@ConnectProcedure()`.
60
76
 
61
77
  ## Ловушки
62
78
 
63
- - **Перенос переизобретением не считается.** Строка, которая уже лежала в дереве, при переезде
64
- меняет отступ, оставаясь тем же кодом; сверка идёт без отступов, иначе каждый переезд читался
65
- бы как новый код.
66
- - **Ответ, данный до чтения образца, образец отменяет.** Согласованная форма переигрывается,
67
- как только находится готовая: договорённость слабее того, что уже написано и работает.
68
- - **Подсказка, зовущая за ненаписанным, останавливает работу.** Пока свой вариант не написан,
69
- правило зовёт за готовым — а не за тем, что «должно появиться».
79
+ - Перенос переизобретением не считается: строку, которая уже лежит в файле, гард из
80
+ проверяемого текста вычёркивает, а сверка идёт без отступов при переезде блок меняет
81
+ отступ, оставаясь тем же кодом.
82
+ - Ответ, данный до чтения образца, образец отменяет: согласованная форма выборки списка
83
+ переигрывалась вместе с контрактом через два вопроса после того, как была принята.
@@ -2,49 +2,70 @@
2
2
  name: seo
3
3
  kind: rule
4
4
  law: search-visibility
5
- description: Правило под закон «Видимость в поиске». Брать при любой правке, доходящей до разметки публичной части — шаблоны страниц, заголовок и описание, структурированные данные, канонический адрес, языковые ссылки, маршруты, карта сайта, правила обхода, конфиг прокси. Готовый код — в паттернах seo-page и seo-verify. Чем это названо здесь — в implementation.md рядом.
5
+ description: Правило под «Закон о видимости в поиске». Брать при любой правке, доходящей до разметки публичного сайта — шаблоны страниц, meta/title/description, JSON-LD, canonical, hreflang, маршруты сайта, sitemap, robots, nginx. Называет восемь локалей, где что лежит и чего у нас нет. Готовый код — в паттернах seo-page и seo-verify.
6
6
  ---
7
7
 
8
- # Видимость в поиске — каким приёмом
8
+ # Видимость в поиске — как это устроено здесь
9
9
 
10
- Правило под закон `{{lawsDir}}/search-visibility.md`. Закон говорит, что должно быть верно;
11
- здесь — каким приёмом это держится. Какие локали набраны, где лежит служба тегов и чем зовётся
12
- канонический путь — `implementation.md` рядом.
10
+ Правило под закон `docs/constitution/application/search-visibility.md`. Закон говорит, что должно быть
11
+ верно; здесь — чем это названо в этом коде, где лежит и что из закона у нас не применяется.
13
12
 
14
- ## Когда берётся
13
+ ## Как это называется здесь
15
14
 
16
- Любая правка, доходящая до разметки публичной части: шаблон страницы, теги в голове документа,
17
- структурированные данные, маршруты, карта сайта, правила обхода, конфиг прокси.
15
+ | В законе | Здесь |
16
+ | ---------------------------- | ---------------------------------------------------------------------------- |
17
+ | язык страницы | локаль; их восемь — `en`, `ru`, `de`, `zh-Hans`, `zh-Hant`, `ko`, `th`, `hi` |
18
+ | язык по умолчанию | `DEFAULT_LOCALE`, то есть `en`; отдаётся из корня, без префикса в адресе |
19
+ | язык, перевод которого готов | `readyLocales` — поле объекта, а не список локалей сайта |
20
+ | канонический адрес | `${SITE_ORIGIN}${propertyPath(slug, locale)}` |
21
+ | структурированные данные | JSON-LD типа `LodgingBusiness` |
22
+ | прежний адрес страницы | прежний slug объекта |
18
23
 
19
- ## Что здесь действует
24
+ Локаль по умолчанию отдаётся из корня, остальные — с префиксом `/<код>/`. Префикс — часть
25
+ маршрута, а не часть сборки: `<base href>` в разметке всегда `/`.
20
26
 
21
- - **Свои теги помечены собственным атрибутом и при повторном применении переписываются.** Чужое
22
- в голове документа не трогается: после оживления там остаются теги от отдачи сервером.
23
- - **Канонический адрес ведёт на локализованный путь**, а не на корень и не на адрес локали по
24
- умолчанию.
25
- - **Языковые ссылки строятся по локалям, перевод которых готов**, плюс ссылка на локаль по
26
- умолчанию. Полный список локалей для этого не годится: переводы содержимого заполняются
27
- отдельно и готовы не всегда.
28
- - **Список альтернативных локалей не включает локаль самой страницы** — иначе она объявлена и
27
+ ## Где это лежит
28
+
29
+ В этом дереве таблица в `implementation.md` рядом. Пути живут там, а не здесь: правило
30
+ переносится между репозиториями, раскладка — нет, и путь, названный в правиле, врёт в первом
31
+ же дереве, которое держит код иначе.
32
+
33
+ ## Как закон применяется здесь
34
+
35
+ - **Свои теги помечены атрибутом `data-<префикс>-seo`** и при повторном применении переписываются.
36
+ Чужое в `<head>` не трогается: после гидратации там остаются теги от отдачи сервером.
37
+ - **`canonical` ведёт на локализованный путь** `${SITE_ORIGIN}${propertyPath(slug, locale)}`,
38
+ а не на корень и не на адрес локали по умолчанию.
39
+ - **`hreflang` строится по `readyLocales`**, плюс `x-default` на локаль по умолчанию. Список
40
+ локалей сайта для этого не годится: переводы контента заполняет бэк, и готовы они не всегда.
41
+ - **`og:locale:alternate` не включает локаль самой страницы** — иначе она объявлена и
29
42
  основной, и альтернативной сразу.
30
- - **Адрес в структурированных данных — канонический адрес самой страницы**, не корень.
31
- - **Перенаправление с прежнего адреса отдаётся с временем жизни и кэшируется прокси.** Ключ
32
- кэша строится без строки запроса, поэтому запросы с ней идут мимо кэша.
43
+ - **`url` в JSON-LD — канонический адрес страницы объекта**, не корень сайта.
44
+ - **Корень локали отдаёт страницу единственного объекта** с каноническим адресом на `/{slug}`;
45
+ посетителя уводит туда nginx ответом 302.
46
+ - **Перенаправление с прежнего адреса отдаётся с `Cache-Control: max-age`** и покрыто
47
+ `proxy_cache_valid 200 301`. Ключ кэша строится без `$args`, поэтому запросы со строкой
48
+ запроса идут мимо кэша (`proxy_cache_bypass` / `proxy_no_cache $is_args`).
49
+
50
+ ## Чего из закона здесь нет
51
+
52
+ Листинга объектов нет — корень локали отдаёт единственный объект. Появится листинг —
53
+ канонический адрес корня пересматривается; это `Q-SV-1` в законе.
33
54
 
34
55
  ## Паттерны
35
56
 
36
- - `seo-page` — правка разметки страницы: теги, структурированные данные, новый маршрут, новая
37
- страница в карте сайта.
57
+ - `seo-page` — правка разметки страницы: теги, JSON-LD, новый маршрут, новая страница в карте.
38
58
  - `seo-verify` — проверка отданной разметки на прод-сборке.
39
59
 
40
60
  ## Ловушки
41
61
 
42
- - **Тег, добавленный мимо общей службы, не помечен и потому не переписывается.** Он переживёт
43
- переход между страницами и останется от чужой страницы.
44
- - **Новый маршрут без ветки под каждую локаль существует только в локали по умолчанию.**
45
- Остальные адреса отдадут отказ и поисковику, и читателю.
46
- - **Новая страница не попадает в карту сайта сама** — карта строится из записей, а не из
62
+ - Тег, добавленный мимо `PropertySeoService`, не помечен `data-<префикс>-seo` он не будет ни
63
+ переписан, ни удалён при следующем применении и переживёт переход между страницами.
64
+ - Новый маршрут сайта без ветки в `app.routes.ts` под каждую локаль существует только в
65
+ локали по умолчанию: `/de/<путь>` отдаст 404 и поисковику, и гостю.
66
+ - Новая страница не попадает в `sitemap.xml` сама — карта строится из объектов, а не из
47
67
  маршрутов.
48
- - **Адрес режется по первому знаку вопроса и только им.** Простой разрез по всем вхождениям
49
- теряет всё после второго, и перенаправление приходит на страницу без разметки источника
50
- источник обращения считается неверно.
68
+ - Адрес режется по **первому** `?` и только им: `req.url.split('?')` теряет всё после второго
69
+ знака, и перенаправление приходит на страницу без UTM-хвоста источник заявки считается
70
+ неверно. Разрез живёт в `splitRequestUrl`
71
+ (слой `util` домена страницы объекта) и покрыт спеками; свой не заводить.
@@ -2,34 +2,58 @@
2
2
  name: shared-code
3
3
  kind: rule
4
4
  law: shared-code
5
- description: Правило под закон «Общий код приложений». Брать, когда значение должно одинаково пониматься всеми приложениями — предел выборки, набор операторов условия, направление порядка, длина поля, форма запроса и ответа списка. Откуда берётся общее, что считается копией и что ловит проверка повторов. Готовый код — в паттерне shared-code-new. Чем это названо здесь — в implementation.md рядом.
5
+ description: Правило под «Закон об общем коде приложений». Брать, когда значение должно одинаково пониматься сайтом, админкой и бэкендом — предел выборки, набор операторов условия, направление порядка, длина поля, форма запроса и ответа списка. Называет, что берётся из @rt-tools/utils, что из @<область>/common/util и что ловит check:dupes. Готовый код — в паттерне shared-code-new.
6
6
  ---
7
7
 
8
- # Общий код — каким приёмом
8
+ # Общий код — как это устроено здесь
9
9
 
10
- Правило под закон `{{lawsDir}}/shared-code.md`. Закон говорит, что общим быть обязано; здесь —
11
- каким приёмом это держится. Из какого пакета и какой либы что берётся `implementation.md`
12
- рядом.
10
+ Правило под закон `docs/constitution/shared-code.md`. Закон говорит, что общим быть обязано;
11
+ здесь откуда это берётся в этом дереве, чем названо и чего у нас нет.
13
12
 
14
- ## Когда берётся
13
+ ## Как это называется здесь
15
14
 
16
- Значение, которое должны одинаково понимать разные приложения: число-настройка, набор значений,
17
- форма запроса и ответа списка, длина поля.
15
+ | В законе | Здесь |
16
+ | ------------------------- | --------------------------------------------------------------------------------------------- |
17
+ | общий пакет | `@rt-tools/utils` — собран без фреймворка, зависит от одного `tslib`, грузится под голым Node |
18
+ | общая либа проекта | `@<область>/common/util`; её тег входит в набор `UNIVERSAL` и виден всем трём приложениям |
19
+ | число-настройка | `DEFAULT_PAGE_SIZE`, `MAX_PAGE_SIZE` |
20
+ | набор значений | `FILTER_OPERATOR_TYPE_ENUM`, `LIST_SORT_ORDER_ENUM` |
21
+ | сверка значения с набором | `listSortOrderOf`, `listFilterOperatorOf` |
22
+ | выборка списка | `IPageModel`, `ISortModel`, `IFilterModel`, `IListState` |
18
23
 
19
- ## Что здесь действует
24
+ ## Где это лежит
20
25
 
21
- - **Число-настройка лежит в общей либе и оттуда берётся всеми сторонами.** Умолчание доводом не
22
- передаётся: пока довод есть, домен вправе назвать своё число и называет, расходясь с
23
- соседним на единицу, которую никто не заметит.
24
- - **Набор значений из общего пакета заново не объявляется.** Своё перечисление с теми же
26
+ В этом дереве — таблица в `implementation.md` рядом. Пути живут там, а не здесь: правило
27
+ переносится между репозиториями, раскладка нет, и путь, названный в правиле, врёт в первом
28
+ же дереве, которое держит код иначе.
29
+
30
+ ## Как закон применяется здесь
31
+
32
+ - **Число-настройка лежит в `libs/common/util` и оттуда берётся обеими сторонами.** Умолчание
33
+ доводом не передаётся: пока довод есть, домен вправе назвать своё число, и у промокодов так
34
+ появилось `25` против `20` у остальных.
35
+ - **Набор значений из `@rt-tools/utils` заново не объявляется.** Своё перечисление с теми же
25
36
  членами считается копией, даже если имена разошлись.
26
- - **Значение из набора сверяется общей функцией, а промах разбирает вызывающий.** Сервер
27
- отбивает запрос отказом, экран берёт умолчание: политика на непонятное значение у сторон
28
- разная, и общей может быть только сверка.
29
- - **Общим делается лишь то, где политика одна.** Переводчик, у которого стороны читают
30
- непонятное значение по-разному, общим не становится — он расходится молча.
31
- - **Перечисления полей порядка и отбора домена копией не считаются.** Они повторяют имена, по
32
- которым сортирует сервер именно этого домена, и совпадение здесь случайное.
37
+ - **Значение из набора сверяется парой общих функций, а промах разбирает вызывающий.** Сервер
38
+ отбивает запрос `InvalidArgument`, экран берёт умолчание.
39
+ - **Общим стал только маппер страницы.** У сторон разная политика на непонятное значение, и
40
+ общим может быть лишь то, где она одна: номер меньше единицы обе стороны читают как первую
41
+ страницу.
42
+ - **Перечисления полей порядка и отбора домена копией не считаются.** `EActivitySortProperty`
43
+ и подобные повторяют имена, по которым сортирует сервер именно этого домена.
44
+ - **Строковая настройка и таблица соответствий сверяются по значению, а не по имени.** Имя
45
+ здесь не ключ: `BEM_BLOCK` и `LOG_CONTEXT` объявлены десятками, и значения у них свои, а
46
+ один и тот же перевод статуса живёт под тремя разными именами.
47
+
48
+ ## Чего из закона здесь нет
49
+
50
+ Перевод сущности устроен по-разному: фронт переводит наследником `BaseMapper`, бэкенд —
51
+ свободными функциями `xxxToProto` без общей основы. Это долг `Q-S-1`, а не выбор: новый код на
52
+ бэкенде общей основы не заводит, но и своей второй не пишет.
53
+
54
+ Найденные совпадения строк и таблиц приняты долгом целиком: перевод статуса брони в контракт
55
+ лежит тремя копиями, набор принимаемых вложений — двумя, имя события правки — тремя.
56
+ Сокращать это — работа по доменам, и она заведена вопросом `Q-S-3`.
33
57
 
34
58
  ## Паттерны
35
59
 
@@ -37,9 +61,10 @@ description: Правило под закон «Общий код приложе
37
61
 
38
62
  ## Ловушки
39
63
 
40
- - **Помощник приведения типов для сверки с набором не годится.** Значение вне набора он пишет в
41
- журнал и возвращает строкой, то есть глотает ровно тот случай, ради которого сверку и завели.
42
- - **Накопленные повторы лежат в списке исключений и отказом не считаются.** Гейт падает только
43
- на новом; список исключений только сокращается.
44
- - **Проверка не ловит ту же логику, написанную заново под другим именем.** Совпадение она ищет
45
- по тексту, а не по смыслу.
64
+ - `typeCast.getAsType` для сверки с набором не годится: значение вне набора он пишет в консоль
65
+ и возвращает строкой `'unknown'`.
66
+ - Накопленные повторы лежат в `tools/dupes-allowlist.json` под ключом `debt` и отказом не
67
+ считаются гейт падает только на новом. Список только сокращается.
68
+ - Ту же логику, написанную заново под другим именем, проверка не ловит, и такой проверки не
69
+ будет: две одинаковые по форме проверки из разных доменов копией не считаются. Заметить это
70
+ может только тот, кто читает правку, — так сказано и в законе.