@rt-tools/agent-kit 0.22.0 → 0.24.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 (101) hide show
  1. package/README.md +8 -2
  2. package/assets/checks/board-long-work.github.mjs +101 -0
  3. package/assets/checks/board-runs.github.mjs +34 -0
  4. package/assets/checks/board.github.mjs +1 -1
  5. package/assets/checks/check-board.github.mjs +30 -2
  6. package/assets/checks/check-doc-paths.mjs +24 -5
  7. package/assets/checks/check-file-size.mjs +8 -2
  8. package/assets/checks/check-prose-style.mjs +10 -1
  9. package/assets/checks/check-reuse.mjs +4 -1
  10. package/assets/checks/check-schema-drift.mjs +65 -6
  11. package/assets/checks/rt-kit-checks.config.mjs +13 -0
  12. package/assets/checks/signals.mjs +41 -1
  13. package/assets/commands/next-session.md +16 -5
  14. package/assets/defaults/gate-map.sh +13 -0
  15. package/assets/defaults/project.sh +26 -0
  16. package/assets/defaults/shell.sh +18 -3
  17. package/assets/hooks/browser-guard-device-id.sh +42 -12
  18. package/assets/hooks/browser-guard-no-asking.sh +5 -1
  19. package/assets/hooks/dispatch.sh +40 -9
  20. package/assets/hooks/docs-guard.sh +10 -0
  21. package/assets/hooks/exam-guard.sh +66 -16
  22. package/assets/hooks/git-guard-delivery-draft.sh +78 -0
  23. package/assets/hooks/git-guard-delivery.sh +64 -122
  24. package/assets/hooks/git-guard-main.sh +39 -4
  25. package/assets/hooks/git-guard-push-tests.sh +71 -3
  26. package/assets/hooks/glossary-load.sh +23 -2
  27. package/assets/hooks/grill-gate.sh +62 -0
  28. package/assets/hooks/hook-input.sh +17 -6
  29. package/assets/hooks/rule-source-guard.sh +11 -0
  30. package/assets/hooks/stand-login-guard.sh +101 -0
  31. package/assets/hooks/write-targets.sh +37 -4
  32. package/assets/laws/autonomous-work.md +30 -0
  33. package/assets/laws/project-documentation.md +8 -0
  34. package/assets/laws/verifiability.md +12 -2
  35. package/assets/laws/work-conduct.md +59 -65
  36. package/assets/patterns/autonomous-work-run.md +105 -0
  37. package/assets/patterns/browser-verification-measure.md +41 -1
  38. package/assets/patterns/browser-verification-stand.md +57 -16
  39. package/assets/patterns/doc-style-human.md +75 -0
  40. package/assets/patterns/doc-style-write.md +16 -0
  41. package/assets/patterns/git-workflow-commit.azure.md +12 -0
  42. package/assets/patterns/git-workflow-commit.github.md +16 -3
  43. package/assets/patterns/git-workflow-commit.gitlab.md +12 -0
  44. package/assets/patterns/git-workflow-merge.md +8 -0
  45. package/assets/patterns/git-workflow-pr-ready.md +93 -0
  46. package/assets/patterns/git-workflow-pr.azure.md +1 -1
  47. package/assets/patterns/git-workflow-pr.github.md +1 -1
  48. package/assets/patterns/git-workflow-pr.gitlab.md +1 -1
  49. package/assets/patterns/task-flow-start.md +48 -48
  50. package/assets/patterns/testing-e2e.md +18 -8
  51. package/assets/patterns/ts-procedure.md +3 -2
  52. package/assets/pitfalls/task-flow.md +40 -40
  53. package/assets/rules/autonomous-work.md +92 -0
  54. package/assets/rules/browser-verification.md +35 -3
  55. package/assets/rules/deploy-flow.azure.md +7 -0
  56. package/assets/rules/deploy-flow.github.md +7 -0
  57. package/assets/rules/deploy-flow.gitlab.md +7 -0
  58. package/assets/rules/doc-style.md +54 -0
  59. package/assets/rules/git-workflow.azure.md +8 -0
  60. package/assets/rules/git-workflow.github.md +59 -63
  61. package/assets/rules/git-workflow.gitlab.md +8 -0
  62. package/assets/rules/reuse-first.md +25 -5
  63. package/assets/rules/styling-bem.md +8 -1
  64. package/assets/rules/task-flow.md +108 -109
  65. package/assets/rules/testing.md +21 -0
  66. package/assets/skills/agent-kit.md +72 -82
  67. package/lib/commands.d.ts.map +1 -1
  68. package/lib/commands.js +69 -2
  69. package/lib/commands.js.map +1 -1
  70. package/lib/enroll.d.ts.map +1 -1
  71. package/lib/enroll.js +1 -1
  72. package/lib/enroll.js.map +1 -1
  73. package/lib/observations.d.ts +10 -1
  74. package/lib/observations.d.ts.map +1 -1
  75. package/lib/observations.js +1 -0
  76. package/lib/observations.js.map +1 -1
  77. package/lib/override-marks.d.ts +24 -0
  78. package/lib/override-marks.d.ts.map +1 -0
  79. package/lib/override-marks.js +98 -0
  80. package/lib/override-marks.js.map +1 -0
  81. package/lib/push-gate.d.ts +14 -0
  82. package/lib/push-gate.d.ts.map +1 -0
  83. package/lib/push-gate.js +93 -0
  84. package/lib/push-gate.js.map +1 -0
  85. package/lib/shipment.d.ts.map +1 -1
  86. package/lib/shipment.js +1 -1
  87. package/lib/shipment.js.map +1 -1
  88. package/package.json +1 -1
  89. package/rt-tools-agent-kit-0.24.0.tgz +0 -0
  90. package/assets/laws/application/access.md +0 -34
  91. package/assets/laws/application/locales.md +0 -33
  92. package/assets/laws/application/search-visibility.md +0 -24
  93. package/assets/patterns/permissions-procedure.md +0 -71
  94. package/assets/patterns/seo-page.md +0 -104
  95. package/assets/patterns/seo-verify.md +0 -83
  96. package/assets/patterns/translations-content.md +0 -107
  97. package/assets/patterns/translations-key.md +0 -64
  98. package/assets/rules/permissions.md +0 -116
  99. package/assets/rules/seo.md +0 -139
  100. package/assets/rules/translations.md +0 -96
  101. package/rt-tools-agent-kit-0.22.0.tgz +0 -0
