@rt-tools/agent-kit 0.20.0 → 0.22.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 (78) hide show
  1. package/assets/checks/check-reuse.mjs +11 -1
  2. package/assets/checks/check-specs.mjs +8 -23
  3. package/assets/checks/signals/core.json +12 -2
  4. package/assets/checks/signals.mjs +17 -0
  5. package/assets/checks/spec-proposed.mjs +122 -0
  6. package/assets/hooks/browser-guard-device-id.sh +3 -0
  7. package/assets/hooks/browser-guard-no-asking.sh +3 -0
  8. package/assets/hooks/browser-guard-no-listing.sh +3 -0
  9. package/assets/hooks/browser-guard-no-other-drivers.sh +3 -0
  10. package/assets/hooks/browser-guard-require-select.sh +3 -0
  11. package/assets/hooks/claim-guard.sh +4 -0
  12. package/assets/hooks/conscience-guard.sh +4 -0
  13. package/assets/hooks/deny-tail.sh +12 -0
  14. package/assets/hooks/dev-server-guard.sh +3 -0
  15. package/assets/hooks/docs-guard.sh +4 -11
  16. package/assets/hooks/exam-guard.sh +3 -0
  17. package/assets/hooks/git-guard-delivery.sh +4 -4
  18. package/assets/hooks/git-guard-main.sh +4 -9
  19. package/assets/hooks/git-guard-push-tests.sh +3 -0
  20. package/assets/hooks/grill-gate.sh +5 -0
  21. package/assets/hooks/guard-note.sh +45 -0
  22. package/assets/hooks/handoff-entry-guard.sh +3 -0
  23. package/assets/hooks/handoff-write.sh +58 -7
  24. package/assets/hooks/hook-input.sh +1 -0
  25. package/assets/hooks/override-write-guard.sh +4 -4
  26. package/assets/hooks/postmortem-guard.sh +4 -0
  27. package/assets/hooks/proposal-guard.sh +11 -0
  28. package/assets/hooks/prose-style-guard.sh +3 -0
  29. package/assets/hooks/qa-dataid-guard.sh +3 -0
  30. package/assets/hooks/rerun-guard.sh +3 -0
  31. package/assets/hooks/reuse-first-guard.sh +3 -0
  32. package/assets/hooks/rule-article.sh +1 -1
  33. package/assets/hooks/rule-source-guard.sh +4 -4
  34. package/assets/hooks/sql-guard.sh +3 -0
  35. package/assets/hooks/task-context-load.sh +1 -1
  36. package/assets/hooks/task-flow-draft-guard.sh +3 -0
  37. package/assets/hooks/task-flow-guard.sh +3 -0
  38. package/assets/hooks/turn-entry-load.sh +27 -2
  39. package/assets/hooks/turn-exit-guard.sh +4 -0
  40. package/assets/hooks/waiting-turn-guard.sh +32 -10
  41. package/assets/hooks/window-fill-guard.sh +3 -0
  42. package/assets/hooks/work-start-guard.sh +5 -0
  43. package/assets/hooks/write-targets.sh +59 -10
  44. package/assets/laws/delivery.md +5 -0
  45. package/assets/laws/frontend-application.md +10 -0
  46. package/assets/laws/work-conduct.md +8 -0
  47. package/assets/patterns/browser-verification-stand.md +24 -9
  48. package/assets/patterns/git-workflow-pr.github.md +29 -22
  49. package/assets/patterns/spec-driven-domain.md +5 -0
  50. package/assets/patterns/spec-driven-rule.md +41 -1
  51. package/assets/patterns/task-flow-archive.md +12 -0
  52. package/assets/patterns/task-flow-handoff.md +14 -1
  53. package/assets/patterns/task-flow-resume.md +11 -0
  54. package/assets/patterns/task-flow-start.md +40 -40
  55. package/assets/patterns/testing-e2e.md +15 -0
  56. package/assets/patterns/testing-unit.md +13 -0
  57. package/assets/pitfalls/git-workflow.github.md +13 -0
  58. package/assets/pitfalls/spec-driven.md +20 -0
  59. package/assets/pitfalls/task-flow.md +78 -48
  60. package/assets/rules/git-workflow.azure.md +5 -0
  61. package/assets/rules/git-workflow.github.md +26 -23
  62. package/assets/rules/git-workflow.gitlab.md +5 -0
  63. package/assets/rules/spec-driven.md +23 -24
  64. package/assets/rules/turn-conduct.md +13 -15
  65. package/assets/rules/turn-entry.md +6 -4
  66. package/assets/rules/typescript-conventions.md +7 -0
  67. package/assets/skills/agent-kit-extend.md +11 -0
  68. package/assets/skills/agent-kit.md +12 -2
  69. package/lib/commands.d.ts.map +1 -1
  70. package/lib/commands.js +36 -1
  71. package/lib/commands.js.map +1 -1
  72. package/lib/observations.d.ts +13 -1
  73. package/lib/observations.d.ts.map +1 -1
  74. package/lib/observations.js +7 -1
  75. package/lib/observations.js.map +1 -1
  76. package/package.json +1 -1
  77. package/rt-tools-agent-kit-0.22.0.tgz +0 -0
  78. package/rt-tools-agent-kit-0.20.0.tgz +0 -0
@@ -26,6 +26,16 @@
26
26
  рабочим и молча ничего не делает.
27
27
  - **Набор записей приходит с сервера страницей, а не целиком.** Список, растущий вместе с
28
28
  данными, однажды перестаёт открываться, и заметно это становится уже у пользователя.
