@rt-tools/agent-kit 0.23.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 (61) hide show
  1. package/README.md +8 -2
  2. package/assets/checks/board.github.mjs +1 -1
  3. package/assets/checks/check-board.github.mjs +14 -0
  4. package/assets/checks/check-doc-paths.mjs +24 -5
  5. package/assets/checks/check-file-size.mjs +8 -2
  6. package/assets/checks/check-prose-style.mjs +10 -1
  7. package/assets/defaults/gate-map.sh +13 -0
  8. package/assets/defaults/project.sh +10 -0
  9. package/assets/defaults/shell.sh +18 -3
  10. package/assets/hooks/browser-guard-device-id.sh +42 -12
  11. package/assets/hooks/dispatch.sh +40 -9
  12. package/assets/hooks/docs-guard.sh +10 -0
  13. package/assets/hooks/exam-guard.sh +66 -16
  14. package/assets/hooks/git-guard-delivery-draft.sh +78 -0
  15. package/assets/hooks/git-guard-delivery.sh +49 -111
  16. package/assets/hooks/git-guard-main.sh +39 -4
  17. package/assets/hooks/git-guard-push-tests.sh +49 -3
  18. package/assets/hooks/hook-input.sh +17 -6
  19. package/assets/hooks/rule-source-guard.sh +11 -0
  20. package/assets/hooks/stand-login-guard.sh +101 -0
  21. package/assets/hooks/write-targets.sh +37 -4
  22. package/assets/laws/verifiability.md +12 -2
  23. package/assets/laws/work-conduct.md +59 -65
  24. package/assets/patterns/browser-verification-measure.md +41 -1
  25. package/assets/patterns/browser-verification-stand.md +56 -15
  26. package/assets/patterns/doc-style-human.md +75 -0
  27. package/assets/patterns/git-workflow-commit.azure.md +12 -0
  28. package/assets/patterns/git-workflow-commit.github.md +16 -3
  29. package/assets/patterns/git-workflow-commit.gitlab.md +12 -0
  30. package/assets/patterns/git-workflow-merge.md +8 -0
  31. package/assets/patterns/task-flow-start.md +1 -1
  32. package/assets/patterns/testing-e2e.md +18 -8
  33. package/assets/pitfalls/task-flow.md +40 -40
  34. package/assets/rules/browser-verification.md +29 -3
  35. package/assets/rules/doc-style.md +36 -0
  36. package/assets/rules/git-workflow.github.md +20 -23
  37. package/assets/rules/reuse-first.md +8 -2
  38. package/assets/rules/styling-bem.md +8 -1
  39. package/assets/rules/task-flow.md +73 -73
  40. package/assets/rules/testing.md +21 -0
  41. package/assets/skills/agent-kit.md +60 -70
  42. package/lib/commands.d.ts.map +1 -1
  43. package/lib/commands.js +63 -2
  44. package/lib/commands.js.map +1 -1
  45. package/lib/enroll.d.ts.map +1 -1
  46. package/lib/enroll.js +1 -1
  47. package/lib/enroll.js.map +1 -1
  48. package/lib/observations.d.ts +10 -1
  49. package/lib/observations.d.ts.map +1 -1
  50. package/lib/observations.js +1 -0
  51. package/lib/observations.js.map +1 -1
  52. package/lib/override-marks.d.ts +24 -0
  53. package/lib/override-marks.d.ts.map +1 -0
  54. package/lib/override-marks.js +98 -0
  55. package/lib/override-marks.js.map +1 -0
  56. package/lib/shipment.d.ts.map +1 -1
  57. package/lib/shipment.js +1 -1
  58. package/lib/shipment.js.map +1 -1
  59. package/package.json +1 -1
  60. package/rt-tools-agent-kit-0.24.0.tgz +0 -0
  61. package/rt-tools-agent-kit-0.23.0.tgz +0 -0
