@rt-tools/agent-kit 0.12.0 → 0.14.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 (53) hide show
  1. package/assets/checks/board-runs.github.mjs +132 -0
  2. package/assets/checks/board.github.mjs +43 -41
  3. package/assets/checks/check-board.github.mjs +73 -8
  4. package/assets/checks/check-schema-drift.mjs +28 -5
  5. package/assets/checks/rt-kit-checks.config.mjs +12 -0
  6. package/assets/checks/task-new.github.mjs +15 -3
  7. package/assets/commands/feedback.md +8 -0
  8. package/assets/defaults/project.sh +50 -5
  9. package/assets/hooks/browser-guard-no-other-drivers.sh +1 -1
  10. package/assets/hooks/claim-guard.sh +34 -0
  11. package/assets/hooks/dev-server-guard.sh +1 -1
  12. package/assets/hooks/dispatch.sh +48 -15
  13. package/assets/hooks/git-guard-delivery-folder.sh +99 -0
  14. package/assets/hooks/git-guard-delivery-signature.sh +10 -4
  15. package/assets/hooks/git-guard-delivery.sh +21 -69
  16. package/assets/hooks/git-guard-push-tests.sh +2 -2
  17. package/assets/hooks/hook-input.sh +62 -0
  18. package/assets/hooks/override-write-guard.sh +107 -0
  19. package/assets/hooks/rerun-guard.sh +1 -1
  20. package/assets/hooks/rule-source-guard.sh +126 -0
  21. package/assets/hooks/task-flow-guard.sh +18 -0
  22. package/assets/hooks/turn-exit-guard.sh +33 -1
  23. package/assets/hooks/waiting-turn-guard.sh +39 -3
  24. package/assets/patterns/git-workflow-pr.azure.md +4 -4
  25. package/assets/patterns/git-workflow-pr.github.md +4 -4
  26. package/assets/patterns/git-workflow-pr.gitlab.md +4 -4
  27. package/assets/patterns/task-flow-archive.md +23 -21
  28. package/assets/patterns/task-flow-close.md +98 -96
  29. package/assets/patterns/task-flow-resume.md +5 -3
  30. package/assets/pitfalls/agent-kit.md +80 -0
  31. package/assets/pitfalls/git-workflow.github.md +8 -0
  32. package/assets/rules/deploy-flow.azure.md +15 -7
  33. package/assets/rules/deploy-flow.github.md +15 -6
  34. package/assets/rules/deploy-flow.gitlab.md +15 -7
  35. package/assets/rules/git-workflow.github.md +10 -5
  36. package/assets/rules/task-flow.md +44 -26
  37. package/assets/rules/turn-conduct.md +33 -0
  38. package/assets/skills/agent-kit.md +32 -77
  39. package/assets/templates/proposal.md +16 -1
  40. package/lib/proposals.d.ts +5 -1
  41. package/lib/proposals.d.ts.map +1 -1
  42. package/lib/proposals.js +74 -5
  43. package/lib/proposals.js.map +1 -1
  44. package/lib/shipment.d.ts.map +1 -1
  45. package/lib/shipment.fixture.d.ts +39 -0
  46. package/lib/shipment.fixture.d.ts.map +1 -0
  47. package/lib/shipment.fixture.js +99 -0
  48. package/lib/shipment.fixture.js.map +1 -0
  49. package/lib/shipment.js +59 -7
  50. package/lib/shipment.js.map +1 -1
  51. package/package.json +1 -1
  52. package/rt-tools-agent-kit-0.14.0.tgz +0 -0
  53. package/rt-tools-agent-kit-0.12.0.tgz +0 -0
@@ -55,10 +55,10 @@ description: Правило под «Закон о ведении работы»
55
55
  | `задача-взята` | задача в колонке работы, ветка по номеру, папка | написать замысел | `task-flow-start` |
56
56
  | `замысел-записан` | замысел лежит и после записи не правится | делать первый этап | `task-flow-start` |
57
57
  | `этап-идёт` | этап начат | доделать этап и отметить в ходе работы | `task-flow-resume` |
58
- | `этапы-кончились` | все этапы отмечены | прогнать набор и открыть PR черновиком | `task-flow-close` |
58
+ | `этапы-кончились` | все этапы отмечены | прогнать набор, влить договорённость, привести тексты | `task-flow-close` |
59
+ | `разбор-кончился` | набор зелёный, тексты приведены | разобрать папку последним коммитом | `task-flow-archive` |
60
+ | `папка-разобрана` | папки в ветке нет, запись в архиве есть | открыть PR черновиком | `task-flow-close` |
59
61
  | `работа-отдана` | PR открыт черновиком | взять следующую задачу | `task-flow-resume` |
60
- | `разбор-кончился` | прогон зелёный, замечаний нет | влить договорённость, привести тексты, разобрать папку | `task-flow-close` |
61
- | `папка-разобрана` | папки в ветке нет, запись в архиве есть | снять черновик и попросить влить | `task-flow-archive` |
62
62
  | `влито` | PR слит человеком | разбор работы правилами и сверка очереди | `task-flow-archive` |
63
63
 
64
64
  Ни у одного состояния обязательное действие не звучит как «ждать»: ожидание чужого шага
@@ -72,6 +72,12 @@ description: Правило под «Закон о ведении работы»
72
72
  Перечень показывается владельцу в начале работы, и на нём же отмечается, где стоим: иначе после
73
73
  шести вопросов не видно ни того, что будет дальше, ни сколько всего впереди.
74
74
 
75
+ Последние два состояния объявить на диске уже нечем: ход работы уезжает вместе с папкой, а
76
+ папка разбирается раньше, чем открывается PR. Признак у них поэтому в истории ветки — коммит
77
+ разбора папки, — и читает его гард, а не строка в файле. Цена названа прямо: с этой минуты и до
78
+ слияния состояние работы виду не подлежит, и хвост из четырёх шагов — открыть PR, дождаться
79
+ прогона, снять черновик, попросить влить — держится паттерном, а не объявлением.
80
+
75
81
  ## Ход
