@rt-tools/agent-kit 0.21.0 → 0.23.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.
- package/assets/checks/board-long-work.github.mjs +101 -0
- package/assets/checks/board-runs.github.mjs +34 -0
- package/assets/checks/check-board.github.mjs +16 -2
- package/assets/checks/check-reuse.mjs +4 -1
- package/assets/checks/check-schema-drift.mjs +65 -6
- package/assets/checks/rt-kit-checks.config.mjs +13 -0
- package/assets/checks/signals.mjs +41 -1
- package/assets/commands/next-session.md +16 -5
- package/assets/defaults/project.sh +16 -0
- package/assets/hooks/browser-guard-no-asking.sh +5 -1
- package/assets/hooks/claim-guard.sh +1 -0
- package/assets/hooks/conscience-guard.sh +1 -0
- package/assets/hooks/git-guard-delivery.sh +15 -11
- package/assets/hooks/git-guard-push-tests.sh +22 -0
- package/assets/hooks/glossary-load.sh +23 -2
- package/assets/hooks/grill-gate.sh +64 -0
- package/assets/hooks/hook-input.sh +1 -0
- package/assets/hooks/postmortem-guard.sh +1 -0
- package/assets/hooks/proposal-guard.sh +8 -0
- package/assets/hooks/turn-exit-guard.sh +1 -0
- package/assets/hooks/waiting-turn-guard.sh +1 -0
- package/assets/hooks/work-start-guard.sh +2 -0
- package/assets/laws/autonomous-work.md +30 -0
- package/assets/laws/frontend-application.md +10 -0
- package/assets/laws/project-documentation.md +8 -0
- package/assets/laws/work-conduct.md +3 -0
- package/assets/patterns/autonomous-work-run.md +105 -0
- package/assets/patterns/browser-verification-stand.md +1 -1
- package/assets/patterns/doc-style-write.md +16 -0
- package/assets/patterns/git-workflow-pr-ready.md +93 -0
- package/assets/patterns/git-workflow-pr.azure.md +1 -1
- package/assets/patterns/git-workflow-pr.github.md +1 -1
- package/assets/patterns/git-workflow-pr.gitlab.md +1 -1
- package/assets/patterns/task-flow-start.md +47 -47
- package/assets/patterns/ts-procedure.md +3 -2
- package/assets/rules/autonomous-work.md +92 -0
- package/assets/rules/browser-verification.md +6 -0
- package/assets/rules/deploy-flow.azure.md +7 -0
- package/assets/rules/deploy-flow.github.md +7 -0
- package/assets/rules/deploy-flow.gitlab.md +7 -0
- package/assets/rules/doc-style.md +18 -0
- package/assets/rules/git-workflow.azure.md +13 -0
- package/assets/rules/git-workflow.github.md +59 -57
- package/assets/rules/git-workflow.gitlab.md +13 -0
- package/assets/rules/reuse-first.md +17 -3
- package/assets/rules/task-flow.md +62 -63
- package/assets/skills/agent-kit.md +20 -20
- package/lib/commands.d.ts.map +1 -1
- package/lib/commands.js +6 -0
- package/lib/commands.js.map +1 -1
- package/lib/push-gate.d.ts +14 -0
- package/lib/push-gate.d.ts.map +1 -0
- package/lib/push-gate.js +93 -0
- package/lib/push-gate.js.map +1 -0
- package/package.json +1 -1
- package/rt-tools-agent-kit-0.23.0.tgz +0 -0
- package/assets/laws/application/access.md +0 -34
- package/assets/laws/application/locales.md +0 -33
- package/assets/laws/application/search-visibility.md +0 -24
- package/assets/patterns/permissions-procedure.md +0 -71
- package/assets/patterns/seo-page.md +0 -104
- package/assets/patterns/seo-verify.md +0 -83
- package/assets/patterns/translations-content.md +0 -107
- package/assets/patterns/translations-key.md +0 -64
- package/assets/rules/permissions.md +0 -116
- package/assets/rules/seo.md +0 -139
- package/assets/rules/translations.md +0 -96
- package/rt-tools-agent-kit-0.21.0.tgz +0 -0
|
@@ -101,6 +101,7 @@ if command -v skill_for >/dev/null 2>&1 && [ -n "$rules_dir" ]; then
|
|
|
101
101
|
edited="$(tail -n 400 "$transcript" 2>/dev/null | jq -s -r '
|
|
102
102
|
def is_input:
|
|
103
103
|
.type == "user"
|
|
104
|
+
and ((.isCompactSummary // false) | not)
|
|
104
105
|
and (((.message.content // []) | if type == "array"
|
|
105
106
|
then ([.[] | select(.type == "tool_result")] | length)
|
|
106
107
|
else 0 end) == 0);
|
|
@@ -133,6 +134,7 @@ fi
|
|
|
133
134
|
verdict="$(tail -n 400 "$transcript" 2>/dev/null | jq -s -r --arg re "$read_re" --arg tool "$tool" --arg need "$need_re" --arg rules "$rules_dir" '
|
|
134
135
|
def is_input:
|
|
135
136
|
.type == "user"
|
|
137
|
+
and ((.isCompactSummary // false) | not)
|
|
136
138
|
and (((.message.content // []) | if type == "array"
|
|
137
139
|
then ([.[] | select(.type == "tool_result")] | length)
|
|
138
140
|
else 0 end) == 0);
|
|
@@ -163,6 +165,68 @@ verdict="$(tail -n 400 "$transcript" 2>/dev/null | jq -s -r --arg re "$read_re"
|
|
|
163
165
|
| if ($asked_prose or $asking_now) and ($read | not) then "ask" else "pass" end
|
|
164
166
|
' 2>/dev/null)"
|
|
165
167
|
|
|
168
|
+
# Второй признак того же гарда: на этот вопрос владелец уже отвечал.
|
|
169
|
+
#
|
|
170
|
+
# Первый признак судит, читались ли правила, и на разрешённой работе молчит. А промах бывает
|
|
171
|
+
# другой: владелец дал указание прямой репликой, исполнитель нашёл факт, который меняет цену
|
|
172
|
+
# указания, но не его смысл, — и вместо строки о цене задал меню, где два варианта из трёх
|
|
173
|
+
# предлагали отменить решение владельца. Работа встала до ответа, разрешённая минутой раньше.
|
|
174
|
+
#
|
|
175
|
+
# Судится пересечение слов: вопрос, который сейчас уходит, против последней реплики владельца —
|
|
176
|
+
# и только там, где в записи уже был вызов инструмента вопроса, то есть владелец на вопрос
|
|
177
|
+
# отвечал. Понимания текста здесь нет и не нужно: три общих значимых слова означают тот же
|
|
178
|
+
# предмет, а разбор из шести вопросов идёт по разным предметам и порога не набирает.
|
|
179
|
+
#
|
|
180
|
+
# ОТКАЗ В ПОЛЬЗУ РАБОТЫ: нет вопроса в вызове, нет реплики владельца, нет прошлого вопроса —
|
|
181
|
+
# признак молчит.
|
|
182
|
+
if [ -n "$tool" ]; then
|
|
183
|
+
asked_json="$(printf '%s' "$input" | jq -r '(.tool_input.questions // []) | tostring' 2>/dev/null)"
|
|
184
|
+
seen="$(tail -n 400 "$transcript" 2>/dev/null | jq -s -r --arg now "$asked_json" '
|
|
185
|
+
def is_input:
|
|
186
|
+
.type == "user"
|
|
187
|
+
and ((.isCompactSummary // false) | not)
|
|
188
|
+
and (((.message.content // []) | if type == "array"
|
|
189
|
+
then ([.[] | select(.type == "tool_result")] | length)
|
|
190
|
+
else 0 end) == 0);
|
|
191
|
+
|
|
192
|
+
def words: [splits("[^\\p{L}\\p{N}]+")] | map(select(length >= 5)) | unique;
|
|
193
|
+
|
|
194
|
+
(map(is_input) | rindex(true)) as $i
|
|
195
|
+
| if $i == null then "нет" else
|
|
196
|
+
(.[$i] | (.message.content // []) | if type == "array"
|
|
197
|
+
then ([.[] | select(.type == "text") | .text] | join(" "))
|
|
198
|
+
else (. // "") end) as $said
|
|
199
|
+
| ([.[:$i][] | select(.type == "assistant") | (.message.content // [])[]
|
|
200
|
+
| select(.type == "tool_use") | select(.name == "AskUserQuestion")] | length) as $before
|
|
201
|
+
| if $before == 0 or ($said | length) == 0 then "нет" else
|
|
202
|
+
(($now | words) - (($now | words) - ($said | words))) as $common
|
|
203
|
+
| if ($common | length) >= 3 then "было" else "нет" end
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
' 2>/dev/null)"
|
|
207
|
+
|
|
208
|
+
if [ "$seen" = "было" ]; then
|
|
209
|
+
reason="BLOCKED by grill-gate: на этот вопрос владелец уже отвечал в этом разговоре — продолжай работу, а не переспрашивай.
|
|
210
|
+
|
|
211
|
+
Указание владельца действует до его отмены. Новый факт против действующего указания — это строка в ответе о цене, а не новый вопрос: переспрашивают только то, чего указание не покрывает. Промах здесь не в форме вопроса, а в остановке работы, которая уже разрешена.
|
|
212
|
+
|
|
213
|
+
Вопрос всё-таки о другом предмете — назови в нём то, чего в прежнем ответе владельца нет: признак судит общие слова вопроса и последней реплики владельца, а не смысл."
|
|
214
|
+
|
|
215
|
+
# Общий хвост отказа: два законных хода и законная форма обхода, если она у отказа есть.
|
|
216
|
+
# shellcheck disable=SC1090
|
|
217
|
+
[ -f "$rt_hooks_dir/deny-tail.sh" ] && . "$rt_hooks_dir/deny-tail.sh" 2>/dev/null
|
|
218
|
+
command -v rt_deny_tail >/dev/null 2>&1 || rt_deny_tail() { :; }
|
|
219
|
+
deny_tail_text="$(rt_deny_tail "")"
|
|
220
|
+
[ -n "$deny_tail_text" ] && reason="${reason}
|
|
221
|
+
|
|
222
|
+
${deny_tail_text}"
|
|
223
|
+
|
|
224
|
+
jq -n --arg r "$reason" '{hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"deny",permissionDecisionReason:$r}}' 2>/dev/null \
|
|
225
|
+
|| printf '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"deny","permissionDecisionReason":"grill-gate: на этот вопрос уже отвечали."}}\n'
|
|
226
|
+
exit 0
|
|
227
|
+
fi
|
|
228
|
+
fi
|
|
229
|
+
|
|
166
230
|
[ "$verdict" = "ask" ] || exit 0
|
|
167
231
|
|
|
168
232
|
if [ -n "$tool" ]; then
|
|
@@ -95,6 +95,7 @@ rt_turn_has_text() {
|
|
|
95
95
|
got="$(tail -n 400 "$transcript" 2>/dev/null | jq -s -r '
|
|
96
96
|
def is_input:
|
|
97
97
|
.type == "user"
|
|
98
|
+
and ((.isCompactSummary // false) | not)
|
|
98
99
|
and (((.message.content // []) | if type == "array"
|
|
99
100
|
then ([.[] | select(.type == "tool_result")] | length)
|
|
100
101
|
else 0 end) == 0);
|
|
@@ -69,6 +69,7 @@ admitted_re='был неправ|был не прав|ошибс|моя ошиб
|
|
|
69
69
|
verdict="$(tail -n 400 "$transcript" 2>/dev/null | jq -s -r --arg re "$admitted_re" '
|
|
70
70
|
def is_input:
|
|
71
71
|
.type == "user"
|
|
72
|
+
and ((.isCompactSummary // false) | not)
|
|
72
73
|
and (((.message.content // []) | if type == "array"
|
|
73
74
|
then ([.[] | select(.type == "tool_result")] | length)
|
|
74
75
|
else 0 end) == 0);
|
|
@@ -63,10 +63,18 @@ context_re='пропозал|слою правил|слоя правил|сло
|
|
|
63
63
|
# Ход — это всё, что записано после последнего настоящего ввода владельца. Ответ инструмента
|
|
64
64
|
# приходит той же ролью, поэтому строки с `tool_result` вводом не считаются.
|
|
65
65
|
#
|
|
66
|
+
# Сводка сжатия — тоже не ввод, хотя приходит ролью владельца и ответом инструмента не является.
|
|
67
|
+
# Она пересказывает ходы, которые уже кончились, и просьба, исполненная вчера, читается в ней как
|
|
68
|
+
# сказанная сейчас: ход, где о предложениях не было сказано ни слова, отбивался на пересказе
|
|
69
|
+
# чужой просьбы — каталог предложений при этом был пуст, и отправлять было нечего вовсе. По
|
|
70
|
+
# словам сводка от речи владельца неотличима и богаче её, потому что пересказывает всю сессию
|
|
71
|
+
# разом; узнаётся она признаком записи, а не текстом.
|
|
72
|
+
#
|
|
66
73
|
# Хвост в 400 строк: запись хода растёт всю сессию, а судится только последний ход.
|
|
67
74
|
verdict="$(tail -n 400 "$transcript" 2>/dev/null | jq -s -r --arg asked "$asked_re" --arg ctx "$context_re" '
|
|
68
75
|
def is_input:
|
|
69
76
|
.type == "user"
|
|
77
|
+
and ((.isCompactSummary // false) | not)
|
|
70
78
|
and (((.message.content // []) | if type == "array"
|
|
71
79
|
then ([.[] | select(.type == "tool_result")] | length)
|
|
72
80
|
else 0 end) == 0);
|
|
@@ -197,6 +197,7 @@ started_re='task:new|task:move|board\.mjs[[:space:]]+move|git[[:space:]]+checkou
|
|
|
197
197
|
verdict="$(tail -n 400 "$transcript" 2>/dev/null | jq -s -r --arg work "$work_re" --arg read "$read_re" --arg part "$part_re" --arg wait "$wait_re" --arg handover "$handover_re" --arg started "$started_re" '
|
|
198
198
|
def is_input:
|
|
199
199
|
.type == "user"
|
|
200
|
+
and ((.isCompactSummary // false) | not)
|
|
200
201
|
and (((.message.content // []) | if type == "array"
|
|
201
202
|
then ([.[] | select(.type == "tool_result")] | length)
|
|
202
203
|
else 0 end) == 0);
|
|
@@ -102,6 +102,7 @@ verdict="$(tail -n 400 "$transcript" 2>/dev/null | jq -s -r \
|
|
|
102
102
|
--arg read "$read_re" --arg red "$red_re" --arg ready "$ready_re" --arg vow "$vow_re" '
|
|
103
103
|
def is_input:
|
|
104
104
|
.type == "user"
|
|
105
|
+
and ((.isCompactSummary // false) | not)
|
|
105
106
|
and (((.message.content // []) | if type == "array"
|
|
106
107
|
then ([.[] | select(.type == "tool_result")] | length)
|
|
107
108
|
else 0 end) == 0);
|
|
@@ -69,6 +69,7 @@ rt_needs rt_is_app_code work-start-guard || exit 0
|
|
|
69
69
|
asked="$(tail -n 400 "$transcript" 2>/dev/null | jq -s -r '
|
|
70
70
|
def is_input:
|
|
71
71
|
.type == "user"
|
|
72
|
+
and ((.isCompactSummary // false) | not)
|
|
72
73
|
and (((.message.content // []) | if type == "array"
|
|
73
74
|
then ([.[] | select(.type == "tool_result")] | length)
|
|
74
75
|
else 0 end) == 0);
|
|
@@ -91,6 +92,7 @@ asked="$(tail -n 400 "$transcript" 2>/dev/null | jq -s -r '
|
|
|
91
92
|
edited="$(tail -n 400 "$transcript" 2>/dev/null | jq -s -r '
|
|
92
93
|
def is_input:
|
|
93
94
|
.type == "user"
|
|
95
|
+
and ((.isCompactSummary // false) | not)
|
|
94
96
|
and (((.message.content // []) | if type == "array"
|
|
95
97
|
then ([.[] | select(.type == "tool_result")] | length)
|
|
96
98
|
else 0 end) == 0);
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Закон об автономном заходе
|
|
2
|
+
|
|
3
|
+
Заход, в котором владельца нет рядом. Он не отвечает на вопросы, не нажимает кнопок и не видит
|
|
4
|
+
работы до утра: всё, что исполнитель сделал за ночь, читается им разом и уже случившимся.
|
|
5
|
+
|
|
6
|
+
## Статьи
|
|
7
|
+
|
|
8
|
+
- **Автономный заход объявляет владелец, и кончается он его словом либо названной границей.**
|
|
9
|
+
Сам себя заход не продлевает: работа без конца — это работа, о которой владелец не просил.
|
|
10
|
+
- **Развилка закрывается умолчанием, а не ожиданием.** Вопрос, который некому задать, не
|
|
11
|
+
останавливает работу: исполнитель берёт умолчание, записывает его вместе с ценой ошибки и идёт
|
|
12
|
+
дальше. Стоящий заход не отвечает ни на один вопрос и стоит ровно столько же, сколько
|
|
13
|
+
отвеченный неверно.
|
|
14
|
+
- **Задача, упирающаяся в слово владельца, откладывается целиком.** Наполовину сделанная
|
|
15
|
+
выглядит сделанной: следующий заход читает её как закрытую и не возвращается к ней.
|
|
16
|
+
- **Действие, которое видно снаружи, в автономном заходе не делается.** Отправка чужой стороне,
|
|
17
|
+
публикация, слияние, письмо: их последствия отменяет человек, а его нет. Отмена стоит дороже
|
|
18
|
+
ожидания.
|
|
19
|
+
- **Работа, которую нечем подтвердить, не берётся.** Признак готовности автономного захода —
|
|
20
|
+
команда с известным выводом, а не взгляд исполнителя: подтвердить его некому.
|
|
21
|
+
- **Каждый шаг кончается следом на диске.** Заход обрывается без предупреждения — по окну, по
|
|
22
|
+
отказу, по сети, — и всё, что не записано, исчезает вместе с ним.
|
|
23
|
+
- **Работы идут чередой, а не веером из одного основания.** Каждая следующая ветвится от
|
|
24
|
+
предыдущей: расхождение тогда всплывает у того, у кого обе правки под рукой, а не у владельца,
|
|
25
|
+
у которого нет ни одной.
|
|
26
|
+
- **Заход кончается списком, а не итогом.** Владелец читает ночь целиком: по каждой работе видно,
|
|
27
|
+
что сделано, где лежит, чем подтверждено и чего она ждёт. Итог «сделано много» не говорит ни о
|
|
28
|
+
чём из четырёх.
|
|
29
|
+
- **Отказ стража автономный заход не кончает.** Он называет пропущенный шаг, и шаг делается;
|
|
30
|
+
заход, вставший на отказе до утра, теряет ночь целиком.
|
|
@@ -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
|
а не у того, кто это написал. Места, где иначе нельзя, названы поимённо.
|
|
@@ -43,6 +43,14 @@
|
|
|
43
43
|
- **Число в тексте пересчитывается тем же изменением, которым пишется, и за этим тоже следит
|
|
44
44
|
автор.** Устаревшее число выглядит так же, как свежее, а машине их не различить: дата, версия и
|
|
45
45
|
номер — такие же числа, и проверка, которая знает один способ записи, на другом ошибается молча.
|
|
46
|
+
- **Слово, значащее в дереве что-то определённое, записано в словаре дерева.** Слова, которого
|
|
47
|
+
там нет, у читателя нет тоже: оно вводится в словарь тем же изменением, которым впервые
|
|
48
|
+
употреблено, либо заменяется тем, что уже есть. Словарь читается до того, как текст написан, а
|
|
49
|
+
не сверяется после: сверка после находит слово, вокруг которого уже построен абзац.
|
|
50
|
+
- **Документ, собираемый из нескольких источников, правится в источнике.** Правка собранного вида
|
|
51
|
+
живёт до следующей сборки и пропадает молча — а выглядит она ровно так же, как правка, которая
|
|
52
|
+
осталась. Собранный документ обязан называть свой источник в себе самом: сказанное о нём где-то
|
|
53
|
+
ещё до читателя может не доехать, а собранный вид доезжает всегда.
|
|
46
54
|
- **Отказ от слова распространяется на всё, что уже прочитано снаружи, а не только на файлы.**
|
|
47
55
|
Название работы, её описание и запись о правке живут вне дерева: поиск по файлам их не видит,
|
|
48
56
|
проверки текстов на них не смотрят, и отказ выглядит сделанным ровно до того, как читатель
|
|
@@ -55,6 +55,9 @@
|
|
|
55
55
|
просьбы, даже когда свой даёт тот же ответ: равноценность инструментов решает тот, кто просит.
|
|
56
56
|
Одинаковый результат равноценностью не является — у названного инструмента бывает видно то,
|
|
57
57
|
чего у заменителя нет вовсе.
|
|
58
|
+
- **Случившаяся подмена называется в том же докладе.** Чем сделано вместо названного и почему —
|
|
59
|
+
двумя словами, рядом с ответом на просьбу. Без этой строки владелец узнаёт о подмене, повторяя
|
|
60
|
+
указание: к той минуте работа по нему уже сделана не тем, и заплачено за неё дважды.
|
|
58
61
|
- **Понимание записано там, где идёт работа.** Оставленное в переписке живёт у одного участника и
|
|
59
62
|
до следующего дня; работу продолжает тот, у кого этой переписки нет.
|
|
60
63
|
- **Сказанное владельцем записывается его словами и задним числом не переписывается.** Пересказ
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: autonomous-work-run
|
|
3
|
+
kind: pattern
|
|
4
|
+
rule: autonomous-work
|
|
5
|
+
description: Паттерн правила autonomous-work. Брать, когда владелец ушёл и работа идёт ночь напролёт — готовый цикл одной работы, ветвление чередой, запись умолчания вместо вопроса, список к утру, разбор отказа стража. Не брать для обычного хода работы — это паттерны task-flow-start и task-flow-resume.
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Ночь без владельца
|
|
9
|
+
|
|
10
|
+
Паттерн правила `autonomous-work`. Что при этом должно быть верно — закон
|
|
11
|
+
`docs/constitution/autonomous-work.md`.
|
|
12
|
+
|
|
13
|
+
## Когда брать
|
|
14
|
+
|
|
15
|
+
- Владелец сказал, что уходит, и просил работать самостоятельно.
|
|
16
|
+
- За заход берётся больше одной задачи подряд, и отдать их наружу нельзя.
|
|
17
|
+
- Работа упёрлась в вопрос, а спросить некого.
|
|
18
|
+
|
|
19
|
+
## Череда веток
|
|
20
|
+
|
|
21
|
+
Первая работа ветвится от главной, каждая следующая — от предыдущей:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
git checkout -b <КЛЮЧ>-<номер-1>-<slug> main # первая за ночь
|
|
25
|
+
git checkout -b <КЛЮЧ>-<номер-2>-<slug> <КЛЮЧ>-<номер-1>-<slug>
|
|
26
|
+
git checkout -b <КЛЮЧ>-<номер-3>-<slug> <КЛЮЧ>-<номер-2>-<slug>
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Порядок вливания — снизу вверх, и он называется в списке к утру номерами. Ветка, заведённая от
|
|
30
|
+
главной посреди череды, столкнётся с соседкой в общих файлах — указателях, счётчиках, журналах —
|
|
31
|
+
и разбирать это будет владелец.
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
git log --oneline --graph --decorate main..HEAD | head -20 # чем стоит череда сейчас
|
|
35
|
+
git branch --list '<КЛЮЧ>-*' --format='%(refname:short) %(upstream:short)'
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Цикл одной работы
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
npm run task:move -- <номер> in-progress # взята
|
|
42
|
+
git checkout -b <КЛЮЧ>-<номер>-<slug> <прошлая ветка> # череда, а не веер
|
|
43
|
+
cp -r docs/tasks/_template docs/tasks/<КЛЮЧ>-<номер>-<slug>
|
|
44
|
+
# разбор просьбы с умолчаниями, замысел, состояние «этап-идёт»
|
|
45
|
+
git add docs/tasks/<КЛЮЧ>-<номер>-<slug> && git commit # папка едет в историю сразу
|
|
46
|
+
# работа этапами: каждый кончается коммитом и прогоном признака готовности
|
|
47
|
+
# папка разбирается в описание прошлого последним коммитом
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Наружу за ночь не уходит ничего: ни `git push`, ни `gh pr create`, ни публикация, ни отметка
|
|
51
|
+
груза. Ветка остаётся местной, и утром владелец сам решает, что из неё отдавать.
|
|
52
|
+
|
|
53
|
+
## Умолчание вместо вопроса
|
|
54
|
+
|
|
55
|
+
Пишется в разбор просьбы, в раздел решений, — там же, где записался бы ответ владельца:
|
|
56
|
+
|
|
57
|
+
```markdown
|
|
58
|
+
- **<что принято>** — <довод>. Спросить было некого: заход автономный. Цена ошибки:
|
|
59
|
+
<что придётся переделать, если владелец решит иначе>.
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Цена ошибки — не вежливость, а признак: она отделяет умолчание, которое можно переиграть за
|
|
63
|
+
десять минут, от того, ради которого работу лучше отложить целиком.
|
|
64
|
+
|
|
65
|
+
## Отложенная задача
|
|
66
|
+
|
|
67
|
+
Задача, которой нужно слово владельца, не берётся: состояние на борде остаётся прежним, а в
|
|
68
|
+
список к утру идёт строка.
|
|
69
|
+
|
|
70
|
+
```markdown
|
|
71
|
+
- **RT-<номер> — <заголовок>.** Отложена: <какой вопрос и почему умолчания у него нет>.
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Список к утру
|
|
75
|
+
|
|
76
|
+
Дописывается по ходу, после каждой закрытой работы, а не собирается в конце.
|
|
77
|
+
|
|
78
|
+
```markdown
|
|
79
|
+
| Работа | Ветка | Чем подтверждена | Чего ждёт |
|
|
80
|
+
| ------ | ----- | ---------------- | --------- |
|
|
81
|
+
| RT-… — <что сделано> | `<ветка>` (на `<предыдущей>`) | `<команда>` — <вывод> | заявки и слияния |
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Порядок строк — порядок вливания. Ниже списка — отложенные задачи с их вопросами.
|
|
85
|
+
|
|
86
|
+
## Отказ стража
|
|
87
|
+
|
|
88
|
+
Отказ называет пропущенный шаг: шаг делается, вызов повторяется, работа идёт дальше. Ночь
|
|
89
|
+
кончается работой, а не спором со стражем.
|
|
90
|
+
|
|
91
|
+
- Отказ требует того, что делать нельзя (пуш, заявка), — работа доводится до места, где
|
|
92
|
+
требование исполнимо утром, и уходит в список к утру строкой «ждёт заявки».
|
|
93
|
+
- Отказ повторяется на том же месте дважды — значит пропущенный шаг понят неверно: читается
|
|
94
|
+
правило, названное в отказе, а не переписывается формулировка.
|
|
95
|
+
|
|
96
|
+
## Частые промахи
|
|
97
|
+
|
|
98
|
+
- **Ветка заведена от главной по привычке.** Череда рвётся молча, и цена всплывает у владельца
|
|
99
|
+
на втором вливании.
|
|
100
|
+
- **Умолчание принято и не записано.** Утром оно неотличимо от знания, и владелец узнаёт о нём,
|
|
101
|
+
только когда работа сделана не так.
|
|
102
|
+
- **Задача взята и отложена наполовину.** На борде она в работе, в ветке — половина правки;
|
|
103
|
+
следующий заход читает её как начатую и не начинает заново.
|
|
104
|
+
- **Список к утру собран по памяти в последнюю минуту.** К этому часу окно уже сжималось, и
|
|
105
|
+
половина ночи в него не попала.
|
|
@@ -169,7 +169,7 @@ done
|
|
|
169
169
|
|
|
170
170
|
- **Одиночная сборка проекта серверы переживают, и отказываться от неё незачем.** Замерено
|
|
171
171
|
ответом до и после: все порты остались за своими процессами. Осторожность здесь стоит дороже
|
|
172
|
-
проверки —
|
|
172
|
+
проверки — целый набор проверок разметки остался незапущенным ровно потому, что сборку
|
|
173
173
|
сочли опасной, не замерив. Сборка из кэша замером не является: она не собирает вовсе, и видно
|
|
174
174
|
это по её длительности.
|
|
175
175
|
- Свой дев-сервер не поднимать: сайт на {{sitePort}}, админка на {{adminPort}}, API на {{apiPort}} уже подняты
|
|
@@ -104,6 +104,22 @@ description: Паттерн правила doc-style. Брать при напи
|
|
|
104
104
|
его вместе со смыслом. Считается это грепом по тексту, и число само по себе отказом не бывает:
|
|
105
105
|
судит его тот, кто правит следующим.
|
|
106
106
|
|
|
107
|
+
## Новое слово в словаре
|
|
108
|
+
|
|
109
|
+
Слово, значащее в дереве что-то определённое, живёт в словаре. Порядок — четыре шага, и первый
|
|
110
|
+
из них не пропускается: словарь читается до того, как текст написан, а не сверяется после.
|
|
111
|
+
|
|
112
|
+
1. **Поиск по собранному словарю целиком**, включая раздел отвергнутых слов. Слово, от которого
|
|
113
|
+
дерево отказалось, стоит там же — и вводить его заново значит отменять чужое решение молча.
|
|
114
|
+
2. **Выбор своего раздела.** Раздел надстройки замещает одноимённый раздел набора целиком:
|
|
115
|
+
название берётся такое, какого в наборе нет.
|
|
116
|
+
3. **Строка парой — слово и что это.** Без второй половины слово не введено: оно названо.
|
|
117
|
+
4. **Раскладка тем же изменением**, и сверка при этом зелёная. Слово, введённое в надстройку и не
|
|
118
|
+
разложенное, у читателя ещё не появилось.
|
|
119
|
+
|
|
120
|
+
Слово, от которого дерево отказывается, заводится там же и с заменой в той же строке: отказ без
|
|
121
|
+
замены исполнить нечем — пишущий видит запрет и не видит, чем его закрыть.
|
|
122
|
+
|
|
107
123
|
## Факт проверяется, а не вспоминается
|
|
108
124
|
|
|
109
125
|
Перед тем как написать, что код делает X, — открыть код и посмотреть. Пересказ по памяти
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: git-workflow-pr-ready
|
|
3
|
+
kind: pattern
|
|
4
|
+
rule: git-workflow
|
|
5
|
+
description: Паттерн правила git-workflow. Брать, когда заявки уже открыты: снятие черновика, разбор красного прогона, доведение череды до готовности, порядок «сначала довести отданное, потом брать новое». Открытие одной заявки — паттерн git-workflow-pr, порядок череды — git-workflow-stack.
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Доведение заявки до готовности
|
|
9
|
+
|
|
10
|
+
Паттерн правила `git-workflow`. Что при этом должно быть верно — закон
|
|
11
|
+
`docs/constitution/delivery.md`. Открытие заявки — паттерн `git-workflow-pr`, порядок череды —
|
|
12
|
+
`git-workflow-stack`, разбор одного конфликта — `git-workflow-merge`.
|
|
13
|
+
|
|
14
|
+
## Когда брать
|
|
15
|
+
|
|
16
|
+
- Заявка открыта черновиком, и прогон по ней кончился.
|
|
17
|
+
- Открыто несколько заявок разом, и надо решить, что делать раньше — доводить их или брать
|
|
18
|
+
следующую задачу.
|
|
19
|
+
- Прогон заявки красный, а правка в ветке зелёная у себя.
|
|
20
|
+
|
|
21
|
+
## Черновик — состояние работы, а не её вид
|
|
22
|
+
|
|
23
|
+
Черновик говорит одно: работу нельзя вливать. Кнопка слияния у него заблокирована хостингом, и
|
|
24
|
+
владелец, открывший список, видит не «сделано», а «делается». Заявка с зелёным прогоном и
|
|
25
|
+
неснятым черновиком — сделанная работа, выглядящая незаконченной, и поправить это может только
|
|
26
|
+
тот, кто её отдал.
|
|
27
|
+
|
|
28
|
+
Отсюда порядок: **черновик снимается тем же ходом, которым прочитан зелёный прогон.** Не
|
|
29
|
+
следующим, не после соседней задачи.
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
gh pr ready <номер>
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Снятие черновика — последний шаг отдачи работы, а не отдельное дело. Пока он не сделан, работа
|
|
36
|
+
не отдана, сколько бы коммитов ни лежало в ветке.
|
|
37
|
+
|
|
38
|
+
## Сначала довести отданное, потом брать новое
|
|
39
|
+
|
|
40
|
+
Открытая заявка — работа в полёте: её прогон стареет, её ветка отстаёт от главной, её тело
|
|
41
|
+
описывает дерево на день написания. Всё это чинится дёшево сегодня и дорого через три задачи.
|
|
42
|
+
|
|
43
|
+
Очередь поэтому такая:
|
|
44
|
+
|
|
45
|
+
1. Прогоны открытых заявок прочитаны, черновики с зелёных сняты.
|
|
46
|
+
2. Красные разобраны и починены.
|
|
47
|
+
3. И только потом берётся следующая задача.
|
|
48
|
+
|
|
49
|
+
Взять новую задачу, оставив позади десяток заявок черновиками, — значит отчитаться о работе,
|
|
50
|
+
которой у владельца нет: он видит список «делается», а не список «готово».
|
|
51
|
+
|
|
52
|
+
## Состояние читается разом
|
|
53
|
+
|
|
54
|
+
Чем оно читается и как разбирается каждое поле — паттерн `git-workflow-freshness`; здесь
|
|
55
|
+
только то, что делают с прочитанным. Пустой прогон разбирается наравне с красным: у заявки
|
|
56
|
+
череды база не главная ветка, и фильтр по базе такое событие до конвейера не пропускает.
|
|
57
|
+
|
|
58
|
+
## Красный прогон разбирается по шагу, а не по заявке
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
gh pr checks <номер>
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Ответ называет упавший прогон и ссылку на него. Дальше у хостинга спрашивается имя упавшего
|
|
65
|
+
шага — `gh api` по этому прогону, отбором `select(.conclusion=="failure")` по его шагам.
|
|
66
|
+
|
|
67
|
+
Имя шага и есть диагноз. Правка идёт в ту ветку череды, где причина возникла, а не в ту, где
|
|
68
|
+
прогон покраснел: у череды красным становится всё, что стоит выше причины.
|
|
69
|
+
|
|
70
|
+
## Правка причины — в нижнюю ветку, оттуда волной вверх
|
|
71
|
+
|
|
72
|
+
Причина, общая для всей череды, чинится один раз в самой нижней ветке. Наверх она едет
|
|
73
|
+
вливанием, а не перебазированием: перебазирование переписывает историю уже отданных веток, и
|
|
74
|
+
хостинг закрывает заявки верхних как слитые, хотя в главной их правок нет.
|
|
75
|
+
|
|
76
|
+
Волна идёт снизу вверх, по одной ветке, и каждый шаг — три отдельных вызова: переключение на
|
|
77
|
+
следующую ветку череды, вливание предыдущей в неё, отправка. Одной строкой их не пишут: гард
|
|
78
|
+
поставки такую команду отбивает, и правильно — набор проверок гоняется по тому дереву, какое
|
|
79
|
+
лежит на момент разбора команды, то есть по прежней ветке.
|
|
80
|
+
|
|
81
|
+
Пропущенная посередине ветка оставляет свою заявку красной и ломает порядок вливания, поэтому
|
|
82
|
+
волна проходит череду целиком.
|
|
83
|
+
|
|
84
|
+
## Частые промахи
|
|
85
|
+
|
|
86
|
+
- Черновик не снят, потому что «ещё гоняется прогон». Прогон кончился час назад; читается его
|
|
87
|
+
состояние, а не память о том, что он шёл.
|
|
88
|
+
- Взята следующая задача, а десяток заявок остался черновиками. Для владельца сделанного нет.
|
|
89
|
+
- Красное чинится в той ветке, где покраснело. У череды это верхняя, а причина — в нижней, и
|
|
90
|
+
починка сверху оставляет красным всё под собой.
|
|
91
|
+
- Причина разослана по веткам перебазированием. Отданные ветки переписаны, заявки закрыты как
|
|
92
|
+
слитые, правок в главной нет.
|
|
93
|
+
- Пустой прогон принят за зелёный. Событие до конвейера не дошло, и заявка не проверена вовсе.
|
|
@@ -180,7 +180,7 @@ npm run task:move -- 86 in-review
|
|
|
180
180
|
8. **Правка вёрстки подтверждена замером**, а не взглядом, и снята при узком экране — паттерн
|
|
181
181
|
`browser-verification-measure`.
|
|
182
182
|
9. **Правка разметки публичного сайта проверена на прод-сборке по всем локалям перевода** —
|
|
183
|
-
|
|
183
|
+
правило видимости в поиске того дерева, где оно есть.
|
|
184
184
|
10. **PR привязан к рабочему элементу**, ревьювер и исполнитель стоят, а тело собрано по
|
|
185
185
|
образцу — разделы «Что сделано», «Чем подтверждено» и «Оставшийся шаг». Раздел оставшегося
|
|
186
186
|
шага к этому моменту говорит, что шагов не осталось: черновик снимается после разбора
|
|
@@ -242,7 +242,7 @@ npm run task:move -- 86 in-review
|
|
|
242
242
|
8. **Правка вёрстки подтверждена замером**, а не взглядом, и снята при узком экране — паттерн
|
|
243
243
|
`browser-verification-measure`.
|
|
244
244
|
9. **Правка разметки публичного сайта проверена на прод-сборке по всем локалям перевода** — паттерн
|
|
245
|
-
|
|
245
|
+
правило видимости в поиске того дерева, где оно есть.
|
|
246
246
|
10. **Тело PR собрано по образцу** — начинается строкой `Closes #<номер>`, несёт разделы «Что
|
|
247
247
|
сделано», «Чем подтверждено» и «Оставшийся шаг», а метки, ревьювер и исполнитель стоят.
|
|
248
248
|
Раздел оставшегося шага к этому моменту говорит, что шагов не осталось: черновик снимается
|
|
@@ -194,7 +194,7 @@ npm run task:move -- 86 in-review
|
|
|
194
194
|
8. **Правка вёрстки подтверждена замером**, а не взглядом, и снята при узком экране — паттерн
|
|
195
195
|
`browser-verification-measure`.
|
|
196
196
|
9. **Правка разметки публичного сайта проверена на прод-сборке по всем локалям перевода** —
|
|
197
|
-
|
|
197
|
+
правило видимости в поиске того дерева, где оно есть.
|
|
198
198
|
10. **Описание MR собрано по образцу** — начинается строкой `Closes #<номер>`, несёт разделы
|
|
199
199
|
«Что сделано», «Чем подтверждено» и «Оставшийся шаг», а метки, ревьювер и исполнитель
|
|
200
200
|
стоят. Раздел оставшегося шага к этому моменту говорит, что шагов не осталось: черновик
|