@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.
Files changed (74) hide show
  1. package/README.md +21 -7
  2. package/assets/agents/conscience.md +58 -0
  3. package/assets/agents/prose-editor.md +44 -0
  4. package/assets/agents/strict-teacher.md +59 -0
  5. package/assets/checks/board.github.mjs +56 -1
  6. package/assets/checks/check-board.github.mjs +24 -0
  7. package/assets/checks/check-doc-paths.mjs +4 -15
  8. package/assets/checks/check-dupes.mjs +5 -6
  9. package/assets/checks/check-file-size.mjs +6 -20
  10. package/assets/checks/check-prose-style.mjs +137 -0
  11. package/assets/checks/check-reuse.mjs +5 -5
  12. package/assets/checks/check-styles.mjs +5 -5
  13. package/assets/checks/lib-common.mjs +3 -3
  14. package/assets/checks/rt-kit-checks.config.mjs +85 -1
  15. package/assets/defaults/project.sh +50 -0
  16. package/assets/docs/GLOSSARY.md +17 -15
  17. package/assets/hooks/browser-guard-device-id.sh +9 -1
  18. package/assets/hooks/browser-guard-no-asking.sh +9 -1
  19. package/assets/hooks/browser-guard-no-listing.sh +9 -1
  20. package/assets/hooks/browser-guard-no-other-drivers.sh +7 -1
  21. package/assets/hooks/browser-guard-require-select.sh +11 -2
  22. package/assets/hooks/claim-guard.sh +113 -0
  23. package/assets/hooks/conscience-guard.sh +98 -0
  24. package/assets/hooks/deny-tail.sh +32 -0
  25. package/assets/hooks/dev-server-guard.sh +8 -2
  26. package/assets/hooks/docs-guard.sh +14 -2
  27. package/assets/hooks/exam-guard.sh +121 -0
  28. package/assets/hooks/git-guard-delivery-signature.sh +75 -0
  29. package/assets/hooks/git-guard-delivery.sh +154 -68
  30. package/assets/hooks/git-guard-main.sh +12 -0
  31. package/assets/hooks/git-guard-push-tests.sh +49 -2
  32. package/assets/hooks/grill-gate.sh +12 -0
  33. package/assets/hooks/handoff-entry-guard.sh +71 -0
  34. package/assets/hooks/postmortem-guard.sh +12 -0
  35. package/assets/hooks/proposal-guard.sh +12 -0
  36. package/assets/hooks/prose-style-guard.sh +73 -0
  37. package/assets/hooks/qa-dataid-guard.sh +12 -1
  38. package/assets/hooks/rerun-guard.sh +86 -0
  39. package/assets/hooks/reuse-first-guard.sh +12 -1
  40. package/assets/hooks/roles.sh +32 -0
  41. package/assets/hooks/skill-gate.sh +12 -0
  42. package/assets/hooks/sql-guard-write.sh +2 -1
  43. package/assets/hooks/sql-guard.sh +12 -1
  44. package/assets/hooks/task-flow-guard.sh +71 -7
  45. package/assets/hooks/turn-exit-guard.sh +189 -0
  46. package/assets/hooks/waiting-turn-guard.sh +12 -0
  47. package/assets/hooks/window-fill-guard.sh +12 -1
  48. package/assets/patterns/task-flow-close.md +8 -8
  49. package/assets/patterns/task-flow-handoff.md +1 -1
  50. package/assets/patterns/task-flow-resume.md +30 -4
  51. package/assets/patterns/task-flow-start.md +15 -6
  52. package/assets/rules/git-workflow.azure.md +36 -9
  53. package/assets/rules/git-workflow.github.md +74 -9
  54. package/assets/rules/git-workflow.gitlab.md +40 -12
  55. package/assets/rules/task-flow.md +123 -71
  56. package/assets/rules/testing.md +15 -1
  57. package/assets/samples/tasks/_template/plan.md +4 -1
  58. package/assets/samples/tasks/_template/progress.md +1 -0
  59. package/assets/skills/agent-kit.md +33 -0
  60. package/assets/templates/project.sh +16 -0
  61. package/bin/agent-kit.d.ts.map +1 -1
  62. package/bin/agent-kit.js +4 -2
  63. package/bin/agent-kit.js.map +1 -1
  64. package/lib/config.d.ts +8 -0
  65. package/lib/config.d.ts.map +1 -1
  66. package/lib/config.js +1 -0
  67. package/lib/config.js.map +1 -1
  68. package/lib/enroll.d.ts +9 -1
  69. package/lib/enroll.d.ts.map +1 -1
  70. package/lib/enroll.js +65 -18
  71. package/lib/enroll.js.map +1 -1
  72. package/package.json +1 -1
  73. package/rt-tools-agent-kit-0.10.0.tgz +0 -0
  74. 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