76
82
 
77
83
  Ход работы от просьбы владельца до закрытия: где стоит разбор, что требует гард и куда девается
@@ -93,16 +99,16 @@ flowchart TD
93
99
  H --> I{Этап сделан}
94
100
  I -->|Да| J[Отметка в ходе работы — единственном месте, где отмечается сделанное]
95
101
  J --> I
96
- I -->|Этапы кончились| K[PR открывается; исполнитель называет номер, чего ждёт и что сделает следом]
102
+ I -->|Этапы кончились| T[Набор прогоняется, договорённость вливается в спек домена, тексты приводятся к сделанному]
103
+ T --> N[Папка задачи разбирается последним коммитом: разбор просьбы — в описание прошлого, находки — к замыслу эпика, замысел — прочь]
104
+ N --> S[Сверка очереди работ]
105
+ S --> K[PR открывается черновиком; исполнитель называет номер, чего ждёт и что сделает следом]
97
106
  K --> W[Разбор закрытой работы правилами уходит в фон, находки ложатся на диск]
98
107
  W --> L[Пока PR ждёт разбора, берётся следующая задача]
99
108
  L --> M{Разбор и прогон кончились}
100
- M -->|Красный прогон или замечания| V[Чинится в той же ветке: замысел на диске ещё нужен]
109
+ M -->|Красный прогон или замечания| V[Чинится в той же ветке: замысла на диске уже нет, и признак работы гард берёт из истории ветки]
101
110
  V --> M