29
+ - **У значения, положенного приложением в браузер, есть род: устройство либо вошедший.**
30
+ Хранилище браузера переживает и перезагрузку, и выход, и смену того, кто вошёл; род решает,
31
+ снимается ли значение при выходе. По имени ключа он не виден: язык интерфейса принадлежит
32
+ устройству, выбранная рабочая область — вошедшему, и по именам они неразличимы. Умолчания у
33
+ рода нет тоже: оставленное значение сеанса отдаёт следующему вошедшему чужой выбор, а снятая
34
+ настройка устройства заставляет выбирать язык при каждом входе. Значит род называет человек, и
35
+ называет до того, как ключ заведён.
36
+ - **Значения сеанса снимает то же место, которое снимает вход.** Уход по истёкшему входу идёт
37
+ мимо кнопки выхода, и ключ, снятый своим экраном, остаётся лежать — то есть достаётся
38
+ следующему вошедшему на этом устройстве.
29
39
  - **Окружение, которого нет на сервере, приходит экрану извне, а не берётся у среды.**
30
40
  Взятое напрямую, оно компилируется и падает при отдаче страницы сервером — то есть у гостя,
31
41
  а не у того, кто это написал. Места, где иначе нельзя, названы поимённо.
@@ -32,6 +32,11 @@
32
32
  «здесь нет», «этого не заводили», «такого файла не бывает» — произносится только как вывод
33
33
  команды. Не проверенное отрицание опаснее вопроса: вопрос владелец поправит, а факт от
34
34
  исполнителя примет на веру, потому что тот в дерево смотрит.
35
+ - **Пустой вывод утверждением не бывает.** Он значит либо «ноль строк», либо «спрошено не то», и
36
+ различает эти два только повторный вызов — без конвейера и без глушения потока ошибок: в
37
+ конвейере код возврата принадлежит последнему звену, а заглушённый поток уносит текст промаха.
38
+ Пока такой вызов не сделан, о дереве не сказано ничего, а «ничего нет» — самая дорогая из
39
+ возможных ошибок: она отменяет работу, которую владелец велел сделать.
35
40
  - **Отрицание, полученное одним источником, отрицанием не является.** Ответ «не найдено», отказ в
36
41
  доступе и пустой список говорят о правах спрашивающего, а не о предмете: тот же вопрос, заданный
37
42
  оттуда, где предмет виден, отвечает обратным. Сказать «этого нет» можно только после второго
@@ -50,6 +55,9 @@
50
55
  просьбы, даже когда свой даёт тот же ответ: равноценность инструментов решает тот, кто просит.
51
56
  Одинаковый результат равноценностью не является — у названного инструмента бывает видно то,
52
57
  чего у заменителя нет вовсе.
58
+ - **Случившаяся подмена называется в том же докладе.** Чем сделано вместо названного и почему —
59
+ двумя словами, рядом с ответом на просьбу. Без этой строки владелец узнаёт о подмене, повторяя
60
+ указание: к той минуте работа по нему уже сделана не тем, и заплачено за неё дважды.
53
61
  - **Понимание записано там, где идёт работа.** Оставленное в переписке живёт у одного участника и
54
62
  до следующего дня; работу продолжает тот, у кого этой переписки нет.
55
63
  - **Сказанное владельцем записывается его словами и задним числом не переписывается.** Пересказ
@@ -54,15 +54,26 @@ npx nx build admin --base-href=/
54
54
  Angular DevTools, нужна ещё и dev-конфигурация (`--configuration=development`): прод-сборка не
55
55
  публикует `window.ng`. Выводы о размере бандла и минификации на такой сборке делать нельзя.
56
56
 
57
- Сессия кладётся в `localStorage['<ключ сессии админки>']` **строкой JSON** (`JSON.stringify(token)`),
58
- иначе приложение её не прочитает. Токен не подписывается руками, а берётся у живого API:
59
- `POST /<область>.v1.AuthService/Login`. Команду с паролем классификатор блокирует обходить не
60
- надо, спрашивать разрешение у владельца.
61
-
62
- Взять уже открытую сессию нельзя: чтение того же ключа из браузера
63
- блокируется. Оба пути к своему стенду упираются в пароль, поэтому остаётся третий — смотреть
64
- на админке владельца, где вход уже сделан. Свой стенд нужен, только когда проверяют
65
- прод-сборку, `--base-href` или конфиг nginx; чтобы просто посмотреть экраны, он не нужен.
57
+ Собранное отдаётся прокси дерева с его настоящим конфигом, а не статическим сервером. Причин
58
+ две, и обе всплывают в первую минуту: приложение ходит за процедурами на свой же origin, а по
59
+ глубокой ссылке статический сервер отвечает `404`маршрутов одностраничного приложения он не
60
+ знает и на его точку входа не откатывается. Гард дев-серверов такой сервер отбивает наравне с
61
+ `nx serve`, и это указание на верный ход, а не помеха: готовый вызов — раздел «Стенд под
62
+ настоящим nginx» ниже, он и отдаёт собранное, и проксирует процедуры.
63
+
64
+ Вход делается формой, как у человека. Учётная запись местной базы разработки не секрет
65
+ владельца: её кладёт туда засев того же дерева, а пароль лежит в файле окружения рядом.
66
+ Разрешения на неё не спрашивают — спрашивают про боевую запись, а её на местном стенде не
67
+ бывает вовсе. Там, где приёмника рядом нет, ответ входа подменяет сам стенд: путь у приложения
68
+ остаётся тот же, что у человека.
69
+
70
+ Токен руками не подписывается и в хранилище браузера руками не кладётся: положенный так, он
71
+ проверяет экран, миновав живой путь входа, — то есть отвечает не на тот вопрос. Просьба к
72
+ человеку набрать пароль, открыть вкладку или нажать кнопку означает неверно выбранный путь, а
73
+ не нехватку прав у исполнителя.
74
+
75
+ Стенд владельца запасным путём не бывает: он собирает главную ветку и о правке в рабочем дереве
76
+ не говорит ничего.
66
77
 
67
78
  ## Стенд API
68
79
 
@@ -163,6 +174,10 @@ done
163
174
  это по её длительности.