@@ -0,0 +1,105 @@
1
+ ---
2
+ name: autonomous-work-run
3
+ kind: pattern
4
+ rule: autonomous-work
5
+ description: Паттерн правила autonomous-work. Брать, когда владелец ушёл и работа идёт ночь напролёт — готовый цикл одной работы, ветвление чередой, запись умолчания вместо вопроса, список к утру, разбор отказа стража. Не брать для обычного хода работы — это паттерны task-flow-start и task-flow-resume.
6
+ ---
7
+
8
+ # Ночь без владельца
9
+
10
+ Паттерн правила `autonomous-work`. Что при этом должно быть верно — закон
11
+ `docs/constitution/autonomous-work.md`.
12
+
13
+ ## Когда брать
14
+
15
+ - Владелец сказал, что уходит, и просил работать самостоятельно.
16
+ - За заход берётся больше одной задачи подряд, и отдать их наружу нельзя.
17
+ - Работа упёрлась в вопрос, а спросить некого.
18
+
19
+ ## Череда веток
20
+
21
+ Первая работа ветвится от главной, каждая следующая — от предыдущей:
22
+
23
+ ```bash
24
+ git checkout -b <КЛЮЧ>-<номер-1>-<slug> main # первая за ночь
25
+ git checkout -b <КЛЮЧ>-<номер-2>-<slug> <КЛЮЧ>-<номер-1>-<slug>
26
+ git checkout -b <КЛЮЧ>-<номер-3>-<slug> <КЛЮЧ>-<номер-2>-<slug>
27
+ ```
28
+
29
+ Порядок вливания — снизу вверх, и он называется в списке к утру номерами. Ветка, заведённая от
30
+ главной посреди череды, столкнётся с соседкой в общих файлах — указателях, счётчиках, журналах —
31
+ и разбирать это будет владелец.
32
+
33
+ ```bash
34
+ git log --oneline --graph --decorate main..HEAD | head -20 # чем стоит череда сейчас
35
+ git branch --list '<КЛЮЧ>-*' --format='%(refname:short) %(upstream:short)'
36
+ ```
37
+
38
+ ## Цикл одной работы
39
+
40
+ ```bash
41
+ npm run task:move -- <номер> in-progress # взята
42
+ git checkout -b <КЛЮЧ>-<номер>-<slug> <прошлая ветка> # череда, а не веер
43
+ cp -r docs/tasks/_template docs/tasks/<КЛЮЧ>-<номер>-<slug>
44
+ # разбор просьбы с умолчаниями, замысел, состояние «этап-идёт»
45
+ git add docs/tasks/<КЛЮЧ>-<номер>-<slug> && git commit # папка едет в историю сразу
46
+ # работа этапами: каждый кончается коммитом и прогоном признака готовности
47
+ # папка разбирается в описание прошлого последним коммитом
48
+ ```
49
+
50
+ Наружу за ночь не уходит ничего: ни `git push`, ни `gh pr create`, ни публикация, ни отметка
51
+ груза. Ветка остаётся местной, и утром владелец сам решает, что из неё отдавать.
52
+
53
+ ## Умолчание вместо вопроса
54
+
55
+ Пишется в разбор просьбы, в раздел решений, — там же, где записался бы ответ владельца:
56
+
57
+ ```markdown
58
+ - **<что принято>** — <довод>. Спросить было некого: заход автономный. Цена ошибки:
59
+ <что придётся переделать, если владелец решит иначе>.
60
+ ```
61
+
62
+ Цена ошибки — не вежливость, а признак: она отделяет умолчание, которое можно переиграть за
63
+ десять минут, от того, ради которого работу лучше отложить целиком.
64
+
65
+ ## Отложенная задача
66
+
67
+ Задача, которой нужно слово владельца, не берётся: состояние на борде остаётся прежним, а в
68
+ список к утру идёт строка.
69
+
70
+ ```markdown
71
+ - **RT-<номер> — <заголовок>.** Отложена: <какой вопрос и почему умолчания у него нет>.
72
+ ```
73
+
74
+ ## Список к утру
75
+
76
+ Дописывается по ходу, после каждой закрытой работы, а не собирается в конце.
77
+
78
+ ```markdown
79
+ | Работа | Ветка | Чем подтверждена | Чего ждёт |
80
+ | ------ | ----- | ---------------- | --------- |
81
+ | RT-… — <что сделано> | `<ветка>` (на `<предыдущей>`) | `<команда>` — <вывод> | заявки и слияния |
82
+ ```
83
+
84
+ Порядок строк — порядок вливания. Ниже списка — отложенные задачи с их вопросами.
85
+
86
+ ## Отказ стража
87
+
88
+ Отказ называет пропущенный шаг: шаг делается, вызов повторяется, работа идёт дальше. Ночь
89
+ кончается работой, а не спором со стражем.
90
+
91
+ - Отказ требует того, что делать нельзя (пуш, заявка), — работа доводится до места, где
92
+ требование исполнимо утром, и уходит в список к утру строкой «ждёт заявки».
93
+ - Отказ повторяется на том же месте дважды — значит пропущенный шаг понят неверно: читается
94
+ правило, названное в отказе, а не переписывается формулировка.
95
+
96
+ ## Частые промахи
97
+
98
+ - **Ветка заведена от главной по привычке.** Череда рвётся молча, и цена всплывает у владельца
99
+ на втором вливании.
100
+ - **Умолчание принято и не записано.** Утром оно неотличимо от знания, и владелец узнаёт о нём,
101
+ только когда работа сделана не так.
102
+ - **Задача взята и отложена наполовину.** На борде она в работе, в ветке — половина правки;
103
+ следующий заход читает её как начатую и не начинает заново.
104
+ - **Список к утру собран по памяти в последнюю минуту.** К этому часу окно уже сжималось, и
105
+ половина ночи в него не попала.
@@ -19,7 +19,7 @@ description: Паттерн правила browser-verification. Брать, к
19
19
  ## Вывод подкрепляется числом
