@rt-tools/agent-kit 0.17.0 → 0.18.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-epics.github.mjs +123 -0
- package/assets/checks/board-gh.github.mjs +121 -0
- package/assets/checks/board.github.mjs +77 -73
- package/assets/checks/check-board.github.mjs +39 -3
- package/assets/checks/check-hook-scope.mjs +126 -0
- package/assets/checks/check-profile-drift.mjs +195 -0
- package/assets/checks/check-specs.mjs +1 -1
- package/assets/checks/rt-kit-checks.config.mjs +13 -0
- package/assets/checks/spec-anchors.mjs +2 -2
- package/assets/defaults/project.sh +21 -117
- package/assets/defaults/shell.sh +139 -0
- package/assets/hooks/browser-guard-no-other-drivers.sh +41 -2
- package/assets/hooks/claim-guard.sh +22 -1
- package/assets/hooks/docs-guard.sh +1 -1
- package/assets/hooks/exam-guard.sh +85 -12
- package/assets/hooks/git-guard-delivery-conflict.sh +85 -0
- package/assets/hooks/git-guard-delivery-signature.sh +14 -5
- package/assets/hooks/git-guard-delivery.sh +15 -2
- package/assets/hooks/grill-gate.sh +63 -6
- package/assets/hooks/proposal-guard.sh +11 -4
- package/assets/hooks/rule-source-guard.sh +8 -13
- package/assets/hooks/task-context-load.sh +22 -0
- package/assets/hooks/task-flow-context.sh +9 -0
- package/assets/hooks/task-flow-draft-guard.sh +17 -0
- package/assets/hooks/turn-exit-guard.sh +45 -87
- package/assets/hooks/waiting-turn-guard.sh +65 -2
- package/assets/hooks/work-start-guard.sh +166 -0
- package/assets/hooks/write-targets.sh +24 -0
- package/assets/laws/verifiability.md +23 -0
- package/assets/patterns/browser-verification-measure.md +3 -1
- package/assets/patterns/browser-verification-stand.md +10 -2
- package/assets/patterns/doc-style-write.md +21 -1
- package/assets/patterns/git-workflow-commit.github.md +18 -3
- package/assets/patterns/git-workflow-docker.md +8 -0
- package/assets/patterns/git-workflow-freshness.md +87 -0
- package/assets/patterns/git-workflow-merge.md +23 -0
- package/assets/patterns/git-workflow-pr.github.md +6 -0
- package/assets/patterns/git-workflow-restart.md +18 -0
- package/assets/patterns/git-workflow-stack.md +30 -0
- package/assets/patterns/lib-layers-move.md +4 -0
- package/assets/patterns/spec-driven-domain.md +13 -1
- package/assets/patterns/spec-driven-rule.md +5 -4
- package/assets/patterns/spec-driven-sweep.md +1 -1
- package/assets/patterns/status-report-table.github.md +1 -1
- package/assets/patterns/task-flow-archive.md +12 -2
- package/assets/patterns/task-flow-close.md +31 -24
- package/assets/patterns/task-flow-handoff.md +14 -2
- package/assets/patterns/task-flow-resume.md +18 -3
- package/assets/patterns/task-flow-start.md +23 -23
- package/assets/patterns/testing-e2e.md +23 -0
- package/assets/patterns/turn-entry-map.md +1 -1
- package/assets/pitfalls/agent-kit.md +4 -6
- package/assets/pitfalls/doc-style.md +10 -0
- package/assets/pitfalls/git-workflow.github.md +20 -0
- package/assets/pitfalls/spec-driven.md +6 -0
- package/assets/pitfalls/task-flow.md +20 -0
- package/assets/pitfalls/testing.md +6 -0
- package/assets/pitfalls/turn-conduct.md +35 -0
- package/assets/rules/browser-verification.md +19 -0
- package/assets/rules/deploy-flow.github.md +9 -0
- package/assets/rules/doc-style.md +6 -0
- package/assets/rules/git-workflow.azure.md +17 -2
- package/assets/rules/git-workflow.github.md +89 -90
- package/assets/rules/git-workflow.gitlab.md +12 -4
- package/assets/rules/lists.md +5 -0
- package/assets/rules/observability.needs-app.md +4 -0
- package/assets/rules/reuse-first.md +6 -0
- package/assets/rules/spec-driven.md +29 -25
- package/assets/rules/task-flow.md +52 -46
- package/assets/rules/testing.md +27 -1
- package/assets/rules/turn-conduct.md +46 -43
- package/assets/skills/agent-kit.md +9 -0
- package/package.json +1 -1
- package/rt-tools-agent-kit-0.18.0.tgz +0 -0
- package/rt-tools-agent-kit-0.17.0.tgz +0 -0
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# rt-hook: Stop
|
|
3
|
+
# Требует: hooks/deny-tail.sh, hooks/profile-check.sh
|
|
4
|
+
# Гард начала работы: ход, правивший код приложения, не заканчивается, пока владелец в этом же
|
|
5
|
+
# ходе о работе не просил. Stop.
|
|
6
|
+
#
|
|
7
|
+
# Зачем именно так. Статья «заход работу не начинает сам» держится памятью исполнителя, и
|
|
8
|
+
# держится плохо: заход, открывшийся после чистки контекста, получает от хука запуска состояние
|
|
9
|
+
# незаконченной работы и замысел с этапами — и оба говорят, что делать, если работать. О том,
|
|
10
|
+
# работать ли, не говорит ни один. Строка с путём к файлу, присланная владельцем, прочитана как
|
|
11
|
+
# поручение, и заход правит полсотни файлов, которых у него никто не просил.
|
|
12
|
+
#
|
|
13
|
+
# Соседние гарды этого не ловят, и не по недосмотру: каждый судит своё. Гард замысла требует
|
|
14
|
+
# замысел на диске — он лежит; гард эпика отбивает чужую задачу — задача своя; гард разговора
|
|
15
|
+
# судит ход, в котором задан вопрос, — а вопроса не задали ровно потому, что решили не
|
|
16
|
+
# спрашивать. Все три судят предмет правки и её порядок, и ни один не спрашивает, кто эту
|
|
17
|
+
# правку заказал.
|
|
18
|
+
#
|
|
19
|
+
# Просьба ловится формой, а не смыслом. Смысл машине не виден, и гард, взявшийся его понимать,
|
|
20
|
+
# отбивал бы работу по настроению; поэтому судится обратное — то, что просьбой не бывает ни при
|
|
21
|
+
# каком прочтении: пустая реплика, одно слово и путь к файлу. Всё остальное считается просьбой,
|
|
22
|
+
# и это выбрано намеренно: ложный отказ здесь стоит дороже пропуска — он останавливает работу,
|
|
23
|
+
# которую владелец заказал.
|
|
24
|
+
#
|
|
25
|
+
# Код приложения узнаётся признаком дерева `rt_is_app_code` — тем же, которым его узнаёт гард
|
|
26
|
+
# замысла. Дерево, признака не объявившее, этого гарда не получает: судить ему нечем.
|
|
27
|
+
#
|
|
28
|
+
# ОТКАЗ В ПОЛЬЗУ РАБОТЫ: нет `jq`, нет записи хода, нет признака дерева, повторный заход, любая
|
|
29
|
+
# своя ошибка — ход РАЗРЕШАЕТСЯ (exit 0). Сломанный гард не имеет права заклинить работу.
|
|
30
|
+
|
|
31
|
+
. "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/utf8.sh" 2>/dev/null || true
|
|
32
|
+
. "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/hook-input.sh" 2>/dev/null || true
|
|
33
|
+
|
|
34
|
+
rt_hook_read
|
|
35
|
+
input="$RT_HOOK_INPUT"
|
|
36
|
+
[ -z "$input" ] && exit 0
|
|
37
|
+
|
|
38
|
+
command -v jq >/dev/null 2>&1 || exit 0
|
|
39
|
+
|
|
40
|
+
# Повторный заход по тому же ходу не судится: гард сказал своё один раз и отпускает.
|
|
41
|
+
active="$(printf '%s' "$input" | jq -r '.stop_hook_active // false' 2>/dev/null)"
|
|
42
|
+
[ "$active" = "true" ] && exit 0
|
|
43
|
+
|
|
44
|
+
transcript="$(printf '%s' "$input" | jq -r '.transcript_path // empty' 2>/dev/null)"
|
|
45
|
+
[ -z "$transcript" ] && exit 0
|
|
46
|
+
[ -f "$transcript" ] || exit 0
|
|
47
|
+
|
|
48
|
+
rt_hooks_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
49
|
+
|
|
50
|
+
# Профиль дерева: сперва умолчание пакета, поверх него — надстройка проекта, если она есть.
|
|
51
|
+
for profile in "$rt_hooks_dir/../rt-kit/defaults/project.sh" "$rt_hooks_dir/../defaults/project.sh" \
|
|
52
|
+
"${CLAUDE_PROJECT_DIR:-.}/.claude/rt-kit/defaults/project.sh" "${CLAUDE_PROJECT_DIR:-.}/.claude/rt-kit/project.sh"; do
|
|
53
|
+
# shellcheck disable=SC1090
|
|
54
|
+
[ -f "$profile" ] && . "$profile" 2>/dev/null
|
|
55
|
+
done
|
|
56
|
+
|
|
57
|
+
# shellcheck disable=SC1090
|
|
58
|
+
[ -f "$rt_hooks_dir/profile-check.sh" ] && . "$rt_hooks_dir/profile-check.sh"
|
|
59
|
+
command -v rt_needs >/dev/null 2>&1 || rt_needs() { command -v "$1" >/dev/null 2>&1; }
|
|
60
|
+
rt_needs rt_is_app_code work-start-guard || exit 0
|
|
61
|
+
|
|
62
|
+
# Ход — это всё, что записано после последнего настоящего ввода владельца. Ответ инструмента
|
|
63
|
+
# приходит той же ролью, поэтому строки с `tool_result` вводом не считаются.
|
|
64
|
+
#
|
|
65
|
+
# Хвост в 400 строк: запись хода растёт всю сессию, а судится только последний ход.
|
|
66
|
+
asked="$(tail -n 400 "$transcript" 2>/dev/null | jq -s -r '
|
|
67
|
+
def is_input:
|
|
68
|
+
.type == "user"
|
|
69
|
+
and (((.message.content // []) | if type == "array"
|
|
70
|
+
then ([.[] | select(.type == "tool_result")] | length)
|
|
71
|
+
else 0 end) == 0);
|
|
72
|
+
|
|
73
|
+
(map(is_input) | rindex(true)) as $i
|
|
74
|
+
| if $i == null then "" else
|
|
75
|
+
(.[$i].message.content
|
|
76
|
+
| if type == "string" then .
|
|
77
|
+
elif type == "array" then (map(if type == "object" then (.text // "") else tostring end) | join("\n"))
|
|
78
|
+
else "" end)
|
|
79
|
+
end
|
|
80
|
+
' 2>/dev/null)"
|
|
81
|
+
|
|
82
|
+
# Ввода в записи нет вовсе — судить нечего: ход разрешается.
|
|
83
|
+
[ -z "$asked" ] && exit 0
|
|
84
|
+
|
|
85
|
+
# Правленные за ход файлы: инструменты правки называют путь полем, а команда оболочки — своим
|
|
86
|
+
# разбором, и его здесь нет намеренно. Гард судит явную правку файла: команда, пишущая в код
|
|
87
|
+
# мимо инструмента правки, остаётся его известной границей.
|
|
88
|
+
edited="$(tail -n 400 "$transcript" 2>/dev/null | jq -s -r '
|
|
89
|
+
def is_input:
|
|
90
|
+
.type == "user"
|
|
91
|
+
and (((.message.content // []) | if type == "array"
|
|
92
|
+
then ([.[] | select(.type == "tool_result")] | length)
|
|
93
|
+
else 0 end) == 0);
|
|
94
|
+
|
|
95
|
+
(map(is_input) | rindex(true)) as $i
|
|
96
|
+
| (if $i == null then [] else .[$i:] end)
|
|
97
|
+
| [.[] | select(.type == "assistant") | (.message.content // [])[]
|
|
98
|
+
| select(.type == "tool_use")
|
|
99
|
+
| select(.name == "Edit" or .name == "Write" or .name == "MultiEdit" or .name == "NotebookEdit")
|
|
100
|
+
| (.input.file_path // .input.notebook_path // empty)]
|
|
101
|
+
| unique | .[]
|
|
102
|
+
' 2>/dev/null)"
|
|
103
|
+
|
|
104
|
+
[ -z "$edited" ] && exit 0
|
|
105
|
+
|
|
106
|
+
touched=""
|
|
107
|
+
while IFS= read -r path; do
|
|
108
|
+
[ -z "$path" ] && continue
|
|
109
|
+
if rt_is_app_code "$path" 2>/dev/null; then
|
|
110
|
+
touched="$path"
|
|
111
|
+
break
|
|
112
|
+
fi
|
|
113
|
+
done <<EOF
|
|
114
|
+
$edited
|
|
115
|
+
EOF
|
|
116
|
+
|
|
117
|
+
# Код приложения за ход не правился: гард молчит. Разбор, тексты и обвязка идут своим ходом —
|
|
118
|
+
# требовать слова владельца на них значило бы запретить разведку до просьбы.
|
|
119
|
+
[ -z "$touched" ] && exit 0
|
|
120
|
+
|
|
121
|
+
# Просьбой не бывает ни при каком прочтении: пустая реплика, одно слово, путь к файлу. Судится
|
|
122
|
+
# первая непустая строка ввода: развёрнутая просьба своей первой строкой уже просьба, а путь,
|
|
123
|
+
# присланный один, ею не станет и дальше.
|
|
124
|
+
first="$(printf '%s' "$asked" | tr -d '\r' | sed -n '/[^[:space:]]/{p;q;}')"
|
|
125
|
+
words="$(printf '%s' "$asked" | tr -s '[:space:]' '\n' | grep -c '[^[:space:]]' 2>/dev/null || echo 0)"
|
|
126
|
+
|
|
127
|
+
case "$first" in
|
|
128
|
+
# Служебная отметка о прерывании: своей просьбы в ней нет.
|
|
129
|
+
'[Request interrupted'*) kind="прерывание" ;;
|
|
130
|
+
*)
|
|
131
|
+
if [ "$words" -le 1 ] 2>/dev/null; then
|
|
132
|
+
case "$first" in
|
|
133
|
+
*/*) kind="путь к файлу" ;;
|
|
134
|
+
*) kind="одно слово" ;;
|
|
135
|
+
esac
|
|
136
|
+
else
|
|
137
|
+
kind=""
|
|
138
|
+
fi
|
|
139
|
+
;;
|
|
140
|
+
esac
|
|
141
|
+
|
|
142
|
+
# Просьба в ходе есть: работа заказана, и гард отпускает.
|
|
143
|
+
[ -z "$kind" ] && exit 0
|
|
144
|
+
|
|
145
|
+
reason="BLOCKED by work-start-guard: за ход правился код приложения — «${touched}», — а последняя реплика владельца просьбой не была: ${kind}.
|
|
146
|
+
|
|
147
|
+
Заход работу не начинает сам. Передача прошлого захода, состояние задачи из хука запуска и назначенный эпик говорят, что делать, если работать, и молчат о том, работать ли. Строка с адресом называет файл, а не действие: прочитанная как поручение, она даёт заходу задание, которого владелец не давал.
|
|
148
|
+
|
|
149
|
+
Ход отсюда один: назови владельцу состояние работы и спроси, продолжать ли, — и дождись ответа. Сделанное этим ходом не откатывается само: скажи, что уже правлено.
|
|
150
|
+
|
|
151
|
+
Гард судит один ход: следующий заход не отбивается."
|
|
152
|
+
|
|
153
|
+
# Общий хвост отказа: два законных хода. Файл может быть не разложен — тогда хвоста нет,
|
|
154
|
+
# а причина отказа остаётся прежней.
|
|
155
|
+
# shellcheck disable=SC1090
|
|
156
|
+
[ -f "$rt_hooks_dir/deny-tail.sh" ] && . "$rt_hooks_dir/deny-tail.sh" 2>/dev/null
|
|
157
|
+
command -v rt_deny_tail >/dev/null 2>&1 || rt_deny_tail() { :; }
|
|
158
|
+
deny_tail_text="$(rt_deny_tail "")"
|
|
159
|
+
[ -n "$deny_tail_text" ] && reason="${reason}
|
|
160
|
+
|
|
161
|
+
${deny_tail_text}"
|
|
162
|
+
|
|
163
|
+
jq -n --arg r "$reason" '{decision:"block",reason:$r}' 2>/dev/null \
|
|
164
|
+
|| printf '{"decision":"block","reason":"work-start-guard: код правился, а просьбы владельца в этом ходе не было."}\n'
|
|
165
|
+
|
|
166
|
+
exit 0
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Цели записи, названные командой оболочки прямо: перенаправление, `tee`, правка на месте,
|
|
3
|
+
# копирование поверх. Печатает по одной в строке.
|
|
4
|
+
#
|
|
5
|
+
# Строки `# rt-hook:` здесь нет намеренно: это помощник, а не хук. Он ничего не решает сам и
|
|
6
|
+
# зовётся оттуда, где судят правку файла, — гардом места правки и гардом экзамена. Общий он
|
|
7
|
+
# потому, что признак записи у обоих один и тот же: разойдясь, две копии пропустили бы разные
|
|
8
|
+
# формы записи, и заметить это было бы нечем.
|
|
9
|
+
#
|
|
10
|
+
# Общий признак записи здесь не берётся намеренно — он широк, в нём и имя интерпретатора, и
|
|
11
|
+
# запуск разложенной проверки читался бы как правка её самой. Снятие копии сюда тоже не входит:
|
|
12
|
+
# снятый файл раскладка кладёт заново, и так чинят копию, которую переписал форматтер.
|
|
13
|
+
|
|
14
|
+
rt_write_targets() {
|
|
15
|
+
tr "\"'\`" ' ' \
|
|
16
|
+
| sed -E 's/>>?/\n>/g' \
|
|
17
|
+
| sed -nE '
|
|
18
|
+
s/^>[[:space:]]*([^[:space:]|&;]+).*/\1/p
|
|
19
|
+
s/(^|.*[[:space:]])tee[[:space:]]+(-a[[:space:]]+)?([^[:space:]|&;]+).*/\3/p
|
|
20
|
+
s/(^|.*[[:space:]])sed[[:space:]]+-i[[:space:]]+([^[:space:]]+[[:space:]]+)*([^[:space:]|&;]+)$/\3/p
|
|
21
|
+
s/(^|.*[[:space:]])(cp|mv|install)[[:space:]]+([^[:space:]]+[[:space:]]+)+([^[:space:]|&;]+).*/\4/p
|
|
22
|
+
' \
|
|
23
|
+
| sort -u
|
|
24
|
+
}
|
|
@@ -86,3 +86,26 @@
|
|
|
86
86
|
- **Решение, зависящее от текущего момента, получает момент снаружи.** Иначе проверить его можно
|
|
87
87
|
только подкруткой часов, а подкрученные часы действуют и на всё, что оказалось рядом: проверка
|
|
88
88
|
начинает зависеть от того, что к ней отношения не имеет.
|
|
89
|
+
- **Признак проверки выбирается по вопросу, на который отвечают, а не по тому, что задела
|
|
90
|
+
правка.** Признак, снятый с предмета правки, отвечает про неё же: правка собирала адрес — и
|
|
91
|
+
наличие адреса в отданной странице подтверждает, что приложение его собрало, а не что по нему
|
|
92
|
+
что-то приедет. Спрашивают при этом «показывается ли», и разницы между двумя ответами не видно
|
|
93
|
+
ни в одном выводе: оба выглядят как «проверено». Признак поэтому называется вместе с вопросом,
|
|
94
|
+
и первым проверяется, что он на этот вопрос отвечает.
|
|
95
|
+
- **Чужой отказ называет своё состояние, а не причину на своей стороне.** Ответ внешней службы
|
|
96
|
+
говорит, чем кончился запрос у неё; что лежит в настройке, которую она читала, он не говорит
|
|
97
|
+
никогда — и достроенное из него утверждение выглядит выводом из наблюдения, которого не было.
|
|
98
|
+
Значение, о котором идёт речь, читается там, где оно лежит, а решение, выведенное из
|
|
99
|
+
непроверенной причины, наружу не выносится вовсе: оно просит чинить не то.
|
|
100
|
+
- **Про прошлое поведение отвечает код той версии, о которой спрашивают.** Тело правки говорит,
|
|
101
|
+
что она сменила, и молчит о том, что было верно до неё: условие, в которое дописано ещё одно
|
|
102
|
+
обязательное значение, гасит показанное раньше — и видно это только сравнением с версией до
|
|
103
|
+
правки, а не чтением самой правки. Слово того, кто видел прежнее поведение своими глазами, —
|
|
104
|
+
показание: расхождение между ним и текстом в истории разбирается чтением кода на его стороне
|
|
105
|
+
расхождения.
|
|
106
|
+
- **Проверка, снятая с набора ради того, чтобы прогон прошёл, работу не подтверждает.** Набор
|
|
107
|
+
собирается умолчанием и надстройкой дерева, и выкушенная надстройкой проверка ничем не отличима
|
|
108
|
+
от той, которой в дереве нет вовсе: прогон зелен потому, что её никто не звал. Соседняя статья
|
|
109
|
+
судит место, выведенное из-под проверки, — здесь выведено не место, а вся она, и следа в коде
|
|
110
|
+
не остаётся никакого. Расхождение закрывается тем, на что проверка указала, а несогласие с ней
|
|
111
|
+
самой — вопрос к владельцу.
|
|
@@ -67,7 +67,9 @@ description: Паттерн правила browser-verification. Брать, к
|
|
|
67
67
|
- Между кликами обязателен `await`: синхронный цикл «кликнул — прочитал DOM» читает состояние
|
|
68
68
|
до перерисовки и возвращает устаревшие значения.
|
|
69
69
|
- `select_browser` протухает через 300 секунд. На длинной проверке это срабатывает посреди
|
|
70
|
-
работы — это не сбой стенда, повторить вызов и продолжить. Закреплённый профиль —
|
|
70
|
+
работы — это не сбой стенда, повторить вызов и продолжить. Закреплённый профиль называет само дерево — переменной окружения либо своим файлом рядом с
|
|
71
|
+
настройками раскладки; спрашивается он у помощника, а не помнится: идентификатор локален для
|
|
72
|
+
машины и в пакет не едет вовсе.
|
|
71
73
|
|
|
72
74
|
## Поведение роутера воспроизводится нажатиями
|
|
73
75
|
|
|
@@ -133,8 +133,11 @@ pnpm install --frozen-lockfile # из ../<префикс>-base: node_mod
|
|
|
133
133
|
коммит — проверяют сборкой, а не тем, что он в главной ветке.
|
|
134
134
|
- Стенд второго дерева поднимают на своих портах: {{sitePort}}, {{adminPort}} и {{apiPort}} заняты владельцем, а {{ssrPort}}
|
|
135
135
|
занимать нельзя — стенд разработчика ходит по имени `ssr:{{ssrPort}}`.
|
|
136
|
-
- Оба стенда держат поднятыми
|
|
137
|
-
заметишь только то, что успел запомнить.
|
|
136
|
+
- Оба стенда держат поднятыми одновременно — ровно до конца сравнения: если сравнивать по памяти
|
|
137
|
+
между двумя запусками, заметишь только то, что успел запомнить. Со сравнением стенд второго
|
|
138
|
+
дерева гасится тем же ходом, а не оставляется до конца захода: оставленные стенды копятся
|
|
139
|
+
заходами и держат соединения к хранилищу, и правило говорит об этом ловушкой. Снятие отбито —
|
|
140
|
+
стенд называется владельцу в конце захода поимённо, с портом.
|
|
138
141
|
|
|
139
142
|
## Состояние портов снимается до первой сборки захода
|
|
140
143
|
|
|
@@ -160,6 +163,11 @@ done
|
|
|
160
163
|
это по её длительности.
|
|
161
164
|
- Свой дев-сервер не поднимать: сайт на {{sitePort}}, админка на {{adminPort}}, API на {{apiPort}} уже подняты
|
|
162
165
|
владельцем, и второй экземпляр отбивается гардом.
|
|
166
|
+
- **Дев-сервер владельца — место, где смотрят, а не место, где проверяют.** Вывод о том, что
|
|
167
|
+
экран верен, делается на прод-сборке за настоящим прокси: разметка от сервера, локали, кэш,
|
|
168
|
+
перенаправления и заголовки на дев-сервере либо другие, либо отсутствуют вовсе. Взгляд на
|
|
169
|
+
поднятый дев-сервер годится, чтобы понять, что происходит на незнакомом экране, и не годится
|
|
170
|
+
подтверждением: подтверждение — замер на стенде из прод-сборки.
|
|
163
171
|
- **Общая сборка глушит все три дев-сервера владельца, а не только API.** После
|
|
164
172
|
`nx run-many -t build` ложатся и сайт на {{sitePort}}, и админка на {{adminPort}}. Собирать надо то, что
|
|
165
173
|
проверяешь (`npx nx build site`), а не всё дерево. Если серверы легли, поднять их обратно
|
|
@@ -48,7 +48,7 @@ description: Паттерн правила doc-style. Брать при напи
|
|
|
48
48
|
|
|
49
49
|
«Не планируется», «не будет», «отдельная фича по запросу» — это намерение владельца, а не
|
|
50
50
|
свойство системы. Что не сделано — да, почему не сделано — да, что не будет сделано никогда —
|
|
51
|
-
нет. Вместо приговора — открытый вопрос `Q
|
|
51
|
+
нет. Вместо приговора — открытый вопрос `Q-<буква закона>-<номер>` с тем, что решение изменит.
|
|
52
52
|
|
|
53
53
|
Ошибка беззвучная: сверять утверждение о будущем не с чем, оно проходит любую проверку. Так в
|
|
54
54
|
первый живой спек попало «онлайн-оплаты нет и не планируется», хотя оплата в планах.
|
|
@@ -73,12 +73,32 @@ description: Паттерн правила doc-style. Брать при напи
|
|
|
73
73
|
фразе, которую читают, и не ловится в файле на восемьсот строк: оборот всплывает по всему
|
|
74
74
|
тексту, и глазами он не считается.
|
|
75
75
|
|
|
76
|
+
Берётся чужой оборот оттуда, что писавший только что читал, и сильнее всего это бьёт при письме
|
|
77
|
+
в том же роде: ни одного слова вне словаря в такой фразе нет, каждое проходит вычитку по
|
|
78
|
+
отдельности, а непонятной оказывается фраза целиком. Вводный абзац поэтому пишется последним —
|
|
79
|
+
когда источник уже выветрился.
|
|
80
|
+
|
|
76
81
|
Вторая половина проверки — счёт, и там, где дерево разложило проверку слога
|
|
77
82
|
`checks/check-prose-style.mjs`, набор оборотов накоплен в ней самой: каждый признак назван
|
|
78
83
|
вместе с заменой, а находка печатается файлом и строкой. Оборот, найденный вычиткой,
|
|
79
84
|
дописывается в набор тем же ходом — иначе следующий пишущий начинает с пустого места и
|
|
80
85
|
собирает его заново из своей памяти.
|
|
81
86
|
|
|
87
|
+
Читается не только слово, но и то, одно ли у фразы прочтение. Три причины нечитаемости приходят
|
|
88
|
+
по очереди, и правка одной заводит следующую — статью закона переписывали трижды, и каждый раз
|
|
89
|
+
владелец не понимал её по новой причине:
|
|
90
|
+
|
|
91
|
+
```text
|
|
92
|
+
✗ не различать их нельзя — двойное отрицание читается наоборот сказанному
|
|
93
|
+
✗ у каждого видно, откуда он взялся — местоимение без хозяина, а «взялся» не называет действия
|
|
94
|
+
✗ либо снимается ролью, либо остаётся навсегда — два исхода через «или» без правила, по которому наступает тот или другой
|
|
95
|
+
|
|
96
|
+
✓ выданное лично снимается только лично: роль этого не трогает
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Довод «без такого устройства было бы так» в текст не идёт вовсе: он спорит с тем, чего нет, и
|
|
100
|
+
читается как «это к чему?».
|
|
101
|
+
|
|
82
102
|
Чего не видит ни слух, ни она: повтора законного оборота. Каждое его появление законно
|
|
83
103
|
поодиночке, а стоящий в тексте десятками он уже не приём, а тик — читатель перестаёт замечать
|
|
84
104
|
его вместе со смыслом. Считается это грепом по тексту, и число само по себе отказом не бывает:
|
|
@@ -115,7 +115,7 @@ $GH api graphql -f query='mutation { deleteIssue(input: {issueId: "<node-id>"})
|
|
|
115
115
|
составная команда отклоняется целиком — ветки в ней ещё нет:
|
|
116
116
|
|
|
117
117
|
```bash
|
|
118
|
-
✗ git checkout -b <КЛЮЧ>-85-guest-token && git commit -m 'feat(
|
|
118
|
+
✗ git checkout -b <КЛЮЧ>-85-guest-token && git commit -m 'feat(<область>): …'
|
|
119
119
|
✓ git checkout -b <КЛЮЧ>-85-guest-token
|
|
120
120
|
✓ git commit -F -
|
|
121
121
|
```
|
|
@@ -166,11 +166,25 @@ GIT_COMMITTER_NAME="<бот>" GIT_COMMITTER_EMAIL="<номер>+<бот>@users.n
|
|
|
166
166
|
конце заголовка не принимается, длина — до 150 знаков.
|
|
167
167
|
|
|
168
168
|
```
|
|
169
|
-
feat(
|
|
170
|
-
fix(
|
|
169
|
+
feat(<область>): availability calendar with season prices
|
|
170
|
+
fix(<область>): reject overlapping booking dates
|
|
171
171
|
chore(deploy): docker-compose for vps
|
|
172
172
|
```
|
|
173
173
|
|
|
174
|
+
## Файлы своей работы называются поимённо
|
|
175
|
+
|
|
176
|
+
Рабочее дерево одно, а работ в нём бывает несколько: чужая незакоммиченная папка, оставленный
|
|
177
|
+
черновик, правка соседнего захода. Каталог, добавленный целиком, уносит их с собой, и в главную
|
|
178
|
+
ветку уезжает то, о чём эта работа не просила.
|
|
179
|
+
|
|
180
|
+
```bash
|
|
181
|
+
git add docs/tasks/<КЛЮЧ>-<номер>-<slug>/plan.md docs/tasks/<КЛЮЧ>-<номер>-<slug>/progress.md
|
|
182
|
+
git diff --cached --name-only # что действительно встало в индекс
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
Проверки индекса после добавления мало: однажды добавленный чужой файл становится
|
|
186
|
+
отслеживаемым и дальше едет молча — следующий `git add` его уже не спрашивает.
|
|
187
|
+
|
|
174
188
|
## Документ едет тем же коммитом
|
|
175
189
|
|
|
176
190
|
`docs-guard` требует пару и называет её сам. Обход — строка в теле, причина обязательна:
|
|
@@ -182,6 +196,7 @@ Docs-skip: правка только в тестах хука, зеркала у
|
|
|
182
196
|
## Частые промахи
|
|
183
197
|
|
|
184
198
|
- `gh` в оболочке пользователя подменён — звать `/opt/homebrew/bin/gh` напрямую.
|
|
199
|
+
- Каталог добавлен целиком при чужом незакоммиченном рядом: чужая папка задачи уехала в главную ветку и стала отслеживаемой.
|
|
185
200
|
- `git add` с несколькими путями не добавляет ничего, если хоть один путь не существует:
|
|
186
201
|
команда обрывается на первом промахе целиком, а не пропускает его. Следующий
|
|
187
202
|
`git commit --amend` при этом уносит в коммит всё, что осталось в индексе, — так в коммит
|
|
@@ -96,6 +96,14 @@ docker builder prune --force --filter until=24h # вчерашний кэ
|
|
|
96
96
|
Освобождают отбором, а не общей чисткой: у сценария чистки образов сперва спрашивают, что он
|
|
97
97
|
снял бы, и только потом дают снимать.
|
|
98
98
|
|
|
99
|
+
**Снятие образов отбивает не гейт, а режим захода.** В автоматическом режиме команда с
|
|
100
|
+
необратимым удалением — снятие образа, чистка слоёв — отклоняется независимо от разрешений, и
|
|
101
|
+
разрешающее правило на ту же команду отказа не снимает. Повторный вызов отбивается так же: отказ
|
|
102
|
+
не зависит от того, как команда набрана, поэтому переформулировка тут не путь. Ходов отсюда два —
|
|
103
|
+
освободить место тем, что удаления не требует, либо назвать это владельцу: режим переключает он,
|
|
104
|
+
и снимает образы тоже он. Названное в конце захода стоит целого прогона: конвейер всё это время
|
|
105
|
+
стоит красным по нехватке места.
|
|
106
|
+
|
|
99
107
|
## Демон поднимается своим CLI
|
|
100
108
|
|
|
101
109
|
Открытие приложения виртуальную машину не поднимает: приложение считается запущенным, а демон
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: git-workflow-freshness
|
|
3
|
+
kind: pattern
|
|
4
|
+
rule: git-workflow
|
|
5
|
+
description: Паттерн правила git-workflow. Брать перед пушем, при взятии задачи и после каждого известного слияния: чтение всех своих открытых заявок разом, отделение отставания от спора в файлах, сверка локальной вершины с хостингом.
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Свежесть открытых заявок
|
|
9
|
+
|
|
10
|
+
Паттерн правила `git-workflow`. Что при этом должно быть верно — закон
|
|
11
|
+
`docs/constitution/delivery.md`. Разбор одного конфликта — паттерн `git-workflow-merge`,
|
|
12
|
+
череда веток из одного основания — `git-workflow-stack`.
|
|
13
|
+
|
|
14
|
+
## Когда брать
|
|
15
|
+
|
|
16
|
+
Три места, и в каждом чтение обязательно:
|
|
17
|
+
|
|
18
|
+
- перед пушем — вершина главной могла уйти, пока шла работа;
|
|
19
|
+
- при взятии новой задачи — прежние заявки остались открытыми и о себе не напомнят;
|
|
20
|
+
- после каждого слияния, о котором стало известно, — оно отставило все остальные разом.
|
|
21
|
+
|
|
22
|
+
## Что читается
|
|
23
|
+
|
|
24
|
+
Одной командой по всем своим открытым заявкам, а не по той, чья ветка сейчас взята:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
/opt/homebrew/bin/gh pr list --author '<машинная запись>' --state open \
|
|
28
|
+
--json number,headRefName,isDraft,mergeable,mergeStateStatus,statusCheckRollup
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
| Поле | Что говорит |
|
|
32
|
+
| -------------------- | --------------------------------------------------------------------------------- |
|
|
33
|
+
| `mergeable` | `MERGEABLE` — кнопку нажать можно; `CONFLICTING` — нельзя; `UNKNOWN` — ещё не посчитано |
|
|
34
|
+
| `mergeStateStatus` | `BEHIND` — отстала от главной; `DIRTY` — спор в файлах; `BLOCKED` — ждёт разбора |
|
|
35
|
+
| `isDraft` | черновик: кнопка слияния заблокирована хостингом при любом цвете прогона |
|
|
36
|
+
| `statusCheckRollup` | прогон на вершине: пустой список — прогона нет вовсе, а не «зелено» |
|
|
37
|
+
|
|
38
|
+
`UNKNOWN` означает «ещё не посчитано» и читается как «спросить снова через несколько секунд»,
|
|
39
|
+
а не как «конфликтов нет».
|
|
40
|
+
|
|
41
|
+
## Спор в файлах отделяется от отставания
|
|
42
|
+
|
|
43
|
+
Метка хостинга говорит одно — «нажать нельзя». Что именно чинить, отвечает слияние деревьев,
|
|
44
|
+
и отвечает без сети:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
git fetch origin
|
|
48
|
+
git merge-tree "$(git merge-base origin/main <ветка>)" origin/main <ветка> | grep -c '^<<<<<<<'
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Ноль — ветка просто отстала, и лечится это вливанием главной. Больше нуля — спор в файлах, и
|
|
52
|
+
каждый разбирается по роду файла: паттерн `git-workflow-merge`.
|
|
53
|
+
|
|
54
|
+
## Что делается с найденным
|
|
55
|
+
|
|
56
|
+
| Найдено | Что делается |
|
|
57
|
+
| -------------------------------------- | ------------------------------------------------------------------ |
|
|
58
|
+
| отстала, спора нет | главная вливается в ветку и отправляется тем же ходом |
|
|
59
|
+
| спор в файлах | разбор по роду файла, затем отправка |
|
|
60
|
+
| прогона на вершине нет | вершина перезапускается или дожидается — цвета нет ни у той, ни у другой |
|
|
61
|
+
| черновик при зелёном прогоне | черновик снимается — у него кнопка слияния заблокирована |
|
|
62
|
+
| заявка открыта человеком, а не машиной | ревьювера ей уже не поставить: заводится заново машинной записью |
|
|
63
|
+
|
|
64
|
+
Внутри одной ветки порядок один: влить главную → прогнать набор гейта → отправить → перечитать
|
|
65
|
+
состояние у хостинга. Перечитывание — часть работы, а не отчёт о ней.
|
|
66
|
+
|
|
67
|
+
## Локальная вершина сверяется с той, что на хостинге
|
|
68
|
+
|
|
69
|
+
Догнанная в рабочем дереве и не отправленная ветка работой не считается: человек видит прежнее
|
|
70
|
+
состояние.
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
git rev-parse HEAD
|
|
74
|
+
/opt/homebrew/bin/gh pr view <номер> --json headRefOid --jq .headRefOid
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Разошлись — отправка не сделана, и это первое, что чинится.
|
|
78
|
+
|
|
79
|
+
## Частые промахи
|
|
80
|
+
|
|
81
|
+
- Состояние заявки прочитано по памяти прошлого хода: между ходами человек влил соседнюю работу.
|
|
82
|
+
- `UNKNOWN` прочитан как «конфликтов нет» — хостинг ещё считал.
|
|
83
|
+
- Читалась заявка текущей ветки, а брошенные остались отставшими: спрашиваются все свои открытые.
|
|
84
|
+
- Главная влита в рабочем дереве и не отправлена: снаружи это «не сделано ничего».
|
|
85
|
+
- Отставание лечили разбором конфликта: спора в файлах не было вовсе, хватило бы вливания.
|
|
86
|
+
- Спор разрешили выбором стороны целиком: род файла решает приём, и стороны у него разные.
|
|
87
|
+
- Круг догоняния начат по окрику человека, а не сам: к этой минуте отстали уже все заявки.
|
|
@@ -10,6 +10,10 @@ description: Паттерн правила git-workflow. Брать, когда
|
|
|
10
10
|
Паттерн правила `git-workflow`. Что при этом должно быть верно — закон
|
|
11
11
|
`docs/constitution/delivery.md`.
|
|
12
12
|
|
|
13
|
+
**Вызовы здесь даны клиентом GitHub.** Дерево на другом хостинге читает их как форму, а команду
|
|
14
|
+
берёт у своего клиента: имена полей и подкоманд у клиентов разные, а спрашиваемое — одно.
|
|
15
|
+
Соответствие называет редакция правила поставки, разложенная в этом дереве.
|
|
16
|
+
|
|
13
17
|
## Когда брать
|
|
14
18
|
|
|
15
19
|
- PR отмечен конфликтующим, и его надо вернуть к сливаемому состоянию.
|
|
@@ -115,13 +119,32 @@ PR описывал дерево на день, когда его написал
|
|
|
115
119
|
/opt/homebrew/bin/gh api -X PATCH repos/<владелец>/<репозиторий>/pulls/<номер> -f body="$(cat тело.md)"
|
|
116
120
|
```
|
|
117
121
|
|
|
122
|
+
## Зелёный прогон стареет вместе с главной веткой
|
|
123
|
+
|
|
124
|
+
Прогон говорит про то основание, на котором шёл. Пока он идёт, а заявка ждёт разбора, главная
|
|
125
|
+
ветка живёт своей жизнью, и локальная ссылка об этом молчит: она описывает день, когда её
|
|
126
|
+
подтянули. Перед тем как назвать заявку готовой к слиянию, отставание спрашивается у хранилища:
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
/opt/homebrew/bin/gh api repos/<владелец>/<репозиторий>/compare/<главная>...<ветка> --jq .behind_by
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Ноль — заявка готова. Больше нуля — главная вливается, набор проверок пересматривается по тому,
|
|
133
|
+
что ветка везёт теперь, и прогон идёт заново: зелёные задания прошлого прогона после вливания не
|
|
134
|
+
значат ничего.
|
|
135
|
+
|
|
118
136
|
## Частые промахи
|
|
119
137
|
|
|
120
138
|
- «Сохранить обе стороны» применено ко всем файлам одинаково: в спеке это верно, в коде и в
|
|
121
139
|
списке работ — нет.
|
|
140
|
+
- Команда мержа взята без переменных подписи: коммит слияния подписан человеком, и отбивает его
|
|
141
|
+
набор пуша — на том шаге, где все проверки уже зелёные. Чинится это переписыванием ветки, а не
|
|
142
|
+
правкой одного коммита: за слиянием обычно уже лежат разрешения конфликтов.
|
|
122
143
|
- Сторона ветки перенесена без сверки с очередью работ: одна работа стала двумя записями.
|
|
123
144
|
- После разрешения прогнана сборка, а проверки текстов — нет: конфликта в них сборке не видно.
|
|
124
145
|
- Тело PR оставлено прежним: ревьювер читает утверждение о дереве, которого больше нет.
|
|
146
|
+
- Заявка названа готовой по зелёному прогону: задания шли от основания, которого в главной ветке
|
|
147
|
+
уже нет.
|
|
125
148
|
- Раздел, снятый главной веткой, вернулся «сохранением обеих сторон»: в спеке два экземпляра
|
|
126
149
|
одного абзаца, и снятый читается как действующий.
|
|
127
150
|
- Черри-пик взят без переменной коммиттера: автор у коммита прежний, коммиттер — человек, и отправку отбивает набор проверок.
|
|
@@ -177,6 +177,12 @@ $GH api "repos/$REPO/pulls/321" \
|
|
|
177
177
|
назначить автора ревьювером нельзя, а отказа на такой запрос не приходит. Владельцу называют
|
|
178
178
|
то, что прочитали, а не то, что заказывали.
|
|
179
179
|
|
|
180
|
+
Прочитанное расхождение имеет назначенное действие, а не остаётся наблюдением: автора у заявки
|
|
181
|
+
не сменить, правкой это не чинится. Заявка закрывается и открывается заново токеном машинной
|
|
182
|
+
записи — с переносом тела, меток, ревьювера и исполнителя. Номер при этом сменится, и строки,
|
|
183
|
+
которые ссылались на прежний, переписываются тем же ходом. Развилка, вынесенная тут владельцу,
|
|
184
|
+
означает, что порядок поставки прочитан и не исполнен: ход отсюда один.
|
|
185
|
+
|
|
180
186
|
Учётная запись, из-под которой пришлось пушить, в этот вызов не переносится: пуш и авторство
|
|
181
187
|
PR выбираются отдельно, и `GH_TOKEN` для публикации — всегда токен бота.
|
|
182
188
|
|
|
@@ -41,6 +41,24 @@ docker inspect <контейнер> --format '{{.Config.Image}}'
|
|
|
41
41
|
`integrations` из логов исчезает, хотя `API is running` остаётся на месте. После перезапуска —
|
|
42
42
|
тот же `inspect` и наличие ожидаемых строк в логе.
|
|
43
43
|
|
|
44
|
+
## Откат: тот же вызов с прежним sha
|
|
45
|
+
|
|
46
|
+
Откат — не отдельный механизм, а тот же подъём по sha, только взятому на шаг назад. Прежний sha
|
|
47
|
+
берётся у реестра, где чистка оставляет три последних, — глубже отката нет:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
docker image ls '<реестр>/<образ>' --format '{{.Tag}}\t{{.CreatedAt}}' | sort -k2 -r | head -3
|
|
51
|
+
IMAGE_TAG='<прежний sha>' docker compose -f docker-compose.prod.yml --env-file .env.prod up -d --no-build
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Откат возвращает прежний образ, но не прежнюю схему хранилища: миграция, уехавшая с новой
|
|
55
|
+
версией, остаётся применённой, и прежний образ работает с изменённой схемой. Правка схемы
|
|
56
|
+
поэтому и делится на совместимую и несовместимую — паттерн миграции.
|
|
57
|
+
|
|
58
|
+
Откаченный прод сходится с главной веткой не сразу: главная везёт правку, которой на проде уже
|
|
59
|
+
нет. Строкой очереди работ это не видно вовсе, и владельцу называется словами, вместе с sha, на
|
|
60
|
+
который откатились.
|
|
61
|
+
|
|
44
62
|
## Частые промахи
|
|
45
63
|
|
|
46
64
|
- Вывод «прод жив, значит выкатилось» — код ответа подмену образа не показывает.
|
|
@@ -11,6 +11,10 @@ description: Паттерн правила git-workflow. Брать, когда
|
|
|
11
11
|
`docs/constitution/delivery.md`. Разбор одного конфликта — паттерн `git-workflow-merge`,
|
|
12
12
|
открытие одной заявки — `git-workflow-pr`.
|
|
13
13
|
|
|
14
|
+
**Вызовы здесь даны клиентом GitHub.** Дерево на другом хостинге читает их как форму, а команду
|
|
15
|
+
берёт у своего клиента: имена полей и подкоманд у клиентов разные, а спрашиваемое — одно.
|
|
16
|
+
Соответствие называет редакция правила поставки, разложенная в этом дереве.
|
|
17
|
+
|
|
14
18
|
## Когда брать
|
|
15
19
|
|
|
16
20
|
- Из одного основания заведено больше двух веток, и все ждут слияния.
|
|
@@ -48,6 +52,13 @@ gh pr create --base <предыдущая ветка> --title '[<КЛЮЧ>-<но
|
|
|
48
52
|
Порядок вливания стоит в теле каждой заявки строкой «стоит на #<номер>, вливать после него»:
|
|
49
53
|
владелец вливает по списку, а родство веток по списку не видно.
|
|
50
54
|
|
|
55
|
+
**Конвейер дерева проверяется на то, слушает ли он заявку с таким основанием.** Событие заявки
|
|
56
|
+
хостинг шлёт с фильтром по базе, и дерево, оставившее в фильтре одну главную ветку, всей череде
|
|
57
|
+
прогонов не даёт вовсе: заявка стоит зелёно-пустой, а вернуть событие нечем — ни новый коммит,
|
|
58
|
+
ни перезакрытие заявки базы не меняют. Спрашивается это до того, как череду заводить: у
|
|
59
|
+
настройки конвейера, а не у страницы заявки, где отсутствие прогона выглядит так же, как его
|
|
60
|
+
ожидание.
|
|
61
|
+
|
|
51
62
|
```bash
|
|
52
63
|
# порядок череды целиком — снизу вверх
|
|
53
64
|
gh pr list --state open --json number,headRefName,baseRefName \
|
|
@@ -141,6 +152,25 @@ done | sort | uniq -c | sort -rn | head
|
|
|
141
152
|
конфликт там, где подряд его бы не было. Порядок называется владельцу в теле заявки — кнопки
|
|
142
153
|
нажимает он, а о родстве веток не знает.
|
|
143
154
|
|
|
155
|
+
## Волна веток проверяется пробным слиянием
|
|
156
|
+
|
|
157
|
+
Ветка, лежащая невлитой рядом с соседками, зелена сама по себе: линт, тесты, сборки и сверки
|
|
158
|
+
она проходит одна. Сталкивается она с ними тем, чего на отдельной ветке не видит ни одна
|
|
159
|
+
проверка — тот же следующий свободный номер сценария, тот же заведённый новым файл, та же
|
|
160
|
+
правленная строка отметки. После слияния первые два чинятся уже в главной и стоят отдельной
|
|
161
|
+
работы.
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
git fetch origin
|
|
165
|
+
git checkout -b probe-merge origin/main
|
|
166
|
+
for b in <ветка-1> <ветка-2> <ветка-3>; do git merge --no-edit "origin/$b" || break; done
|
|
167
|
+
npm run check:all
|
|
168
|
+
git checkout - && git branch -D probe-merge
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
Ветка пробы удаляется, а находки и порядок слияния записываются туда, где живёт замысел линии
|
|
172
|
+
работ: пробное слияние отвечает на «что столкнётся», а не заменяет собой отдачу.
|
|
173
|
+
|
|
144
174
|
## Частые промахи
|
|
145
175
|
|
|
146
176
|
- Открыты все заявки разом, потому что ветки были готовы. Готовность ветки признаком того, что
|
|
@@ -77,6 +77,10 @@ grep -rn "@<область>/<семья>/<домен>" libs/ apps/ | sed 's/:.*/
|
|
|
77
77
|
npx nx run-many -t lint --projects=<список по изменённым файлам>
|
|
78
78
|
```
|
|
79
79
|
|
|
80
|
+
Этот прогон — быстрый, по горячим следам переноса, и набором перед пушем он не бывает:
|
|
81
|
+
набор, собранный по изменённым файлам, пропускает то, до чего правка дошла связями. Перед
|
|
82
|
+
пушем идёт тот же набор, что гоняет конвейер, и теми же командами.
|
|
83
|
+
|
|
80
84
|
## Проверить
|
|
81
85
|
|
|
82
86
|
```bash
|