164
175
  - Свой дев-сервер не поднимать: сайт на {{sitePort}}, админка на {{adminPort}}, API на {{apiPort}} уже подняты
165
176
  владельцем, и второй экземпляр отбивается гардом.
177
+ - **Отбитый статический сервер читается как запрет проверки, а он указание на верный ход.**
178
+ Отдавать собранное им нельзя не из осторожности: приложение ходит на свой origin, а глубокая
179
+ ссылка отвечает `404`. Отсюда идут не к обходу гарда, а к прокси дерева с его настоящим
180
+ конфигом.
166
181
  - **Дев-сервер владельца — место, где смотрят, а не место, где проверяют.** Вывод о том, что
167
182
  экран верен, делается на прод-сборке за настоящим прокси: разметка от сервера, локали, кэш,
168
183
  перенаправления и заголовки на дев-сервере либо другие, либо отсутствуют вовсе. Взгляд на
@@ -94,10 +94,14 @@ GH_TOKEN="$TOKEN" gh pr create --title '[<КЛЮЧ>-86] Письмо владе
94
94
  /opt/homebrew/bin/gh issue view 86 --json labels --jq '.labels | map(.name) | join(",")'
95
95
  ```
96
96
 
97
- Строка `Closes #<номер>` обязательна: без неё PR не прикрепляется к задаче, и сверка очереди
98
- это находит. Она же и означает, что задача закрывается целиком — половину задачи одним PR не
99
- выкатывают: у задачи одна ветка, и работа, которая в неё не влезает, делится на задачи до
100
- того, как ветка заводится.
97
+ Строка `Closes #<номер>` обязательна: без неё PR не прикрепляется к задаче, и сверка очереди это
98
+ находит. Она же означает, что задача закрывается целиком — половину задачи одним PR не выкатывают:
99
+ работа, которая в одну ветку не влезает, делится на задачи до того, как ветка заводится.
100
+
101
+ Отказ о превышенной квоте языка запросов (`API rate limit already exceeded`) заявки не заводит
102
+ вовсе; открытие идёт тогда вызовом REST — `$GH api -X POST "repos/$REPO/pulls" -f head=… -f
103
+ base=… -f title=… -F body=@<файл>`, — а метки и ревьювер ставятся следом. Текст такого отказа
104
+ читается как временный, но квота у записи не исчерпана, а равна нулю: ждать нечего.
101
105
 
102
106
  У уже открытого PR то же ставится тремя вызовами REST. `gh pr edit` здесь не годится: он
103
107
  запрашивает карточки Projects (classic), получает отказ о снятом API и до правки не доходит.
@@ -177,11 +181,10 @@ $GH api "repos/$REPO/pulls/321" \
177
181
  назначить автора ревьювером нельзя, а отказа на такой запрос не приходит. Владельцу называют
178
182
  то, что прочитали, а не то, что заказывали.
179
183
 
180
- Прочитанное расхождение имеет назначенное действие, а не остаётся наблюдением: автора у заявки
181
- не сменить, правкой это не чинится. Заявка закрывается и открывается заново токеном машинной
182
- записи с переносом тела, меток, ревьювера и исполнителя. Номер при этом сменится, и строки,
183
- которые ссылались на прежний, переписываются тем же ходом. Развилка, вынесенная тут владельцу,
184
- означает, что порядок поставки прочитан и не исполнен: ход отсюда один.
184
+ Прочитанное расхождение имеет назначенное действие: автора у заявки не сменить, и она
185
+ закрывается и открывается заново токеном машинной записи с переносом тела, меток, ревьювера и
186
+ исполнителя. Номер сменится, и ссылки на прежний переписываются тем же ходом. Развилка, вынесенная
187
+ тут владельцу, означает, что порядок поставки прочитан и не исполнен.
185
188
 
186
189
  Учётная запись, из-под которой пришлось пушить, в этот вызов не переносится: пуш и авторство
187
190
  PR выбираются отдельно, и `GH_TOKEN` для публикации — всегда токен бота.
@@ -191,10 +194,16 @@ PR выбираются отдельно, и `GH_TOKEN` для публикац
191
194
  фразой владелец успевает влить PR, и всё сказанное о нём после этого — про вчерашний день. Так
192
195
  владельцу и было предложено влить то, что он влил часом раньше.
193
196
 
194
- Читается состояние и перед пушем в ветку, у которой есть заявка, а не только перед словом о
195
- ней. Влитая заявка означает, что ветки на сервере уже нет: пуш её не обновит, а заведёт заново,
196
- и вклад останется вне главной ветки. Ответ пуша говорит это одной строкойотметкой о новой
197
- ветке вместо перечня коммитов, — и её читают: на удачную отправку такой ответ похож целиком.
197
+ Читается состояние и перед пушем в ветку, у которой есть заявка. Влитая заявка означает, что
198
+ ветки на сервере уже нет: пуш её не обновит, а заведёт заново, и вклад останется вне главной
199
+ ветки. Ответ пуша говорит это отметкой о новой ветке вместо перечня коммитов на удачную
200
+ отправку он похож целиком.
201
+
202
+ **Итог прогона читается отдельным вызовом, а не кодом возврата ожидания.** Команда клиента,
203
+ которая ждёт конца прогона, возвращает ноль и на красном: дождалась она конца, а не успеха.
204
+ Прочитанный как исход, этот ноль делает зелёным то, что упало, и сказанное владельцу о зелёном
205
+ прогоне оказывается про красный. Состояние и заключение прогона спрашивают своим вызовом — тем
206
+ же, каким читают состояние заявки.
198
207
 
199
208
  Открытый PR означает, что задача ждёт разбора, — колонка переставляется тем же движением:
200
209
 
@@ -212,13 +221,11 @@ npm run task:move -- 86 in-review
212
221
  гоняет ничего. Линтеры, юниты и сценарии хуков снимает гейт пуша — ниже то, чего он не знает.