@@ -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
  ```
@@ -170,9 +211,9 @@ done
170
211
  - **Одиночная сборка проекта серверы переживают, и отказываться от неё незачем.** Замерено
171
212
  ответом до и после: все порты остались за своими процессами. Осторожность здесь стоит дороже
172
213
  проверки — целый набор проверок разметки остался незапущенным ровно потому, что сборку
173
- сочли опасной, не замерив. Сборка из кэша замером не является: она не собирает вовсе, и видно
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
+ сценариев из скольких, время проверки.
@@ -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
@@ -76,7 +76,7 @@ Agent(subagent_type: "Explore", prompt: "<тема просьбы>: что по
76
76
  Ведёт главный агент: субагент до владельца не достучится. Команда — `/grill-me`, один вопрос
77
77
  за раз, к каждому — свой рекомендуемый ответ с доводом.
78
78
 
79
- Шесть вопросов задаются всегда, даже когда задача кажется понятной:
79
+ Шесть вопросов закрываются все, а задаются те, на которые разведка не ответила:
80
80
 
81
81
  | Вопрос | Зачем |
82
82
  | -------------------------------------------- | -------------------------------------------------------- |
@@ -33,13 +33,16 @@ description: Паттерн правила testing. Брать при правк
33
33
  ## Прогон
34
34
 
35
35
  ```bash