102
- M -->|Зелено и замечаний нет| T[Договорённость вливается в спек домена, тексты приводятся к сделанному]
103
- T --> N[Папка задачи разбирается последним коммитом: разбор просьбы — в описание прошлого, находки — к замыслу эпика, замысел — прочь]
104
- N --> S[Сверка очереди работ]
105
- S --> O[Черновик снимается, слияние нажимает человек]
111
+ M -->|Зелено и замечаний нет| O[Черновик снимается, слияние нажимает человек]
106
112
  ```
107
113
 
108
114
  ## Как закон применяется здесь
@@ -112,9 +118,11 @@ flowchart TD
112
118
  в ходе работы состояние и названную в замысле договорённость о продукте.
113
119
  - **Гард судит объявленный переход, а не наличие файлов.** Артефакт на диске не говорит, дошла
114
120
  ли работа до правки кода: пустой замысел, положенный ради снятия отказа, лежит так же, как
115
- написанный. Отказ снимает объявленное состояние, и снимают его четыре — `этап-идёт`,
116
- `этапы-кончились`, `работа-отдана` и `разбор-кончился`: прогон бывает красным, а разбор — с
117
- замечаниями, и починка идёт в ту же ветку.
121
+ написанный. Отказ снимает объявленное состояние, и снимают его три — `этап-идёт`,
122
+ `этапы-кончились` и `разбор-кончился`. Четвёртый путь у отказа не состояние, а история ветки:
123
+ папка, разобранная её коммитом, означает отданную работу, и правка по замечаниям разбора идёт
124
+ без замысла на диске — объявить состояние после уборки уже нечем. Прогон бывает красным, а
125
+ разбор — с замечаниями, и починка идёт в ту же ветку.
118
126
  - **Отказ по состоянию называет обязательное действие того состояния, которое объявлено.**
119
127
  Исполнитель, которому сказано только «не в том состоянии», перепишет строку состояния вместо
120
128
  того, чтобы сделать шаг.
@@ -136,13 +144,20 @@ flowchart TD
136
144
  дважды стало поводом обойти отказ гарда, вместо того чтобы завести папку и пойти дальше.
137
145
  Заводится она всегда и до первой правки; сколько заходов уйдёт на работу, заранее не знает
138
146
  никто.
147
+ - **Папка задачи разбирается последним коммитом до открытия PR, а не после одобрения.** Прежде
148
+ она стояла после: пока идёт разбор, замысел нужен на диске, иначе правку по замечаниям
149
+ отбивает гард. Но кнопку слияния нажимает человек на хостинге, куда гард не достаёт, и
150
+ вливает он, как только видит зелёное, — закрывающему коммиту места не остаётся вовсе. Трижды
151
+ подряд папка уехала в главную ветку неразобранной, и разобрать её было уже некому: работа
152
+ перешла к следующей задаче, а PR закрылся. Цена перестановки названа прямо: замысла с этой
153
+ минуты на диске нет, и признак отданной работы гард берёт из истории ветки.
139
154
  - **Открыв PR, исполнитель называет владельцу три вещи: номер, чего ждёт и что сделает
140
155
  следом.** Ждёт он прогона — до его конца о работе ничего не известно, кроме того, что она
141
- запушена. Следом идёт уборка: разбор папки задачи последним коммитом. Сказанное так владелец
142
- читает однозначно, а зелёный прогон на странице — нет: он говорит, что не сломано, и молчит
143
- о том, что ветка ждёт ещё одного коммита. Трижды подряд PR был влит внутри этого молчания.
144
- - **Просьба о слиянии — отдельный ход, и раньше уборки её не бывает.** Порядок один: PR открыт
145
- черновиком прогон зелёныйпапка задачи разобрана и запушена → черновик снят →
156
+ запушена. Следом идёт снятие черновика. Сказанное так владелец читает однозначно, а зелёный
157
+ прогон на странице — нет: он говорит, что не сломано, и молчит о том, что кнопка слияния у
158
+ черновика заблокирована.
159
+ - **Просьба о слиянии — отдельный ход, и раньше зелёного прогона её не бывает.** Порядок один:
160
+ папка задачи разобрана и запушена PR открыт черновиком прогон зелёный → черновик снят →
146
161
  исполнитель просит влить, называя номер. До этой просьбы работа не готова, сколько бы зелёного
147
162
  на её странице ни было.
148
163
  - **PR открывается черновиком, а не в конце работы.** Пока правка кода не выложена в PR,
@@ -161,7 +176,9 @@ flowchart TD
161
176
  целиком, поэтому «не читал» основанием не бывает.
162
177
  - **Папка задачи заводится черновиком и получает номер командой.** До конца разбора
163
178
  неизвестно, сколько задач из него выйдет, поэтому номер не может быть первым;
164
- `npm run task:new` переименовывает черновик и проставляет шапку замысла.
179
+ `npm run task:new` переименовывает черновик и проставляет шапку замысла. Папку он и собирает —
180
+ с образца, снимая с копий шапку раскладки: оставленная в копии, она отбивает первую же правку
181
+ разбора просьбы, а отказ уводит править образец пакета вместо копии под задачу.
165
182
  - **Брошенный разбор виден.** Черновик старше недели перечисляет сверка очереди работ —
166
183
  задачи за ним ещё нет, и спросить о нём некого.
167
184
  - **Замысел эпика лежит там, где его найдут без сети и после мержа.** Карточка в очереди работ
@@ -192,21 +209,22 @@ flowchart TD
192
209
  это заготовка правки чужого дерева, и часть заготовок отпадает при первом же чтении; уехавшая
193
210
  без разбора, она становится работой того, кто её не заказывал. Сводка наблюдений уезжает
194
211
  всегда: она говорит, чем пользовались и чем нет, и мнением не является.
195
- - **Договорённость вливается в спек домена последним коммитом PR.** К этому моменту код
196
- написан, привязки известны, и в главной ветке директория `proposed/` не появляется вовсе.
212
+ - **Договорённость вливается в спек домена одним из последних коммитов ветки, до открытия
213
+ PR.** К этому моменту код написан, привязки известны, и в главной ветке директория
214
+ `proposed/` не появляется вовсе.
197
215
  Готовые к вливанию перечисляет `npm run check:specs`.
198
216
  - **Папка закрытой задачи разбирается, а не переносится целиком.** В `docs/archive/` уезжает
199
217
  то, что объясняет состоявшееся решение; остальное удаляется. Неразобранную ловит сверка
200
218
  очереди работ.
201
- - **Слияние отбивается, пока ветка везёт папку своей задачи.** Требование стоит на слиянии, а
202
- не на открытии PR: до слияния папка ещё нужна правка по замечаниям разбора идёт в ту
203
- же ветку, а без замысла на диске её отбивает гард хода работы. На открытии PR о лежащей
204
- папке говорится вслух, и только. Судится содержимое ветки, а не рабочее дерево: снесённая,
205
- но не закоммиченная папка въехала бы вместе с веткой.
219
+ - **Открытие PR отбивается, пока ветка везёт папку своей задачи.** Требование стоит здесь, а
220
+ не на слиянии: слияние нажимает человек на хостинге, где гардов нет вовсе, и его отказ до
221
+ владельца не доходит. На слиянии та же проверка остаётся вторым рубежом она ловит слияние,
222
+ идущее командой. Судится содержимое ветки, а не рабочее дерево: снесённая, но не
223
+ закоммиченная папка въехала бы вместе с веткой.
206
224
  - **Ветка, снёсшая папку, обязана прибавить запись в архив.** Снести дешевле, чем разобрать, и
207
225
  первым уходит разбор просьбы — единственная запись слов владельца. Что именно увезено,
208
226
  требование не судит: это судит владелец.
209
- - **Обход — строка `Task-folder-skip: <причина>` в PR или в самой команде слияния.**
227
+ - **Обход — строка `Task-folder-skip: <причина>` в PR или в самой команде.**
210
228
  Работа, вливаемая частями, папку до конца не разбирает. Чтение из команды работает и без
211
229
  сети: единственный сетевой путь отбивал бы оффлайн то самое слияние, причина которого
212
230
  написана в PR. Пустая причина обходом не считается, а сам обход снимает отказ, но не
@@ -101,6 +101,13 @@ flowchart TD
101
101
  работы и то, что за ход по ней сделано: правку файла или команду, меняющую дерево. Ход, в
102
102
  котором не было ни того ни другого, возвращается исполнителю вместе со следующим шагом из
103
103
  хода работы. Отданную и влитую работу страж не судит: она уже дождалась чужого шага.
104
+ - **Снятая папка задачи снимает требование состояния, а ход не кончает.** Ход работы уезжает
105
+ вместе с папкой, а папка разбирается до открытия заявки: с этой минуты и до слияния строки
106
+ состояния нет вовсе, и первый признак стражу взять неоткуда. Отпускать по этому признаку ход
107
+ нельзя — снятая папка означает середину отдачи, а не её конец: между уборкой и заявкой работу
108
+ не видит никто, кроме того, кто её сделал. Дальше ход судится вторым признаком, как всякий
109
+ другой; ход, в котором заявку открыли или прочитали, второй признак пропускает сам. Прежде
110
+ страж выходил здесь молча — и ход, которому до отдачи оставался один шаг, закрывался пустым.
104
111
  - **Работа без ветки и без папки задачи судится тем же стражем по второму признаку.** Состояния
105
112
  у неё нет, и первый признак взять неоткуда, — но ход, в котором не было ни одной правки
106
113
  дерева, не кончается и здесь. Раньше страж отпускал такую работу молча, и защищена она была
@@ -125,6 +132,13 @@ flowchart TD
125
132
  годится: состояние дерева меняется, и вчерашний вывод о нынешнем молчит. Восемь разборов
126
133
  подряд пришлись на этот промах, и каждый раз в правило дописывалась ещё одна статья —
127
134
  держит его теперь машина.
135
+ - **Гард утверждения ждёт текст ответа, а не судит запись, какой застал.** Текст ложится в
136
+ запись хода не раньше, чем хост зовёт хук, и гард, прочитавший файл первым, видит ход, в
137
+ котором владельцу не сказано ничего. Молчит он при этом честно — и снаружи неотличим от гарда,
138
+ который посмотрел и пропустил: одним таким ходом мимо прошли сразу трое. Не дождавшись текста,
139
+ гард возвращает ход: пустая запись означает не «сказать было нечего», а «прочитать нечего».
140
+ Отказ этот принадлежит одному гарду: печатай своё решение все трое, вывод перестал бы
141
+ разбираться целиком.
128
142
  - **Слово-утверждение гард ловит, неверный вывод — нет.** Об образце, судимом по одному его
129
143
  файлу, и о пути, которым человек не пойдёт, судить нечем: там нет ни слова, ни команды, с
130
144
  которой сверять. Это известная граница гарда, и держат её статьи ниже, а не он.
@@ -136,6 +150,17 @@ flowchart TD
136
150
  и отбивал бы работу вместо промаха, а вывод команды о прогоне в записи хода уже лежит. Слова
137
151
  «беру следующую задачу» гард действием не считает — ровно потому, что их и произносят вместо
138
152
  неё.
153
+ - **Ход, отдавший работу, доводит её до снятого черновика.** Черновик читается владельцем как
154
+ «работа не кончена»: кнопка слияния у него заблокирована самим хостингом, а по списку заявок
155
+ готовое от недоделанного не отличить — серое и там и там. Следующая задача берётся сверх этого,
156
+ а не вместо: требование взять её исполняется буквально и оставляет отданное невидимым. Две
157
+ готовые заявки простояли так, пока владелец не вернул исполнителя сам. Стережёт это гард
158
+ ожидания: ход, открывший заявку, не кончается, пока состояние отданной работы не спрошено
159
+ командой того же хода.
160
+ - **«Жду прогона» — утверждение о чужом шаге, а не состояние работы.** Прогон бывает зелёным
161
+ час, а бывает не встав вовсе — и второе само не чинится. Слово это требует команды того же
162
+ хода, которая прогон показывает, и без неё не говорится: сказанное без команды владелец читает
163
+ как «работа ещё идёт» и ждёт напрасно. Держит это гард утверждения, а не память исполнителя.
139
164
  - **Конец прогона узнаётся возвратом фоновой команды, а не взглядом на страницу.** Ожидание,
140
165
  запущенное в фоне отдельным ходом, возвращает исполнителя к PR само; до тех пор ход занят
141
166
  следующей задачей. Взгляд на страницу этого не даёт: он либо повторяется вхолостую, либо не
@@ -164,6 +189,14 @@ flowchart TD
164
189
  читается как работа — тем полнее, чем аккуратнее он составлен: он пронумерован, в нём названы
165
190
  цифры, и именно поэтому пустота хода за ним не видна. Владельцу называется, что уже сделано и
166
191
  что осталось за его словом, — а не выбор из вариантов вместо и того и другого.
192
+ - **Признак необратимости берётся из списка, а не выводится доводом.** Список составлен тем, кто
193
+ обратимость уже взвесил: необратимое в нём осталось, обратимое из него снято. Довод «действие
194
+ уходит наружу и не откатывается» приходит в контекст всегда, а список — только когда его
195
+ прочитали, и применённый поверх списка довод отменяет список целиком. Отменяет молча: со
196
+ стороны это выглядит осторожностью, а не пропуском шага. Действия, которого в списке нет,
197
+ исполнитель на слово владельца не гейтит, даже если оно уходит наружу. Шаг, снятый из списка
198
+ разбором прошлого промаха, тем более: довод, которым его туда возвращают, уже разобран и
199
+ отклонён — и дважды подряд готовая работа вставала перед снятым шагом.
167
200
  - **Ход, в котором исполнитель признал промах, не заканчивается, пока записи о происшествии
168
201
  нет.** Отбивает гард происшествия — на завершении хода: к моменту признания промах уже
169
202
  случился, и ловить раньше нечего. Признание ловится набором образцов, а не пониманием смысла;
@@ -9,6 +9,9 @@ description: Переносимый слой правил агента — за
9
9
  настраивает надстройками. Разложенный файл несёт шапку и правке не подлежит — правится либо
10
10
  пакет, либо надстройка.
11
11
 
12
+ **Холодная часть:** `pitfalls.md` рядом — ловушки, грабли, на которые уже наступали. Грузится по
13
+ требованию, а не вместе со скилом.
14
+
12
15
  ## Когда брать
13
16
 
14
17
  - В файле, который собираешься править, стоит шапка
@@ -44,6 +47,20 @@ description: Переносимый слой правил агента — за
44
47
  разложенного хуже целого. Правится либо надстройка здесь, либо сам ресурс — а это работа того
45
48
  дерева, где пакет живёт исходниками, и здесь о ней не сказано ничего.
46
49
 
50
+ **Держит это гард места правки, а не память.** Правку файла с шапкой раскладки он отбивает в
51
+ минуту правки и называет адрес: источник, если дерево его держит, иначе надстройку. Прежде об
52
+ этом знал один `sync --check`, и говорил он на следующей раскладке — то есть в чужой ветке и
53
+ чужим ходом: отказ приходил тому, кто в этот день правил соседний ресурс. Правит копию не
54
+ только рука: форматтер дерева, дошедший до разложенного файла, переписывает его по-своему, и
55
+ раскладка читает это ровно как правку руками. Снятие копии гард пропускает: снятый файл
56
+ раскладка кладёт заново, и так эту поломку и чинят.
57
+
58
+ **Надстройка правится по разделу, а не кладётся целиком.** Разделы в неё дописывают разные ветки
59
+ и разные заходы, и положенная целиком она уносит все, которых эта правка не касалась: на их место
60
+ молча возвращается пакетный текст, раскладка после этого сходится, а сказанного деревом о себе
61
+ больше нет. Держит это второй гард — он отбивает запись поверх непустой надстройки и называет
62
+ размер того, что затрут: строки и число разделов. Дописывание в конец и правка по месту проходят.
63
+
47
64
  ## Команды
48
65
 
49
66
  ```bash