213
222
 
214
223
  1. **Главная ветка влита в эту ветку** — `git fetch origin && git merge origin/main`.
215
- Свежесть основания между ветками одного захода не наследуется: вторая и третья ветка
216
- отводятся после своего `fetch`, а не от ссылки, подтянутой под первую. Пока идёт работа,
217
- главная уходит вперёд — чаще всего собственным PR того же исполнителя, влитым час
218
- назад.
219
- Всё, что проверяется ниже, проверяется от этого основания: PR с разошедшейся ветки
220
- показывает ревьюверу правку вперемешку с чужой. Порядок и разбор конфликта — паттерн
221
- `git-workflow-merge`.
224
+ Свежесть основания между ветками одного захода не наследуется: вторая и третья отводятся
225
+ после своего `fetch`, а не от ссылки под первую. Пока идёт работа, главная уходит вперёд —
226
+ чаще всего собственным PR того же исполнителя. Всё, что проверяется ниже, проверяется от
227
+ этого основания: PR с разошедшейся ветки показывает ревьюверу правку вперемешку с чужой.
228
+ Порядок и разбор конфликта паттерн `git-workflow-merge`.
222
229
  2. **В ветке только та правка, за которой её заводили** — `git diff main...HEAD --stat`. Чужой
223
230
  домен в списке файлов означает, что правка расползлась, и её надо вернуть в свои границы.
224
231
  3. **Ни мока, ни подменённого ответа, ни отладочной строки** — `git diff main...HEAD` читается
@@ -281,8 +288,8 @@ in-review`, — и `npm run check:board` прогоняется ещё раз:
281
288
  - Второй строкой `Closes` в одном PR задача больше не закрывается: две задачи в одной ветке
282
289
  откатываются только вместе. Либо это одна задача — и вторая поглощается, — либо две ветки.
283
290
  - Половина задачи, уехавшая своим PR, тоже промах: тело такого PR начинается со слов «Часть
284
- #<номер>» вместо `Closes`, задача остаётся открытой, и после отката видно её целой. Работа,
285
- которая в одну ветку не влезает, делится на задачи до того, как ветка заводится.
291
+ #<номер>» вместо `Closes`, и задача остаётся открытой. Работа, которая в одну ветку не
292
+ влезает, делится на задачи до того, как ветка заводится.
286
293
  - PR открыт без ревьювера: он не попадает во входящие владельца вовсе, и очередь стоит,
287
294
  выглядя работающей. Так шестнадцать PR ждали разбора, которого никто не запрашивал.
288
295
  - Метки поставлены по названию PR, а не прочитаны у задачи: область теряется, и по борде не
@@ -108,6 +108,11 @@ docs/specs/<домен>/
108
108
  Тогда отказ подаётся владельцу как занятые даты, а не как ошибка базы
109
109
  ```
110
110
 
111
+ **«Тогда» называет один исход.** Формулировка через «либо» описывает два устройства сразу, и
112
+ какое из них работает, по сценарию не узнать: сценарий зелен при любом. Развилка описывается
113
+ двумя сценариями с разным «Дано» — тогда смена поведения роняет ровно тот из них, который
114
+ перестал быть верным, а не проходит молча.
115
+
111
116
  Идентификатор ставится в начало заголовка теста, через тире. Сценарий без теста помечается
112
117
  `Не покрыто: <причина>`, сценарий с неполным тестом — `Покрытие: частичное — <чего не
113
118
  хватает>`.
@@ -86,6 +86,11 @@ description: Правило под «Закон о поставке». Брат
86
86
  пустой она не бывает. Выводить требование из фразы в тексте нельзя: разложено у потребителя не
87
87
  всё, и текст, сказавший «правку отбивает гард», врёт в дереве, где гарда нет.
88
88
 
89
+ Читается строка списком идентификаторов ресурсов, поэтому проза в ней превращается в требования,
90
+ которых нет: «требует команду строки запуска и токен дерева» раскладка разбирает на два имени и
91
+ печатает оба неотвеченными — в её выводе такие строки неотличимы от настоящей нехватки.
92
+ Требование объявляется идентификатором ресурса либо не объявляется вовсе.
93
+
89
94
  Разделы: `## Как это называется здесь` · `## Где это лежит` · `## Как закон применяется
90
95
  здесь` · `## Чего из закона здесь нет` · `## Паттерны` · `## Ловушки`.
91
96
 
@@ -114,7 +119,26 @@ description: Паттерн правила git-workflow. Брать … Не б
114
119
  ---
115
120
  ```
116
121
 
117
- Разделы: `## Когда брать` · готовый код · `## Частые промахи`. Привязки у паттерна нет:
122
+ Разделы: `## Когда брать` · готовый код · `## Как пишется сама статья
123
+
124
+ **Довод при статье называет устройство, на котором он верен.** Довод стареет вместе с тем, что
125
+ его вызвало: запрет писать в чужую настройку стоял на том, что слияние JSON молча теряет
126
+ несовпавшее, — и был верен, пока куском была карта из двух десятков объявлений. Пришёл
127
+ диспетчер, кусок стал строкой на событие, довод перестал быть верным, а стоял ещё две редакции
128
+ и читался действующим запретом. Названное устройство — то, по чему при следующей правке видно,
129
+ что довод пора перечитать.
130
+
131
+ **Требование к шагу стоит в том разделе, который этот шаг ведёт.** Написанное ниже читается
132
+ после шага: раздел об оставшемся шаге требовался и прежде, и образцы стояли готовые, — но само
133
+ требование лежало ниже описания открытия заявки. Читающий доходил до открытия, открывал заявку
134
+ и требования ещё не видел; выглядит это забывчивостью, а не порядком текста.
135
+
136
+ **Статья о непроверяемом называет, какое именно средство её не берёт.** «Проверить машиной
137
+ нечем» стареет вместе с тем, чем проверяли: тело заявки не читает сверка очереди работ — и это
138
+ было записано как «не читает никто», хотя гард поставки разбирает ту же строку команды и по ней
139
+ судит заголовок. Названное средство даёт следующей правке точку, от которой отсчитывать.
140
+
141
+ ## Частые промахи`. Привязки у паттерна нет:
118
142
  проверка его не сверяет, потому что сверять готовый код с ним самим нечем.