36
- npx nx e2e site-e2e -- --project=chromium
37
- npx nx e2e admin-e2e -- --project=chromium
38
- BASE_URL=http://localhost:{{dockerSitePort}} npx nx e2e site-e2e -- --project=chromium # против внешнего стенда
36
+ npx nx e2e <цель набора> -- --project=chromium
37
+ BASE_URL=http://localhost:$PROXY_SITE_PORT npx nx e2e <цель набора> -- --project=chromium # против внешнего стенда
39
38
  ```
40
39
 
41
- По умолчанию конфиг идёт на {{sitePort}} и подхватывает уже поднятый сервер. С `BASE_URL` свой сервер
42
- не стартует вовсе.
40
+ Имена целей раннера и номера портов у каждого дерева свои: паттерн их не знает. Дерево называет
41
+ их своим профилем — переменными окружения либо разделом надстройки при этом паттерне, где стоят
42
+ готовые команды с его именами.
43
+
44
+ По умолчанию конфиг идёт на порт стенда сайта и подхватывает уже поднятый сервер. С `BASE_URL`
45
+ свой сервер не запускается.
43
46
 
44
47
  Полный набор админки гоняется **одним воркером** (`--workers=1`): тесты с настоящей сессией
45
48
  правят одни и те же объекты живой базы и в параллельном прогоне мешают друг другу. Одни и те
@@ -107,9 +110,9 @@ expect(answer.headers()['location']).toBe('/новый-адрес');
107
110
 
108
111
  ## Частые промахи
109
112
 
110
- - **Порт {{ssrPort}} занимать осторожно:** стенд разработчика на {{sitePort}} ходит по тому же имени
111
- `ssr:{{ssrPort}}` через `host-gateway`, и пока на нём висит чужой процесс, стенд отдаёт чужую
112
- сборку.
113
+ - **Порт сервера отрисовки занимать осторожно:** стенд разработчика ходит по тому же имени
114
+ `ssr:<порт отрисовки>` через `host-gateway`, и пока на нём висит чужой процесс, стенд отдаёт
115
+ чужую сборку.
113
116
  - Браузер стоит один — chromium; узкий экран — `--project=mobile-chrome`. Ошибка «Executable
114
117
  doesn't exist» разобрана в правиле `testing`: она же приходит после смены версии Playwright.
115
118
  - Спеки админки без сессии пропускаются молча — прогон выглядит успешным, а проверено меньше
@@ -125,6 +128,13 @@ expect(answer.headers()['location']).toBe('/новый-адрес');
125
128
  успевает его прочитать, и тест краснеет через раз. Проверяют либо конечное состояние
126
129
  (`data-state` строки стал `done`), либо то же промежуточное — но на ответе, который тест сам
127
130
  задержал и сам отпускает.
131
+ - **Двойник, перенесённый из спеки в общий файл набора, выходит из-под исключения проверки
132
+ повторов.** Она пропускает файлы спек и читает всё остальное, включая двойники и настройки, а
133
+ сверяет имя объявления — не значение и не назначение. Настройка, лежавшая в спеке под общим
134
+ именем, после переноса совпадает с одноимённой в другом слое, и проверка краснеет на файле,
135
+ которого правка не касалась: разбор такого красного занимает отдельный ход. Имя при переносе
136
+ получает приставку своего набора; строка в список известного не заводится — он только
137
+ сокращается.
128
138
  - **Каталог сборки, удалённый под смонтированным томом, оставляет контейнер с пустым
129
139
  корнем:** стенд отвечает 403 на всё, и падают сразу все тесты. Контейнер после
130
140
  `rm -rf dist/apps/<приложение>` пересоздаётся.
@@ -14,11 +14,10 @@
14
14
  тела задачи в дереве ещё верно.
15
15
 
16
16
  - **Разрешение владельца, оставленное в репозитории, теряется на каждой новой ветке.** Оно
17
- записано номером в файл ветки, а следующая ветка отводится от главной и его не несёт: гард
18
- отбивает работу, которую владелец разрешил, и повторяется это ровно столько раз, сколько
19
- веток заведут до слияния разрешившего коммита. Отказ гарда при этом называет файл и молчит о
20
- том, что запись принадлежит ветке: из его текста это читается как настройка дерева. Пока
21
- разрешение живёт в репозитории, номер дописывается заново в каждую новую ветку.
17
+ записано в файл ветки, а следующая ветка отводится от главной и его не несёт: гард запрещает
18
+ разрешённую работу столько раз, сколько веток заведут до слияния. Отказ называет файл и молчит
19
+ о том, что запись принадлежит ветке. Пока разрешение живёт в репозитории, номер дописывается в
20
+ каждую новую ветку.
22
21
  - **Имя чужого дерева в файлы репозитория не пишется, а путь к образцу — пишется.** Обе вещи
23
22
  живут рядом с передачей захода именно поэтому: там законен полный путь, а в репозитории —
24
23
  только ссылка без имени.
@@ -37,13 +36,11 @@
37
36
  договорённость обязана его пережить: её сценарии получают номера в общей нумерации домена,
38
37
  и на них ссылаются заголовки тестов. Обратное тоже верно — ход работы не кладётся в
39
38
  `proposed/`: спек, в котором завелись шаги, снова становится планом и умирает после мержа.
40
- - **У меню нет строки «вопрос не тот».** Меню годится там, где выбор действительно закрыт;
41
- пока постановка вопроса не подтверждена, отвергнуть её владельцу нечем — он выбирает из
42
- вариантов неверной посылки, и выйти из набора может только руками. Настройки владельца,
43
- требующие меню, требование не снимают: тогда к каждому вопросу добавляется свободный вариант,
44
- и он же — единственное место, где вопрос отвергается целиком. Три вопроса ушли одним меню, у
45
- одного постановка была ложной. Там, где под сомнением сама уместность вопроса, его сперва
46
- проверяют по репликам владельца — и чаще всего не задают.
39
+ - **У меню нет строки «вопрос не тот».** Меню годится там, где выбор закрыт; пока постановка
40
+ вопроса не подтверждена, владельцу нечем её отвергнуть — он выбирает из вариантов неверной
41
+ посылки. Если настройки требуют меню, к каждому вопросу добавляется свободный вариант. Три
42
+ вопроса ушли одним меню, у одного постановка была ложной: уместность вопроса сперва проверяют
43
+ по репликам владельца.
47
44
  - **Субагент вопросов владельцу не задаёт.** Ни роли, ни конвейер до него не достучатся —
48
45
  они возвращают текст главному агенту. Поэтому разбор ведёт главный агент, а роли стоят по
49
46
  обе стороны от него.
@@ -91,11 +88,9 @@
91
88
  соседа.
92
89
 
93
90
  - **Копия образца папки задачи несёт шапку раскладки, и первая же правка отбивается гардом.**
94
- Образец разложен пакетом наравне с остальным разложенным, а гард места правки судит по шапке:
95
- копия под задачу выглядит разложенным файлом, и отказ называет адрес источника пакета — то
96
- есть уводит править образец вместо своей копии. Команда заведения задачи шапку снимает сама,
97
- копирование руками — нет. Правится такая копия только записью заново: правку по месту гард
98
- отбивает и её.
91
+ Копия под задачу выглядит разложенным файлом, и отказ называет адрес источника пакета уводит
92
+ править образец вместо копии. Команда заведения задачи снимает шапку сама, копирование руками
93
+ нет. Такая копия правится только записью заново.
99
94
  - **Имя проекта в признаке готовности этапа спрашивается у сборщика, а не пишется по памяти.**
100
95
  На неизвестное имя сборщик отвечает «задач не запущено» и выходит нулём: команда признака не
101
96
  прогнала ни одной пробы и промолчала так же, как зелёный прогон. Видно это только по числу
@@ -122,19 +117,15 @@
122
117
  профиль дерева, и лежит он вне дерева кода.
123
118
 
124
119
  - **Тело задачи, написанное вперёд замысла, называет способ, и способ стареет раньше дефекта.**
125
- Задача серии заводится за недели до того, как её берут, и к этому дню предложенный ход бывает
126
- уже неверен: привезённое пакетом умеет то, что задача звала написать, названного места в
127
- дереве не осталось, а посылки, на которых ход стоял, разведка не подтверждает. Читается такое
128
- тело как действующее указание, поэтому работа начинается с чтения того файла, к которому
129
- задача относится, а не с исполнения её тела. Расхождение уходит решением по ходу и меняет
130
- способ, но не цель.
120
+ Задачу серии заводят за недели до взятия; к этому дню предложенный способ бывает уже неверен:
121
+ пакет уже умеет то, что задача звала написать, названного места в дереве нет. Работа поэтому
122
+ начинается с чтения файла, к которому задача относится, а не с исполнения её тела. Расхождение
123
+ меняет способ, но не цель.
131
124
 
132
125
  - **Работа, упершаяся в разрешение, доводится до конца без той части, которую разрешение
133
- открывает.** Чужой шаг бывает двух родов: прогон и разбор идут сами и кончатся без
134
- исполнителя, а отбитое разрешение не кончится никогда его ждут, и ход, объявивший об этом,
135
- останавливает работу целиком. Делается всё, что от разрешения не зависит, а непройденное
136
- пишется строками тела заявки — там его читает ревьювер; реплика владельцу живёт до следующего
137
- сообщения и в приёмку не попадает.
126
+ открывает.** Прогон и разбор кончатся сами, а отбитое разрешение не кончится никогда: ход,
127
+ объявивший ожидание, останавливает работу целиком. Делается всё, что от разрешения не зависит;
128
+ непройденное пишется в тело заявки, где его читает ревьювер.
138
129
 
139
130
  - **Слово владельца об устройстве — постановка, а не решение.** Названное им обычно уже живёт
140
131
  в дереве под этим самым словом: у него есть имя на экране, раздел в спеке и поле в модели, и
@@ -144,17 +135,11 @@
144
135
  спрашивается до правки.
145
136
 
146
137
  - **Указание работать по ходу — это указание делать его шаги, включая меняющие историю.**
147
- Отметка сделанного этапа, отправка ветки и открытие заявки ходом предписаны, и отдельного
148
- слова на каждый не требуется: спросить надо о том, чего в ходе нет вовсе. Изъятие приходит
149
- из общего запрета дерева на действие без просьбы, прочитанного как требование буквального
150
- слова, и получается указание, исполненное наполовину. Граница у этого одна: прямое слово
151
- владельца о самом шаге. Сказано «ветку не отправляй» — она не отправляется, сколько бы раз
152
- ход её ни предписывал.
153
-
154
- - **Строка ожидания живёт дольше причины, по которой её написали.** Записанная в ход работы,
155
- она приходит в следующий заход раньше любой реплики и подтверждает себя сама: три указания
156
- подряд не пересилили одну строку на диске. Снимается она тем же ходом, которым владелец
157
- ответил, — а не тем, в котором о ней вспомнили.
138
+ Отметка этапа, отправка ветки и открытие заявки предписаны ходом, отдельного слова на каждый
139
+ не нужно. Общий запрет дерева на действие без просьбы, прочитанный буквально, даёт указание,
140
+ исполненное наполовину. Граница одна: прямое слово владельца о самом шаге «ветку не
141
+ отправляй» действует, сколько бы раз ход её ни предписывал.
142
+
158
143
 
159
144
  - **Очередь работ эпиком не кончается.** Занятый другими исполнителями или законченный эпик
160
145
  означает следующую задачу из очереди, а не остановку: «свободных задач эпика нет» ответом на
@@ -165,6 +150,21 @@
165
150
  колонку разбора и снятие её папки — обязательные шаги закрытия, и оба случаются тем же ходом,
166
151
  которым открывается заявка. Ход, в котором больше ничего нет, работу не двигает, сколько бы
167
152
  команд в нём ни стояло.
153
+ - **Черновик папки без номера теряется молча.** Он лежит вне истории, и его не видят ни борда,
154
+ ни сверка очереди, ни следующий заход. Порог брошенного разбора эту потерю не ловит: работа
155
+ проигрывает соседним поручениям в тот же час, а неделя проходит потом.
156
+ - **Стопка веток стоит дороже, и цена у неё названная.** Заявка в соседнюю ветку не запускает
157
+ конвейер, объявленный на базу главной; слияние базовой ветки закрывает заявку следующей как
158
+ слитую, хотя её правок в главной нет; конфликт от чужого слияния разрешается в каждой ветке
159
+ стопки заново. Расстановка, выбранная молча, собирает всю цену и не показывает ни одной её
160
+ части. Задачи, идущие одна из другой по коду, законно живут ветками от главной, пока правка
161
+ следующей не опирается на код предыдущей.
162
+
163
+ - **Находка посреди этапа ощущается частью текущей работы, когда предмет соседний.** Своего
164
+ признака у этого нет: до первой правки находки в дереве нет, а после неё файлы соседней работы
165
+ от файлов своей не отличаются. Поэтому сверяется перечень условий выхода замысла, а не ощущение.
166
+ Однажды такую правку остановил посторонний отказ по роду файла, а не сверка с замыслом: без него
167
+ она уехала бы в чужую ветку, и раздельного отката у двух работ не было бы.
168
168
 
169
169
  ## Поведение исполнителя — по разборам происшествий
170
170
 
@@ -207,7 +207,7 @@
207
207
  перечисляет шесть обязательных вопросов таблицей, и пустая таблица проходит наравне с
208
208
  заполненной.
209
209
  - **Отказ гарда разговора исполняется задним числом.** Владелец видит незаданный вопрос вместе с
210
- отбитым ходом: прозаический вопрос инструментом не является, и раньше поймать его нечем. Гард
210
+ отбитым ходом: прозаический вопрос не вызов инструмента, и раньше поймать его нечем. Гард
211
211
  отмечает пропуск, но не отменяет его.
212
212
  - **Обход требования папки нужен там, где работа вливается частями.** Тогда её до конца не
213
213
  разбирают, а причина остаётся в заявке.