@rt-tools/agent-kit 0.10.0 → 0.12.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/check-file-size.mjs +19 -4
- package/assets/checks/check-state-next.mjs +202 -0
- package/assets/checks/check-states.mjs +142 -0
- package/assets/checks/check-turn-map.mjs +146 -0
- package/assets/checks/rt-kit-checks.config.mjs +16 -2
- package/assets/defaults/project.sh +67 -7
- package/assets/defaults/turn-map.md +48 -0
- package/assets/hooks/browser-device-id.sh +2 -0
- package/assets/hooks/browser-guard-device-id.sh +5 -1
- package/assets/hooks/browser-guard-no-asking.sh +5 -1
- package/assets/hooks/browser-guard-no-listing.sh +2 -0
- package/assets/hooks/browser-guard-no-other-drivers.sh +7 -3
- package/assets/hooks/browser-guard-require-select.sh +6 -2
- package/assets/hooks/claim-guard.sh +5 -1
- package/assets/hooks/commit-msg.sh +2 -0
- package/assets/hooks/conscience-guard.sh +5 -1
- package/assets/hooks/constitution-index.sh +2 -0
- package/assets/hooks/dev-server-guard.sh +7 -3
- package/assets/hooks/dispatch.sh +69 -0
- package/assets/hooks/docs-guard.sh +8 -4
- package/assets/hooks/exam-guard.sh +7 -3
- package/assets/hooks/git-guard-delivery-signature.sh +2 -0
- package/assets/hooks/git-guard-delivery.sh +39 -5
- package/assets/hooks/git-guard-main.sh +8 -4
- package/assets/hooks/git-guard-push-tests.sh +8 -4
- package/assets/hooks/glossary-load.sh +2 -0
- package/assets/hooks/grill-gate.sh +6 -2
- package/assets/hooks/handoff-entry-guard.sh +6 -2
- package/assets/hooks/handoff-write.sh +124 -0
- package/assets/hooks/hook-input.sh +54 -0
- package/assets/hooks/lint-after-edit.sh +7 -3
- package/assets/hooks/observe.sh +2 -0
- package/assets/hooks/postmortem-guard.sh +5 -1
- package/assets/hooks/proposal-guard.sh +5 -1
- package/assets/hooks/prose-style-guard.sh +7 -3
- package/assets/hooks/qa-dataid-guard.sh +6 -2
- package/assets/hooks/rerun-guard.sh +7 -3
- package/assets/hooks/reuse-first-guard.sh +7 -3
- package/assets/hooks/roles.sh +2 -0
- package/assets/hooks/rule-article.sh +99 -0
- package/assets/hooks/skill-gate-layers.sh +2 -0
- package/assets/hooks/skill-gate-rearm.sh +5 -1
- package/assets/hooks/skill-gate.sh +25 -2
- package/assets/hooks/skill-loaded.sh +5 -1
- package/assets/hooks/sql-guard-parse.sh +2 -0
- package/assets/hooks/sql-guard-request.sh +4 -1
- package/assets/hooks/sql-guard-target.sh +2 -0
- package/assets/hooks/sql-guard-write.sh +2 -0
- package/assets/hooks/sql-guard.sh +6 -2
- package/assets/hooks/task-context-load.sh +2 -0
- package/assets/hooks/task-flow-guard.sh +8 -4
- package/assets/hooks/turn-entry-load.sh +62 -0
- package/assets/hooks/turn-exit-guard.sh +44 -17
- package/assets/hooks/utf8.sh +35 -0
- package/assets/hooks/waiting-turn-guard.sh +5 -1
- package/assets/hooks/window-fill-guard.sh +37 -5
- package/assets/laws/work-conduct.md +34 -9
- package/assets/patterns/dependencies-upgrade.md +1 -1
- package/assets/patterns/doc-style-write.md +3 -3
- package/assets/patterns/git-workflow-commit.azure.md +2 -202
- package/assets/patterns/git-workflow-commit.github.md +2 -258
- package/assets/patterns/git-workflow-commit.gitlab.md +1 -217
- package/assets/patterns/git-workflow-docker.md +3 -3
- package/assets/patterns/git-workflow-merge.md +3 -2
- package/assets/patterns/git-workflow-migration.md +3 -3
- package/assets/patterns/git-workflow-pr.azure.md +224 -0
- package/assets/patterns/git-workflow-pr.github.md +280 -0
- package/assets/patterns/git-workflow-pr.gitlab.md +240 -0
- package/assets/patterns/git-workflow-restart.md +3 -3
- package/assets/patterns/git-workflow-secrets.md +3 -3
- package/assets/patterns/task-flow-archive.md +193 -0
- package/assets/patterns/task-flow-close.md +11 -160
- package/assets/patterns/task-flow-handoff.md +22 -4
- package/assets/patterns/task-flow-resume.md +6 -0
- package/assets/patterns/task-flow-start.md +41 -0
- package/assets/patterns/turn-entry-map.md +81 -0
- package/assets/pitfalls/doc-style.md +80 -0
- package/assets/pitfalls/git-workflow.azure.md +50 -0
- package/assets/pitfalls/git-workflow.github.md +78 -0
- package/assets/pitfalls/git-workflow.gitlab.md +49 -0
- package/assets/pitfalls/spec-driven.md +36 -0
- package/assets/pitfalls/styling-bem.md +45 -0
- package/assets/pitfalls/task-flow.md +62 -0
- package/assets/pitfalls/testing.md +70 -0
- package/assets/rules/deploy-flow.azure.md +106 -0
- package/assets/rules/deploy-flow.github.md +113 -0
- package/assets/rules/deploy-flow.gitlab.md +108 -0
- package/assets/rules/doc-style.md +25 -76
- package/assets/rules/git-workflow.azure.md +6 -92
- package/assets/rules/git-workflow.github.md +14 -127
- package/assets/rules/git-workflow.gitlab.md +6 -93
- package/assets/rules/spec-driven.md +39 -30
- package/assets/rules/styling-bem.md +20 -39
- package/assets/rules/task-flow.md +17 -186
- package/assets/rules/testing.md +3 -64
- package/assets/rules/turn-conduct.md +206 -0
- package/assets/rules/turn-entry.md +93 -0
- package/assets/rules/typescript-conventions.md +15 -0
- package/assets/skills/agent-kit.md +35 -12
- package/assets/templates/pitfalls.md +10 -0
- package/assets/templates/rule.md +5 -3
- package/lib/assets.d.ts.map +1 -1
- package/lib/assets.js +6 -1
- package/lib/assets.js.map +1 -1
- package/lib/cargo.d.ts +42 -0
- package/lib/cargo.d.ts.map +1 -1
- package/lib/cargo.js +2 -0
- package/lib/cargo.js.map +1 -1
- package/lib/cascade.d.ts.map +1 -1
- package/lib/cascade.js +19 -1
- package/lib/cascade.js.map +1 -1
- package/lib/commands.d.ts.map +1 -1
- package/lib/commands.js +65 -4
- package/lib/commands.js.map +1 -1
- package/lib/config.d.ts +16 -1
- package/lib/config.d.ts.map +1 -1
- package/lib/config.js +8 -0
- package/lib/config.js.map +1 -1
- package/lib/hooks-map.d.ts +13 -0
- package/lib/hooks-map.d.ts.map +1 -1
- package/lib/hooks-map.js +33 -1
- package/lib/hooks-map.js.map +1 -1
- package/lib/observations.d.ts +35 -1
- package/lib/observations.d.ts.map +1 -1
- package/lib/observations.js +14 -2
- package/lib/observations.js.map +1 -1
- package/lib/ship.d.ts +2 -0
- package/lib/ship.d.ts.map +1 -1
- package/lib/ship.js +2 -0
- package/lib/ship.js.map +1 -1
- package/lib/thresholds.d.ts +49 -0
- package/lib/thresholds.d.ts.map +1 -0
- package/lib/thresholds.js +151 -0
- package/lib/thresholds.js.map +1 -0
- package/package.json +1 -1
- package/rt-tools-agent-kit-0.12.0.tgz +0 -0
- package/assets/commands/agent-kit-digest.md +0 -88
- package/assets/commands/rules-review.md +0 -98
- package/rt-tools-agent-kit-0.10.0.tgz +0 -0
|
@@ -13,6 +13,8 @@
|
|
|
13
13
|
# команда становилась одним сегментом, доказательство чтения из первой строки покрывало
|
|
14
14
|
# неразобранный вызов из второй, и запись на боевую базу проходила молча — тот же обход,
|
|
15
15
|
# что чинили для `&&`, только набранный с новой строки.
|
|
16
|
+
. "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/utf8.sh" 2>/dev/null || true
|
|
17
|
+
|
|
16
18
|
normalize() {
|
|
17
19
|
perl -0ne '
|
|
18
20
|
# Продолжение длинной команды обратным слешем — это одна строка, а не две: без
|
|
@@ -10,6 +10,9 @@
|
|
|
10
10
|
|
|
11
11
|
# Три вида ввода: запрос через подключение редактора, команда оболочки и та же команда,
|
|
12
12
|
# завёрнутая в универсальный исполнитель. Инструмент, не названный здесь, гарда не касается.
|
|
13
|
+
. "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/utf8.sh" 2>/dev/null || true
|
|
14
|
+
. "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/hook-input.sh" 2>/dev/null || true
|
|
15
|
+
|
|
13
16
|
sql_read_request() {
|
|
14
17
|
sql=""
|
|
15
18
|
context=""
|
|
@@ -23,7 +26,7 @@ sql_read_request() {
|
|
|
23
26
|
# Терминал IDE исполняет ту же командную строку и кладёт её в то же поле, что и Bash:
|
|
24
27
|
# без этой ветки весь гард обходился сменой инструмента.
|
|
25
28
|
Bash | mcp__webstorm__execute_terminal_command | mcp__webstorm__execute_tool)
|
|
26
|
-
cmd="$(
|
|
29
|
+
cmd="$(rt_hook_cmd)"
|
|
27
30
|
[ -z "$cmd" ] && exit 0
|
|
28
31
|
|
|
29
32
|
# Универсальный исполнитель зовёт ЛЮБОЙ инструмент редактора по имени, в том числе
|
|
@@ -18,6 +18,8 @@
|
|
|
18
18
|
# подключения, и по одному SQL отличить прод от локальной копии невозможно. Поэтому
|
|
19
19
|
# подключения опознаются в лицо. Список сверяется вызовом list_database_connections;
|
|
20
20
|
# добавили новое — допишите сюда, иначе оно попадёт в «неизвестные» ниже.
|
|
21
|
+
. "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/utf8.sh" 2>/dev/null || true
|
|
22
|
+
|
|
21
23
|
sql_resolve_target() {
|
|
22
24
|
is_prod=""
|
|
23
25
|
PROD_CONNECTIONS="${RT_PROD_CONNECTIONS:-}"
|
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
|
|
8
8
|
# Глаголы ищутся в сегментах ВЫЗОВА, а не по всей строке: иначе слово из шаблона поиска в
|
|
9
9
|
# соседнем звене цепочки объявляло записью читающую команду.
|
|
10
|
+
. "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/utf8.sh" 2>/dev/null || true
|
|
11
|
+
|
|
10
12
|
sql_detect_write() {
|
|
11
13
|
is_write=""
|
|
12
14
|
write_scope="$segments"
|
|
@@ -40,11 +40,15 @@
|
|
|
40
40
|
# пропускаются целиком — иначе слова «drop» и «update» в описании коммита читались бы как
|
|
41
41
|
# запрос. Доставки запроса внутри такой команды не бывает, так что цена послабления нулевая.
|
|
42
42
|
|
|
43
|
-
|
|
43
|
+
. "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/utf8.sh" 2>/dev/null || true
|
|
44
|
+
. "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/hook-input.sh" 2>/dev/null || true
|
|
45
|
+
|
|
46
|
+
rt_hook_read
|
|
47
|
+
input="$RT_HOOK_INPUT"
|
|
44
48
|
[ -z "$input" ] && exit 0
|
|
45
49
|
command -v jq >/dev/null 2>&1 || exit 0
|
|
46
50
|
|
|
47
|
-
tool="$(
|
|
51
|
+
tool="$(rt_hook_tool)"
|
|
48
52
|
|
|
49
53
|
# Рабочий каталог нужен одному правилу — разрешению адреса миграции: `DATABASE_URL`
|
|
50
54
|
# обычно не назван в команде и лежит в `.env` рядом с проектом.
|
|
@@ -14,6 +14,8 @@
|
|
|
14
14
|
# FAIL-OPEN: нет `jq`, не git-репозиторий, нет папки задачи — выходим молча. Сессия важнее
|
|
15
15
|
# контекста.
|
|
16
16
|
|
|
17
|
+
. "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/utf8.sh" 2>/dev/null || true
|
|
18
|
+
|
|
17
19
|
ROOT="${CLAUDE_PROJECT_DIR:-.}"
|
|
18
20
|
command -v jq >/dev/null 2>&1 || exit 0
|
|
19
21
|
cd "$ROOT" 2>/dev/null || exit 0
|
|
@@ -25,7 +25,11 @@
|
|
|
25
25
|
# FAIL-OPEN: нет jq, не git-репозиторий, битый ввод, чужой инструмент → пропуск. Сломанный
|
|
26
26
|
# гард не должен мешать работать.
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
. "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/utf8.sh" 2>/dev/null || true
|
|
29
|
+
. "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/hook-input.sh" 2>/dev/null || true
|
|
30
|
+
|
|
31
|
+
rt_hook_read
|
|
32
|
+
input="$RT_HOOK_INPUT"
|
|
29
33
|
[ -z "$input" ] && exit 0
|
|
30
34
|
command -v jq >/dev/null 2>&1 || exit 0
|
|
31
35
|
|
|
@@ -43,7 +47,7 @@ done
|
|
|
43
47
|
[ -f "$rt_hooks_dir/profile-check.sh" ] && . "$rt_hooks_dir/profile-check.sh"
|
|
44
48
|
command -v rt_needs >/dev/null 2>&1 || rt_needs() { command -v "$1" >/dev/null 2>&1; }
|
|
45
49
|
|
|
46
|
-
tool="$(
|
|
50
|
+
tool="$(rt_hook_tool)"
|
|
47
51
|
candidates=""
|
|
48
52
|
case "$tool" in
|
|
49
53
|
# Инструмент редактора заводит файл теми же двумя данными, только называет их иначе —
|
|
@@ -59,7 +63,7 @@ case "$tool" in
|
|
|
59
63
|
# имён гард стоял бы объявленным на них и молча пропускал — состояние хуже необъявленного,
|
|
60
64
|
# потому что снаружи выглядит закрытым.
|
|
61
65
|
Bash | mcp__webstorm__execute_terminal_command | mcp__webstorm__execute_tool)
|
|
62
|
-
cmd="$(
|
|
66
|
+
cmd="$(rt_hook_cmd)"
|
|
63
67
|
[ -z "$cmd" ] && exit 0
|
|
64
68
|
# Универсальный исполнитель прячет настоящую команду во вложенной строке: без её разбора
|
|
65
69
|
# путь стоит за кавычкой, и до него не дотягивается ни один образец.
|
|
@@ -125,7 +129,7 @@ deny() {
|
|
|
125
129
|
}
|
|
126
130
|
|
|
127
131
|
# Ветку смотрим там же, где пойдёт правка: у worktree она своя.
|
|
128
|
-
workdir="$(
|
|
132
|
+
workdir="$(rt_hook_cwd)"
|
|
129
133
|
[ -z "$workdir" ] && workdir="${CLAUDE_PROJECT_DIR:-.}"
|
|
130
134
|
cd "$workdir" 2>/dev/null || exit 0
|
|
131
135
|
git rev-parse --is-inside-work-tree >/dev/null 2>&1 || exit 0
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# rt-hook: SessionStart startup|resume|compact|clear
|
|
3
|
+
# Требует: hooks/handoff-write.sh
|
|
4
|
+
# SessionStart: передача прошлого захода и карта хода уезжают в контекст на каждом запуске.
|
|
5
|
+
#
|
|
6
|
+
# Передачу пишет хук перед сжатием — и на этом её путь кончался: читать её не умел никто, и
|
|
7
|
+
# в новый заход её клал человек. Написанная и не прочитанная, она равна ненаписанной.
|
|
8
|
+
#
|
|
9
|
+
# Второе, чего заход после сжатия не знает, — как здесь ведут работу. Перечень состояний с
|
|
10
|
+
# обязательными действиями лежит в правиле, а правило после сжатия не загружено: заход первым
|
|
11
|
+
# движением идёт читать его целиком, то есть тратит на восстановление порядка ту часть окна,
|
|
12
|
+
# ради которой сжатие и случилось. Карта отвечает на этот вопрос до того, как он задан.
|
|
13
|
+
#
|
|
14
|
+
# Подаётся это на всех четырёх запусках, а не только после сжатия: заход после обрыва и заход
|
|
15
|
+
# после очистки начинают с того же пустого места, и разница между ними исполнителю не видна.
|
|
16
|
+
#
|
|
17
|
+
# FAIL-OPEN: не git-репозиторий, нет ветки, нет каталога передач, нет карты, нечитаемый файл —
|
|
18
|
+
# хук выходит нулём и молчит о той части, которой нет. Запуск он не отбивает никогда: сессия
|
|
19
|
+
# важнее контекста.
|
|
20
|
+
|
|
21
|
+
. "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/utf8.sh" 2>/dev/null || true
|
|
22
|
+
|
|
23
|
+
ROOT="${CLAUDE_PROJECT_DIR:-.}"
|
|
24
|
+
cd "$ROOT" 2>/dev/null || exit 0
|
|
25
|
+
git rev-parse --is-inside-work-tree >/dev/null 2>&1 || exit 0
|
|
26
|
+
|
|
27
|
+
branch="$(git branch --show-current 2>/dev/null)"
|
|
28
|
+
|
|
29
|
+
# Профиль дерева: сперва умолчание пакета, поверх него — надстройка проекта, если она есть.
|
|
30
|
+
rt_hooks_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
31
|
+
for profile in "$rt_hooks_dir/../rt-kit/defaults/project.sh" "$rt_hooks_dir/../defaults/project.sh" \
|
|
32
|
+
"$ROOT/.claude/rt-kit/defaults/project.sh" "$ROOT/.claude/rt-kit/project.sh"; do
|
|
33
|
+
# shellcheck disable=SC1090
|
|
34
|
+
[ -f "$profile" ] && . "$profile" 2>/dev/null
|
|
35
|
+
done
|
|
36
|
+
|
|
37
|
+
handoff_dir="${RT_HANDOFF_DIR:-.claude/handoff}"
|
|
38
|
+
map="$rt_hooks_dir/../rt-kit/defaults/turn-map.md"
|
|
39
|
+
[ -f "$map" ] || map="$ROOT/.claude/rt-kit/defaults/turn-map.md"
|
|
40
|
+
|
|
41
|
+
# Передача берётся по имени текущей ветки. Чужая, поданная как своя, описывает работу, которой
|
|
42
|
+
# в этом дереве нет, — поэтому имя файла собирается из ветки, а не выбирается из каталога.
|
|
43
|
+
handoff=''
|
|
44
|
+
[ -n "$branch" ] && handoff="$ROOT/$handoff_dir/$branch.md"
|
|
45
|
+
|
|
46
|
+
if [ -n "$handoff" ] && [ -r "$handoff" ]; then
|
|
47
|
+
printf 'ПЕРЕДАЧА ПРОШЛОГО ЗАХОДА — `%s/%s.md`, ниже целиком.\n\n' "$handoff_dir" "$branch"
|
|
48
|
+
printf 'Она написана прошлым заходом и описывает минуту, когда её собрали. Всё, что в ней\n'
|
|
49
|
+
printf 'стоит о дереве, проверяется деревом: команда этого хода главнее записанной вчера.\n\n'
|
|
50
|
+
cat "$handoff" 2>/dev/null
|
|
51
|
+
printf '\n\n'
|
|
52
|
+
fi
|
|
53
|
+
|
|
54
|
+
if [ -r "$map" ]; then
|
|
55
|
+
printf 'КАРТА ХОДА — состояния работы и то, чем ход кончается. Ниже целиком.\n\n'
|
|
56
|
+
printf 'Это выжимка правила ведения работы, а не оно само: правило объясняет, карта\n'
|
|
57
|
+
printf 'называет. Своё состояние заход читает в разделе «Где стоим» хода работы.\n\n'
|
|
58
|
+
cat "$map" 2>/dev/null
|
|
59
|
+
printf '\n'
|
|
60
|
+
fi
|
|
61
|
+
|
|
62
|
+
exit 0
|
|
@@ -22,14 +22,21 @@
|
|
|
22
22
|
# 5. Передача захода написана — окно кончилось.
|
|
23
23
|
# 6. Владелец сказал остановиться.
|
|
24
24
|
#
|
|
25
|
-
#
|
|
26
|
-
#
|
|
25
|
+
# Работа без ветки и без папки задачи судится вторым признаком. Состояния у неё нет, и первый
|
|
26
|
+
# признак взять неоткуда, — но ход, в котором не было ни одной правки дерева, не кончается и
|
|
27
|
+
# здесь: просьба владельца «разложи», «обнови», «посмотри» живёт без задачи и без ветки, и
|
|
28
|
+
# защищена она была меньше всего. Отпускают такой ход те же четыре вещи: вопрос, отказ гарда,
|
|
29
|
+
# написанная передача и слово владельца об остановке.
|
|
27
30
|
#
|
|
28
31
|
# ОТКАЗ В ПОЛЬЗУ РАБОТЫ: при любой ошибке, нехватке `jq`, отсутствии записи хода, папки задачи
|
|
29
32
|
# или строки состояния ход РАЗРЕШАЕТСЯ (exit 0). Сломанный страж не имеет права заклинить
|
|
30
33
|
# разговор.
|
|
31
34
|
|
|
32
|
-
|
|
35
|
+
. "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/utf8.sh" 2>/dev/null || true
|
|
36
|
+
. "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/hook-input.sh" 2>/dev/null || true
|
|
37
|
+
|
|
38
|
+
rt_hook_read
|
|
39
|
+
input="$RT_HOOK_INPUT"
|
|
33
40
|
[ -z "$input" ] && exit 0
|
|
34
41
|
command -v jq >/dev/null 2>&1 || exit 0
|
|
35
42
|
|
|
@@ -41,22 +48,25 @@ transcript="$(printf '%s' "$input" | jq -r '.transcript_path // empty' 2>/dev/nu
|
|
|
41
48
|
[ -z "$transcript" ] && exit 0
|
|
42
49
|
[ -f "$transcript" ] || exit 0
|
|
43
50
|
|
|
44
|
-
workdir="$(
|
|
51
|
+
workdir="$(rt_hook_cwd)"
|
|
45
52
|
[ -z "$workdir" ] && workdir="${CLAUDE_PROJECT_DIR:-.}"
|
|
46
53
|
cd "$workdir" 2>/dev/null || exit 0
|
|
47
54
|
git rev-parse --is-inside-work-tree >/dev/null 2>&1 || exit 0
|
|
48
55
|
|
|
49
|
-
branch="$(git branch --show-current 2>/dev/null)"
|
|
50
|
-
[ -z "$branch" ] && exit 0
|
|
51
|
-
|
|
52
56
|
root="$(git rev-parse --show-toplevel 2>/dev/null)"
|
|
53
57
|
[ -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
|
|
|
58
|
-
|
|
59
|
-
|
|
59
|
+
# Ветка, папка задачи и строка состояния берутся, пока они есть. Пусто — ход судится вторым
|
|
60
|
+
# признаком, а не отпускается: отсюда раньше уходили нулём, и работа по слову владельца
|
|
61
|
+
# кончалась объявлением намерения молча.
|
|
62
|
+
branch="$(git branch --show-current 2>/dev/null)"
|
|
63
|
+
tasks_dir="${RT_TASKS_DIR:-docs/tasks}"
|
|
64
|
+
progress=""
|
|
65
|
+
state=""
|
|
66
|
+
if [ -n "$branch" ] && [ -f "$root/$tasks_dir/$branch/progress.md" ]; then
|
|
67
|
+
progress="$root/$tasks_dir/$branch/progress.md"
|
|
68
|
+
state="$(sed -n 's/^[[:space:]]*[-*][[:space:]]*\*\*Состояние:\*\*[[:space:]]*`\([^`]*\)`.*/\1/p' "$progress" 2>/dev/null | head -1)"
|
|
69
|
+
fi
|
|
60
70
|
|
|
61
71
|
# Работа, дошедшая до этих двух состояний, чужого шага уже дождалась: дальше её двигает
|
|
62
72
|
# владелец, и ход, закрытый здесь, ничего не роняет.
|
|
@@ -66,7 +76,8 @@ esac
|
|
|
66
76
|
|
|
67
77
|
# Следующий шаг из хода работы — его страж и называет в отказе: исполнитель, которому сказано
|
|
68
78
|
# только «работа не кончена», перечитывает ту же строку сам.
|
|
69
|
-
next_step="
|
|
79
|
+
next_step=""
|
|
80
|
+
[ -n "$progress" ] && next_step="$(sed -n 's/^[[:space:]]*[-*][[:space:]]*\*\*Следующий шаг:\*\*[[:space:]]*\(.*\)/\1/p' "$progress" 2>/dev/null | head -1)"
|
|
70
81
|
[ -z "$next_step" ] && next_step="что стоит в разделе «Где стоим» хода работы"
|
|
71
82
|
|
|
72
83
|
# Команда, меняющая дерево или его состояние. Чтение и поиск сюда не входят намеренно: ими и
|
|
@@ -117,10 +128,14 @@ commands="$(printf '%s' "$verdict" | jq -r '.ran // ""' 2>/dev/null)"
|
|
|
117
128
|
# Страж сравнивает номер этапа с тем, что лежит в истории ветки, и на выросшем номере требует
|
|
118
129
|
# команды из строки «Чем проверяется» — она стоит в замысле обратными кавычками. Приём, записанный
|
|
119
130
|
# прозой, страж не читает: подтвердить его выводом нечем, и это его известная граница.
|
|
120
|
-
stage_now="
|
|
121
|
-
stage_was="
|
|
131
|
+
stage_now=""
|
|
132
|
+
stage_was=""
|
|
133
|
+
if [ -n "$progress" ]; then
|
|
134
|
+
stage_now="$(sed -n 's/^[[:space:]]*[-*][[:space:]]*\*\*Этап:\*\*[[:space:]]*\([0-9][0-9]*\).*/\1/p' "$progress" 2>/dev/null | head -1)"
|
|
135
|
+
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)"
|
|
136
|
+
fi
|
|
122
137
|
|
|
123
|
-
if [ -n "$stage_now" ] && [ -n "$stage_was" ] && [ "$stage_now" -gt "$stage_was" ] 2>/dev/null; then
|
|
138
|
+
if [ -n "$progress" ] && [ -n "$stage_now" ] && [ -n "$stage_was" ] && [ "$stage_now" -gt "$stage_was" ] 2>/dev/null; then
|
|
124
139
|
plan="$root/$tasks_dir/$branch/plan.md"
|
|
125
140
|
# Контракт закрытого этапа, а не начатого: подтверждается то, что объявлено сделанным.
|
|
126
141
|
contract="$(awk -v n="$stage_was" '
|
|
@@ -162,7 +177,18 @@ fi
|
|
|
162
177
|
|
|
163
178
|
[ "$worked" = "true" ] && exit 0
|
|
164
179
|
|
|
165
|
-
|
|
180
|
+
if [ -z "$state" ]; then
|
|
181
|
+
reason="BLOCKED by turn-exit-guard: за этот ход не сделано ничего — ни правки, ни команды, меняющей дерево. Папки задачи у этой работы нет, и состояние взять неоткуда, но ход это не кончает.
|
|
182
|
+
|
|
183
|
+
Ход кончается четырьмя способами, и других нет: вопрос владельцу, ответа на который в правилах нет; отказ гарда; заполненное окно захода; отданная работа с начатой следующей. Названная и не запущенная команда выходом не является: строка «сейчас запущу» — объявление намерения, а оно прямо названо ложным концом хода.
|
|
184
|
+
|
|
185
|
+
Работа по слову владельца — «разложи», «обнови», «посмотри» — идёт без задачи и без ветки, и остановить её нечем, кроме этого признака.
|
|
186
|
+
|
|
187
|
+
Запусти названное этим же ходом. Владелец сказал остановиться — так и напиши: страж читает его слово, а не пересказ.
|
|
188
|
+
|
|
189
|
+
Страж судит один ход: следующий заход не отбивается."
|
|
190
|
+
else
|
|
191
|
+
reason="BLOCKED by turn-exit-guard: работа в состоянии '${state}', а за этот ход по ней не сделано ничего — ни правки, ни команды, меняющей дерево.
|
|
166
192
|
|
|
167
193
|
Ход кончается четырьмя способами, и других нет: вопрос владельцу, ответа на который в правилах нет; отказ гарда; заполненное окно захода; отданная работа с начатой следующей. Отчёт о сделанном выходом не является — он выглядит работой лучше всякой другой, и пустоты за ним не видно.
|
|
168
194
|
|
|
@@ -171,6 +197,7 @@ reason="BLOCKED by turn-exit-guard: работа в состоянии '${state}
|
|
|
171
197
|
Сделай его этим же ходом. Владелец сказал остановиться — так и напиши: страж читает его слово, а не пересказ.
|
|
172
198
|
|
|
173
199
|
Страж судит один ход: следующий заход не отбивается."
|
|
200
|
+
fi
|
|
174
201
|
|
|
175
202
|
# Общий хвост отказа: два законных хода. Файл может быть не разложен — тогда хвоста нет,
|
|
176
203
|
# а причина отказа остаётся прежней.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Локаль исполнения гарда. Подключается первой строкой тела: `. "<каталог гардов>/utf8.sh"`.
|
|
3
|
+
#
|
|
4
|
+
# Образцы гардов написаны словами того языка, на котором говорит дерево, и совпадение с ними
|
|
5
|
+
# зависит от локали процесса. В локали C складывание регистра работает только для латиницы:
|
|
6
|
+
# «В дереве этого нет» под образцом `(в дереве|здесь)…` не находится вовсе, гард выходит нулём
|
|
7
|
+
# и пропускает ход, который обязан был вернуть. Счёт `{0,30}` там же считает байты, а не буквы,
|
|
8
|
+
# и буква вне латиницы весит два.
|
|
9
|
+
#
|
|
10
|
+
# Заметить это на своей машине нечем: терминал разработчика идёт в UTF-8, а служба, запускающая
|
|
11
|
+
# ту же работу, наследует пустую локаль. Три сценария так и падали в конвейере, проходя на
|
|
12
|
+
# машине, где их писали.
|
|
13
|
+
#
|
|
14
|
+
# FAIL-OPEN: локали UTF-8 в системе не нашлось — ничего не объявляется, и гард работает как
|
|
15
|
+
# прежде. Отобрать у него работу из-за отсутствия локали хуже, чем оставить прежнее поведение.
|
|
16
|
+
|
|
17
|
+
rt_use_utf8_locale() {
|
|
18
|
+
case "${LC_ALL:-${LC_CTYPE:-${LANG:-}}}" in
|
|
19
|
+
*UTF-8* | *utf-8* | *UTF8* | *utf8*) return 0 ;;
|
|
20
|
+
esac
|
|
21
|
+
|
|
22
|
+
local available candidate
|
|
23
|
+
available="$(locale -a 2>/dev/null)" || return 0
|
|
24
|
+
|
|
25
|
+
for candidate in C.UTF-8 en_US.UTF-8 ru_RU.UTF-8; do
|
|
26
|
+
if printf '%s\n' "$available" | grep -qx -- "$candidate"; then
|
|
27
|
+
export LC_ALL="$candidate"
|
|
28
|
+
return 0
|
|
29
|
+
fi
|
|
30
|
+
done
|
|
31
|
+
|
|
32
|
+
return 0
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
rt_use_utf8_locale
|
|
@@ -30,7 +30,11 @@
|
|
|
30
30
|
# ОТКАЗ В ПОЛЬЗУ РАБОТЫ: при любой ошибке, нехватке `jq`, отсутствии записи хода и повторном
|
|
31
31
|
# заходе ход РАЗРЕШАЕТСЯ (exit 0). Сломанный гард не имеет права заклинить разговор.
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
. "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/utf8.sh" 2>/dev/null || true
|
|
34
|
+
. "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/hook-input.sh" 2>/dev/null || true
|
|
35
|
+
|
|
36
|
+
rt_hook_read
|
|
37
|
+
input="$RT_HOOK_INPUT"
|
|
34
38
|
[ -z "$input" ] && exit 0
|
|
35
39
|
|
|
36
40
|
command -v jq >/dev/null 2>&1 || exit 0
|
|
@@ -16,13 +16,24 @@
|
|
|
16
16
|
# Место между порогами и есть то, на что закрывается заход: дописать ход работы, написать
|
|
17
17
|
# передачу, закоммитить проверенное.
|
|
18
18
|
#
|
|
19
|
+
# Там, где дерево объявило порог сжатия ниже порога остановки, напоминание говорит обратное:
|
|
20
|
+
# точку остановки выбирать не надо, потому что заход через порог пройдёт сам — сжатие придёт
|
|
21
|
+
# первым, передачу к тому времени напишет свой хук, и работа продолжится тем же заходом. Отбой
|
|
22
|
+
# при этом остаётся: он превращается из конца захода в страховку на случай, когда сжатие не
|
|
23
|
+
# пришло. Напоминание, зовущее закрывать заход там, где закрывать его не надо, — это остановка
|
|
24
|
+
# работы без причины, и стоит она ровно того же, что и отбой.
|
|
25
|
+
#
|
|
19
26
|
# Размер окна берётся из настройки дерева. Из записи захода он не выводится: модель записана
|
|
20
27
|
# там без пометки о расширенном окне, и заход на широкое окно от захода на узкое неотличим.
|
|
21
28
|
#
|
|
22
29
|
# ОТКАЗ В ПОЛЬЗУ РАБОТЫ: нет размера окна, нет записи захода, нет разборщика, битый разбор —
|
|
23
30
|
# работа РАЗРЕШАЕТСЯ (exit 0). Сломанный гард не имеет права заклинить работу.
|
|
24
31
|
|
|
25
|
-
|
|
32
|
+
. "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/utf8.sh" 2>/dev/null || true
|
|
33
|
+
. "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/hook-input.sh" 2>/dev/null || true
|
|
34
|
+
|
|
35
|
+
rt_hook_read
|
|
36
|
+
input="$RT_HOOK_INPUT"
|
|
26
37
|
[ -z "$input" ] && exit 0
|
|
27
38
|
|
|
28
39
|
command -v jq >/dev/null 2>&1 || exit 0
|
|
@@ -49,6 +60,21 @@ esac
|
|
|
49
60
|
|
|
50
61
|
warn_pct="${RT_WINDOW_WARN_PCT:-40}"
|
|
51
62
|
stop_pct="${RT_WINDOW_STOP_PCT:-50}"
|
|
63
|
+
|
|
64
|
+
# Доля, на которой контекст сжимает сам инструмент. Объявлена деревом — заход через порог
|
|
65
|
+
# проходит сам: сжатие приходит первым, передачу к тому моменту уже написал свой хук, и работа
|
|
66
|
+
# идёт дальше тем же заходом. Не объявлена — прежний порядок: заход кончается передачей.
|
|
67
|
+
#
|
|
68
|
+
# От этого зависит текст напоминания, а не отказ. Отбой на пороге остановки остаётся в обоих
|
|
69
|
+
# случаях: он и есть страховка на случай, когда сжатие не пришло — настройка снята, версия
|
|
70
|
+
# другая, сжатие отказало. Отобрав отбой у дерева, объявившего сжатие, страж пустил бы такой
|
|
71
|
+
# заход до предела окна, где работа теряется целиком.
|
|
72
|
+
compact_pct="${CLAUDE_AUTOCOMPACT_PCT_OVERRIDE:-}"
|
|
73
|
+
case "$compact_pct" in
|
|
74
|
+
'' | *[!0-9]*) compact_pct='' ;;
|
|
75
|
+
esac
|
|
76
|
+
[ -n "$compact_pct" ] && [ "$compact_pct" -ge "$stop_pct" ] 2>/dev/null && compact_pct=''
|
|
77
|
+
|
|
52
78
|
tasks_dir="${RT_TASKS_DIR:-docs/tasks}"
|
|
53
79
|
handoff_dir="${RT_HANDOFF_DIR:-.claude/handoff}"
|
|
54
80
|
|
|
@@ -99,7 +125,13 @@ if [ "$event" = "PostToolUse" ]; then
|
|
|
99
125
|
printf '%s' "$step" > "$mark" 2>/dev/null
|
|
100
126
|
|
|
101
127
|
if [ "$pct" -ge "$stop_pct" ]; then
|
|
102
|
-
|
|
128
|
+
if [ -n "$compact_pct" ]; then
|
|
129
|
+
text="ЗАПОЛНЕНИЕ ОКНА ${pct}% (${fill_k}k из ${window_k}k) — заход закрывается сейчас. Сжатие объявлено на ${compact_pct}% и не пришло: порог остановки ${stop_pct}% пройден, а контекст прежний. Всё, кроме записи хода работы, передачи и команд поставки, уже отбивается — закрывай заход и скажи владельцу, что сжатие не сработало."
|
|
130
|
+
else
|
|
131
|
+
text="ЗАПОЛНЕНИЕ ОКНА ${pct}% (${fill_k}k из ${window_k}k) — заход закрывается сейчас. Всё, кроме записи хода работы, передачи и команд поставки, уже отбивается."
|
|
132
|
+
fi
|
|
133
|
+
elif [ -n "$compact_pct" ]; then
|
|
134
|
+
text="ЗАПОЛНЕНИЕ ОКНА ${pct}% (${fill_k}k из ${window_k}k). Точку остановки выбирать не надо: на ${compact_pct}% инструмент сожмёт контекст сам, передачу к тому времени напишет хук, и работа пойдёт дальше этим же заходом. Порог остановки ${stop_pct}% — страховка на случай, если сжатие не придёт. Работай дальше."
|
|
103
135
|
else
|
|
104
136
|
text="ЗАПОЛНЕНИЕ ОКНА ${pct}% (${fill_k}k из ${window_k}k). Пора выбирать точку остановки: с ${stop_pct}% останется только закрыть заход. Доведи текущий шаг до состояния, с которого следующий заход продолжит, перепиши «Где стоим» в ходе работы, напиши передачу и отдай владельцу путь к ней — паттерн task-flow-handoff."
|
|
105
137
|
fi
|
|
@@ -114,9 +146,9 @@ fi
|
|
|
114
146
|
[ "$event" = "PreToolUse" ] || exit 0
|
|
115
147
|
[ "$pct" -ge "$stop_pct" ] || exit 0
|
|
116
148
|
|
|
117
|
-
tool="$(
|
|
118
|
-
path="$(
|
|
119
|
-
cmd="$(
|
|
149
|
+
tool="$(rt_hook_tool)"
|
|
150
|
+
path="$(rt_hook_file)"
|
|
151
|
+
cmd="$(rt_hook_cmd)"
|
|
120
152
|
|
|
121
153
|
allowed=0
|
|
122
154
|
case "$tool" in
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
# Закон о ведении работы
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
длится дольше
|
|
5
|
-
ничего, а владелец помнит и вынужден пересказывать. Пересказ каждый раз выходит короче
|
|
6
|
-
предыдущего, и работа доделывается по обрывку исходной просьбы.
|
|
3
|
+
Закон устанавливает как ведётся работа от момента постановки задачи до её закрытия. Работа
|
|
4
|
+
длится дольше одной сессии и должна сохранять весь контекст и состояние при переходах из одной сессии в другую.
|
|
7
5
|
|
|
8
6
|
## Статьи
|
|
9
7
|
|
|
@@ -13,15 +11,13 @@
|
|
|
13
11
|
- **Пробел закрывается вопросом владельцу, а не догадкой.** Догадка неотличима от знания:
|
|
14
12
|
она попадает в работу молча и обнаруживается только при приёмке, когда переделывать дороже
|
|
15
13
|
всего.
|
|
16
|
-
- **Работа,
|
|
14
|
+
- **Работа, которая требует изменений по образцу, начинается с чтения самого образца.** Пересказ
|
|
17
15
|
образца образцом не является: по нему собирается понимание того, кто пересказывал, и
|
|
18
16
|
расхождение всплывает на приёмке целой работой, а не строкой. Читается та часть образца,
|
|
19
17
|
которую работа повторяет, и читается целиком — об устройстве чужого дерева по одному его
|
|
20
18
|
файлу не судят.
|
|
21
|
-
- **Образец,
|
|
22
|
-
|
|
23
|
-
третий — по пересказу второго, и к четвёртому от образца не остаётся ничего, кроме слова
|
|
24
|
-
«образец».
|
|
19
|
+
- **Образец, указанный в ходе работы должен быть доступен каждой сессии во время работы.**
|
|
20
|
+
Ссылка на образец нужно указывать непосредственно в файле с описанием прогресса работы.
|
|
25
21
|
- **Вопрос, у которого есть очевидный ответ, работу не останавливает.** Исполнитель называет
|
|
26
22
|
допущение, идёт дальше и записывает его туда же, где идёт работа. Останавливает только тот
|
|
27
23
|
пробел, при котором любая догадка делает работу опасной или бесполезной. Вопрос, заданный
|
|
@@ -71,6 +67,15 @@
|
|
|
71
67
|
владельца.** Владелец знает, что работа не кончена, но не знает, где именно она стоит;
|
|
72
68
|
пересказ он даёт по своей памяти, а не по ходу работы, и следующий заход начинает с чужой
|
|
73
69
|
картины.
|
|
70
|
+
- **Переданное прошлым заходом приходит в новый заход само, а не кладётся рукой.** Передача,
|
|
71
|
+
написанная и не прочитанная, равна ненаписанной: следующий заход о ней не знает и начинает с
|
|
72
|
+
пустого места — то есть с того же, ради чего её и писали. Кладёт её человек ровно до тех пор,
|
|
73
|
+
пока это не поручено машине; поручённое машине не забывается ночью.
|
|
74
|
+
- **Порядок ведения работы приходит в заход вместе с работой, а не разыскивается им.** Заход,
|
|
75
|
+
знающий задачу и не знающий, что с ней делают дальше, первым движением идёт читать правило
|
|
76
|
+
целиком — и тратит на это ту часть места, ради которой заход и начали заново. Приходит
|
|
77
|
+
короткая выжимка: состояния, обязательное действие каждого и то, чем ход кончается. Правило
|
|
78
|
+
она не заменяет и заменить не может — она отвечает на вопрос «что делать», а не «почему».
|
|
74
79
|
- **Замысел и ход работы — разные записи.** Замысел — то, с чем сверяют результат при
|
|
75
80
|
приёмке; правленный по ходу, он перестаёт отличаться от PR, и приёмке сверять нечего.
|
|
76
81
|
- **Сделанное отмечается в одном месте.** Две записи об одном разъезжаются молча, и после
|
|
@@ -142,6 +147,26 @@
|
|
|
142
147
|
остановиться позволяет только предел заполнения окна. Заход, закрытый на готовой задаче,
|
|
143
148
|
оставляет владельцу пустое место и стоит целого захода на возвращение к тому, что и так было
|
|
144
149
|
под рукой.
|
|
150
|
+
- **Предел заполнения окна останавливает заход только там, где инструмент не сжимает контекст
|
|
151
|
+
сам.** Где сжатие объявлено и приходит раньше предела, окно — не конец захода, а его
|
|
152
|
+
продолжение: контекст сжимается, записанное состояние работы возвращается в него, и работа
|
|
153
|
+
идёт дальше тем же заходом. Останавливаться там незачем, а остановка стоит того же, что и
|
|
154
|
+
всякая другая: владелец возвращает исполнителя в работу руками.
|
|
155
|
+
- **Порог, на котором заход останавливают, стоит позже порога, на котором его продолжают.**
|
|
156
|
+
Два порога на одном числе — это не согласие, а гонка, и выигрывает её тот, кто ближе к
|
|
157
|
+
действию: остановка приходит на вызове, а сжатие — между ходами. Расстояние между ними
|
|
158
|
+
объявляется, а не выводится разницей: сжатие идёт не мгновенно, и порог, отстоящий на волос,
|
|
159
|
+
требование «раньше» удовлетворяет, а работу не спасает.
|
|
160
|
+
- **Переход из состояния в состояние исполнителя не останавливает.** Обязательное
|
|
161
|
+
действие сделано — следующее начинается тем же движением, без отчёта владельцу и без его
|
|
162
|
+
слова. Граница между состояниями выглядит законченным куском лучше всякой другой вехи:
|
|
163
|
+
сделанное названо, отчитаться есть чем, — и отчёт встаёт ровно на то место, где должно было
|
|
164
|
+
стоять следующее действие. Владелец читает такой отчёт как сделанную работу, а работа стоит.
|
|
165
|
+
- **Текст, ведущий состояние работы, называет, что делается сразу за ним.** Дочитанный до
|
|
166
|
+
последнего приёма, он кончается ничем: следующего движения в нём нет, и исполнитель выводит
|
|
167
|
+
его из пустоты — то есть останавливается. Названное движение стоит там же, где приёмы, и
|
|
168
|
+
своими словами: одинаковая на все состояния строка перестаёт замечаться раньше, чем
|
|
169
|
+
понадобится.
|
|
145
170
|
- **Работа, отданная на разбор, освобождает исполнителя, а не останавливает его.** Отданное на
|
|
146
171
|
разбор ждёт владельца, а не машину: следующая задача эпика берётся тем же движением, которым
|
|
147
172
|
предыдущая ушла на разбор.
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: dependencies-upgrade
|
|
3
3
|
kind: pattern
|
|
4
4
|
rule: dependencies
|
|
5
|
-
description: Паттерн правила dependencies. Брать при подъёме версий пакетов — выбор верхней границы по peer-диапазонам, порядок проверок, граница переформатирования после обновления форматтера, разбор новых правил линтера. Не брать для
|
|
5
|
+
description: Паттерн правила dependencies. Брать при подъёме версий пакетов — выбор верхней границы по peer-диапазонам, порядок проверок, граница переформатирования после обновления форматтера, разбор новых правил линтера. Не брать для ветки и коммита — это паттерн git-workflow-commit, для PR — git-workflow-pr.
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# Подъём версий
|
|
@@ -19,7 +19,7 @@ description: Паттерн правила doc-style. Брать при напи
|
|
|
19
19
|
|
|
20
20
|
Само правило укладывается в одно предложение. Следом — не больше двух предложений о том, что
|
|
21
21
|
сломается иначе, и только если из самой фразы это не видно. Обоснование, у которого была
|
|
22
|
-
альтернатива, идёт в
|
|
22
|
+
альтернатива, идёт в ловушки — холодную часть правила, — а не в сам закон: разросшийся буллет читают по
|
|
23
23
|
диагонали, а закон говорит, что верно, — не почему когда-то выбрали так.
|
|
24
24
|
|
|
25
25
|
```
|
|
@@ -41,8 +41,8 @@ description: Паттерн правила doc-style. Брать при напи
|
|
|
41
41
|
Место исполнения меняется при первом же рефакторинге, и документ, который его называет,
|
|
42
42
|
устаревает молча. Для него заведён отдельный файл — `implementation.md` рядом.
|
|
43
43
|
|
|
44
|
-
Исключение —
|
|
45
|
-
есть место, где на него наступают.
|
|
44
|
+
Исключение — ловушки в холодной части правила: там устройство кода называется прямо, потому
|
|
45
|
+
что ловушка и есть место, где на него наступают.
|
|
46
46
|
|
|
47
47
|
## Без утверждений о будущем
|
|
48
48
|
|