@@ -103,13 +120,24 @@ npx agent-kit propose # отправить груз в приём: сво
103
120
  сам агент: до неё такое слово адреса не получало вовсе и умирало вместе с сессией. Оба пути
104
121
  пишут в один файл дня и в одной форме; в сеть не ходит ни один — увозит их отправка.
105
122
 
106
- Каждая запись несёт три строки: **место** — куда правка встаёт в ресурсе, **повод** — что пошло
107
- не так без неё, и **чем закрывается** какие надстройки и добавки этого дерева снимаются, когда
108
- правка приедет редакцией пакета. Третья пишется затем, что предложение уезжает наружу, а
123
+ Каждая запись несёт четыре строки: **место** — куда правка встаёт в ресурсе, **повод** — что
124
+ пошло не так без неё, **ближайшее** точная цитата той строки ресурса, к которой это ближе
125
+ всего, и чего она не покрывает, и **чем закрывается** какие надстройки и добавки этого дерева
126
+ снимаются, когда правка приедет редакцией пакета.
127
+
128
+ Третья строка — единственная, которую проверяет машина: цитата ищется в ресурсе, и ненайденная
129
+ отбивает блок. Ближайшего нет вовсе — так и пишется: «нет». Стоит она затем, чтобы ресурс был
130
+ прочитан: разбор происшествия кончается предложением дописать статью в тот же ресурс, который
131
+ промах уже описывал, и вторая статья о том же дороже, чем её отсутствие. Похожесть текстов сюда
132
+ не годится — замер показал, что законное соседство двух статей одного правила и законный перенос
133
+ удачной статьи на соседнее место дают одно и то же число. Отбитый блок остаётся лежать с
134
+ отметкой «отбито» и причиной: видно, что разбор был, и видно, почему он не стал правкой.
135
+
136
+ Четвёртая пишется затем, что предложение уезжает наружу, а
109
137
  надстройка остаётся лежать здесь: без неё дерево не может сказать, какие из его надстроек