119
143
 
120
144
  ## Описание: триста знаков и один вопрос
@@ -183,6 +207,19 @@ description: Паттерн правила git-workflow. Брать … Не б
183
207
 
184
208
  ## Частые промахи
185
209
 
210
+ - Проверка требования «шаг делается» поставлена на вызов самого шага. Гард, привязанный к
211
+ вызову, ловит форму сделанного и не видит несделанного вовсе: снятие черновика, которого не
212
+ было, не даёт ему ни одного события. У такого требования проверка стоит на завершении хода —
213
+ там видно, чего за ход не случилось.
214
+ - Привязка называет одно место исполнения, а исполняют утверждение несколько сторон. Сверка
215
+ смотрит наличие строки, а не полноту списка: договорённость обещала объявление нескольких
216
+ событий, привязка вела в сборку настройки, а диспетчер читал одно — и молчали обе стороны.
217
+ Статья, чьё утверждение исполняют несколько мест, называет их все.
218
+ - Список, названный деревом, лежит в компаньоне, а общий довод — в самом правиле. В контекст
219
+ общий довод приходит всегда, а список — только когда его прочитали, и проигрывает он в ту же
220
+ минуту, когда решение принимают по памяти. Всякий раз, когда правило требует «список назван
221
+ деревом», оно требует и того, чтобы список читали раньше общих слов, — и говорит это прямо.
222
+
186
223
  - Закон назвал файл проекта — проверка отбивает. Путям место в правиле.
187
224
  - Спутник с привязкой лежит рядом с законом: он привязывает закон к этому проекту, а зелёная
188
225
  проверка это утвердит, потому что структура совпадёт с тем, чего проверка сама и ждёт.
@@ -190,6 +227,9 @@ description: Паттерн правила git-workflow. Брать … Не б
190
227
  правку сущности оказались привязаны к механике, которую не зовёт ни один экран.
191
228
  - Утверждение переформулировали, а строку в привязке не тронули: связь идёт по тексту, и
192
229
  проверка перестанет её находить.
230
+ - Пустая привязка отбивается сверкой спеков, и вердикт для неё нужен дословный: принимаются
231
+ «Не исполняется», «Не применимо», «Не проверяется». Фраза своими словами — «держится текстом»,
232
+ «проверяется глазами» — вердиктом не считается, и узнаётся это отказом сверки.
193
233
  - Строка привязки дописана в конец компаньона, а не вставлена в таблицу. После таблицы там идут
194
234
  ещё разделы — «Чем это проверяется», «Что ещё стоит знать при чтении кода», — и приписанная в
195
235
  конец строка в таблицу не попадает вовсе: утверждение читается непривязанным, а сверка спеков
@@ -199,6 +199,18 @@ npm run cargo:mark -- --state fixed \
199
199
  правит ссылки во всех текстах, которые на неё смотрят, ради ровного ряда имён. Порядок между
200
200
  частями не нужен — находки друг друга не продолжают, и владелец читает их разом.
201
201
 
202
+ **Строка находки, закрытой другой работой, помечается номером той работы.** Починенная по
203
+ частям находка со стороны списка неотличима от несделанной: оба её промаха были закрыты своими
204
+ задачами и записаны предметно, каждый при своём поддомене, — а разбор ушёл на то, чтобы
205
+ выяснить, что чинить нечего. Номер стоит в самой строке, и следующий читатель этого пути не
206
+ проходит.
207
+
208
+ **Пункт сборной находки проверяется на живость до того, как берётся в работу.** Мелочи,
209
+ списанные в один пункт, не стареют вместе: из четырёх промахов живыми оказались два, один был
210
+ закрыт другой работой, четвёртый не воспроизводился — и разведка этого заняла больше времени,
211
+ чем сами правки. Сборная задача поэтому стоит ровно столько, сколько разведка её состава, и
212
+ это говорится владельцу до того, как её берут.
213
+
202
214
  **Наружу без слова владельца уезжает только сводка наблюдений.** Она говорит, чем пользовались
203
215
  и чем не пользовались ни разу, — это факт, и мнением он не станет. Предложение — другое дело:
204
216
  это заготовка правки чужого дерева, и часть заготовок отпадает при первом же чтении. Уехавшая
@@ -59,13 +59,26 @@ description: Паттерн правила turn-conduct. Брать, когда
59
59
 
60
60
  ### Передача
61
61
 
62
- Кладётся вне дерева, одним файлом на веткукаталог передачи называет профиль дерева:
62
+ Кладётся разделом `## Передача захода` в ход работы задачи в тот же файл, где стоит «Где
63
+ стоим». Он коммитится и едет в ветку, поэтому передача переживает переход на другую машину:
64
+ работу, оборванную заполнением окна, подхватывают там, где её продолжают. Коммитит и пушит
65
+ раздел исполнитель, тем же ходом, что и остальную запись работы.
66
+
67
+ Раздел один: второе сжатие переписывает прежний, а не дописывает второй — две правды об одной
68
+ работе в одном файле хуже одной устаревшей. Заголовки внутри него идут третьим уровнем, чтобы
69
+ не сталкиваться с разделами самого хода работы.
70
+
71
+ У работы без папки задачи — отсоединённая голова, ветка без заведённой задачи — разделу лечь
72
+ некуда, и передача уезжает файлом вне дерева; каталог называет профиль:
63
73
 
64
74
  ```bash
65
75
  mkdir -p <каталог передачи>
66
76
  # файл — <каталог передачи>/<ветка>.md
67
77
  ```
