@rt-tools/agent-kit 0.9.0 → 0.10.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/README.md +21 -7
- package/assets/agents/conscience.md +58 -0
- package/assets/agents/prose-editor.md +44 -0
- package/assets/agents/strict-teacher.md +59 -0
- package/assets/checks/board.github.mjs +56 -1
- package/assets/checks/check-board.github.mjs +24 -0
- package/assets/checks/check-doc-paths.mjs +4 -15
- package/assets/checks/check-dupes.mjs +5 -6
- package/assets/checks/check-file-size.mjs +6 -20
- package/assets/checks/check-prose-style.mjs +137 -0
- package/assets/checks/check-reuse.mjs +5 -5
- package/assets/checks/check-styles.mjs +5 -5
- package/assets/checks/lib-common.mjs +3 -3
- package/assets/checks/rt-kit-checks.config.mjs +85 -1
- package/assets/defaults/project.sh +50 -0
- package/assets/docs/GLOSSARY.md +17 -15
- package/assets/hooks/browser-guard-device-id.sh +9 -1
- package/assets/hooks/browser-guard-no-asking.sh +9 -1
- package/assets/hooks/browser-guard-no-listing.sh +9 -1
- package/assets/hooks/browser-guard-no-other-drivers.sh +7 -1
- package/assets/hooks/browser-guard-require-select.sh +11 -2
- package/assets/hooks/claim-guard.sh +113 -0
- package/assets/hooks/conscience-guard.sh +98 -0
- package/assets/hooks/deny-tail.sh +32 -0
- package/assets/hooks/dev-server-guard.sh +8 -2
- package/assets/hooks/docs-guard.sh +14 -2
- package/assets/hooks/exam-guard.sh +121 -0
- package/assets/hooks/git-guard-delivery-signature.sh +75 -0
- package/assets/hooks/git-guard-delivery.sh +154 -68
- package/assets/hooks/git-guard-main.sh +12 -0
- package/assets/hooks/git-guard-push-tests.sh +49 -2
- package/assets/hooks/grill-gate.sh +12 -0
- package/assets/hooks/handoff-entry-guard.sh +71 -0
- package/assets/hooks/postmortem-guard.sh +12 -0
- package/assets/hooks/proposal-guard.sh +12 -0
- package/assets/hooks/prose-style-guard.sh +73 -0
- package/assets/hooks/qa-dataid-guard.sh +12 -1
- package/assets/hooks/rerun-guard.sh +86 -0
- package/assets/hooks/reuse-first-guard.sh +12 -1
- package/assets/hooks/roles.sh +32 -0
- package/assets/hooks/skill-gate.sh +12 -0
- package/assets/hooks/sql-guard-write.sh +2 -1
- package/assets/hooks/sql-guard.sh +12 -1
- package/assets/hooks/task-flow-guard.sh +71 -7
- package/assets/hooks/turn-exit-guard.sh +189 -0
- package/assets/hooks/waiting-turn-guard.sh +12 -0
- package/assets/hooks/window-fill-guard.sh +12 -1
- package/assets/patterns/task-flow-close.md +8 -8
- package/assets/patterns/task-flow-handoff.md +1 -1
- package/assets/patterns/task-flow-resume.md +30 -4
- package/assets/patterns/task-flow-start.md +15 -6
- package/assets/rules/git-workflow.azure.md +36 -9
- package/assets/rules/git-workflow.github.md +74 -9
- package/assets/rules/git-workflow.gitlab.md +40 -12
- package/assets/rules/task-flow.md +123 -71
- package/assets/rules/testing.md +15 -1
- package/assets/samples/tasks/_template/plan.md +4 -1
- package/assets/samples/tasks/_template/progress.md +1 -0
- package/assets/skills/agent-kit.md +33 -0
- package/assets/templates/project.sh +16 -0
- package/bin/agent-kit.d.ts.map +1 -1
- package/bin/agent-kit.js +4 -2
- package/bin/agent-kit.js.map +1 -1
- package/lib/config.d.ts +8 -0
- package/lib/config.d.ts.map +1 -1
- package/lib/config.js +1 -0
- package/lib/config.js.map +1 -1
- package/lib/enroll.d.ts +9 -1
- package/lib/enroll.d.ts.map +1 -1
- package/lib/enroll.js +65 -18
- package/lib/enroll.js.map +1 -1
- package/package.json +1 -1
- package/rt-tools-agent-kit-0.10.0.tgz +0 -0
- package/rt-tools-agent-kit-0.9.0.tgz +0 -0
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# rt-hook: Stop
|
|
3
|
+
# Требует: hooks/deny-tail.sh
|
|
4
|
+
# Страж выходов хода: ход, в котором по работе не сделано ничего, не заканчивается, пока работа
|
|
5
|
+
# не отдана. Stop.
|
|
6
|
+
#
|
|
7
|
+
# Зачем именно так. Правило перечисляет четыре законных выхода хода — вопрос без ответа в
|
|
8
|
+
# правилах, отказ гарда, заполненное окно, отданная работа с начатой следующей, — и держится
|
|
9
|
+
# это памятью исполнителя. Держится плохо: ход, кончившийся отчётом о сделанном, выглядит
|
|
10
|
+
# работой лучше всякой другой — он полон, в нём названы номера и состояния, и пустоты за ним не
|
|
11
|
+
# видно ни владельцу, ни самому заходу. Владелец назвал это прямо: прерываться посреди работы
|
|
12
|
+
# нельзя, и запрет должна держать машина.
|
|
13
|
+
#
|
|
14
|
+
# Что считается работой: правка файла и команда, меняющая дерево или его состояние. Чтение,
|
|
15
|
+
# поиск и разговор работой не считаются — именно ими и заполняется ход, который встал.
|
|
16
|
+
#
|
|
17
|
+
# Что отпускает ход:
|
|
18
|
+
# 1. Работа отдана либо влита — состояние работы говорит об этом само.
|
|
19
|
+
# 2. За ход была работа: правка файла или команда, меняющая дерево.
|
|
20
|
+
# 3. Вопрос владельцу инструментом опроса.
|
|
21
|
+
# 4. Отказ гарда — он кончает ход по правилу.
|
|
22
|
+
# 5. Передача захода написана — окно кончилось.
|
|
23
|
+
# 6. Владелец сказал остановиться.
|
|
24
|
+
#
|
|
25
|
+
# Чего страж не судит. Заход вне ветки задачи и работу без папки: состояние там объявлять
|
|
26
|
+
# негде, и отбивать было бы не за что. Это его известная граница.
|
|
27
|
+
#
|
|
28
|
+
# ОТКАЗ В ПОЛЬЗУ РАБОТЫ: при любой ошибке, нехватке `jq`, отсутствии записи хода, папки задачи
|
|
29
|
+
# или строки состояния ход РАЗРЕШАЕТСЯ (exit 0). Сломанный страж не имеет права заклинить
|
|
30
|
+
# разговор.
|
|
31
|
+
|
|
32
|
+
input="$(cat 2>/dev/null)"
|
|
33
|
+
[ -z "$input" ] && exit 0
|
|
34
|
+
command -v jq >/dev/null 2>&1 || exit 0
|
|
35
|
+
|
|
36
|
+
# Повторный заход по тому же ходу не судится: страж сказал своё один раз и отпускает.
|
|
37
|
+
active="$(printf '%s' "$input" | jq -r '.stop_hook_active // false' 2>/dev/null)"
|
|
38
|
+
[ "$active" = "true" ] && exit 0
|
|
39
|
+
|
|
40
|
+
transcript="$(printf '%s' "$input" | jq -r '.transcript_path // empty' 2>/dev/null)"
|
|
41
|
+
[ -z "$transcript" ] && exit 0
|
|
42
|
+
[ -f "$transcript" ] || exit 0
|
|
43
|
+
|
|
44
|
+
workdir="$(printf '%s' "$input" | jq -r '.cwd // empty' 2>/dev/null)"
|
|
45
|
+
[ -z "$workdir" ] && workdir="${CLAUDE_PROJECT_DIR:-.}"
|
|
46
|
+
cd "$workdir" 2>/dev/null || exit 0
|
|
47
|
+
git rev-parse --is-inside-work-tree >/dev/null 2>&1 || exit 0
|
|
48
|
+
|
|
49
|
+
branch="$(git branch --show-current 2>/dev/null)"
|
|
50
|
+
[ -z "$branch" ] && exit 0
|
|
51
|
+
|
|
52
|
+
root="$(git rev-parse --show-toplevel 2>/dev/null)"
|
|
53
|
+
[ -z "$root" ] && exit 0
|
|
54
|
+
tasks_dir="${RT_TASKS_DIR:-docs/tasks}"
|
|
55
|
+
progress="$root/$tasks_dir/$branch/progress.md"
|
|
56
|
+
[ -f "$progress" ] || exit 0
|
|
57
|
+
|
|
58
|
+
state="$(sed -n 's/^[[:space:]]*[-*][[:space:]]*\*\*Состояние:\*\*[[:space:]]*`\([^`]*\)`.*/\1/p' "$progress" 2>/dev/null | head -1)"
|
|
59
|
+
[ -z "$state" ] && exit 0
|
|
60
|
+
|
|
61
|
+
# Работа, дошедшая до этих двух состояний, чужого шага уже дождалась: дальше её двигает
|
|
62
|
+
# владелец, и ход, закрытый здесь, ничего не роняет.
|
|
63
|
+
case "$state" in
|
|
64
|
+
работа-отдана | влито) exit 0 ;;
|
|
65
|
+
esac
|
|
66
|
+
|
|
67
|
+
# Следующий шаг из хода работы — его страж и называет в отказе: исполнитель, которому сказано
|
|
68
|
+
# только «работа не кончена», перечитывает ту же строку сам.
|
|
69
|
+
next_step="$(sed -n 's/^[[:space:]]*[-*][[:space:]]*\*\*Следующий шаг:\*\*[[:space:]]*\(.*\)/\1/p' "$progress" 2>/dev/null | head -1)"
|
|
70
|
+
[ -z "$next_step" ] && next_step="что стоит в разделе «Где стоим» хода работы"
|
|
71
|
+
|
|
72
|
+
# Команда, меняющая дерево или его состояние. Чтение и поиск сюда не входят намеренно: ими и
|
|
73
|
+
# заполняется ход, который встал.
|
|
74
|
+
work_re='git (add|commit|push|checkout|merge|rm)|npm run|pnpm (run|exec)|nx (build|test|run)|gh (pr|issue|api|run)|task:(new|move)|mkdir|cp |mv |rm |sed -i|tee |>>?[[:space:]]*[^|&]'
|
|
75
|
+
|
|
76
|
+
verdict="$(tail -n 400 "$transcript" 2>/dev/null | jq -s -r --arg work "$work_re" '
|
|
77
|
+
def is_input:
|
|
78
|
+
.type == "user"
|
|
79
|
+
and (((.message.content // []) | if type == "array"
|
|
80
|
+
then ([.[] | select(.type == "tool_result")] | length)
|
|
81
|
+
else 0 end) == 0);
|
|
82
|
+
|
|
83
|
+
(map(is_input) | rindex(true)) as $i
|
|
84
|
+
| (if $i == null then [] else .[$i:] end) as $turn
|
|
85
|
+
| [$turn[] | select(.type == "assistant") | (.message.content // [])[] | select(.type == "tool_use")] as $uses
|
|
86
|
+
# Правка файла — работа по определению, каким бы инструментом она ни шла.
|
|
87
|
+
| ($uses | map(.name // "") | any(test("^(Edit|Write|MultiEdit|NotebookEdit)$"))) as $edited
|
|
88
|
+
| ($uses | map(.name // "") | any(test("AskUserQuestion"))) as $asked
|
|
89
|
+
| ($uses | map((.input.command // "")) | join("\n")) as $ran
|
|
90
|
+
| ($ran | test($work)) as $ran_work
|
|
91
|
+
# Отказ гарда и передача захода — оба кончают ход по правилу.
|
|
92
|
+
| ([$turn[] | select(.type == "user") | .message.content // [] | select(type == "array") | .[]
|
|
93
|
+
| select(.type == "tool_result") | .content
|
|
94
|
+
| if type == "string" then .
|
|
95
|
+
elif type == "array" then (map(if type == "object" then (.text // "") else tostring end) | join("\n"))
|
|
96
|
+
else tostring end] | join("\n")) as $out
|
|
97
|
+
| (($out | test("BLOCKED by|Отбито гейтом")) or ($ran | test("BLOCKED by"))) as $denied
|
|
98
|
+
| ($ran | test("handoff")) as $handed
|
|
99
|
+
# Слово владельца об остановке: судится его собственная реплика, а не пересказ исполнителя.
|
|
100
|
+
| ([$turn[] | select(.type == "user") | .message.content
|
|
101
|
+
| if type == "string" then . elif type == "array"
|
|
102
|
+
then (map(if type == "object" then (.text // "") else "" end) | join("\n")) else "" end] | join("\n")) as $said
|
|
103
|
+
| ($said | test("останов|стоп|хватит|подожди|не надо|прерв|отложи")) as $told_stop
|
|
104
|
+
| { worked: ($edited or $ran_work), released: ($asked or $denied or $handed or $told_stop), ran: $ran }
|
|
105
|
+
' 2>/dev/null)"
|
|
106
|
+
|
|
107
|
+
[ -z "$verdict" ] && exit 0
|
|
108
|
+
|
|
109
|
+
worked="$(printf '%s' "$verdict" | jq -r '.worked // false' 2>/dev/null)"
|
|
110
|
+
released="$(printf '%s' "$verdict" | jq -r '.released // false' 2>/dev/null)"
|
|
111
|
+
commands="$(printf '%s' "$verdict" | jq -r '.ran // ""' 2>/dev/null)"
|
|
112
|
+
|
|
113
|
+
[ "$released" = "true" ] && exit 0
|
|
114
|
+
|
|
115
|
+
# Контракт этапа. Отметка «этап сделан» — утверждение о дереве, и подтверждается оно выводом
|
|
116
|
+
# команды, а не словами: этап, отмеченный по памяти, через заход неотличим от проверенного.
|
|
117
|
+
# Страж сравнивает номер этапа с тем, что лежит в истории ветки, и на выросшем номере требует
|
|
118
|
+
# команды из строки «Чем проверяется» — она стоит в замысле обратными кавычками. Приём, записанный
|
|
119
|
+
# прозой, страж не читает: подтвердить его выводом нечем, и это его известная граница.
|
|
120
|
+
stage_now="$(sed -n 's/^[[:space:]]*[-*][[:space:]]*\*\*Этап:\*\*[[:space:]]*\([0-9][0-9]*\).*/\1/p' "$progress" 2>/dev/null | head -1)"
|
|
121
|
+
stage_was="$(git -C "$root" show "HEAD:$tasks_dir/$branch/progress.md" 2>/dev/null | sed -n 's/^[[:space:]]*[-*][[:space:]]*\*\*Этап:\*\*[[:space:]]*\([0-9][0-9]*\).*/\1/p' | head -1)"
|
|
122
|
+
|
|
123
|
+
if [ -n "$stage_now" ] && [ -n "$stage_was" ] && [ "$stage_now" -gt "$stage_was" ] 2>/dev/null; then
|
|
124
|
+
plan="$root/$tasks_dir/$branch/plan.md"
|
|
125
|
+
# Контракт закрытого этапа, а не начатого: подтверждается то, что объявлено сделанным.
|
|
126
|
+
contract="$(awk -v n="$stage_was" '
|
|
127
|
+
$0 ~ "^### " n "\\." { inside = 1; next }
|
|
128
|
+
/^### / { inside = 0 }
|
|
129
|
+
inside && /\*\*Чем проверяется:\*\*/ { print }
|
|
130
|
+
' "$plan" 2>/dev/null)"
|
|
131
|
+
missing=""
|
|
132
|
+
while IFS= read -r cmd; do
|
|
133
|
+
[ -z "$cmd" ] && continue
|
|
134
|
+
printf '%s' "$commands" | grep -qF -- "$cmd" || missing="$missing\n $cmd"
|
|
135
|
+
done <<EOF
|
|
136
|
+
$(printf '%s' "$contract" | grep -o '`[^`]*`' | tr -d '`')
|
|
137
|
+
EOF
|
|
138
|
+
if [ -n "$missing" ]; then
|
|
139
|
+
reason="BLOCKED by turn-exit-guard: этап ${stage_was} объявлен закрытым, а команды, которыми он проверяется, за этот ход не запускались:$(printf '%b' "$missing")
|
|
140
|
+
|
|
141
|
+
Отметка «этап сделан» — утверждение о дереве, и подтверждается оно выводом команды, а не словами: через заход отмеченное по памяти неотличимо от проверенного.
|
|
142
|
+
|
|
143
|
+
Запусти их этим же ходом либо верни прежний номер этапа в ход работы.
|
|
144
|
+
|
|
145
|
+
Страж судит один ход: следующий заход не отбивается."
|
|
146
|
+
# Общий хвост отказа: два законных хода. Файл может быть не разложен — тогда хвоста нет,
|
|
147
|
+
# а причина отказа остаётся прежней.
|
|
148
|
+
# shellcheck disable=SC1090
|
|
149
|
+
[ -f "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/deny-tail.sh" ] \
|
|
150
|
+
&& . "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/deny-tail.sh" 2>/dev/null
|
|
151
|
+
command -v rt_deny_tail >/dev/null 2>&1 || rt_deny_tail() { :; }
|
|
152
|
+
deny_tail_text="$(rt_deny_tail "")"
|
|
153
|
+
[ -n "$deny_tail_text" ] && reason="${reason}
|
|
154
|
+
|
|
155
|
+
${deny_tail_text}"
|
|
156
|
+
|
|
157
|
+
jq -n --arg r "$reason" '{decision:"block",reason:$r}' 2>/dev/null \
|
|
158
|
+
|| printf '{"decision":"block","reason":"turn-exit-guard: закрытый этап не подтверждён выводом команды."}\n'
|
|
159
|
+
exit 0
|
|
160
|
+
fi
|
|
161
|
+
fi
|
|
162
|
+
|
|
163
|
+
[ "$worked" = "true" ] && exit 0
|
|
164
|
+
|
|
165
|
+
reason="BLOCKED by turn-exit-guard: работа в состоянии '${state}', а за этот ход по ней не сделано ничего — ни правки, ни команды, меняющей дерево.
|
|
166
|
+
|
|
167
|
+
Ход кончается четырьмя способами, и других нет: вопрос владельцу, ответа на который в правилах нет; отказ гарда; заполненное окно захода; отданная работа с начатой следующей. Отчёт о сделанном выходом не является — он выглядит работой лучше всякой другой, и пустоты за ним не видно.
|
|
168
|
+
|
|
169
|
+
Следующий шаг записан в ходе работы: ${next_step}
|
|
170
|
+
|
|
171
|
+
Сделай его этим же ходом. Владелец сказал остановиться — так и напиши: страж читает его слово, а не пересказ.
|
|
172
|
+
|
|
173
|
+
Страж судит один ход: следующий заход не отбивается."
|
|
174
|
+
|
|
175
|
+
# Общий хвост отказа: два законных хода. Файл может быть не разложен — тогда хвоста нет,
|
|
176
|
+
# а причина отказа остаётся прежней.
|
|
177
|
+
# shellcheck disable=SC1090
|
|
178
|
+
[ -f "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/deny-tail.sh" ] \
|
|
179
|
+
&& . "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/deny-tail.sh" 2>/dev/null
|
|
180
|
+
command -v rt_deny_tail >/dev/null 2>&1 || rt_deny_tail() { :; }
|
|
181
|
+
deny_tail_text="$(rt_deny_tail "")"
|
|
182
|
+
[ -n "$deny_tail_text" ] && reason="${reason}
|
|
183
|
+
|
|
184
|
+
${deny_tail_text}"
|
|
185
|
+
|
|
186
|
+
jq -n --arg r "$reason" '{decision:"block",reason:$r}' 2>/dev/null \
|
|
187
|
+
|| printf '{"decision":"block","reason":"turn-exit-guard: работа не кончена — следующий шаг стоит в ходе работы."}\n'
|
|
188
|
+
|
|
189
|
+
exit 0
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
# rt-hook: Stop
|
|
3
|
+
# Требует: hooks/deny-tail.sh
|
|
3
4
|
# Гард ожидания: ход, сообщающий владельцу о чужом шаге, не заканчивается, пока в нём не было ни
|
|
4
5
|
# одного действия по следующей задаче. Stop.
|
|
5
6
|
#
|
|
@@ -110,6 +111,17 @@ reason="BLOCKED by waiting-turn-guard: ${said}, а действия по сле
|
|
|
110
111
|
|
|
111
112
|
Гард судит один ход: следующий заход не отбивается."
|
|
112
113
|
|
|
114
|
+
# Общий хвост отказа: два законных хода. Файл может быть не разложен — тогда хвоста нет,
|
|
115
|
+
# а причина отказа остаётся прежней.
|
|
116
|
+
# shellcheck disable=SC1090
|
|
117
|
+
[ -f "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/deny-tail.sh" ] \
|
|
118
|
+
&& . "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/deny-tail.sh" 2>/dev/null
|
|
119
|
+
command -v rt_deny_tail >/dev/null 2>&1 || rt_deny_tail() { :; }
|
|
120
|
+
deny_tail_text="$(rt_deny_tail "")"
|
|
121
|
+
[ -n "$deny_tail_text" ] && reason="${reason}
|
|
122
|
+
|
|
123
|
+
${deny_tail_text}"
|
|
124
|
+
|
|
113
125
|
jq -n --arg r "$reason" '{decision:"block",reason:$r}' 2>/dev/null \
|
|
114
126
|
|| printf '{"decision":"block","reason":"waiting-turn-guard: PR открыт — тем же ходом берётся следующая задача."}\n'
|
|
115
127
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
# rt-hook: PostToolUse .*
|
|
3
|
-
# Требует: hooks/profile-check.sh
|
|
3
|
+
# Требует: hooks/profile-check.sh, hooks/deny-tail.sh
|
|
4
4
|
# rt-hook: PreToolUse .*
|
|
5
5
|
# Заполнение окна: заход доводится до логической точки заранее, а не обрывается на середине.
|
|
6
6
|
#
|
|
@@ -150,6 +150,17 @@ reason="BLOCKED by window-fill-guard: заполнение окна ${pct}% (${f
|
|
|
150
150
|
|
|
151
151
|
Пропускаются при этом: правка ${tasks_dir}/**, запись передачи, команды поставки и сверки, чтение файлов и вопрос владельцу."
|
|
152
152
|
|
|
153
|
+
# Общий хвост отказа: два законных хода и законная форма обхода, если она у отказа есть.
|
|
154
|
+
# Файл может быть не разложен — тогда хвоста нет, а причина отказа остаётся прежней.
|
|
155
|
+
# shellcheck disable=SC1090
|
|
156
|
+
[ -f "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/deny-tail.sh" ] \
|
|
157
|
+
&& . "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/deny-tail.sh" 2>/dev/null
|
|
158
|
+
command -v rt_deny_tail >/dev/null 2>&1 || rt_deny_tail() { :; }
|
|
159
|
+
deny_tail_text="$(rt_deny_tail "")"
|
|
160
|
+
[ -n "$deny_tail_text" ] && reason="${reason}
|
|
161
|
+
|
|
162
|
+
${deny_tail_text}"
|
|
163
|
+
|
|
153
164
|
jq -n --arg r "$reason" \
|
|
154
165
|
'{hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"deny",permissionDecisionReason:$r}}' 2>/dev/null \
|
|
155
166
|
|| printf '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"deny","permissionDecisionReason":"window-fill-guard: окно заполнено, заход закрывается передачей."}}\n'
|
|
@@ -19,7 +19,7 @@ description: Паттерн правила task-flow. Брать при закр
|
|
|
19
19
|
кода отдаётся владельцу, — то есть до этого паттерна и, как правило, задолго до него. Здесь
|
|
20
20
|
работа доводится до готовности и черновик снимается.
|
|
21
21
|
|
|
22
|
-
##
|
|
22
|
+
## Состояние `этапы-кончились`: работа отдаётся на разбор
|
|
23
23
|
|
|
24
24
|
PR открыт черновиком — и с этой минуты работа ждёт владельца, а не машину. Заход на этом не
|
|
25
25
|
кончается: следующая задача эпика берётся тем же движением, паттерн `task-flow-resume`.
|
|
@@ -102,7 +102,7 @@ PR #<номер> готов к слиянию: прогон зелёный, па
|
|
|
102
102
|
кто пишет тело. Разница между ними одна, и она вся: реплику владелец прочитает, только если
|
|
103
103
|
вернётся в переписку, а раздел он видит там, куда смотрит, нажимая кнопку.
|
|
104
104
|
|
|
105
|
-
##
|
|
105
|
+
## Состояние `разбор-кончился`: договорённость вливается в спек домена
|
|
106
106
|
|
|
107
107
|
Последним коммитом PR, до слияния. Код к этому моменту написан, поэтому привязки
|
|
108
108
|
`файл:символ` известны — правило въезжает в спек домена сразу проверяемым.
|
|
@@ -131,7 +131,7 @@ npm run check:specs # раздел «Пора вливать» называе
|
|
|
131
131
|
npm run check:specs # после вливания: привязки на месте, сценарии не потерялись
|
|
132
132
|
```
|
|
133
133
|
|
|
134
|
-
##
|
|
134
|
+
## Состояние `разбор-кончился`: тексты домена приводятся к сделанному
|
|
135
135
|
|
|
136
136
|
В спек уезжает только то, что записали до кода. Остальные тексты — правила, паттерны, законы
|
|
137
137
|
приложения — после правки никто не перечитывает, и они продолжают описывать старое дерево.
|
|
@@ -172,7 +172,7 @@ grep -rn -A3 "Чего из закона здесь нет" <каталог пр
|
|
|
172
172
|
Что сделали на этом шаге, пишется в тело PR: что перечитали, что изменили, а если ничего
|
|
173
173
|
не изменили — почему. Форма раздела — паттерн `git-workflow-commit`.
|
|
174
174
|
|
|
175
|
-
##
|
|
175
|
+
## Состояние `разбор-кончился`: папка задачи разбирается
|
|
176
176
|
|
|
177
177
|
Разбор идёт по трём исходам, а не по двум.
|
|
178
178
|
|
|
@@ -229,7 +229,7 @@ rm -r docs/tasks/<своя>
|
|
|
229
229
|
Две записи в архиве, а не одна: работы разные, и решения в них разные. Сверка очереди работ
|
|
230
230
|
после этого не называет ни одной папки — этим и проверяется, что разобраны обе.
|
|
231
231
|
|
|
232
|
-
##
|
|
232
|
+
## Состояние `папка-разобрана`: сверка очереди работ
|
|
233
233
|
|
|
234
234
|
```bash
|
|
235
235
|
npm run check:board # папка закрытой задачи среди текущих, брошенные черновики
|
|
@@ -237,7 +237,7 @@ npm run check:specs # договорённость влита, привязк
|
|
|
237
237
|
npm run check:docs # пути, названные в текстах, существуют
|
|
238
238
|
```
|
|
239
239
|
|
|
240
|
-
##
|
|
240
|
+
## Состояние `влито`: работа разбирается правилами — фоном, следом за PR
|
|
241
241
|
|
|
242
242
|
Шаг о слое правил, а не о продукте: что за эту работу грузилось, что помогло, чего не хватило и
|
|
243
243
|
где текст правила разошёлся с деревом. Знает это только тот заход, который работу вёл, — через
|
|
@@ -261,13 +261,13 @@ npm run check:docs # пути, названные в текстах, суще
|
|
|
261
261
|
3. **Вернувшиеся находки принимают одним ходом** — записать и вернуться к прежнему. Разбор,
|
|
262
262
|
отложенный «до удобного момента», не случается вовсе: заход кончается раньше.
|
|
263
263
|
|
|
264
|
-
##
|
|
264
|
+
## Состояние `влито`: находки разбора ложатся в папку задачи и ждут владельца
|
|
265
265
|
|
|
266
266
|
Ответ роли живёт в переписке и умирает вместе с ней, поэтому он сразу ложится на диск — в папку
|
|
267
267
|
задачи, файлом рядом с ходом работы. Пишет его исполнитель: роль файлов не пишет.
|
|
268
268
|
|
|
269
269
|
Папка задачи умирает со слиянием, а находки должны пережить весь эпик — владелец читает их
|
|
270
|
-
разом, когда эпик кончился. Поэтому при разборе папки
|
|
270
|
+
разом, когда эпик кончился. Поэтому при разборе папки файл находок не удаляется вместе
|
|
271
271
|
с остальным, а **переезжает к замыслу эпика**: там его найдут и после того, как ветка въехала.
|
|
272
272
|
Работа вне эпика показывает находки владельцу сразу, тем же ходом.
|
|
273
273
|
|
|
@@ -41,7 +41,7 @@ description: Паттерн правила task-flow. Брать, когда з
|
|
|
41
41
|
Незакрытый этап — тоже законная точка, если в ходе работы записано, что именно из него сделано и
|
|
42
42
|
чем это подтверждено. Незаконная точка одна: правка, о которой не записано ничего.
|
|
43
43
|
|
|
44
|
-
##
|
|
44
|
+
## Заход закрывается передачей, состояние работы при этом не меняется
|
|
45
45
|
|
|
46
46
|
Уборку этого шага — главную ветку, влитые ветки и запись самой передачи — делает команда
|
|
47
47
|
`next-session`: она проходит его целиком и называет путь к передаче последней строкой. Ниже —
|
|
@@ -34,7 +34,27 @@ description: Паттерн правила task-flow. Брать при возв
|
|
|
34
34
|
ней проверяется деревом — сборкой, тестами, чтением файла, — а не вопросом.
|
|
35
35
|
- **Не править замысел.** С ним сверяют результат; пересмотр идёт записью в ходе работы.
|
|
36
36
|
|
|
37
|
-
##
|
|
37
|
+
## Вход из передачи захода
|
|
38
|
+
|
|
39
|
+
Передача написана прошлым заходом и лежит вне дерева. Её читают как задание — и берутся за
|
|
40
|
+
работу мимо правила: состояние не сверено, правило не загружено, числа взяты на веру. Отличие
|
|
41
|
+
входа из передачи от обычного захода одно: всё, что в ней написано, проверяется деревом, потому
|
|
42
|
+
что писалась она вчера.
|
|
43
|
+
|
|
44
|
+
Порядок короткий, четыре шага:
|
|
45
|
+
|
|
46
|
+
1. **Правило ведения работы и этот паттерн — первым движением**, до первой реплики владельцу.
|
|
47
|
+
2. **Ветка и состояние работы читаются в дереве**, а не в передаче: `git branch --show-current`
|
|
48
|
+
и строка состояния в ходе работы. Разошлось с передачей — верно дерево.
|
|
49
|
+
3. **Числа из передачи пересчитываются** на текущем коммите. Оценка «работы вдвое больше» при
|
|
50
|
+
пересчёте не подтвердилась ни разу.
|
|
51
|
+
4. **Следующий шаг берётся из хода работы**, а не из раздела передачи о нём: ход работы
|
|
52
|
+
коммитится, передача — нет, и разойтись они успевают за один заход.
|
|
53
|
+
|
|
54
|
+
Чего в передаче нет и не будет: слов владельца — они в разборе просьбы; решений с причинами —
|
|
55
|
+
они в ходе работы; замысла — он на диске. Передача пересказывает, а не заменяет.
|
|
56
|
+
|
|
57
|
+
## Вход в заход: строка состояния сверяется с деревом
|
|
38
58
|
|
|
39
59
|
Сверить «Где стоим» с деревом. Запись описывает день, когда её сделали:
|
|
40
60
|
|
|
@@ -44,9 +64,11 @@ git log --oneline origin/main..HEAD
|
|
|
44
64
|
```
|
|
45
65
|
|
|
46
66
|
Разошлось — «Где стоим» правится сразу, до работы: следующий заход поверит записи, а не
|
|
47
|
-
дереву.
|
|
67
|
+
дереву. Строка состояния правится вместе с ним: гард читает её, и оставленная от прошлого
|
|
68
|
+
захода она либо отбивает законную правку, либо пропускает работу, которая до правки кода ещё
|
|
69
|
+
не дошла.
|
|
48
70
|
|
|
49
|
-
##
|
|
71
|
+
## Состояние `этап-идёт`: этап делается и отмечается в ходе работы
|
|
50
72
|
|
|
51
73
|
Раздел «Где стоим» **перезаписывается**, а не дописывается — это первое, что читает следующий
|
|
52
74
|
заход, и единственное, что переживает обрезку по объёму:
|
|
@@ -54,6 +76,7 @@ git log --oneline origin/main..HEAD
|
|
|
54
76
|
```markdown
|
|
55
77
|
## Где стоим
|
|
56
78
|
|
|
79
|
+
- **Состояние:** `этап-идёт`
|
|
57
80
|
- **Этап:** 3 из 6 — гард и хук запуска
|
|
58
81
|
- **Сделано:** закон заведён, папка задачи и образец написаны
|
|
59
82
|
- **Следующий шаг:** сценарии обоих хуков, затем подключение в настройках
|
|
@@ -71,6 +94,9 @@ git log --oneline origin/main..HEAD
|
|
|
71
94
|
- **PR:** #1396, ждёт разбора · отвечено 3 замечания из 5 · не сделано: разбор папки задачи
|
|
72
95
|
```
|
|
73
96
|
|
|
97
|
+
С открытием PR состояние становится `работа-отдана`, и обязательное действие у него другое —
|
|
98
|
+
следующая задача, а не ожидание разбора.
|
|
99
|
+
|
|
74
100
|
Решение, принятое по ходу, — вместе с причиной и с тем, что было альтернативой:
|
|
75
101
|
|
|
76
102
|
```markdown
|
|
@@ -94,7 +120,7 @@ git log --oneline origin/main..HEAD
|
|
|
94
120
|
- Доэтапное, не этой работы: сверка очереди перечисляет шесть закрытых задач вне борды.
|
|
95
121
|
```
|
|
96
122
|
|
|
97
|
-
##
|
|
123
|
+
## Состояние `работа-отдана`: следующая задача берётся тем же движением
|
|
98
124
|
|
|
99
125
|
Задача закрыта, PR открыт и ждёт владельца — заход на этом не кончается. Отданное на разбор
|
|
100
126
|
ждёт человека, а не машину: пока эпик не кончился, следующая его задача берётся сразу, тем же
|
|
@@ -21,7 +21,7 @@ description: Паттерн правила task-flow. Брать в начале
|
|
|
21
21
|
не начинается заново. Весь список — в правиле `task-flow`; он же показывается владельцу в начале
|
|
22
22
|
работы, чтобы после шести вопросов было видно, что впереди.
|
|
23
23
|
|
|
24
|
-
###
|
|
24
|
+
### Состояние `просьба-не-разобрана`: разведка — до первого вопроса
|
|
25
25
|
|
|
26
26
|
Вопрос, ответ на который лежит в коде, владельцу не задаётся: он обесценивает и остальные.
|
|
27
27
|
|
|
@@ -40,7 +40,7 @@ Agent(subagent_type: "Explore", prompt: "<тема просьбы>: что по
|
|
|
40
40
|
считается сделанным, разведка исполняется как настроение: прочитанный переданный текст сходит
|
|
41
41
|
за неё, и по текущему дереву не запускается ни одной команды.
|
|
42
42
|
|
|
43
|
-
###
|
|
43
|
+
### Состояние `просьба-не-разобрана`: разбор с владельцем
|
|
44
44
|
|
|
45
45
|
Ведёт главный агент: субагент до владельца не достучится. Команда — `/grill-me`, один вопрос
|
|
46
46
|
за раз, к каждому — свой рекомендуемый ответ с доводом.
|
|
@@ -89,7 +89,7 @@ mkdir -p docs/tasks/_draft-<slug>
|
|
|
89
89
|
cp docs/tasks/_template/grill.md docs/tasks/_draft-<slug>/grill.md
|
|
90
90
|
```
|
|
91
91
|
|
|
92
|
-
###
|
|
92
|
+
### Состояние `разбор-закрыт`: конвейер после разбора
|
|
93
93
|
|
|
94
94
|
Вопросов больше не будет — дальше роли:
|
|
95
95
|
|
|
@@ -107,7 +107,7 @@ Workflow(name: "plan", args: "docs/tasks/_draft-<slug>")
|
|
|
107
107
|
«не входит». Находка критика, расходящаяся с ответом владельца, относится владельцу — она не
|
|
108
108
|
исполняется молча и не считается закрытой правкой текста.
|
|
109
109
|
|
|
110
|
-
###
|
|
110
|
+
### Состояние `договорённость-записана`: серия задач объявляется эпиком
|
|
111
111
|
|
|
112
112
|
Разбор кончился одной задачей — шаг пропускается. Вышло несколько, и порядок между ними
|
|
113
113
|
значим — эпик объявляется здесь, до первой из них, и дважды: карточкой в очереди работ с меткой
|
|
@@ -134,7 +134,7 @@ Workflow(name: "plan", args: "docs/tasks/_draft-<slug>")
|
|
|
134
134
|
Лежит замысел вне папки задачи: та умирает с мержем первой же задачи. Каталог для него называет
|
|
135
135
|
компаньон правила — у пакета своего пути нет.
|
|
136
136
|
|
|
137
|
-
###
|
|
137
|
+
### Состояние `договорённость-записана`: задача, ветка, папка
|
|
138
138
|
|
|
139
139
|
```bash
|
|
140
140
|
npm run task:new -- --title '<Что не так>' --slug <slug> --label documentation --label area:tooling < тело.md
|
|
@@ -162,7 +162,16 @@ cp docs/tasks/_template/plan.md docs/tasks/<КЛЮЧ>-<номер>-<slug>/plan.m
|
|
|
162
162
|
cp docs/tasks/_template/progress.md docs/tasks/<КЛЮЧ>-<номер>-<slug>/progress.md
|
|
163
163
|
```
|
|
164
164
|
|
|
165
|
-
|
|
165
|
+
В ходе работы первой строкой объявляется состояние — с этой минуты его читает гард:
|
|
166
|
+
|
|
167
|
+
```markdown
|
|
168
|
+
- **Состояние:** `задача-взята`
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
Пока не объявлено состояние, в котором код правится, гард отбивает правку и называет
|
|
172
|
+
обязательное действие того состояния, которое стоит в строке.
|
|
173
|
+
|
|
174
|
+
### Состояние `задача-взята`: шапка замысла
|
|
166
175
|
|
|
167
176
|
Её читает гард:
|
|
168
177
|
|
|
@@ -72,6 +72,22 @@ flowchart TD
|
|
|
72
72
|
открытие, пока вершина главной ветки не стала предком текущей, и называет расхождение числом
|
|
73
73
|
коммитов. PR с разошедшейся ветки показывает ревьюверу свою правку вперемешку с чужой, а всё,
|
|
74
74
|
что автор проверил до публикации, он проверил от основания, которого в главной ветке уже нет.
|
|
75
|
+
- **Несошедшиеся условия поставки называются одним отказом, а не по одному.** Гард копит их все
|
|
76
|
+
и печатает разом. Отбитый по первому промаху исполнитель правит основание, повторяет вызов,
|
|
77
|
+
упирается в заголовок, правит заголовок, упирается в рабочий элемент — и каждый круг стоит
|
|
78
|
+
ещё одного вызова, хотя всё несошедшееся было известно уже на первом.
|
|
79
|
+
- **Условие, известное в начале работы, спрашивается в начале.** Заведение ветки отбивает
|
|
80
|
+
основание, в котором нет вершины главной ветки, и рабочую копию, подписывающую коммиты не той
|
|
81
|
+
почтой, что объявило дерево. На пуше и на открытии PR те же проверки остаются вторым рубежом,
|
|
82
|
+
но там они стоят дороже: основание чинится вливанием с разбором конфликта, подпись —
|
|
83
|
+
переписыванием всей ветки.
|
|
84
|
+
- **Судится то основание, которое названо командой, а не вершина рабочей копии.** Ветку заводят
|
|
85
|
+
и от вершины главной ветки прямо — этой командой основание как раз и берут свежим, — и гард,
|
|
86
|
+
читающий только текущую вершину, отбивал бы её наравне с веткой от вчерашнего дерева.
|
|
87
|
+
Основание, о котором дерево ничего не знает, не судится вовсе.
|
|
88
|
+
- **Ветка без номера рабочего элемента условий поставки не получает.** Локальная ветка под пробу
|
|
89
|
+
законна, и требовать от неё свежего основания значило бы отбивать работу, которая в главную
|
|
90
|
+
не поедет: PR с такой ветки не откроется.
|
|
75
91
|
- **Правка кода отдаётся человеку открытым PR, а не запушенной веткой.** Ветка в списке ветвей
|
|
76
92
|
ему не показывается, в его дела не приходит и обсуждения не имеет: до открытия PR правки для
|
|
77
93
|
человека нет. Открывается он тем же ходом, которым исполнитель говорит, что работу отдаёт, и
|
|
@@ -100,6 +116,12 @@ flowchart TD
|
|
|
100
116
|
элемент переводится в `Active`, PR открыт — в `Resolved`; делает это команда перевода, а не
|
|
101
117
|
набор вызовов по памяти. Перевод идёт сразу за шагом, который его вызвал: очередь работ
|
|
102
118
|
читают между шагами, а не после них.
|
|
119
|
+
- **Рабочий элемент, оставшийся в начальном состоянии, PR не открывает.** Гард поставки называет
|
|
120
|
+
это состояние и команду перевода: по очереди работ такой элемент читается как невзятый, хотя
|
|
121
|
+
работа по нему сделана и выложена. На заведении ветки состояние не спрашивается — там его ещё
|
|
122
|
+
не двигали, и требование отбивало бы первую же команду работы вместе с той, которая его и
|
|
123
|
+
снимает. Имя начального состояния дерево называет само; не названо — состояние не судится
|
|
124
|
+
вовсе.
|
|
103
125
|
- **На доске стоят рабочие элементы, а не PR о них.** Доска показывает, что сделано и что
|
|
104
126
|
осталось; PR отвечает на другой вопрос — как именно сделано, — и открывается из элемента, где
|
|
105
127
|
связь с ним стоит сама. Здесь эта связь ставится при создании PR, поэтому отдельная карточка
|
|
@@ -209,16 +231,21 @@ flowchart TD
|
|
|
209
231
|
проверки под него не заводится: работа опознаётся заголовком рабочего элемента и PR, а это
|
|
210
232
|
сверяется у всех. Сверка очереди имя ветки не судит вовсе: у открытого PR его не переименовать.
|
|
211
233
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
234
|
+
Рабочий элемент в работу гард не переводит: доску он не правит — правка доски в разборе команды
|
|
235
|
+
падала бы вместе со связью и отбивала бы работу вместо промаха. Перевод держится памятью и
|
|
236
|
+
подсказкой, которую печатает команда заведения. Элемент, оставшийся в начальном состоянии, гард
|
|
237
|
+
называет на открытии PR — то есть после того, как его должны были перевести; прочие расхождения
|
|
238
|
+
состояния находит сверка очереди.
|
|
216
239
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
240
|
+
Ревьювера гард здесь не спрашивает: снятие черновика идёт правкой самого PR — тем же вызовом, что
|
|
241
|
+
и остальные его поля, — и от прочих правок машине оно неотличимо. Держится это словарём выше, где
|
|
242
|
+
разбор PR ведёт ревьювер, и памятью того, кто черновик снимает.
|
|
243
|
+
|
|
244
|
+
Свежесть самой вершины главной ветки гард спрашивает вторым ярусом — тем же приёмом, что и
|
|
245
|
+
состояние задачи: есть чем спросить, спрашивает; нет сети или доступа — пропускает молча. Первый
|
|
246
|
+
ярус при этом остаётся, и работает он без сети: локальная ссылка отвечает на вопрос «отстало ли
|
|
247
|
+
основание от того, что уже лежит в дереве», удалённая — на вопрос «не протухла ли сама ссылка».
|
|
248
|
+
Без второго яруса молчание гарда значило лишь первое, а читалось как второе.
|
|
222
249
|
|
|
223
250
|
## Паттерны
|
|
224
251
|
|