110
138
  исправленная редакция закрыла, — снять наугад страшно, оставить дёшево, и надстройка остаётся
111
139
  навсегда, молча замещая исправленный раздел пакета. Снимать нечего — так и пишется; пустой
112
- третья строка не бывает.
140
+ четвёртая строка не бывает.
113
141
 
114
142
  Каждому предложению ставится адрес: «пакет», «компаньон» или «дерево». Выгружаются они файлом в `.claude/rt-kit/proposals/`
115
143
  (форма — шаблон `proposal.md`), а `agent-kit propose` увозит в приём те, что адресованы
@@ -205,76 +233,3 @@ npx agent-kit propose # отправить груз в приём: сво
205
233
  - **Выключение живёт в настройке дерева, а не в настройке агента.** Строка, вырезанная из
206
234
  настроек агента руками, теряется на первой же правке того файла, и вернуть роль будет нечем.
207
235
  Здесь оно объявлено в одном месте и видно всякому, кто читает настройку дерева.
208
-
209
- ## Ловушки
210
-
211
- - **Набор, проверяющий гард, читает настройку того дерева, из которого его запустили.** Помощник
212
- берёт настройку по каталогу проекта, а его агент выставляет своим — и `cd` в прогонщике этого
213
- не перебивает. Набор, не заведший своего одноразового дерева, зеленеет от строки в чужой
214
- настройке: шесть сценариев гарда экзамена прошли ровно так, потому что роль была выключена в
215
- дереве, где их гоняли. Своё дерево объявляется на каждый сценарий, а не один раз на файл.
216
-
217
- - **Надстройка замещает раздел целиком, и пакетные пункты в нём приходится держать копией.**
218
- Дописать в раздел одну статью нечем: слияние идёт по заголовку `## `. Дерево, которому нужен
219
- один свой пункт, копирует к нему все пакетные — и с этого дня правка любого из них,
220
- приехавшая с новой версией, до этого дерева не доходит. Сверка разложенного молчит: она
221
- считает расхождением правку на месте, а не замещённый раздел. Признак виден по самим
222
- надстройкам — три из трёх прочитанных кончались абзацем о том, что перенос придётся делать
223
- руками. Поэтому надстройкой берут раздел, у которого пакетных пунктов немного, а разросшийся
224
- замещённый раздел — повод внести своё в пакет, а не держать его копией. Предложение, которым
225
- своё вносят, называет эту надстройку строкой «чем закрывается»: иначе приехавшая редакция и
226
- надстройка, которую она закрыла, не сопоставляются ничем, и надстройка остаётся замещать уже
227
- исправленный раздел.
228
-
229
- - **Готовый код пакета не называет имён одного дерева.** Префикс директив кита, ключи подписей
230
- и имена сущностей принадлежат тому дереву, где паттерн писали; разложенные в соседнем, они
231
- учат звать то, чего там нет вовсе. Имя директивы при этом отличается от имени в примере: по
232
- примеру видно, что он пример, а `<префикс>TableRow` из чужого дерева выглядит рабочим кодом
233
- и правится только после того, как продовая сборка упадёт. Тринадцать таких имён простояли в
234
- четырёх ресурсах пакета, пока их не нашёл потребитель — и не своей сборкой, а надстройкой,
235
- которой перекрыл раздел.
236
-
237
- - **Правила и паттерны при отвергнутом законе в отказе не перечисляются.** Их снимает каскад, а
238
- строка на них становится выводимой: раскладка называет её лишней вместе с законом, из-за
239
- которого она перестала снимать. Отказ мерит слой законов, а не число файлов в пакете.
240
-
241
- - **Линтер по следам правки судит файл целиком, а не внесённую правку.** Импорт, добавленный
242
- отдельным шагом, отбивается как неиспользуемый ещё до того, как появится строка, которая его
243
- зовёт, и работа встаёт на половине. Правка делается одним вызовом либо в порядке «сначала
244
- использование, потом импорт».
245
- - **Прогонять сценарии гардов можно, ничего не раскладывая.** Обвязка набора принимает каталог
246
- гардов переменной, и пакетную редакцию гоняют по сценариям дерева до установки. Заход,
247
- потраченный на диагноз по последствиям, стоил ровно этой строки.
248
- - **Отбитая правка не всегда про текст гарда.** Гард зовут по пути, и файл без права на
249
- запуск отвечает отказом доступа — ненулевым кодом, который читается как «правка отбита».
250
- Набор при этом отбивает подряд всё, включая сборку и тесты, и причины не называет.
251
- - **Правка shell-скрипта заменой по шаблону сверяется `bash -n` сразу.** Замена границ
252
- конструкции не видит: `case` теряет свою `esac`, файл остаётся синтаксически неверным, а
253
- гард с ошибкой синтаксиса отвечает ненулевым кодом — то есть «правка отбита». Два раза за
254
- заход, и оба раза это выглядело дефектом самого гарда.
255
- - **Гард, подписанный не на то, что объявляет, выглядит работающим.** Событие и образец вызова
256
- гард несёт сам, строкой `# rt-hook:`, а зовёт его образец в настройке агента — и эти двое
257
- расходятся молча: путь гарда в настройке назван, файл разложен, набор сценариев зелёный.
258
- Набор тут ничего не ловит намеренно — он зовёт гард напрямую с подставленным вводом и
259
- объявления не читает вовсе. Так гейт правил и разбирал вызовы браузера веткой, которая не
260
- исполнялась ни разу. Расхождение находит сверка раскладки: она сравнивает объявленный образец
261
- с тем, под которым гард стоит, называет обе стороны и идёт в счёт расхождений. Правится
262
- настройка, а верное значение лежит в гарде — тело его разбирает то, что объявлено.
263
- - **Разложенный файл узнаётся по шапке, а не по каталогу.** Раскладка ложится в те же
264
- `tools/`, `.claude/hooks/` и `.claude/skills/`, где лежит своё, поэтому карта гейта,
265
- написанная по путям, требует под него доменное правило — а оно уводит править файл на месте.
266
- Правка на месте теряется на следующей раскладке, и до тех пор выглядит применённой. Ветка по
267
- шапке ставится в карте первой и решает раньше путей.
268
- - **Настройки проверок сливаются по ключам, а списки — замещаются.** Объект `checks.json`
269
- ложится поверх умолчания ключ за ключом на любой глубине: назвав один ключ борды, дерево не
270
- теряет соседних. Список приходит целиком — назвав корни исходников, дерево получает ровно
271
- названное, а не умолчание вместе со своим: «дописать в список» и «убрать из списка» в этой
272
- записи неразличимы.
273
- - **Утверждение правила переезжает вместе с кодом.** Вынесенное в надстройку перестаёт
274
- находиться по прежнему символу, и привязка в спутнике правила врёт молча — сверка спеков
275
- ловит это, но только если её позвать.
276
- - **Признак по подстроке пути судит и то, что лежит вне дерева.** Гарды отдают в профиль
277
- абсолютный путь целиком, и образец вида `*/projects/*` совпадает с домашним каталогом агента
278
- ровно так же, как с кодом дерева: запись в файл вне репозитория была отбита гардом хода
279
- работы с требованием замысла, к ней не относящегося. Путь в профиле сначала приводится к
280
- корню дерева, и всё, что вне корня, признака не получает.
@@ -22,13 +22,26 @@
22
22
  имени: файл уезжает в чужой репозиторий целиком. Найденный адрес дерева отбивает отправку с
