@rt-tools/agent-kit 0.11.0 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/checks/board-runs.github.mjs +87 -0
- package/assets/checks/board.github.mjs +0 -40
- package/assets/checks/check-board.github.mjs +39 -8
- package/assets/checks/check-file-size.mjs +19 -4
- package/assets/checks/check-schema-drift.mjs +28 -5
- package/assets/checks/check-state-next.mjs +10 -2
- package/assets/checks/rt-kit-checks.config.mjs +28 -2
- package/assets/commands/feedback.md +8 -0
- package/assets/defaults/project.sh +59 -6
- package/assets/defaults/turn-map.md +8 -6
- package/assets/hooks/browser-guard-device-id.sh +3 -1
- package/assets/hooks/browser-guard-no-asking.sh +3 -1
- package/assets/hooks/browser-guard-no-other-drivers.sh +6 -4
- package/assets/hooks/browser-guard-require-select.sh +4 -2
- package/assets/hooks/claim-guard.sh +3 -1
- package/assets/hooks/conscience-guard.sh +3 -1
- package/assets/hooks/dev-server-guard.sh +6 -4
- package/assets/hooks/dispatch.sh +69 -0
- package/assets/hooks/docs-guard.sh +6 -4
- package/assets/hooks/exam-guard.sh +5 -3
- package/assets/hooks/git-guard-delivery-folder.sh +99 -0
- package/assets/hooks/git-guard-delivery-signature.sh +10 -4
- package/assets/hooks/git-guard-delivery.sh +58 -74
- package/assets/hooks/git-guard-main.sh +6 -4
- package/assets/hooks/git-guard-push-tests.sh +8 -6
- package/assets/hooks/grill-gate.sh +4 -2
- package/assets/hooks/handoff-entry-guard.sh +4 -2
- package/assets/hooks/handoff-write.sh +27 -6
- package/assets/hooks/hook-input.sh +77 -0
- package/assets/hooks/lint-after-edit.sh +5 -3
- package/assets/hooks/override-write-guard.sh +107 -0
- package/assets/hooks/postmortem-guard.sh +3 -1
- package/assets/hooks/proposal-guard.sh +3 -1
- package/assets/hooks/prose-style-guard.sh +5 -3
- package/assets/hooks/qa-dataid-guard.sh +4 -2
- package/assets/hooks/rerun-guard.sh +6 -4
- package/assets/hooks/reuse-first-guard.sh +5 -3
- package/assets/hooks/rule-article.sh +99 -0
- package/assets/hooks/rule-source-guard.sh +126 -0
- package/assets/hooks/skill-gate-rearm.sh +3 -1
- package/assets/hooks/skill-gate.sh +23 -2
- package/assets/hooks/skill-loaded.sh +3 -1
- package/assets/hooks/sql-guard-request.sh +2 -1
- package/assets/hooks/sql-guard.sh +4 -2
- package/assets/hooks/task-flow-guard.sh +24 -4
- package/assets/hooks/turn-exit-guard.sh +59 -17
- package/assets/hooks/waiting-turn-guard.sh +3 -1
- package/assets/hooks/window-fill-guard.sh +6 -4
- package/assets/laws/work-conduct.md +5 -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 +195 -0
- package/assets/patterns/task-flow-close.md +72 -240
- package/assets/patterns/task-flow-handoff.md +4 -4
- package/assets/patterns/task-flow-resume.md +5 -3
- package/assets/pitfalls/agent-kit.md +80 -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 +114 -0
- package/assets/rules/deploy-flow.github.md +122 -0
- package/assets/rules/deploy-flow.gitlab.md +116 -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 +20 -128
- 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 +56 -222
- package/assets/rules/testing.md +3 -64
- package/assets/rules/turn-conduct.md +210 -0
- package/assets/rules/typescript-conventions.md +15 -0
- package/assets/skills/agent-kit.md +67 -89
- package/assets/templates/pitfalls.md +10 -0
- package/assets/templates/proposal.md +16 -1
- package/assets/templates/rule.md +5 -3
- package/bin/agent-kit.d.ts.map +1 -1
- package/bin/agent-kit.js +1 -42
- package/bin/agent-kit.js.map +1 -1
- package/lib/assets.d.ts.map +1 -1
- package/lib/assets.js +6 -1
- package/lib/assets.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 +1 -0
- 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/proposals.d.ts +5 -1
- package/lib/proposals.d.ts.map +1 -1
- package/lib/proposals.js +74 -5
- package/lib/proposals.js.map +1 -1
- package/lib/ship.d.ts +1 -2
- package/lib/ship.d.ts.map +1 -1
- package/lib/ship.js +0 -54
- package/lib/ship.js.map +1 -1
- package/lib/shipment.d.ts.map +1 -1
- package/lib/shipment.fixture.d.ts +39 -0
- package/lib/shipment.fixture.d.ts.map +1 -0
- package/lib/shipment.fixture.js +99 -0
- package/lib/shipment.fixture.js.map +1 -0
- package/lib/shipment.js +59 -7
- package/lib/shipment.js.map +1 -1
- package/package.json +1 -1
- package/rt-tools-agent-kit-0.13.0.tgz +0 -0
- package/assets/commands/agent-kit-digest.md +0 -89
- package/assets/commands/rules-review.md +0 -98
- package/assets/patterns/cargo-triage-mark.md +0 -119
- package/assets/rules/cargo-triage.md +0 -126
- package/lib/cargo-state.d.ts +0 -62
- package/lib/cargo-state.d.ts.map +0 -1
- package/lib/cargo-state.js +0 -118
- package/lib/cargo-state.js.map +0 -1
- package/rt-tools-agent-kit-0.11.0.tgz +0 -0
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# rt-hook: PreToolUse Edit|Write|MultiEdit|Bash|mcp__webstorm__create_new_file|mcp__webstorm__execute_terminal_command|mcp__webstorm__execute_tool
|
|
3
|
+
# Требует: hooks/profile-check.sh, hooks/deny-tail.sh
|
|
4
|
+
# Гард места правки: слой правил чинится там, где сломано, а не там, где виден.
|
|
5
|
+
#
|
|
6
|
+
# Слой правил правит тот же исполнитель, которым слой правил управляет, и разницы между
|
|
7
|
+
# «исполняю правило» и «правлю правило» в дереве не видно ничем: разложенная копия лежит рядом
|
|
8
|
+
# с обычными файлами и правится так же. Правка доходит до диска и не доходит до места, где
|
|
9
|
+
# промах чинится: в дереве она видна, в пакете её нет, а через месяц раскладка отказывает по
|
|
10
|
+
# правленому файлу целиком — и цену платит тот, кто в этот день правил соседний ресурс.
|
|
11
|
+
#
|
|
12
|
+
# Знал об этом только `sync --check`, и говорил он на следующей раскладке, то есть в чужой
|
|
13
|
+
# ветке и чужим ходом. Гард говорит в минуту правки и называет адрес: источник пакета, если
|
|
14
|
+
# дерево его держит, иначе — надстройку.
|
|
15
|
+
#
|
|
16
|
+
# ЧТО СУДИТСЯ. Файл, который правят, несёт шапку раскладки: `rt-kit v<версия> · <ресурс> ·
|
|
17
|
+
# <дайджест>`. Ресурс из неё и есть адрес: `<источники>/<ресурс>` в дереве пакета,
|
|
18
|
+
# `.claude/rt-kit/overrides/<ресурс>` у потребителя.
|
|
19
|
+
#
|
|
20
|
+
# ОТКАЗ В ПОЛЬЗУ РАБОТЫ: нет `jq`, битый ввод, чужой инструмент, файла нет, шапки в нём нет,
|
|
21
|
+
# не git-репозиторий — правка РАЗРЕШАЕТСЯ. Сломанный гард не имеет права заклинить работу.
|
|
22
|
+
|
|
23
|
+
. "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/utf8.sh" 2>/dev/null || true
|
|
24
|
+
. "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/hook-input.sh" 2>/dev/null || true
|
|
25
|
+
|
|
26
|
+
rt_hook_read
|
|
27
|
+
input="$RT_HOOK_INPUT"
|
|
28
|
+
[ -z "$input" ] && exit 0
|
|
29
|
+
command -v jq >/dev/null 2>&1 || exit 0
|
|
30
|
+
|
|
31
|
+
rt_hooks_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
32
|
+
for profile in "$rt_hooks_dir/../rt-kit/defaults/project.sh" "$rt_hooks_dir/../defaults/project.sh" "${CLAUDE_PROJECT_DIR:-.}/.claude/rt-kit/defaults/project.sh" "${CLAUDE_PROJECT_DIR:-.}/.claude/rt-kit/project.sh"; do
|
|
33
|
+
# shellcheck disable=SC1090
|
|
34
|
+
[ -f "$profile" ] && . "$profile" 2>/dev/null
|
|
35
|
+
done
|
|
36
|
+
|
|
37
|
+
# shellcheck disable=SC1090
|
|
38
|
+
[ -f "$rt_hooks_dir/profile-check.sh" ] && . "$rt_hooks_dir/profile-check.sh"
|
|
39
|
+
command -v rt_needs >/dev/null 2>&1 || rt_needs() { command -v "$1" >/dev/null 2>&1; }
|
|
40
|
+
|
|
41
|
+
# shellcheck disable=SC1090
|
|
42
|
+
[ -f "$rt_hooks_dir/deny-tail.sh" ] && . "$rt_hooks_dir/deny-tail.sh" 2>/dev/null
|
|
43
|
+
command -v rt_deny_tail >/dev/null 2>&1 || rt_deny_tail() { printf ''; }
|
|
44
|
+
|
|
45
|
+
deny() {
|
|
46
|
+
# shellcheck disable=SC1090
|
|
47
|
+
[ -f "$rt_hooks_dir/observe.sh" ] && . "$rt_hooks_dir/observe.sh" 2>/dev/null
|
|
48
|
+
command -v rt_note >/dev/null 2>&1 && rt_note guard-deny res=rule-source-guard
|
|
49
|
+
|
|
50
|
+
reason="$1 $(rt_deny_tail "$2")"
|
|
51
|
+
jq -n --arg r "$reason" '{hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"deny",permissionDecisionReason:$r}}' 2>/dev/null \
|
|
52
|
+
|| printf '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"deny","permissionDecisionReason":"Гард места правки."}}\n'
|
|
53
|
+
exit 0
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
# Цели записи, названные командой прямо. Печатает по одной в строке.
|
|
57
|
+
rt_write_targets() {
|
|
58
|
+
tr "\"'\`" ' ' \
|
|
59
|
+
| sed -E 's/>>?/\n>/g' \
|
|
60
|
+
| sed -nE '
|
|
61
|
+
s/^>[[:space:]]*([^[:space:]|&;]+).*/\1/p
|
|
62
|
+
s/(^|.*[[:space:]])tee[[:space:]]+(-a[[:space:]]+)?([^[:space:]|&;]+).*/\3/p
|
|
63
|
+
s/(^|.*[[:space:]])sed[[:space:]]+-i[[:space:]]+([^[:space:]]+[[:space:]]+)*([^[:space:]|&;]+)$/\3/p
|
|
64
|
+
s/(^|.*[[:space:]])(cp|mv|install)[[:space:]]+([^[:space:]]+[[:space:]]+)+([^[:space:]|&;]+).*/\4/p
|
|
65
|
+
' \
|
|
66
|
+
| sort -u
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
tool="$(rt_hook_tool)"
|
|
70
|
+
candidates=""
|
|
71
|
+
case "$tool" in
|
|
72
|
+
Edit | Write | MultiEdit | mcp__webstorm__create_new_file)
|
|
73
|
+
candidates="$(printf '%s' "$input" | jq -r '.tool_input.file_path // .tool_input.pathInProject // empty' 2>/dev/null)"
|
|
74
|
+
;;
|
|
75
|
+
Bash | mcp__webstorm__execute_terminal_command | mcp__webstorm__execute_tool)
|
|
76
|
+
cmd="$(rt_hook_cmd)"
|
|
77
|
+
[ -z "$cmd" ] && exit 0
|
|
78
|
+
# Здесь берётся не общий признак записи, а цель, названная в команде прямо:
|
|
79
|
+
# перенаправление, `tee`, правка на месте, копирование поверх. Общий признак широк
|
|
80
|
+
# намеренно — в нём и имя интерпретатора, — и запуск разложенной проверки читался бы
|
|
81
|
+
# как правка её самой. Отбитие на чтении стоит дороже пропуска: гард, мешающий читать,
|
|
82
|
+
# выключают в первый же день.
|
|
83
|
+
#
|
|
84
|
+
# Снятие копии сюда не входит намеренно: снятый файл раскладка кладёт заново, и так
|
|
85
|
+
# чинят копию, которую переписал форматтер.
|
|
86
|
+
candidates="$(printf '%s' "$cmd" | rt_write_targets)"
|
|
87
|
+
;;
|
|
88
|
+
*) exit 0 ;;
|
|
89
|
+
esac
|
|
90
|
+
[ -z "$candidates" ] && exit 0
|
|
91
|
+
|
|
92
|
+
root="${CLAUDE_PROJECT_DIR:-.}"
|
|
93
|
+
|
|
94
|
+
# Каталог источников пакета в этом дереве. Есть он только у дерева, которое пакет и везёт; у
|
|
95
|
+
# потребителя источника нет вовсе, и адрес правки у него один — надстройка.
|
|
96
|
+
sources=""
|
|
97
|
+
if rt_needs rt_kit_sources_dir rule-source-guard; then
|
|
98
|
+
sources="$(rt_kit_sources_dir 2>/dev/null)"
|
|
99
|
+
fi
|
|
100
|
+
|
|
101
|
+
while IFS= read -r candidate; do
|
|
102
|
+
[ -z "$candidate" ] && continue
|
|
103
|
+
case "$candidate" in
|
|
104
|
+
/*) ;;
|
|
105
|
+
*) candidate="$root/$candidate" ;;
|
|
106
|
+
esac
|
|
107
|
+
[ -f "$candidate" ] || continue
|
|
108
|
+
|
|
109
|
+
# Шапка стоит в начале файла, но не первой строкой: у сценария её отодвигает `#!`, у
|
|
110
|
+
# правила — заголовок с именем и родом ресурса. Десяти строк хватает обоим, а читать файл
|
|
111
|
+
# целиком нельзя: гард стоит на каждой правке.
|
|
112
|
+
resource="$(head -12 "$candidate" 2>/dev/null | sed -nE 's/.*rt-kit v[^ ]+ · ([^ ]+) · [0-9a-f]+.*/\1/p' | head -1)"
|
|
113
|
+
[ -z "$resource" ] && continue
|
|
114
|
+
|
|
115
|
+
if [ -n "$sources" ] && [ -f "$root/$sources/$resource" ]; then
|
|
116
|
+
deny "BLOCKED by rule-source-guard: «${candidate#"$root"/}» разложен пакетом, и правка на его месте теряется на следующей раскладке — а до тех пор раскладка отказывает по этому файлу целиком, и цену платит тот, кто в этот день правит соседний ресурс. Ресурс — «${resource}». Правь источник: ${sources}/${resource} — потом собери пакет и разложи." \
|
|
117
|
+
"правка, верная только этому дереву, идёт в надстройку .claude/rt-kit/overrides/${resource} — она сливается по разделу «## » и раскладку переживает"
|
|
118
|
+
fi
|
|
119
|
+
|
|
120
|
+
deny "BLOCKED by rule-source-guard: «${candidate#"$root"/}» разложен пакетом @rt-tools/agent-kit, и правка на его месте теряется на следующей раскладке. Ресурс — «${resource}». Правь надстройку: .claude/rt-kit/overrides/${resource} — она сливается по разделу «## » и раскладку переживает." \
|
|
121
|
+
"то, что верно любому дереву, правится в самом пакете и приезжает сюда новой редакцией"
|
|
122
|
+
done <<EOF
|
|
123
|
+
$candidates
|
|
124
|
+
EOF
|
|
125
|
+
|
|
126
|
+
exit 0
|
|
@@ -13,8 +13,10 @@
|
|
|
13
13
|
# отбивает.
|
|
14
14
|
|
|
15
15
|
. "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/utf8.sh" 2>/dev/null || true
|
|
16
|
+
. "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/hook-input.sh" 2>/dev/null || true
|
|
16
17
|
|
|
17
|
-
|
|
18
|
+
rt_hook_read
|
|
19
|
+
input="$RT_HOOK_INPUT"
|
|
18
20
|
[ -z "$input" ] && exit 0
|
|
19
21
|
|
|
20
22
|
sid="$(printf '%s' "$input" | jq -r '.session_id // empty' 2>/dev/null)"
|
|
@@ -24,12 +24,14 @@
|
|
|
24
24
|
# Сломанный гейт не имеет права остановить работу совсем.
|
|
25
25
|
|
|
26
26
|
. "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/utf8.sh" 2>/dev/null || true
|
|
27
|
+
. "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/hook-input.sh" 2>/dev/null || true
|
|
27
28
|
|
|
28
|
-
|
|
29
|
+
rt_hook_read
|
|
30
|
+
input="$RT_HOOK_INPUT"
|
|
29
31
|
[ -z "$input" ] && exit 0
|
|
30
32
|
|
|
31
33
|
sid="$(printf '%s' "$input" | jq -r '.session_id // "nosession"' 2>/dev/null)"
|
|
32
|
-
tool="$(
|
|
34
|
+
tool="$(rt_hook_tool)"
|
|
33
35
|
|
|
34
36
|
# Умолчание карты ищется и рядом с самим хуком: уезжают они вместе.
|
|
35
37
|
rt_hooks_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
@@ -175,6 +177,25 @@ if [ -n "$law" ]; then
|
|
|
175
177
|
&& reason="Отбито гейтом правил: загрузи правило «${req}» инструментом Skill — оно применяет закон ${law_path} к этому дереву — и повтори действие. ${fallback} Для этой области это происходит один раз за сессию."
|
|
176
178
|
fi
|
|
177
179
|
|
|
180
|
+
# Статья, под которую подпадает эта правка. Правило весит от двадцати до шестидесяти килобайт,
|
|
181
|
+
# и отказ, зовущий его целиком, платит за решение полной ценой правила. Статья говорит о своей
|
|
182
|
+
# применимости сама — разбирает её помощник рядом; нет помощника или нет размеченных статей,
|
|
183
|
+
# отказ остаётся прежним, и это тот же отказ в пользу работы, что и везде.
|
|
184
|
+
#
|
|
185
|
+
# Загрузку правила статья не отменяет: она снимает чтение правила целиком, а не сам отказ.
|
|
186
|
+
# Правило целиком остаётся вторым ходом — для того, кому статьи мало.
|
|
187
|
+
# shellcheck disable=SC1090
|
|
188
|
+
[ -f "$rt_hooks_dir/rule-article.sh" ] && . "$rt_hooks_dir/rule-article.sh" 2>/dev/null
|
|
189
|
+
if [ "$kind" != "command" ] && [ "$kind" != "browser" ] && command -v rt_rule_articles >/dev/null 2>&1; then
|
|
190
|
+
article="$(rt_rule_articles "$root/$rules_dir/${req}/SKILL.md" "$target" 2>/dev/null)"
|
|
191
|
+
if [ -n "$article" ]; then
|
|
192
|
+
reason="Отбито гейтом правил. Под эту правку подпадает статья правила «${req}»:
|
|
193
|
+
|
|
194
|
+
${article}
|
|
195
|
+
Статья снимает чтение правила целиком, а не отказ: загрузи правило «${req}» инструментом Skill и повтори действие. ${fallback} Для этой области это происходит один раз за сессию."
|
|
196
|
+
fi
|
|
197
|
+
fi
|
|
198
|
+
|
|
178
199
|
# Общий хвост отказа: два законных хода и законная форма обхода, если она у отказа есть.
|
|
179
200
|
# Файл может быть не разложен — тогда хвоста нет, а причина отказа остаётся прежней.
|
|
180
201
|
# shellcheck disable=SC1090
|
|
@@ -8,8 +8,10 @@
|
|
|
8
8
|
# Хук только наблюдает: он всегда пропускает и ничего не отбивает.
|
|
9
9
|
|
|
10
10
|
. "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/utf8.sh" 2>/dev/null || true
|
|
11
|
+
. "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/hook-input.sh" 2>/dev/null || true
|
|
11
12
|
|
|
12
|
-
|
|
13
|
+
rt_hook_read
|
|
14
|
+
input="$RT_HOOK_INPUT"
|
|
13
15
|
[ -z "$input" ] && exit 0
|
|
14
16
|
|
|
15
17
|
sid="$(printf '%s' "$input" | jq -r '.session_id // "nosession"' 2>/dev/null)"
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
# Три вида ввода: запрос через подключение редактора, команда оболочки и та же команда,
|
|
12
12
|
# завёрнутая в универсальный исполнитель. Инструмент, не названный здесь, гарда не касается.
|
|
13
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
|
|
14
15
|
|
|
15
16
|
sql_read_request() {
|
|
16
17
|
sql=""
|
|
@@ -25,7 +26,7 @@ sql_read_request() {
|
|
|
25
26
|
# Терминал IDE исполняет ту же командную строку и кладёт её в то же поле, что и Bash:
|
|
26
27
|
# без этой ветки весь гард обходился сменой инструмента.
|
|
27
28
|
Bash | mcp__webstorm__execute_terminal_command | mcp__webstorm__execute_tool)
|
|
28
|
-
cmd="$(
|
|
29
|
+
cmd="$(rt_hook_cmd)"
|
|
29
30
|
[ -z "$cmd" ] && exit 0
|
|
30
31
|
|
|
31
32
|
# Универсальный исполнитель зовёт ЛЮБОЙ инструмент редактора по имени, в том числе
|
|
@@ -41,12 +41,14 @@
|
|
|
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
|
|
44
45
|
|
|
45
|
-
|
|
46
|
+
rt_hook_read
|
|
47
|
+
input="$RT_HOOK_INPUT"
|
|
46
48
|
[ -z "$input" ] && exit 0
|
|
47
49
|
command -v jq >/dev/null 2>&1 || exit 0
|
|
48
50
|
|
|
49
|
-
tool="$(
|
|
51
|
+
tool="$(rt_hook_tool)"
|
|
50
52
|
|
|
51
53
|
# Рабочий каталог нужен одному правилу — разрешению адреса миграции: `DATABASE_URL`
|
|
52
54
|
# обычно не назван в команде и лежит в `.env` рядом с проектом.
|
|
@@ -26,8 +26,10 @@
|
|
|
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
|
|
29
30
|
|
|
30
|
-
|
|
31
|
+
rt_hook_read
|
|
32
|
+
input="$RT_HOOK_INPUT"
|
|
31
33
|
[ -z "$input" ] && exit 0
|
|
32
34
|
command -v jq >/dev/null 2>&1 || exit 0
|
|
33
35
|
|
|
@@ -45,7 +47,7 @@ done
|
|
|
45
47
|
[ -f "$rt_hooks_dir/profile-check.sh" ] && . "$rt_hooks_dir/profile-check.sh"
|
|
46
48
|
command -v rt_needs >/dev/null 2>&1 || rt_needs() { command -v "$1" >/dev/null 2>&1; }
|
|
47
49
|
|
|
48
|
-
tool="$(
|
|
50
|
+
tool="$(rt_hook_tool)"
|
|
49
51
|
candidates=""
|
|
50
52
|
case "$tool" in
|
|
51
53
|
# Инструмент редактора заводит файл теми же двумя данными, только называет их иначе —
|
|
@@ -61,7 +63,7 @@ case "$tool" in
|
|
|
61
63
|
# имён гард стоял бы объявленным на них и молча пропускал — состояние хуже необъявленного,
|
|
62
64
|
# потому что снаружи выглядит закрытым.
|
|
63
65
|
Bash | mcp__webstorm__execute_terminal_command | mcp__webstorm__execute_tool)
|
|
64
|
-
cmd="$(
|
|
66
|
+
cmd="$(rt_hook_cmd)"
|
|
65
67
|
[ -z "$cmd" ] && exit 0
|
|
66
68
|
# Универсальный исполнитель прячет настоящую команду во вложенной строке: без её разбора
|
|
67
69
|
# путь стоит за кавычкой, и до него не дотягивается ни один образец.
|
|
@@ -108,6 +110,7 @@ EOF
|
|
|
108
110
|
|
|
109
111
|
# Каталог папок задач: у дерева он свой, но имя обычно общее.
|
|
110
112
|
tasks_dir="${RT_TASKS_DIR:-docs/tasks}"
|
|
113
|
+
main_branch="${RT_MAIN_BRANCH:-main}"
|
|
111
114
|
|
|
112
115
|
# Общий хвост отказа: два законных хода и законная форма обхода, если она у отказа есть. Файл
|
|
113
116
|
# может быть не разложен — тогда хвоста нет, а причина отказа остаётся прежней.
|
|
@@ -127,7 +130,7 @@ deny() {
|
|
|
127
130
|
}
|
|
128
131
|
|
|
129
132
|
# Ветку смотрим там же, где пойдёт правка: у worktree она своя.
|
|
130
|
-
workdir="$(
|
|
133
|
+
workdir="$(rt_hook_cwd)"
|
|
131
134
|
[ -z "$workdir" ] && workdir="${CLAUDE_PROJECT_DIR:-.}"
|
|
132
135
|
cd "$workdir" 2>/dev/null || exit 0
|
|
133
136
|
git rev-parse --is-inside-work-tree >/dev/null 2>&1 || exit 0
|
|
@@ -144,6 +147,23 @@ root="$(git rev-parse --show-toplevel 2>/dev/null)"
|
|
|
144
147
|
dir="$root/$tasks_dir/$branch"
|
|
145
148
|
plan="$dir/plan.md"
|
|
146
149
|
|
|
150
|
+
# Папка, разобранная коммитом этой ветки, — признак того, что работа отдана. Замысел с диска к
|
|
151
|
+
# этой минуте снят намеренно: уборка стоит до открытия заявки, потому что кнопку слияния
|
|
152
|
+
# нажимает человек на хостинге и закрывающему коммиту места после одобрения не остаётся.
|
|
153
|
+
# Правка после уборки — это правка по замечаниям разбора, и требовать под неё замысла значило
|
|
154
|
+
# бы запирать ветку собственным порядком. Признак берётся из истории ветки, а не с диска:
|
|
155
|
+
# снесённая, но не закоммиченная папка отданной работы не означает.
|
|
156
|
+
folder_archived() {
|
|
157
|
+
[ -n "$(git ls-tree -d --name-only HEAD -- "$tasks_dir/$branch" 2>/dev/null | head -1)" ] && return 1
|
|
158
|
+
base="$(git merge-base "$main_branch" HEAD 2>/dev/null)"
|
|
159
|
+
[ -z "$base" ] && return 1
|
|
160
|
+
had="$(git ls-tree -d --name-only "$base" -- "$tasks_dir/$branch" 2>/dev/null | head -1)"
|
|
161
|
+
[ -z "$had" ] && had="$(git log "$base..HEAD" --diff-filter=A --name-only --pretty=format: -- "$tasks_dir/$branch" 2>/dev/null | head -1)"
|
|
162
|
+
[ -n "$had" ]
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
folder_archived && exit 0
|
|
166
|
+
|
|
147
167
|
if [ ! -f "$plan" ]; then
|
|
148
168
|
deny "BLOCKED by task-flow: нет замысла — '${tasks_dir}/${branch}/plan.md'. Собери папку задачи с образца (cp -r ${tasks_dir}/_template ${tasks_dir}/${branch}) и заполни шапку, след задачи и этапы, затем повтори. Правило — скил task-flow."
|
|
149
169
|
fi
|
|
@@ -22,16 +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
|
|
33
37
|
|
|
34
|
-
|
|
38
|
+
rt_hook_read
|
|
39
|
+
input="$RT_HOOK_INPUT"
|
|
35
40
|
[ -z "$input" ] && exit 0
|
|
36
41
|
command -v jq >/dev/null 2>&1 || exit 0
|
|
37
42
|
|
|
@@ -43,22 +48,25 @@ transcript="$(printf '%s' "$input" | jq -r '.transcript_path // empty' 2>/dev/nu
|
|
|
43
48
|
[ -z "$transcript" ] && exit 0
|
|
44
49
|
[ -f "$transcript" ] || exit 0
|
|
45
50
|
|
|
46
|
-
workdir="$(
|
|
51
|
+
workdir="$(rt_hook_cwd)"
|
|
47
52
|
[ -z "$workdir" ] && workdir="${CLAUDE_PROJECT_DIR:-.}"
|
|
48
53
|
cd "$workdir" 2>/dev/null || exit 0
|
|
49
54
|
git rev-parse --is-inside-work-tree >/dev/null 2>&1 || exit 0
|
|
50
55
|
|
|
51
|
-
branch="$(git branch --show-current 2>/dev/null)"
|
|
52
|
-
[ -z "$branch" ] && exit 0
|
|
53
|
-
|
|
54
56
|
root="$(git rev-parse --show-toplevel 2>/dev/null)"
|
|
55
57
|
[ -z "$root" ] && exit 0
|
|
56
|
-
tasks_dir="${RT_TASKS_DIR:-docs/tasks}"
|
|
57
|
-
progress="$root/$tasks_dir/$branch/progress.md"
|
|
58
|
-
[ -f "$progress" ] || exit 0
|
|
59
58
|
|
|
60
|
-
|
|
61
|
-
|
|
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
|
|
62
70
|
|
|
63
71
|
# Работа, дошедшая до этих двух состояний, чужого шага уже дождалась: дальше её двигает
|
|
64
72
|
# владелец, и ход, закрытый здесь, ничего не роняет.
|
|
@@ -66,9 +74,27 @@ case "$state" in
|
|
|
66
74
|
работа-отдана | влито) exit 0 ;;
|
|
67
75
|
esac
|
|
68
76
|
|
|
77
|
+
# То же самое, но объявить это на диске уже нечем: папка задачи разбирается до открытия заявки,
|
|
78
|
+
# и ход работы уезжает вместе с ней. Признак берётся из истории ветки — папка, снятая её
|
|
79
|
+
# коммитом. Без этой ветки хвост работы судился бы вторым признаком, то есть отбивался бы за
|
|
80
|
+
# ход, в котором исполнитель ждёт чужого прогона и ничего в дереве не двигает.
|
|
81
|
+
folder_archived() {
|
|
82
|
+
[ -n "$branch" ] || return 1
|
|
83
|
+
[ -n "$(git ls-tree -d --name-only HEAD -- "$tasks_dir/$branch" 2>/dev/null | head -1)" ] && return 1
|
|
84
|
+
main_branch="${RT_MAIN_BRANCH:-main}"
|
|
85
|
+
base="$(git merge-base "$main_branch" HEAD 2>/dev/null)"
|
|
86
|
+
[ -z "$base" ] && return 1
|
|
87
|
+
had="$(git ls-tree -d --name-only "$base" -- "$tasks_dir/$branch" 2>/dev/null | head -1)"
|
|
88
|
+
[ -z "$had" ] && had="$(git log "$base..HEAD" --diff-filter=A --name-only --pretty=format: -- "$tasks_dir/$branch" 2>/dev/null | head -1)"
|
|
89
|
+
[ -n "$had" ]
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
[ -z "$progress" ] && folder_archived && exit 0
|
|
93
|
+
|
|
69
94
|
# Следующий шаг из хода работы — его страж и называет в отказе: исполнитель, которому сказано
|
|
70
95
|
# только «работа не кончена», перечитывает ту же строку сам.
|
|
71
|
-
next_step="
|
|
96
|
+
next_step=""
|
|
97
|
+
[ -n "$progress" ] && next_step="$(sed -n 's/^[[:space:]]*[-*][[:space:]]*\*\*Следующий шаг:\*\*[[:space:]]*\(.*\)/\1/p' "$progress" 2>/dev/null | head -1)"
|
|
72
98
|
[ -z "$next_step" ] && next_step="что стоит в разделе «Где стоим» хода работы"
|
|
73
99
|
|
|
74
100
|
# Команда, меняющая дерево или его состояние. Чтение и поиск сюда не входят намеренно: ими и
|
|
@@ -119,10 +145,14 @@ commands="$(printf '%s' "$verdict" | jq -r '.ran // ""' 2>/dev/null)"
|
|
|
119
145
|
# Страж сравнивает номер этапа с тем, что лежит в истории ветки, и на выросшем номере требует
|
|
120
146
|
# команды из строки «Чем проверяется» — она стоит в замысле обратными кавычками. Приём, записанный
|
|
121
147
|
# прозой, страж не читает: подтвердить его выводом нечем, и это его известная граница.
|
|
122
|
-
stage_now="
|
|
123
|
-
stage_was="
|
|
148
|
+
stage_now=""
|
|
149
|
+
stage_was=""
|
|
150
|
+
if [ -n "$progress" ]; then
|
|
151
|
+
stage_now="$(sed -n 's/^[[:space:]]*[-*][[:space:]]*\*\*Этап:\*\*[[:space:]]*\([0-9][0-9]*\).*/\1/p' "$progress" 2>/dev/null | head -1)"
|
|
152
|
+
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)"
|
|
153
|
+
fi
|
|
124
154
|
|
|
125
|
-
if [ -n "$stage_now" ] && [ -n "$stage_was" ] && [ "$stage_now" -gt "$stage_was" ] 2>/dev/null; then
|
|
155
|
+
if [ -n "$progress" ] && [ -n "$stage_now" ] && [ -n "$stage_was" ] && [ "$stage_now" -gt "$stage_was" ] 2>/dev/null; then
|
|
126
156
|
plan="$root/$tasks_dir/$branch/plan.md"
|
|
127
157
|
# Контракт закрытого этапа, а не начатого: подтверждается то, что объявлено сделанным.
|
|
128
158
|
contract="$(awk -v n="$stage_was" '
|
|
@@ -164,7 +194,18 @@ fi
|
|
|
164
194
|
|
|
165
195
|
[ "$worked" = "true" ] && exit 0
|
|
166
196
|
|
|
167
|
-
|
|
197
|
+
if [ -z "$state" ]; then
|
|
198
|
+
reason="BLOCKED by turn-exit-guard: за этот ход не сделано ничего — ни правки, ни команды, меняющей дерево. Папки задачи у этой работы нет, и состояние взять неоткуда, но ход это не кончает.
|
|
199
|
+
|
|
200
|
+
Ход кончается четырьмя способами, и других нет: вопрос владельцу, ответа на который в правилах нет; отказ гарда; заполненное окно захода; отданная работа с начатой следующей. Названная и не запущенная команда выходом не является: строка «сейчас запущу» — объявление намерения, а оно прямо названо ложным концом хода.
|
|
201
|
+
|
|
202
|
+
Работа по слову владельца — «разложи», «обнови», «посмотри» — идёт без задачи и без ветки, и остановить её нечем, кроме этого признака.
|
|
203
|
+
|
|
204
|
+
Запусти названное этим же ходом. Владелец сказал остановиться — так и напиши: страж читает его слово, а не пересказ.
|
|
205
|
+
|
|
206
|
+
Страж судит один ход: следующий заход не отбивается."
|
|
207
|
+
else
|
|
208
|
+
reason="BLOCKED by turn-exit-guard: работа в состоянии '${state}', а за этот ход по ней не сделано ничего — ни правки, ни команды, меняющей дерево.
|
|
168
209
|
|
|
169
210
|
Ход кончается четырьмя способами, и других нет: вопрос владельцу, ответа на который в правилах нет; отказ гарда; заполненное окно захода; отданная работа с начатой следующей. Отчёт о сделанном выходом не является — он выглядит работой лучше всякой другой, и пустоты за ним не видно.
|
|
170
211
|
|
|
@@ -173,6 +214,7 @@ reason="BLOCKED by turn-exit-guard: работа в состоянии '${state}
|
|
|
173
214
|
Сделай его этим же ходом. Владелец сказал остановиться — так и напиши: страж читает его слово, а не пересказ.
|
|
174
215
|
|
|
175
216
|
Страж судит один ход: следующий заход не отбивается."
|
|
217
|
+
fi
|
|
176
218
|
|
|
177
219
|
# Общий хвост отказа: два законных хода. Файл может быть не разложен — тогда хвоста нет,
|
|
178
220
|
# а причина отказа остаётся прежней.
|
|
@@ -31,8 +31,10 @@
|
|
|
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
|
|
34
35
|
|
|
35
|
-
|
|
36
|
+
rt_hook_read
|
|
37
|
+
input="$RT_HOOK_INPUT"
|
|
36
38
|
[ -z "$input" ] && exit 0
|
|
37
39
|
|
|
38
40
|
command -v jq >/dev/null 2>&1 || exit 0
|
|
@@ -30,8 +30,10 @@
|
|
|
30
30
|
# работа РАЗРЕШАЕТСЯ (exit 0). Сломанный гард не имеет права заклинить работу.
|
|
31
31
|
|
|
32
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
|
|
33
34
|
|
|
34
|
-
|
|
35
|
+
rt_hook_read
|
|
36
|
+
input="$RT_HOOK_INPUT"
|
|
35
37
|
[ -z "$input" ] && exit 0
|
|
36
38
|
|
|
37
39
|
command -v jq >/dev/null 2>&1 || exit 0
|
|
@@ -144,9 +146,9 @@ fi
|
|
|
144
146
|
[ "$event" = "PreToolUse" ] || exit 0
|
|
145
147
|
[ "$pct" -ge "$stop_pct" ] || exit 0
|
|
146
148
|
|
|
147
|
-
tool="$(
|
|
148
|
-
path="$(
|
|
149
|
-
cmd="$(
|
|
149
|
+
tool="$(rt_hook_tool)"
|
|
150
|
+
path="$(rt_hook_file)"
|
|
151
|
+
cmd="$(rt_hook_cmd)"
|
|
150
152
|
|
|
151
153
|
allowed=0
|
|
152
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
|
пробел, при котором любая догадка делает работу опасной или бесполезной. Вопрос, заданный
|
|
@@ -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
|
|