- ## 10. Работа отдаётся на разбор
22
+ ## Состояние `этапы-кончились`: работа отдаётся на разбор
23
23
 
24
24
  PR открыт черновиком — и с этой минуты работа ждёт владельца, а не машину. Заход на этом не
25
25
  кончается: следующая задача эпика берётся тем же движением, паттерн `task-flow-resume`.
@@ -102,7 +102,7 @@ PR #<номер> готов к слиянию: прогон зелёный, па
102
102
  кто пишет тело. Разница между ними одна, и она вся: реплику владелец прочитает, только если
103
103
  вернётся в переписку, а раздел он видит там, куда смотрит, нажимая кнопку.
104
104
 
105
- ## 12. Договорённость вливается в спек домена
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
- ## 13. Тексты домена приводятся к сделанному
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
- ## 14. Папка задачи разбирается
175
+ ## Состояние `разбор-кончился`: папка задачи разбирается
176
176
 
177
177
  Разбор идёт по трём исходам, а не по двум.
178
178
 
@@ -229,7 +229,7 @@ rm -r docs/tasks/<своя>
229
229
  Две записи в архиве, а не одна: работы разные, и решения в них разные. Сверка очереди работ
230
230
  после этого не называет ни одной папки — этим и проверяется, что разобраны обе.
231
231
 
232
- ## 15. Сверка
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
- ## 16. Работа разбирается правилами — фоном, следом за PR
240
+ ## Состояние `влито`: работа разбирается правилами — фоном, следом за PR
241
241
 
242
242
  Шаг о слое правил, а не о продукте: что за эту работу грузилось, что помогло, чего не хватило и
243
243
  где текст правила разошёлся с деревом. Знает это только тот заход, который работу вёл, — через
@@ -261,13 +261,13 @@ npm run check:docs # пути, названные в текстах, суще
261
261
  3. **Вернувшиеся находки принимают одним ходом** — записать и вернуться к прежнему. Разбор,
262
262
  отложенный «до удобного момента», не случается вовсе: заход кончается раньше.
263
263
 
264
- ## 17. Находки разбора ложатся в папку задачи и ждут владельца
264
+ ## Состояние `влито`: находки разбора ложатся в папку задачи и ждут владельца
265
265
 
266
266
  Ответ роли живёт в переписке и умирает вместе с ней, поэтому он сразу ложится на диск — в папку
267
267
  задачи, файлом рядом с ходом работы. Пишет его исполнитель: роль файлов не пишет.
268
268
 
269
269
  Папка задачи умирает со слиянием, а находки должны пережить весь эпик — владелец читает их
270
- разом, когда эпик кончился. Поэтому при разборе папки (шаг 14) файл находок не удаляется вместе
270
+ разом, когда эпик кончился. Поэтому при разборе папки файл находок не удаляется вместе
271
271
  с остальным, а **переезжает к замыслу эпика**: там его найдут и после того, как ветка въехала.
272
272
  Работа вне эпика показывает находки владельцу сразу, тем же ходом.
273
273
 
@@ -41,7 +41,7 @@ description: Паттерн правила task-flow. Брать, когда з
41
41
  Незакрытый этап — тоже законная точка, если в ходе работы записано, что именно из него сделано и
42
42
  чем это подтверждено. Незаконная точка одна: правка, о которой не записано ничего.
43
43
 
44
- ## 9. Заход закрывается передачей
44
+ ## Заход закрывается передачей, состояние работы при этом не меняется
45
45
 