23
23
  номером строки — это проверка, а не напоминание.
24
24
 
25
- Строк при заголовке три, и все три обязательны:
25
+ Строк при заголовке четыре, и все четыре обязательны:
26
26
 
27
27
  место — куда правка встаёт в ресурсе
28
28
  повод — что пошло не так без неё
29
+ ближайшее — точная цитата той строки ресурса, к которой это ближе всего, и чего она не
30
+ покрывает; ближайшего нет вовсе — так и пишется: «нет»
29
31
  чем закрывается — какие надстройки и добавки этого дерева снимаются, когда правка приедет
30
32
  редакцией пакета
31
33
 
34
+ Третья строка — единственная, которую машина проверяет: цитата ищется в ресурсе, и ненайденная
35
+ отбивает блок. Написана она затем, чтобы ресурс был прочитан. Разбор происшествия кончается
36
+ предложением дописать статью в тот же ресурс, который промах уже описывал, — и снаружи такой
37
+ разбор неотличим от разбора, кончившегося исправлением. Мерить похожесть текстов пробовали
38
+ замером: законное соседство двух статей одного правила даёт 0.345 общих значимых слов, а
39
+ законный перенос удачной статьи на соседнее место — 0.355, и порога между ними нет. Названная
40
+ цитата судится фактом: она в ресурсе либо есть, либо её там нет.
41
+
42
+ Отбитый блок не пропадает: он остаётся лежать с отметкой «отбито» и причиной. Видно, что разбор
43
+ был, и видно, почему он не стал правкой.
44
+
32
45
  Третья пишется затем, что предложение уезжает наружу, а надстройка, ради которой оно написано,
33
46
  остаётся лежать в дереве, и связи между ними нет никакой. Пакет выпускает исправленную редакцию
34
47
  — и сказать, какие надстройки она закрыла, дереву нечем: имя ресурса совпадает у десятка
@@ -45,6 +58,7 @@
45
58
 
46
59
  - **место:** раздел «<заголовок>», в конец