20
20
 
21
21
  `getComputedStyle`, `getBoundingClientRect`, контраст, совпадение центров, попадание во
22
- вьюпорт. «Выглядит нормально» результатом проверки не является.
22
+ вьюпорт. «Выглядит нормально» результатом проверки не бывает.
23
23
 
24
24
  Замер отвечает только на тот вопрос, который задали. Совпадение перечисленных свойств ничего
25
25
  не говорит о правиле, которого в списке замера нет: строки попапа профиля сошлись с образцом
@@ -104,6 +104,20 @@ await (async (address, widths, row) => {
104
104
  Правка числа элементов в контейнере — это правка раскладки: она проверяется при 375, а не
105
105
  только кодами ответа.
106
106
 
107
+ ## Событие ввода подделкой значения не заменяется
108
+
109
+ **Прокрутка, поставленная присвоением, события не даёт.** Значение прокрутки меняется, а подписчик
110
+ не срабатывает ни на документе, ни на окне, ни на корневом узле: признак, который приложение
111
+ считает по событию, остаётся прежним, и сделанная правка читается как несделанная — два захода
112
+ подряд разбирали исправный слушатель.
113
+
114
+ Отсюда две проверки, и ни одна не заменяет другую: механика проверяется настоящим колесом, а
115
+ раскладка под признаком — признаком, поставленным руками. Класс ставится на хост, и меряется то,
116
+ что от него зависит; порог при этом не участвует.
117
+
118
+ Признак того, что случай именно этот: слушатель поставлен, значение прокрутки изменилось, счётчик
119
+ событий нулевой. Разбирать после этого сам слушатель не нужно — он исправен.
120
+
107
121
  ## Ловушки инструмента `computer`
108
122
 
109
123
  - Координаты клика — координаты **скриншота**, а не CSS-пиксели: при вьюпорте 2560 скриншот
@@ -117,6 +131,32 @@ await (async (address, widths, row) => {
117
131
  настройками раскладки; спрашивается он у помощника, а не помнится: идентификатор локален для
118
132
  машины и в пакет не едет вовсе.
119
133
 
134
+ ## Долгий прогон запускается в странице, а не в вызове
135
+
136
+ Вызов инструмента прерывается по своему пределу примерно на сорока пяти секундах, а начатый им
137
+ обход продолжается — результата у него уже никто не спросит. Одна ширина из набора экранов идёт
138
+ около полутора минут, то есть в вызов не помещается ни одна.
139
+
140
+ Порядок такой: вызов кладёт обход в переменную страницы и заканчивается сразу, а следующие вызовы
141
+ спрашивают у той же переменной, готово ли.
142
+
143
+ ```javascript
144
+ globalThis.__probe = { done: false, rows: [] };
145
+ (async () => {
146
+ for (const w of [375, 480, 481, 768, 769, 1080, 1081, 1380]) {
147
+ globalThis.__probe.rows.push(await measureWidth(w));
148
+ }
149
+ globalThis.__probe.done = true;
150
+ })();
151
+
152
+ 'запущено';
153
+ ```
154
+
155
+ Ответ инструмента обрезается примерно на полутора тысячах знаков без пометки: конец сводки
156
+ выглядит не оборванным, а отсутствующим — вывод, собранный за один проход и напечатанный разом,
157
+ читается как более короткий, чем он есть. Собранное поэтому остаётся в переменной целиком, а
158
+ печатается срезами по индексу.
159
+
120
160
  ## Поведение роутера воспроизводится нажатиями
121
161
 
122
162
  Подстановка адреса, `history.pushState` с `popstate` и заход по прямой ссылке поднимают
@@ -17,15 +17,26 @@ description: Паттерн правила browser-verification. Брать, к
17
17
  - Порт отвечает не тем, чего ждали.
18
18
  - Нужен вход в админку.
19
19
 
20
+ ## Номера портов объявляет дерево, а не паттерн
21
+
22
+ Команды ниже называют порты именами — `API_PORT`, `SITE_PORT`, `ADMIN_PORT`, `SSR_PORT`, порты
23
+ стенда и порты прокси. Номеров у паттерна нет: раскладка стендов у каждого дерева своя, а дерево с
24
+ одним приложением, обязанное назвать восемь чужих номеров, называет их выдуманными — и
25
+ подстановки теряют смысл. Номера дерево объявляет своим профилем: в переменных окружения либо
26
+ разделом надстройки при этом паттерне, где стоят его же готовые команды с именами целей раннера.
27
+
28
+ Роль каждого имени — то, о чём говорит проза: порт приёмника, порт сайта, порт админки, порт
29
+ сервера отрисовки. Дерево, у которого такого приложения нет, эти разделы не читает.
30
+
20
31
  ## Сначала — что отвечает на порту
21
32
 
22
33
  До первого запроса, а не после непонятного ответа:
23
34
 
24
35
  ```bash
25
- lsof -nP -iTCP:{{apiPort}} -sTCP:LISTEN
36
+ lsof -nP -iTCP:$API_PORT -sTCP:LISTEN
26
37
  ```
27
38
 
28
- На {{apiPort}} регулярно висит собранный артефакт из прошлой сессии
39
+ На порту приёмника регулярно висит собранный артефакт из прошлой сессии
29
40
  (`node -r dotenv/config dist/apps/api/main.js`): он отвечает 200 старым кодом, а процедуры,
30
41
  заведённой в ветке, у него нет вовсе. Таких процессов бывает несколько, и снимать надо все —
31
42
  по PID из `lsof`, каждый: `pkill` по шаблону `nx serve api` не попадает ни в один.
@@ -34,7 +45,7 @@ lsof -nP -iTCP:{{apiPort}} -sTCP:LISTEN
34
45
 
35
46
  ```bash
36
47
  npx nx build site
37
- PORT={{prodSitePort}} node dist/apps/site/server/server.mjs
48
+ PORT=$SITE_STAND_PORT node dist/apps/site/server/server.mjs
38
49
  ```
39
50
 
40
51
  Это не дев-сервер: гард ловит `nx|ng serve`, пакетные раннеры и статические серверы, а запуск
@@ -72,17 +83,32 @@ Angular DevTools, нужна ещё и dev-конфигурация (`--configur
72
83
  человеку набрать пароль, открыть вкладку или нажать кнопку означает неверно выбранный путь, а
73
84
  не нехватку прав у исполнителя.
74
85
 
86
+ **Спрашивают режим работы, а не ввод.** Ввод в поле пароля отбивает классификатор
87
+ автоматического режима: он судит само действие и адресов не различает — стенд на местном порту
88
+ выглядит для него боевым сайтом. Ход отсюда один: назвать владельцу отбитое действие, попросить
89
+ обычный режим, заполнить форму парой засева самому и вернуться в автоматический режим. Просьба
90
+ «войди сам» и «введи пароль» перекладывает на владельца работу агента и выглядит законной ровно
91
+ потому, что перед ней стоит настоящее препятствие: отбитое поле, чужое расширение в браузере,
92
+ отключившийся профиль. Препятствие остаётся препятствием агента.
93
+
94
+ Пути, которыми препятствие снимается своими силами, — до всякой просьбы: подстановка значения
95
+ инструментом формы по ссылке на элемент, выключение мешающего расширения в профиле браузера,
96
+ подъём стенда на другом адресе. Второй драйвер полем пароля не оправдывается: он заполняет то же
97
+ поле без классификатора, но обойдённый запрет снимается не с одного поля, а со всех сразу.
98
+ Оставленный обычный режим снимает подтверждение и со всех последующих действий захода, поэтому
99
+ возврат — часть входа, а не отдельная уборка.
100
+
75
101
  Стенд владельца запасным путём не бывает: он собирает главную ветку и о правке в рабочем дереве
76
102
  не говорит ничего.
77
103
 
78
104
  ## Стенд API
79
105
 
80
- Собранный артефакт поднимается на свободном порту, а не на {{apiPort}}: на {{apiPort}} отвечает API
106
+ Собранный артефакт поднимается на свободном порту, а не на порту приёмника: там отвечает приёмник
81
107
  владельца, и окружение у него не то, которое проверяется.
82
108
 
83
109
  ```bash
84
110
  npx nx build api
85
- env -u JWT_SECRET NODE_ENV=production API_PORT={{prodApiPort}} DATABASE_URL=… node dist/apps/api/main.js
111
+ env -u JWT_SECRET NODE_ENV=production API_PORT=$API_STAND_PORT DATABASE_URL=… node dist/apps/api/main.js
86
112
  ```
87
113
 
88
114
  Переменные окружения задаются в самой команде, по одной на проверяемый случай. Отказ на
@@ -92,7 +118,7 @@ env -u JWT_SECRET NODE_ENV=production API_PORT={{prodApiPort}} DATABASE_URL=…
92
118
  Процедура зовётся полным именем, как её объявляет контракт:
93
119
 
94
120
  ```bash
95
- curl -sS -X POST http://localhost:{{prodApiPort}}/<область>.v1.AuthService/GetMe \
121
+ curl -sS -X POST http://localhost:$API_STAND_PORT/<область>.v1.AuthService/GetMe \
96
122
  -H 'content-type: application/json' -H "authorization: Bearer $TOKEN" -d '{}'
97
123
  ```
98
124
 
@@ -108,7 +134,7 @@ curl -sS -X POST http://localhost:{{prodApiPort}}/<область>.v1.AuthServic
108
134
  ```bash
109
135
  docker run -d --name <префикс>-stand-nginx \
110
136
  --add-host api:host-gateway --add-host ssr:host-gateway \
111
- -p {{dockerSitePort}}:80 -p {{dockerAdminPort}}:8081 \
137
+ -p $PROXY_SITE_PORT:80 -p $PROXY_ADMIN_PORT:8081 \
112
138
  -v "$PWD/deploy/nginx/main.conf:/etc/nginx/nginx.conf:ro" \
113
139
  -v "$PWD/deploy:/etc/nginx/conf.d:ro" \
114
140
  -v "$PWD/dist/apps/admin/browser:/usr/share/nginx/html/admin:ro" \
@@ -126,9 +152,24 @@ docker run -d --name <префикс>-stand-nginx \
126
152
  тем же, чем и на свой. За настоящим прокси запросы идут со своим заголовком ровно потому, что
127
153
  прокси стоит перед дев-сервером, — оттуда и `-H "Host: localhost"`.
128
154
  - Переменные окружения стенда обязаны смотреть на процессы стенда. `CACHE_REFRESH_URL`,
129
- направленный на {{sitePort}}, сбрасывает кэш мимо того процесса, который держит справочник
155
+ направленный на порт сайта, сбрасывает кэш мимо того процесса, который держит справочник
130
156
  перенаправлений в памяти, — исправный механизм при этом выглядит сломанным.
131
157
 
158
+ ## Стенд живёт ровно столько, сколько проверка
159
+
160
+ Стенд поднят под один вывод и после него не нужен. Брошенный не мешает ничему и не подаёт сигнала:
161
+ имя у него своё, порт свободный, места он почти не занимает — увидеть его можно только вызовом
162
+ списка, а звать его незачем. Разбирать накопленное поэтому приходится человеку, и отличить
163
+ брошенный от живого стенда соседнего дерева он по имени не может.
164
+
165
+ Снос идёт тем же ходом, которым сделан вывод:
166
+
167
+ ```bash
168
+ docker rm -f <имя стенда>
169
+ ```
170
+
171
+ Имя даётся по номеру задачи — так видно, чей стенд остался, если ход всё-таки оборвался.
172
+
132
173
  ## Дерево для сравнения
133
174
 
134
175
  Сказать «это сломала правка» можно, только если видно, что до правки было иначе. Проверяют это
@@ -142,8 +183,8 @@ pnpm install --frozen-lockfile # из ../<префикс>-base: node_mod
142
183
  - Для сравнения берут не главную ветку, а последний коммит, на котором дерево собирается:
143
184
  главная бывает сломана, и тогда «до» и «после» различаются не из-за правки. Собирается ли
144
185
  коммит — проверяют сборкой, а не тем, что он в главной ветке.
145
- - Стенд второго дерева поднимают на своих портах: {{sitePort}}, {{adminPort}} и {{apiPort}} заняты владельцем, а {{ssrPort}}
146
- занимать нельзя — стенд разработчика ходит по имени `ssr:{{ssrPort}}`.
186
+ - Стенд второго дерева поднимают на своих портах: порты сайта, админки и приёмника заняты владельцем, а порт отрисовки
187
+ занимать нельзя — стенд разработчика ходит по имени `ssr:<порт отрисовки>`.
147
188
  - Оба стенда держат поднятыми одновременно — ровно до конца сравнения: если сравнивать по памяти
148
189
  между двумя запусками, заметишь только то, что успел запомнить. Со сравнением стенд второго
149
190
  дерева гасится тем же ходом, а не оставляется до конца захода: оставленные стенды копятся
@@ -157,7 +198,7 @@ pnpm install --frozen-lockfile # из ../<префикс>-base: node_mod
157
198
  приписать своей сборке, ни снять с неё подозрение.
158
199
 
159
200
  ```bash
160
- for u in http://localhost:{{apiPort}}/health http://localhost:{{sitePort}}/ http://localhost:{{adminPort}}/; do
201
+ for u in http://localhost:$API_PORT/health http://localhost:$SITE_PORT/ http://localhost:$ADMIN_PORT/; do
161
202
  printf '%s %s\n' "$(curl -s -o /dev/null -w '%{http_code}' --max-time 5 "$u")" "$u"
162
203
  done
163
204
  ```
@@ -169,10 +210,10 @@ done
169
210
 
170
211
  - **Одиночная сборка проекта серверы переживают, и отказываться от неё незачем.** Замерено
171
212
  ответом до и после: все порты остались за своими процессами. Осторожность здесь стоит дороже
172
- проверки — целая команда паттерна `seo-verify` осталась незапущенной ровно потому, что сборку
173
- сочли опасной, не замерив. Сборка из кэша замером не является: она не собирает вовсе, и видно
213
+ проверки — целый набор проверок разметки остался незапущенным ровно потому, что сборку
214
+ сочли опасной, не замерив. Сборка из кэша замером не бывает: она не собирает вовсе, и видно
174
215
  это по её длительности.
175
- - Свой дев-сервер не поднимать: сайт на {{sitePort}}, админка на {{adminPort}}, API на {{apiPort}} уже подняты
216
+ - Свой дев-сервер не поднимать: сайт, админка и приёмник уже подняты
176
217
  владельцем, и второй экземпляр отбивается гардом.
177
218
  - **Отбитый статический сервер читается как запрет проверки, а он указание на верный ход.**
178
219
  Отдавать собранное им нельзя не из осторожности: приложение ходит на свой origin, а глубокая
@@ -184,8 +225,8 @@ done
184
225
  поднятый дев-сервер годится, чтобы понять, что происходит на незнакомом экране, и не годится
185
226
  подтверждением: подтверждение — замер на стенде из прод-сборки.
186
227
  - **Общая сборка глушит все три дев-сервера владельца, а не только API.** После
187
- `nx run-many -t build` ложатся и сайт на {{sitePort}}, и админка на {{adminPort}}. Собирать надо то, что
228
+ `nx run-many -t build` ложатся и сайт, и админка. Собирать надо то, что
188
229
  проверяешь (`npx nx build site`), а не всё дерево. Если серверы легли, поднять их обратно
189
230
  агент не может — мешает гард, поэтому владельцу говорят об этом сразу, а не в конце сессии.
190
- - Порт {{ssrPort}} занимать осторожно: стенд разработчика на {{sitePort}} ходит по тому же имени `ssr:{{ssrPort}}`
231
+ - Порт отрисовки занимать осторожно: стенд разработчика ходит по тому же имени `ssr:<порт отрисовки>`
191
232
  через `host-gateway`, и пока на нём висит чужой процесс, стенд отдаёт чужую сборку.
@@ -0,0 +1,75 @@
1
+ ---
2
+ name: doc-style-human
3
+ kind: pattern
4
+ rule: doc-style
5
+ description: Паттерн правила doc-style. Брать при написании задачи в очереди работ, описания заявки и ответа владельцу в чате. Образцы «так» и «не так» на каждый из трёх текстов и разбор слов, которые в них заменяются. Форму ответа о состоянии работы называет правило status-report.
6
+ ---
7
+
8
+ # Задача, описание заявки и ответ владельцу
9
+
10
+ Паттерн правила `doc-style`. Три текста читает человек со стороны: он помнит продукт и не
11
+ читал ни одного правила слоя. Здесь — как они пишутся; что при этом должно быть верно, говорит
12
+ раздел «Тексты для человека» самого правила.
13
+
14
+ ## Когда брать
15
+
16
+ - Заводится задача в очереди работ.
17
+ - Пишется описание заявки на слияние.
18
+ - Пишется ответ владельцу в чате — кроме ответа о состоянии работы: его форму называет правило
19
+ `status-report`.
20
+
21
+ ## Слова, которые заменяются
22
+
23
+ Левая колонка — слова слоя правил. Они верны внутри слоя и пусты для того, кто в него не
24
+ заглядывает.
25
+
26
+ | Слово слоя | Чем сказать владельцу |
27
+ | ------------------ | ---------------------------------------------------------- |
28
+ | заявка на слияние | правка, которая ждёт вашего слова |
29
+ | прогон, набор | проверки; «проверки прошли», «проверки красные» |
30
+ | гард, гейт | что именно не пустило и почему |
31
+ | договорённость | о чём договорились по этому экрану |
32
+ | объём правки | что меняется на экране и где |
33
+ | раскладка ресурсов | обновление правил на машине |
34
+
35
+ ## Задача
36
+
37
+ Заголовок называет предмет, тело — что человек не может сделать. Красная проверка стоит в теле
38
+ последней строкой: она говорит, где смотреть, и не говорит, зачем чинить.
39
+
40
+ ```text
41
+ ✗ Сквозная спека берёт пункт заглушкой, а прогон на вершине не доходит до выкатки
42
+ ✓ Раздел «Отчёты» не открывается у пользователя, и из-за этого не идёт выкатка.
43
+ Красная проверка — сквозной набор, шаг «отчёты».
44
+ ```
45
+
46
+ ## Описание заявки
47
+
48
+ Первый абзац — что меняется для человека. Дальше — чем это подтверждено. Имена файлов уместны
49
+ в конце, а не вместо первого абзаца.
50
+
51
+ ```text
52
+ ✗ Ярус личности вызова получил второй признак, набор гейта зелёный
53
+ ✓ Заявки от машинной записи больше не открываются от имени владельца: теперь перед открытием
54
+ спрашивается, кто приходит по токену. Проверки прошли, 26 сценариев.
55
+ ```
56
+
57
+ ## Ответ в чате
58
+
59
+ Отвечает на заданный вопрос первым предложением. Утверждение о дереве идёт вместе с командой и
60
+ её выводом — этого требует правило `status-report`, и в чате оно верно так же.
61
+
62
+ ```text
63
+ ✗ Работа отдана, красное въехало в главную, откат прикрыт гардом
64
+ ✓ Правку я отправил, она ждёт вашего слова. В главной ветке сейчас красный шаг «сборка витрины»
65
+ — упал не на этой правке, вывод: 76 из 76 сценариев не поднялись, витрина лежит.
66
+ ```
67
+
68
+ ## Ловушки
69
+
70
+ - **Короче не значит понятнее.** Текст словами слоя выходит на треть короче и бесполезен тому,
71
+ кто решает, срочная это работа или нет.
72
+ - **«Не X, а Y» выглядит объяснением и ничего не объясняет.** Владелец узнаёт, чего не было, и
73
+ не узнаёт, что есть.
74
+ - **Слово «готово» без числа читается как проверенный факт.** Число — номер прогона, сколько
75
+ сценариев из скольких, время проверки.
@@ -104,6 +104,22 @@ description: Паттерн правила doc-style. Брать при напи
104
104
  его вместе со смыслом. Считается это грепом по тексту, и число само по себе отказом не бывает:
105
105
  судит его тот, кто правит следующим.
106
106
 
107
+ ## Новое слово в словаре
108
+
109
+ Слово, значащее в дереве что-то определённое, живёт в словаре. Порядок — четыре шага, и первый
110
+ из них не пропускается: словарь читается до того, как текст написан, а не сверяется после.
111
+
112
+ 1. **Поиск по собранному словарю целиком**, включая раздел отвергнутых слов. Слово, от которого
113
+ дерево отказалось, стоит там же — и вводить его заново значит отменять чужое решение молча.
114
+ 2. **Выбор своего раздела.** Раздел надстройки замещает одноимённый раздел набора целиком:
115
+ название берётся такое, какого в наборе нет.
116
+ 3. **Строка парой — слово и что это.** Без второй половины слово не введено: оно названо.
117
+ 4. **Раскладка тем же изменением**, и сверка при этом зелёная. Слово, введённое в надстройку и не
118
+ разложенное, у читателя ещё не появилось.
119
+
120
+ Слово, от которого дерево отказывается, заводится там же и с заменой в той же строке: отказ без
121
+ замены исполнить нечем — пишущий видит запрет и не видит, чем его закрыть.
122
+
107
123
  ## Факт проверяется, а не вспоминается
108
124
 
109
125
  Перед тем как написать, что код делает X, — открыть код и посмотреть. Пересказ по памяти
@@ -25,6 +25,13 @@ description: Паттерн правила git-workflow. Брать на зав
25
25
 
26
26
  Все четыре делает одна команда дерева, а не рука: делить их значит забывать последний.
27
27
 
28
+ **Команду, которой нет, паттерн не заменяет вызовами клиента.** Дерево, взявшее пакет впервые,
29
+ получает файлы проверок, но не записи о них в своём манифесте: раскладка правит ресурсы, а
30
+ манифест потребителя ей не принадлежит. Ходов отсюда два: завести команду тем же ходом либо
31
+ повторить все её шаги поимённо по перечню выше. Обход вызовами клиента выглядит исполнением до
32
+ последнего шага перечня: он забывается первым, потому что предыдущие уже дали видимый
33
+ результат.
34
+
28
35
  ```bash
29
36
  npm run task:new -- --title 'Письма владельцу не уходят молча' \
30
37
  --type Bug --area '<проект>\<команда>' --slug mail-owner-silence < описание.md
@@ -151,6 +158,11 @@ Docs-skip: правка только в тестах хука, зеркала у
151
158
 
152
159
  ## Частые промахи
153
160
 
161
+ - **Сверка сразу после добавления отвечает «нет», когда карточка уже стоит.** Очередь работ отдаёт
162
+ новый элемент не в ту же секунду, в какую его завели, а последний шаг читает её следующим
163
+ вызовом. Ответ на это — перечитать очередь целиком, а не завести карточку второй раз: две записи
164
+ об одной задаче снимает только администратор. Сама команда заведения при этом требует правки:
165
+ состояние читается сразу за мутацией, без повтора, и ложный отказ здесь дороже задержки.
154
166
  - Область и итерация не заданы: элемент заведён, но на доску команды не попал.
155
167
  - Состояние взято не из процесса проекта: перевод отвечает отказом на каждой задаче, и это
156
168
  читается как сломанная команда, а не как неверное имя состояния.
@@ -26,6 +26,14 @@ description: Паттерн правила git-workflow. Брать на зав
26
26
 
27
27
  Все четыре шага делает одна команда дерева, а не рука: делить их значит забывать третий.
28
28
 
29
+ **Команду, которой нет, паттерн не заменяет вызовами клиента.** Дерево, взявшее пакет впервые,
30
+ получает файлы проверок, но не записи о них в своём манифесте: раскладка правит ресурсы, а
31
+ манифест потребителя ей не принадлежит. Ходов отсюда два: завести команду тем же ходом либо
32
+ повторить все её шаги поимённо по перечню выше. Обход вызовами клиента выглядит исполнением до
33
+ последнего шага перечня: он забывается первым, потому что предыдущие уже дали видимый
34
+ результат. Так карточка простояла вне
35
+ колонок всю работу — два коммита и два закрытых этапа.
36
+
29
37
  **Локальные ветки этой задачи читаются до заведения новой.** Борда не видит ветки, и задача, по
30
38
  которой работа лежит доделанной в локальной ветке, выглядит открытой у всех: колонку двигают
31
39
  рукой, заявки нет, а ветка видна только на той машине, где её завели. Под один номер так
@@ -87,7 +95,7 @@ gh project item-add <номер борды> --owner <владелец> --url <а
87
95
  который его показывает. Не нашлось ни того ни другого — задача не заводится, а строка документа
88
96
  правится тем ходом, которым её прочитали.
89
97
 
90
- Название тикета говорит, что не так, а не что сделать: PR потом переводит его в сделанное.
98
+ Название задачи говорит, что не так, а не что сделать: PR потом переводит его в сделанное.
91
99
  Номер в заголовок руками не пишется — он известен только после создания, и команда дописывает
92
100
  его сама.
93
101
 
@@ -97,7 +105,7 @@ gh project item-add <номер борды> --owner <владелец> --url <а
97
105
  npm run check:board
98
106
  ```
99
107
 
100
- Она смотрит только открытое: тикеты на борде, номер и исполнителя у каждой открытой задачи,
108
+ Она смотрит только открытое: задачи на борде, номер и исполнителя у каждой открытой задачи,
101
109
  а у каждого открытого PR — номер в заголовке, строку `Closes`, открытую задачу за ним и то,
102
110
  что второго PR с тем же номером нет. Имя ветки не судит: у открытого PR его не переименовать.
103
111
 
@@ -209,6 +217,11 @@ Docs-skip: правка только в тестах хука, зеркала у
209
217
 
210
218
  ## Частые промахи
211
219
 
220
+ - **Сверка сразу после добавления отвечает «нет», когда карточка уже стоит.** Очередь работ отдаёт
221
+ новый элемент не в ту же секунду, в какую его завели, а последний шаг читает её следующим
222
+ вызовом. Ответ на это — перечитать очередь целиком, а не завести карточку второй раз: две записи
223
+ об одной задаче снимает только администратор. Сама команда заведения при этом требует правки:
224
+ состояние читается сразу за мутацией, без повтора, и ложный отказ здесь дороже задержки.
212
225
  - `gh` в оболочке пользователя подменён — звать `/opt/homebrew/bin/gh` напрямую.
213
226
  - Каталог добавлен целиком при чужом незакоммиченном рядом: чужая папка задачи уехала в главную ветку и стала отслеживаемой.
214
227
  - `git add` с несколькими путями не добавляет ничего, если хоть один путь не существует:
@@ -223,7 +236,7 @@ Docs-skip: правка только в тестах хука, зеркала у
223
236
  - `gh project` с `--owner` отвечает `unknown owner type`: владелец борды — другая учётная
224
237
  запись, и правка идёт только через GraphQL.
225
238
  - Заведённую задачу на борду сама она не забирает: репозиторий с ней не связан, и добавление
226
- идёт отдельным вызовом. Два тикета так и остались вне очереди работ — поэтому все четыре
239
+ идёт отдельным вызовом. Две задачи так и остались вне очереди работ — поэтому все четыре
227
240
  шага и делает `npm run task:new`, а не рука.
228
241
  - Исполнитель у задачи не проставляется сам ни при заведении через веб, ни при добавлении на
229
242
  борду: из девяноста девяти открытых задач он стоял у двух.
@@ -25,6 +25,13 @@ description: Паттерн правила git-workflow. Брать на зав
25
25
 
26
26
  Все четыре делает одна команда дерева, а не рука: делить их значит забывать последний.
27
27
 
28
+ **Команду, которой нет, паттерн не заменяет вызовами клиента.** Дерево, взявшее пакет впервые,
29
+ получает файлы проверок, но не записи о них в своём манифесте: раскладка правит ресурсы, а
30
+ манифест потребителя ей не принадлежит. Ходов отсюда два: завести команду тем же ходом либо
31
+ повторить все её шаги поимённо по перечню выше. Обход вызовами клиента выглядит исполнением до
32
+ последнего шага перечня: он забывается первым, потому что предыдущие уже дали видимый
33
+ результат.
34
+
28
35
  ```bash
29
36
  npm run task:new -- --title 'Письма владельцу не уходят молча' \
30
37
  --label bug --label area:api --slug mail-owner-silence < описание.md
@@ -156,6 +163,11 @@ Docs-skip: правка только в тестах хука, зеркала у
156
163
 
157
164
  ## Частые промахи
158
165
 
166
+ - **Сверка сразу после добавления отвечает «нет», когда карточка уже стоит.** Очередь работ отдаёт
167
+ новый элемент не в ту же секунду, в какую его завели, а последний шаг читает её следующим
168
+ вызовом. Ответ на это — перечитать очередь целиком, а не завести карточку второй раз: две записи
169
+ об одной задаче снимает только администратор. Сама команда заведения при этом требует правки:
170
+ состояние читается сразу за мутацией, без повтора, и ложный отказ здесь дороже задержки.
159
171
  - Метка списка не поставлена при заведении: задача есть, а на доске её нет. Доска показывает
160
172
  только то, чью метку знает.
161
173
  - Перевод по списку не снял прежнюю метку: задача стоит в двух списках сразу.
@@ -40,6 +40,7 @@ git diff --name-only --diff-filter=U # что встало конфликт
40
40
  | Что встало конфликтом | Как разрешается |
41
41
  | -------------------------------- | ---------------------------------------------------------------------------------- |
42
42
  | код | ловушка правила `git-workflow` про сторону-удаление; после — `npm run check:dupes` |
43
+ | сборка из описания | собирается заново из описания после того, как описание разрешено: строки в такой файл пишет генератор, и соединённые руками стороны дают файл, которого он не выдаст |
43
44
  | спек в `docs/specs/` | сохранением обеих сторон, если обе дописывали; снятый одной стороной раздел остаётся снятым — правило `spec-driven`; после — `npm run check:specs` |
44
45
  | компаньон правила рядом со скилом | сохранением обеих сторон — те же две дописи в одну таблицу; после — `npm run check:specs` |
45
46
  | список работ (`docs/BACKLOG.md`) | признаком отбора — паттерн `doc-style-sweep` |
@@ -92,6 +93,13 @@ git checkout --theirs docs/BACKLOG.md && git add docs/BACKLOG.md
92
93
 
93
94
  ## Проверки после разрешения
94
95
 
96
+ **Конфликт в разложенном файле, который исполняется, разрешается тем же вызовом, каким
97
+ обнаружен.** Маркеры в теле гарда — синтаксическая ошибка, а не расхождение текста: ветка падает,
98
+ диспетчер отдаёт её код отказом, и следующего вызова оболочки уже не будет — вместе с ней
99
+ отбиваются и остальные двери, названные в объявлении этого гарда. Исполняемый файл узнаётся
100
+ строкой `# rt-hook:` в шапке; отложенное «поправлю потом» здесь означает заход, который нечем
101
+ продолжить.
102
+
95
103
  Конфликт в текстах кода не задевает, и зелёная сборка про него ничего не говорит:
96
104
 
97
105
  ```bash