68
78
 
79
+ Это запасной путь, а не второе законное место: у ветки с папкой задачи такой файл не заводится
80
+ вовсе, и читающая сторона его не смотрит, найдя раздел.
81
+
69
82
  **Черновик передачи пишет хук, а не рука.** Перед сжатием контекста он кладёт по тому же
70
83
  адресу то, что есть на диске: ветку, состояние работы, следующий шаг, незакоммиченное, коммиты
71
84
  сверх главной. Сжатие приходит и тогда, когда напомнить некому — ночью или посреди длинного
@@ -32,6 +32,17 @@ description: Паттерн правила task-flow. Брать при возв
32
32
  замечаниям разбора — папка восстанавливается на время правки, а разбор повторяется тем же
33
33
  коммитом.
34
34
 
35
+ ## Признак этапа проверяется прогоном, а не перечислением по чтению
36
+
37
+ Пересчитанное глазами расходится с тем, что есть: читая проверку, исполнитель насчитал четыре
38
+ выхода пропуском, а их было пять — пятый лежал за отдельной функцией внутри обвязки клиента
39
+ базы и в перечень не попал. Нашёлся он прогоном, и спасла работу именно та строка замысла,
40
+ которая требовала проверить каждый случай командой.
41
+
42
+ Отсюда форма признака: он называет команду и то, что в её выводе означает «сошлось», — а не
43
+ перечень, собранный чтением. Перечень годится, чтобы понять, где смотреть; закрытым этап
44
+ делает вывод команды.
45
+
35
46
  ## Чего не делать
36
47
 
37
48
  - **Не спрашивать владельца о том, что записано.** Ради этого всё и заведено.