47
60
  - **повод:** что в этой задаче пошло не так без этого правила
61
+ - **ближайшее:** «<точная строка правила, к которой это ближе всего>» — <чего она не покрывает>
48
62
  - **чем закрывается:** `.claude/rt-kit/overrides/rules/<правило>.md`, раздел «<заголовок>» —
49
63
  снимается целиком, когда правка приедет редакцией пакета
50
64
 
@@ -55,6 +69,7 @@
55
69
 
56
70
  - **место:** ветка `edit`, рядом с соседним родом файлов
57
71
  - **повод:** свой род файлов, которого у других деревьев нет
72
+ - **ближайшее:** нет — про этот род файлов карта не говорит вовсе
58
73
  - **чем закрывается:** ничего, надстройки под это нет — правка и есть надстройка
59
74
 
60
75
  > Готовый текст правки.
@@ -11,6 +11,8 @@ export interface IProposal {
11
11
  readonly body: string;
12
12
  /** Ссылка на заведённую запись; пусто — не отправлялось. */
13
13
  readonly sent: string;
14
+ /** Поле ближайшего утверждения, как его написали; пусто — поля нет вовсе. */
15
+ readonly nearest: string;
14
16
  /** Файл, в котором блок лежит, путём от корня дерева. */
15
17
  readonly file: string;
16
18
  /** Строка заголовка в файле, считая с единицы. */
@@ -43,6 +45,8 @@ export declare function leaksIn(text: string, marks: readonly string[]): readonl
43
45
  * встречается чаще, чем путь целиком.
44
46
  */
45
47
  export declare function marksOf(root: string, remote: string): readonly string[];
48
+ /** Почему блок не уезжает; пустая строка — уезжает. */
49
+ export declare function nearestMissing(proposal: IProposal, assetsDir: string): string;
46
50
  /**
47
51
  * Пометка об отправке дописывается в блок, а не в конец файла: блоков в файле несколько.
48
52
  *
@@ -52,5 +56,5 @@ export declare function marksOf(root: string, remote: string): readonly string[]
52
56
  * пятая — на четыре, то есть посреди готового текста правки, а последнему блоку её не достаётся
53
57
  * вовсе, и при следующей отправке он уезжает вторым разом.
54
58
  */
55
- export declare function markSent(text: string, proposal: IProposal, url: string): string;
59
+ export declare function markSent(text: string, proposal: IProposal, url: string, field?: string): string;
56
60
  //# sourceMappingURL=proposals.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"proposals.d.ts","sourceRoot":"","sources":["../../../projects/agent-kit/src/lib/proposals.ts"],"names":[],"mappings":"AAeA,mEAAmE;AACnE,eAAO,MAAM,aAAa,EAAE,MAAmC,CAAC;AAEhE,yEAAyE;AACzE,eAAO,MAAM,UAAU,EAAE,MAAgB,CAAC;AAC1C,eAAO,MAAM,YAAY,EAAE,MAAoB,CAAC;AAChD,eAAO,MAAM,OAAO,EAAE,MAAiB,CAAC;AAqCxC,MAAM,WAAW,SAAS;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,8DAA8D;IAC9D,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,4DAA4D;IAC5D,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,yDAAyD;IACzD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,kDAAkD;IAClD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACzB;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,SAAS,EAAE,CAgC/E;AAED,oDAAoD;AACpD,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,SAAS,EAAE,CAUhE;AAED,MAAM,WAAW,KAAK;IAClB,mDAAmD;IACnD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;CACxB;AAKD;;;;;GAKG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,KAAK,EAAE,CAgBhF;AAED;;;GAGG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAIvE;AAED;;;;;;;;GAQG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAQ/E"}
1
+ {"version":3,"file":"proposals.d.ts","sourceRoot":"","sources":["../../../projects/agent-kit/src/lib/proposals.ts"],"names":[],"mappings":"AAeA,mEAAmE;AACnE,eAAO,MAAM,aAAa,EAAE,MAAmC,CAAC;AAEhE,yEAAyE;AACzE,eAAO,MAAM,UAAU,EAAE,MAAgB,CAAC;AAC1C,eAAO,MAAM,YAAY,EAAE,MAAoB,CAAC;AAChD,eAAO,MAAM,OAAO,EAAE,MAAiB,CAAC;AA2ExC,MAAM,WAAW,SAAS;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,8DAA8D;IAC9D,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,4DAA4D;IAC5D,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,6EAA6E;IAC7E,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,yDAAyD;IACzD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,kDAAkD;IAClD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACzB;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,SAAS,EAAE,CAwC/E;AAED,oDAAoD;AACpD,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,SAAS,EAAE,CAUhE;AAED,MAAM,WAAW,KAAK;IAClB,mDAAmD;IACnD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;CACxB;AAKD;;;;;GAKG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,KAAK,EAAE,CAgBhF;AAED;;;GAGG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAIvE;AAOD,uDAAuD;AACvD,wBAAgB,cAAc,CAAC,QAAQ,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAuB7E;AAED;;;;;;;;GAQG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,GAAE,MAAqB,GAAG,MAAM,CAU7G"}
package/lib/proposals.js CHANGED
@@ -39,7 +39,40 @@ function headingOf(line) {
39
39
  return address && !address.includes(' ') && resource ? { address, resource } : null;
40
40
  }
41
41
  /** Пометка об отправке. По ней же предложение узнаётся отправленным. */
42
- const SENT = /^-\s+\*\*отправлено:\*\*\s*(\S+)/m;
42
+ const SENT = /^- +\*\*отправлено:\*\* *(\S+)/m;
43
+ /**
44
+ * Поле ближайшего утверждения: точная цитата строки ресурса, к которой блок относится, — либо
45
+ * слово о том, что ближайшего нет вовсе.
46
+ *
47
+ * Поле обязательно, и обязательно оно затем, чтобы ресурс был прочитан. Мерить похожесть текстов
48
+ * пробовали замером: законное соседство двух статей одного правила дало 0.345 общих значимых
49
+ * слов, а законный перенос удачной статьи на соседнее место — 0.355. Порога между ними нет, и
50
+ * назначенный наугад он отбивал бы правки вместо повторов. Названная цитата судится фактом: она
51
+ * в ресурсе либо есть, либо её там нет.
52
+ */
53
+ const NEAREST = /^- +\*\*ближайшее:\*\* *([^\n]+)/m;
54
+ /**
55
+ * Слово о том, что ближайшего утверждения в ресурсе нет: цитировать нечего.
56
+ *
57
+ * Конец слова здесь проверяется отрицательным просмотром, а не границей слова: границу движок
58
+ * считает по латинице и цифрам, и между «т» и пробелом её нет вовсе — образец с `\b` не совпал
59
+ * бы ни с одним русским словом.
60
+ */
61
+ const NOTHING_NEAR = /^нет(?![а-яё])/i;
62
+ /**
63
+ * Цитата ближайшего утверждения: кавычки-ёлочки, как в остальных текстах дерева.
64
+ *
65
+ * Ищется позициями, а не образцом: образец на «открыть, набрать не-закрывающих, закрыть» линтер
66
+ * отбивает как ветвящийся, и поводы у него есть — строка без закрывающей кавычки перебирается им
67
+ * до конца.
68
+ */
69
+ function quotedIn(text) {
70
+ const from = text.indexOf('«');
71
+ const to = from < 0 ? -1 : text.indexOf('»', from + 1);
72
+ return from >= 0 && to > from ? text.slice(from + 1, to) : '';
73
+ }
74
+ /** Пометка любого рода: по ней считается сдвиг, когда в файл вставляют ещё одну. */
75
+ const MARKED = /^- +\*\*(отправлено|отбито):\*\*/m;
43
76
  /**
44
77
  * Блоки файла предложений.
45
78
  *
@@ -61,7 +94,15 @@ export function parseProposals(text, file) {
61
94
  .trim();
62
95
  const { address, resource } = heading;
63
96
  if (ADDRESSES.includes(address) && !resource.includes('<')) {
64
- found.push({ address, resource, body, file, sent: SENT.exec(body)?.[1] ?? '', line: at + 1 });
97
+ found.push({
98
+ address,
99
+ resource,
100
+ body,
101
+ file,
102
+ sent: SENT.exec(body)?.[1] ?? '',
103
+ nearest: (NEAREST.exec(body)?.[1] ?? '').trim(),
104
+ line: at + 1,
105
+ });
65
106
  }
66
107
  at = -1;
67
108
  };
@@ -117,6 +158,32 @@ export function marksOf(root, remote) {
117
158
  const name = basename(root);
118
159
  return [root, name, remote].filter(Boolean);
119
160
  }
161
+ /** Один пробел вместо любой пробельной вереницы: цитата в блоке перенесена по своей ширине. */
162
+ function flat(text) {
163
+ return text.replace(/\s+/g, ' ').trim();
164
+ }
165
+ /** Почему блок не уезжает; пустая строка — уезжает. */
166
+ export function nearestMissing(proposal, assetsDir) {
167
+ if (!proposal.nearest) {
168
+ return 'поля «ближайшее» нет: назови точную строку ресурса, к которой это относится, и то, чего она не покрывает, — либо напиши «нет»';
169
+ }
170
+ if (NOTHING_NEAR.test(proposal.nearest)) {
171
+ return '';
172
+ }
173
+ const quoted = quotedIn(proposal.nearest);
174
+ if (!quoted) {
175
+ return 'ближайшее названо без цитаты: строка ресурса приводится в кавычках-ёлочках дословно — иначе проверить нечего';
176
+ }
177
+ const path = join(assetsDir, proposal.resource);
178
+ if (!existsSync(path)) {
179
+ // Ресурса у этого дерева нет — сверять не с чем, и отбивать нечего: блок про ресурс,
180
+ // которого пакет здесь не держит, судится на приёмной стороне.
181
+ return '';
182
+ }
183
+ return flat(readFileSync(path, 'utf8')).includes(flat(quoted))
184
+ ? ''
185
+ : `цитаты нет в «${proposal.resource}»: ${flat(quoted).slice(0, 60)}… — либо ресурс не читали, либо утверждение переписано с тех пор`;
186
+ }
120
187
  /**
121
188
  * Пометка об отправке дописывается в блок, а не в конец файла: блоков в файле несколько.
122
189
  *
@@ -126,12 +193,14 @@ export function marksOf(root, remote) {
126
193
  * пятая — на четыре, то есть посреди готового текста правки, а последнему блоку её не достаётся
127
194
  * вовсе, и при следующей отправке он уезжает вторым разом.
128
195
  */
129
- export function markSent(text, proposal, url) {
196
+ export function markSent(text, proposal, url, field = 'отправлено') {
130
197
  const lines = text.split('\n');
131
- const shift = lines.slice(0, proposal.line).filter((line) => SENT.test(line)).length;
198
+ // Сдвиг считает пометки обоих родов: отбитый блок получает свою, и следующая за ним встала бы
199
+ // строкой выше своего места, если бы её не посчитали.
200
+ const shift = lines.slice(0, proposal.line).filter((line) => MARKED.test(line)).length;
132
201
  // Пометка встаёт сразу под заголовок: конец блока определяется следующим заголовком, а его
133
202
  // может и не быть — тогда «конец» пришлось бы искать по пустым строкам в хвосте файла.
134
- lines.splice(proposal.line + shift, 0, `- **отправлено:** ${url}`);
203
+ lines.splice(proposal.line + shift, 0, `- **${field}:** ${url}`);
135
204
  return lines.join('\n');
136
205
  }
137
206
  //# sourceMappingURL=proposals.js.map