46
46
  Уборку этого шага — главную ветку, влитые ветки и запись самой передачи — делает команда
47
47
  `next-session`: она проходит его целиком и называет путь к передаче последней строкой. Ниже —
@@ -34,7 +34,27 @@ description: Паттерн правила task-flow. Брать при возв
34
34
  ней проверяется деревом — сборкой, тестами, чтением файла, — а не вопросом.
35
35
  - **Не править замысел.** С ним сверяют результат; пересмотр идёт записью в ходе работы.
36
36
 
37
- ## 7. Возвращение к работе новым заходом
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
- ## 8. Этап делается и отмечается в ходе работы
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
- ## 11. Следующая задача эпика берётся тем же движением
123
+ ## Состояние `работа-отдана`: следующая задача берётся тем же движением
98
124
 
99
125
  Задача закрыта, PR открыт и ждёт владельца — заход на этом не кончается. Отданное на разбор
100
126
  ждёт человека, а не машину: пока эпик не кончился, следующая его задача берётся сразу, тем же
@@ -21,7 +21,7 @@ description: Паттерн правила task-flow. Брать в начале
21
21
  не начинается заново. Весь список — в правиле `task-flow`; он же показывается владельцу в начале
22
22
  работы, чтобы после шести вопросов было видно, что впереди.
23
23
 
24
- ### 1. Разведка — до первого вопроса
24
+ ### Состояние `просьба-не-разобрана`: разведка — до первого вопроса
25
25
 
26
26
  Вопрос, ответ на который лежит в коде, владельцу не задаётся: он обесценивает и остальные.
27
27
 
@@ -40,7 +40,7 @@ Agent(subagent_type: "Explore", prompt: "<тема просьбы>: что по
40
40
  считается сделанным, разведка исполняется как настроение: прочитанный переданный текст сходит
41
41
  за неё, и по текущему дереву не запускается ни одной команды.
42
42
 
43
- ### 2. Разбор с владельцем
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
- ### 3. Конвейер после разбора
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
- ### 4. Серия задач объявляется эпиком
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
- ### 5. Задача, ветка, папка
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
- ### 6. Шапка замысла
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
- задачи. Отставшее состояние находит сверка очереди но уже после того, как PR открыт.
234
+ Рабочий элемент в работу гард не переводит: доску он не правит правка доски в разборе команды
235
+ падала бы вместе со связью и отбивала бы работу вместо промаха. Перевод держится памятью и
236
+ подсказкой, которую печатает команда заведения. Элемент, оставшийся в начальном состоянии, гард
237
+ называет на открытии PRто есть после того, как его должны были перевести; прочие расхождения
238
+ состояния находит сверка очереди.
216
239
 
217
- Свежесть самой вершины главной ветки гард не проверяет: он судит по тому, что лежит в дереве, и
218
- вершину недельной давности от сегодняшней не отличает. Сетевой вызов сюда не заводится по той же
219
- причине, что и выше, он падал бы вместе со связью. Поэтому гард ловит ветку, отставшую
220
- заведомо, а `git fetch` перед вливанием стоит первым пунктом чеклиста и держится тем же, чем
221
- держатся остальные его пункты.
240
+ Ревьювера гард здесь не спрашивает: снятие черновика идёт правкой самого PR тем же вызовом, что
241
+ и остальные его поля, — и от прочих правок машине оно неотличимо. Держится это словарём выше, где
242
+ разбор PR ведёт ревьювер, и памятью того, кто черновик снимает.
243
+
244
+ Свежесть самой вершины главной ветки гард спрашивает вторым ярусом — тем же приёмом, что и
245
+ состояние задачи: есть чем спросить, спрашивает; нет сети или доступа — пропускает молча. Первый
246
+ ярус при этом остаётся, и работает он без сети: локальная ссылка отвечает на вопрос «отстало ли
247
+ основание от того, что уже лежит в дереве», удалённая — на вопрос «не протухла ли сама ссылка».
248
+ Без второго яруса молчание гарда значило лишь первое, а читалось как второе.
222
249
 
223
250
  ## Паттерны
224
251