@@ -45,12 +45,11 @@ Agent(subagent_type: "Explore", prompt: "<тема просьбы>: что по
45
45
  записи прошлых заходов. Одно место отрицанием не является: «в таком-то месте не нашёл» говорит о
46
46
  месте, а не о дереве, и вопрос называет оба способа, которыми искали.
47
47
 
48
- Разведка кончается не ощущением, а выводом команд. До первого вопроса владельцу исполнитель
49
- знает: как устроен репозиторий (корневая памятка), что лежит в каталоге, о котором пойдёт речь
50
- (`ls`), и есть ли уже написанное по теме (поиск по документации и правилам). «Не знаю»
51
- проверяемым не бывает. Без списка того, что считается сделанным, разведка исполняется как
52
- настроение: прочитанный переданный текст сходит за неё, и по текущему дереву не запускается ни
53
- одной команды.
48
+ Разведка кончается не ощущением, а выводом команд. До первого вопроса владельцу исполнитель знает:
49
+ как устроен репозиторий (корневая памятка), что лежит в каталоге, о котором пойдёт речь (`ls`), и
50
+ есть ли уже написанное по теме (поиск по документации и правилам). Без списка того, что считается
51
+ сделанным, разведка исполняется как настроение: прочитанный переданный текст сходит за неё, и по
52
+ дереву не запускается ни одной команды.
54
53
 
55
54
  Разведка по заведённой задаче кончается воспроизведённым симптомом, а не найденным файлом. Тело
56
55
  задачи описывает дерево на день заведения, и чужие правки въезжают в главную ветку между
@@ -94,11 +93,10 @@ Agent(subagent_type: "Explore", prompt: "<тема просьбы>: что по
94
93
  Форму вопроса задают настройки владельца: где требуют меню, спрашивается меню, и к каждому
95
94
  вопросу добавляется свободный вариант — у закрытого набора нет строки «вопрос не тот».
96
95
 
97
- **Рамка переданного текста на текущее дерево не переносится.** Передача захода, файл
98
- предложений и спек описывают то дерево, в котором писались. Про текущее знает только текущее:
99
- прежде чем принять из такого текста утверждение о раскладке, оно проверяется командой здесь.
100
- Подтверждение при этом находится само признаки дерева, только ставящего пакет, стоят и у
101
- дерева, которое пакет и пишет, и ставит.
96
+ **Рамка переданного текста на текущее дерево не переносится.** Передача захода, файл предложений
97
+ и спек описывают то дерево, в котором писались; про текущее знает только текущее, и утверждение о
98
+ раскладке проверяется командой здесь. Подтверждение находится само признаки дерева, только
99
+ ставящего пакет, стоят и у того, которое пакет пишет и ставит.
102
100
 
103
101
  Сообщение, которым исполнитель останавливается, начинается с того, чего он ждёт:
104
102
 
@@ -108,12 +106,12 @@ Agent(subagent_type: "Explore", prompt: "<тема просьбы>: что по
108
106
  ```
109
107
 
110
108
  Под вопросом идёт таблица положения эпика — та же, что в передаче. Под, а не над: остановка
111
- называется первой строкой, и положение эпика — то, из чего владелец решает, а не то, о чём его
112
- спрашивают. Работа вне эпика таблицы не несёт.
109
+ называется первой строкой, а положение эпика — то, из чего владелец решает. Работа вне эпика
110
+ таблицы не несёт.
113
111
 
114
- Замеры, находки ролей и список решений к этому моменту уже записаны в ход работы, и в
115
- сообщении владельцу они лишние. Отчёт, в конце которого стоит вопрос, выглядит добросовестно
116
- ровно настолько, насколько надёжно вопрос в нём тонет.
112
+ Замеры, находки ролей и список решений к этому моменту записаны в ход работы, и в сообщении
113
+ владельцу они лишние: отчёт, в конце которого стоит вопрос, выглядит добросовестно ровно
114
+ настолько, насколько надёжно вопрос в нём тонет.
117
115
 
118
116
  Ответы пишутся в `docs/tasks/_draft-<slug>/grill.md` — папка ещё черновик, номера нет.
119
117
 
@@ -153,9 +151,9 @@ Workflow(name: "plan", args: "docs/tasks/_draft-<slug>")
153
151
 
154
152
  ### Состояние `договорённость-записана`: серия задач объявляется эпиком
155
153
 
156
- Разбор кончился одной задачей — шаг пропускается. Вышло несколько, и порядок между ними
157
- значим — эпик объявляется здесь, до первой из них, и дважды: карточкой в очереди работ с меткой
158
- эпика и замыслом эпика рядом с ней.
154
+ Разбор кончился одной задачей — шаг пропускается. Вышло несколько, и порядок между ними значим —
155
+ эпик объявляется здесь, до первой из них, и дважды: карточкой в очереди работ с меткой эпика и
156
+ замыслом рядом с ней.
159
157
 
160
158
  Замысел эпика называет три вещи, и ни одна не выводится из остальных:
161
159
 
@@ -171,12 +169,11 @@ Workflow(name: "plan", args: "docs/tasks/_draft-<slug>")
171
169
  ```
172
170
 
173
171
  Состав без порядка порядком не является: две задачи, у которых он держался пониманием, ушли в
174
- работу наоборот, и вторая переделывалась под первую. Назначенный здесь порядок держится до конца
175
- эпика; пересмотр — решение владельца, и записывается он в ход работы той задачи, которая его
176
- вызвала.
172
+ работу наоборот, и вторая переделывалась под первую. Назначенный порядок держится до конца эпика;
173
+ пересмотр — решение владельца, и пишется он в ход работы той задачи, которая его вызвала.
177
174
 
178
- Лежит замысел вне папки задачи: та умирает с мержем первой же задачи. Каталог для него называет
179
- компаньон правила — у пакета своего пути нет.
175
+ Лежит замысел вне папки задачи: та умирает с мержем первой же. Каталог для него называет компаньон
176
+ правила — у пакета своего пути нет.
180
177
 
181
178
  Карточки заводятся на все задачи разом, здесь же: по вызову заведения задачи на каждую строку
182
179
  таблицы, с меткой эпика. Выданные номера возвращаются в ту же таблицу — колонкой или приставкой
@@ -200,20 +197,24 @@ git checkout -b <КЛЮЧ>-<номер>-<slug>
200
197
  npm run task:move -- <номер> in-progress
201
198
  ```
202
199
 
200
+ Тело приходит файлом и пустым не бывает: заведённая пустой задача не говорит ничего ни
201
+ исполнителю, ни владельцу, а дописать её потом можно только со слова владельца — и заводится
202
+ пустое молча, номером наравне с полным.
203
+
203
204
  `task:new` переименовывает `_draft-<slug>` в `<КЛЮЧ>-<номер>-<slug>` и проставляет шапку замысла.
204
205
  Ветка заводится вторым вызовом: составную «завести и сразу коммитить» гард главной ветки
205
206
  отклоняет целиком.
206
207
 
207
- Номер уже выдан — черновика нет и не заводится: папка задачи открывается сразу под именем
208
- ветки, а разбор пишется в неё же. Так начинается половина работ: номер приходит прошлым
209
- заходом, замеченным дефектом или соседней задачей, и шаг с черновиком в этом случае неисполним
210
- целиком переименовывать нечего, `task:new` звать незачем. Ловушка «номер не бывает первым»
211
- сюда не относится: она про то, что задачу не заводят до разбора, а не про то, что с уже
212
- заведённой нельзя работать.
208
+ Номер уже выдан — черновика нет и не заводится: папка открывается сразу под именем ветки, а
209
+ разбор пишется в неё же. Так начинается половина работ: номер приходит прошлым заходом,
210
+ замеченным дефектом или соседней задачей, и шаг с черновиком тогда неисполним переименовывать
211
+ нечего. Ловушка «номер не бывает первым» сюда не относится: она про то, что задачу не заводят до
212
+ разбора, а не про то, что с уже заведённой нельзя работать.
213
213
 
214
- Остальные два файла пишутся, а не кладутся образцом впрок: пустой `plan.md` на диске
215
- неотличим от замысла, у которого нет этапов, а следующий заход читает папку задачи первым
216
- делом и доверяет ей. Образец открывается тем же движением, которым заполняется:
214
+ Остальные два файла пишутся, а не кладутся образцом впрок: пустой `plan.md` неотличим от
215
+ замысла без этапов, а следующий заход доверяет папке задачи. Образец открывается тем же
216
+ движением, которым заполняется, и шапку раскладки с копии снимают тут же иначе правку отобьёт
217
+ гард, а ловушка при правиле объясняет почему:
217
218
 
218
219
  ```bash
219
220
  cp docs/tasks/_template/plan.md docs/tasks/<КЛЮЧ>-<номер>-<slug>/plan.md # и сразу пишется
@@ -229,8 +230,8 @@ cp docs/tasks/_template/progress.md docs/tasks/<КЛЮЧ>-<номер>-<slug>/pr
229
230
  Пока не объявлено состояние, в котором код правится, гард отбивает правку и называет
230
231
  обязательное действие того состояния, которое стоит в строке.
231
232
 
232
- **Следующее движение:** объявив состояние, тот же ход берётся за замысел начиная с его
233
- шапки. Заведённая папка ходом не кончается: в ней ещё нет ни одного написанного файла.
233
+ **Следующее движение:** объявив состояние, тот же ход берётся за замысел с его шапки. Заведённая
234
+ папка ходом не кончается: написанного файла в ней ещё нет ни одного.
234
235
 
235
236
  ### Состояние `задача-взята`: шапка замысла
236
237
 
@@ -270,11 +271,10 @@ cp docs/tasks/_template/progress.md docs/tasks/<КЛЮЧ>-<номер>-<slug>/pr
270
271
  - **Этап:** 1 из 3 — <название первого этапа из замысла>
271
272
  ```
272
273
 
273
- Ход на этой границе не кончается. Написанный замысел выглядит законченным куском: этапы
274
- разложены, файл закоммичен, отчитаться есть чем — и отчёт встаёт ровно на то место, которое
275
- должна была занять работа. Владелец читает такой отчёт как сделанное, а сделано ничего. Так
276
- и вышло 21 августа: заход кончился строкой «следующий шаг — такой-то» при заполнении окна около
277
- двух процентов.
274
+ Ход на этой границе не кончается. Написанный замысел выглядит законченным куском: этапы разложены,
275
+ файл закоммичен, отчитаться есть чем — и отчёт встаёт ровно на то место, которое должна была
276
+ занять работа. Владелец читает его как сделанное, а сделано ничего: 21 августа заход кончился
277
+ строкой «следующий шаг — такой-то» при заполнении окна около двух процентов.
278
278
 
279
279
  Кончают ход четыре вещи, и они те же, что у остальных состояний: предел заполнения окна, отказ
280
280
  гарда, вопрос владельцу и отданная работа, по которой начата следующая. Дочитанный до конца
@@ -90,6 +90,21 @@ expect(answer.headers()['location']).toBe('/новый-адрес');
90
90
  Отличать постоянный переход от временного нужно там, где на нём стоит договорённость: поисковые
91
91
  роботы и браузер запоминают только постоянный, и разница между 301 и 302 видна лишь в ответе.
92
92
 
93
+ ## Состояние экрана после действия ждётся опросом, а не читается следующей строкой
94
+
95
+ Между нажатием и новым содержимым лежит сеть: отбор уходит в адрес нажатием, а строки приезжают
96
+ ответом сервера, и прочитанные сразу — ещё прежние. Спека, читающая столбец следующей строкой,
97
+ проходит на своей машине и краснеет в конвейере: там ответ приходит позже, и разница между
98
+ машинами решает исход.
99
+
100
+ Ждётся поэтому само состояние — опросом до совпадения с ожидаемым, а не одним чтением и не
101
+ паузой на время. Пауза проверяет машину: на свободной её хватает всегда, на занятой не хватает,
102
+ и какая именно спека не успела, оказывается делом случая.
103
+
104
+ Знание это заводится в правиле, а не в общем модуле набора: записанное только кодом, оно
105
+ известно тому, кто его писал, — следующая спека повторяет промах, потому что читает не код, а
106
+ правило.
107
+
93
108
  ## Частые промахи
94
109
 
95
110
  - **Порт {{ssrPort}} занимать осторожно:** стенд разработчика на {{sitePort}} ходит по тому же имени
@@ -106,6 +106,19 @@ globalThis.fetch = (): Promise<Response> => Promise.resolve(Response.json({ succ
106
106
  заголовке и едет в ветке. Признак временного файла — его заголовок не называет ни одного
107
107
  сценария.
108
108
 
109
+ ## Зелёное, которое ничего не проверяет
110
+
111
+ **Проба, ждущая молчания, идёт в паре с пробой, ждущей отказа.** Молчание наступает и тогда,
112
+ когда проверять было нечем: приставка перед командой в конвейере действует на одно звено, и
113
+ хук, получивший пустое значение, молчит ровно так же, как хук, которому нечего отбивать. Две
114
+ пробы из трёх при этом зелены — они и ждали молчания, — и промах виден только по третьей,
115
+ которая ждёт отказа.
116
+
117
+ **Проба на мину, которая не срабатывает, зелена и без починки.** Сценарий на таблицу кодов
118
+ отказа у спека без процедур проходил бы и до правки: читать эту таблицу сверке не приходится
119
+ вовсе. Проверяется такая проба обеими сторонами — прежнее состояние возвращается на минуту и
120
+ прогоняется: не покраснело, значит проба ничего не сторожит.
121
+
109
122
  ## Частые промахи
110
123
 
111
124
  - Либа без своего `vitest.config.mts`: `nx test <проект>` пройдёт зелёным, не запустив ни
@@ -219,8 +219,21 @@
219
219
  ни статус, ни сверка, а команда, прошедшая между прятаньем и возвратом, пишет в те же файлы —
220
220
  и возврат встаёт конфликтом. Так ушли в прятанье восемь незакоммиченных файлов при живом
221
221
  запрете на эту команду: запрет читался в начале захода, а команда набиралась через сорок ходов.
222
+ - **Вывод команды, которой нужен репозиторий, собирается в каталоге репозитория.**
223
+ Перенаправление создаёт файл раньше, чем команда успевает отказать: клиент хостинга, позванный
224
+ из каталога временных файлов, падает строкой о том, что это не репозиторий, а пустой файл к
225
+ этой минуте уже записан. Уехав дальше — в тело заявки, в замысел, в правило, — он стирает
226
+ написанное, и восстанавливается это только историей правок на стороне хостинга. Файл вывода
227
+ поэтому кладётся в дереве, а записанное проверяется до того, как его куда-то отправят.
228
+
222
229
  - **Очередь конвейера, заведённая на всё дерево, снимает ждущего чужим пушем.** Группа
223
230
  одновременности с отменой уже идущего экономит машину ровно до первого соседа: его пуш снимает
224
231
  прогон чужой ветки, и та остаётся с вершиной без единого прогона — заявка при этом выглядит
225
232
  непроверенной, а прогонять её заново некому. Группа поэтому заводится своя на ветку, и общей
226
233
  остаётся только у шагов, которым нужен стенд: их и правда нельзя гнать вдвоём на одной машине.
234
+
235
+ - **Команды дерева зовутся из его корня, а не из каталога пакета.** В подкаталоге со своим
236
+ манифестом запуск берёт его, а не корневой, и отвечает «такой команды нет» — при том что в
237
+ дереве она есть и написана верно. Читается это как поломка команды, а лечится каталогом: путь
238
+ до неё называется от корня, и звать её оттуда.
239
+