@rt-tools/agent-kit 0.20.0 → 0.21.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-reuse.mjs +11 -1
- package/assets/checks/check-specs.mjs +8 -23
- package/assets/checks/signals/core.json +12 -2
- package/assets/checks/signals.mjs +17 -0
- package/assets/checks/spec-proposed.mjs +122 -0
- package/assets/hooks/browser-guard-device-id.sh +3 -0
- package/assets/hooks/browser-guard-no-asking.sh +3 -0
- package/assets/hooks/browser-guard-no-listing.sh +3 -0
- package/assets/hooks/browser-guard-no-other-drivers.sh +3 -0
- package/assets/hooks/browser-guard-require-select.sh +3 -0
- package/assets/hooks/claim-guard.sh +3 -0
- package/assets/hooks/conscience-guard.sh +3 -0
- package/assets/hooks/deny-tail.sh +12 -0
- package/assets/hooks/dev-server-guard.sh +3 -0
- package/assets/hooks/docs-guard.sh +4 -11
- package/assets/hooks/exam-guard.sh +3 -0
- package/assets/hooks/git-guard-delivery.sh +4 -4
- package/assets/hooks/git-guard-main.sh +4 -9
- package/assets/hooks/git-guard-push-tests.sh +3 -0
- package/assets/hooks/grill-gate.sh +3 -0
- package/assets/hooks/guard-note.sh +45 -0
- package/assets/hooks/handoff-entry-guard.sh +3 -0
- package/assets/hooks/handoff-write.sh +58 -7
- package/assets/hooks/override-write-guard.sh +4 -4
- package/assets/hooks/postmortem-guard.sh +3 -0
- package/assets/hooks/proposal-guard.sh +3 -0
- package/assets/hooks/prose-style-guard.sh +3 -0
- package/assets/hooks/qa-dataid-guard.sh +3 -0
- package/assets/hooks/rerun-guard.sh +3 -0
- package/assets/hooks/reuse-first-guard.sh +3 -0
- package/assets/hooks/rule-article.sh +1 -1
- package/assets/hooks/rule-source-guard.sh +4 -4
- package/assets/hooks/sql-guard.sh +3 -0
- package/assets/hooks/task-context-load.sh +1 -1
- package/assets/hooks/task-flow-draft-guard.sh +3 -0
- package/assets/hooks/task-flow-guard.sh +3 -0
- package/assets/hooks/turn-entry-load.sh +27 -2
- package/assets/hooks/turn-exit-guard.sh +3 -0
- package/assets/hooks/waiting-turn-guard.sh +31 -10
- package/assets/hooks/window-fill-guard.sh +3 -0
- package/assets/hooks/work-start-guard.sh +3 -0
- package/assets/hooks/write-targets.sh +59 -10
- package/assets/laws/delivery.md +5 -0
- package/assets/laws/work-conduct.md +5 -0
- package/assets/patterns/browser-verification-stand.md +24 -9
- package/assets/patterns/git-workflow-pr.github.md +29 -22
- package/assets/patterns/spec-driven-domain.md +5 -0
- package/assets/patterns/spec-driven-rule.md +41 -1
- package/assets/patterns/task-flow-archive.md +12 -0
- package/assets/patterns/task-flow-handoff.md +14 -1
- package/assets/patterns/task-flow-resume.md +11 -0
- package/assets/patterns/task-flow-start.md +40 -40
- package/assets/patterns/testing-e2e.md +15 -0
- package/assets/patterns/testing-unit.md +13 -0
- package/assets/pitfalls/git-workflow.github.md +13 -0
- package/assets/pitfalls/spec-driven.md +20 -0
- package/assets/pitfalls/task-flow.md +78 -48
- package/assets/rules/spec-driven.md +23 -24
- package/assets/rules/turn-conduct.md +13 -15
- package/assets/rules/turn-entry.md +6 -4
- package/assets/rules/typescript-conventions.md +7 -0
- package/assets/skills/agent-kit-extend.md +11 -0
- package/assets/skills/agent-kit.md +12 -2
- package/lib/commands.d.ts.map +1 -1
- package/lib/commands.js +36 -1
- package/lib/commands.js.map +1 -1
- package/lib/observations.d.ts +13 -1
- package/lib/observations.d.ts.map +1 -1
- package/lib/observations.js +7 -1
- package/lib/observations.js.map +1 -1
- package/package.json +1 -1
- package/rt-tools-agent-kit-0.21.0.tgz +0 -0
- package/rt-tools-agent-kit-0.20.0.tgz +0 -0
|
@@ -12,6 +12,16 @@
|
|
|
12
12
|
# дерево — ветку, незакоммиченное и коммиты сверх главной. Ничего от себя он не добавляет:
|
|
13
13
|
# передача пересказывает записанное, а не заменяет его.
|
|
14
14
|
#
|
|
15
|
+
# Кладётся она разделом в ход работы задачи — туда же, где лежит состояние. Свой файл вне дерева
|
|
16
|
+
# передачу теряет при переходе на другую машину: каталог закрыт от истории, и работу, оборванную
|
|
17
|
+
# заполнением окна, оттуда не подхватить — состояние есть в ходе работы, а особенности захода,
|
|
18
|
+
# следующий шаг и то, чего делать не надо, остаются на прежней машине. Второй записи об одном и
|
|
19
|
+
# том же при этом не заводится: раздел живёт в том же файле, что и «Где стоим», и уезжает в
|
|
20
|
+
# ветку тем же коммитом. Коммитит его исполнитель — хук не знает, что ещё лежит в индексе.
|
|
21
|
+
#
|
|
22
|
+
# Файл вне дерева остаётся запасным путём: у работы без папки задачи — на отсоединённой голове,
|
|
23
|
+
# на ветке без заведённой задачи — разделу лечь некуда, а молчание здесь стоит захода целиком.
|
|
24
|
+
#
|
|
15
25
|
# Написанное руками не затирается молча: файл один, и последняя запись побеждает. Исполнитель,
|
|
16
26
|
# закрывающий заход по правилу, пишет поверх — его передача полнее, потому что он знает то,
|
|
17
27
|
# чего на диске нет.
|
|
@@ -93,8 +103,15 @@ ahead="$(git log --oneline origin/main..HEAD 2>/dev/null | head -20)"
|
|
|
93
103
|
|
|
94
104
|
target="$root/$handoff_dir/$handoff_name.md"
|
|
95
105
|
|
|
106
|
+
# Заголовок раздела в ходе работы: по нему прежняя передача и находится.
|
|
107
|
+
section='## Передача захода'
|
|
108
|
+
|
|
109
|
+
# Собирается передача во временный файл: и раздел, и запасной путь пишут одно и то же, и второй
|
|
110
|
+
# сборки для второго места не заводится.
|
|
111
|
+
scratch="$(mktemp 2>/dev/null)" || exit 0
|
|
112
|
+
|
|
96
113
|
{
|
|
97
|
-
printf '
|
|
114
|
+
printf 'Собрана хуком перед сжатием контекста (%s).\n\n' "$trigger"
|
|
98
115
|
printf '**Рабочее дерево:** %s\n' "$root"
|
|
99
116
|
if [ -n "$branch" ]; then
|
|
100
117
|
printf '**Ветка:** %s\n\n' "$branch"
|
|
@@ -103,22 +120,56 @@ target="$root/$handoff_dir/$handoff_name.md"
|
|
|
103
120
|
fi
|
|
104
121
|
|
|
105
122
|
if [ -n "$state" ]; then
|
|
106
|
-
printf '
|
|
123
|
+
printf '### Где стоим на минуту сжатия\n\n'
|
|
107
124
|
printf -- '- **Состояние:** %s\n' "$state"
|
|
108
125
|
[ -n "$stage" ] && printf -- '- **Этап:** %s\n' "$stage"
|
|
109
126
|
[ -n "$next_step" ] && printf -- '- **Следующий шаг:** %s\n' "$next_step"
|
|
110
127
|
[ -n "$pull" ] && printf -- '- **PR:** %s\n' "$pull"
|
|
111
128
|
printf '\nХод работы целиком — `%s/%s/progress.md`; замысел рядом с ним.\n\n' "$tasks_dir" "$branch"
|
|
112
129
|
elif [ -z "$branch" ]; then
|
|
113
|
-
printf '
|
|
130
|
+
printf '### Где стоим на минуту сжатия\n\nИмени у головы нет, папки задачи при ней тоже: состояние работы взять неоткуда. Читающая сторона ищет передачу по имени ветки, а не найдя его — по последней записи каталога.\n\n'
|
|
114
131
|
else
|
|
115
|
-
printf '
|
|
132
|
+
printf '### Где стоим на минуту сжатия\n\nПапки задачи у этой ветки нет: состояние работы взять неоткуда.\n\n'
|
|
116
133
|
fi
|
|
117
134
|
|
|
118
|
-
printf '
|
|
119
|
-
printf '
|
|
135
|
+
printf '### Незакоммиченное\n\n```\n%s\n```\n\n' "$uncommitted"
|
|
136
|
+
printf '### Коммиты сверх главной\n\n```\n%s\n```\n\n' "$ahead"
|
|
120
137
|
printf 'Написана хуком перед сжатием контекста. Всё, что здесь стоит, проверяется деревом:\n'
|
|
121
138
|
printf 'передача пересказывает записанное и описывает минуту, когда её собрали.\n'
|
|
122
|
-
} > "$
|
|
139
|
+
} > "$scratch" 2>/dev/null
|
|
140
|
+
|
|
141
|
+
[ -s "$scratch" ] || exit 0
|
|
142
|
+
|
|
143
|
+
# Есть ход работы — передача ложится его разделом, и прежний такой раздел заменяется целиком:
|
|
144
|
+
# дописанный вторым, он оставил бы в одном файле две правды об одной работе.
|
|
145
|
+
if [ -n "$progress" ] && [ -f "$progress" ]; then
|
|
146
|
+
kept="$scratch.kept"
|
|
147
|
+
# Сравнение идёт байтами. Под локалью с национальными настройками `awk` этой системы считает
|
|
148
|
+
# разные кириллические строки равными — «## Где стоим» совпадало с «## Передача захода», — и
|
|
149
|
+
# выемка прежнего раздела сносила состояние работы вместе с ним. Виден промах только там, где
|
|
150
|
+
# локаль объявлена: у исполнителя с `C.UTF-8` набор зелёный, на прогоне конвейера — красный.
|
|
151
|
+
LC_ALL=C awk -v mark="$section" '
|
|
152
|
+
$0 == mark { skip = 1; next }
|
|
153
|
+
skip && /^## / && $0 != mark { skip = 0 }
|
|
154
|
+
!skip { print }
|
|
155
|
+
' "$progress" > "$kept" 2>/dev/null || { rm -f "$scratch" "$kept"; exit 0; }
|
|
156
|
+
|
|
157
|
+
{
|
|
158
|
+
# Хвостовые пустые строки прежнего текста снимаются: раздел приписывается к нему через
|
|
159
|
+
# одну пустую строку, а не через сколько их там осталось.
|
|
160
|
+
awk 'BEGIN { blanks = 0 }
|
|
161
|
+
{ if ($0 ~ /^[[:space:]]*$/) { blanks++; next }
|
|
162
|
+
while (blanks > 0) { print ""; blanks-- }
|
|
163
|
+
print }' "$kept"
|
|
164
|
+
printf '\n%s\n\n' "$section"
|
|
165
|
+
cat "$scratch"
|
|
166
|
+
} > "$progress.rt-handoff" 2>/dev/null \
|
|
167
|
+
&& mv "$progress.rt-handoff" "$progress" 2>/dev/null
|
|
168
|
+
|
|
169
|
+
rm -f "$scratch" "$kept"
|
|
170
|
+
exit 0
|
|
171
|
+
fi
|
|
172
|
+
|
|
173
|
+
mv "$scratch" "$target" 2>/dev/null || rm -f "$scratch"
|
|
123
174
|
|
|
124
175
|
exit 0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
# rt-hook: PreToolUse Write|Bash|mcp__webstorm__create_new_file|mcp__webstorm__execute_terminal_command|mcp__webstorm__execute_tool
|
|
3
|
-
# Требует: hooks/deny-tail.sh
|
|
3
|
+
# Требует: hooks/deny-tail.sh, hooks/guard-note.sh
|
|
4
4
|
# Гард затирания надстройки: запись поверх — не то же самое, что правка.
|
|
5
5
|
#
|
|
6
6
|
# Надстройка дерева сливается с ресурсом пакета по разделу «## »: раздел, который дерево
|
|
@@ -20,6 +20,9 @@
|
|
|
20
20
|
# ОТКАЗ В ПОЛЬЗУ РАБОТЫ: нет `jq`, битый ввод, чужой инструмент, файла нет, файл пуст —
|
|
21
21
|
# правка РАЗРЕШАЕТСЯ. Сломанный гард не имеет права заклинить работу.
|
|
22
22
|
|
|
23
|
+
# Своё имя в наблюдениях: отбой пишет общий хвост отказа, а не сам гард.
|
|
24
|
+
RT_GUARD_NAME=override-write-guard
|
|
25
|
+
|
|
23
26
|
. "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/utf8.sh" 2>/dev/null || true
|
|
24
27
|
. "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/hook-input.sh" 2>/dev/null || true
|
|
25
28
|
|
|
@@ -35,9 +38,6 @@ rt_hooks_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
|
35
38
|
command -v rt_deny_tail >/dev/null 2>&1 || rt_deny_tail() { printf ''; }
|
|
36
39
|
|
|
37
40
|
deny() {
|
|
38
|
-
# shellcheck disable=SC1090
|
|
39
|
-
[ -f "$rt_hooks_dir/observe.sh" ] && . "$rt_hooks_dir/observe.sh" 2>/dev/null
|
|
40
|
-
command -v rt_note >/dev/null 2>&1 && rt_note guard-deny res=override-write-guard
|
|
41
41
|
|
|
42
42
|
reason="$1 $(rt_deny_tail "$2")"
|
|
43
43
|
jq -n --arg r "$reason" '{hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"deny",permissionDecisionReason:$r}}' 2>/dev/null \
|
|
@@ -21,6 +21,9 @@
|
|
|
21
21
|
# ОТКАЗ В ПОЛЬЗУ РАБОТЫ: при любой ошибке, отсутствии записи хода и повторном заходе ход
|
|
22
22
|
# РАЗРЕШАЕТСЯ (exit 0). Сломанный гард не имеет права заклинить разговор.
|
|
23
23
|
|
|
24
|
+
# Своё имя в наблюдениях: отбой пишет общий хвост отказа, а не сам гард.
|
|
25
|
+
RT_GUARD_NAME=postmortem-guard
|
|
26
|
+
|
|
24
27
|
. "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/utf8.sh" 2>/dev/null || true
|
|
25
28
|
. "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/hook-input.sh" 2>/dev/null || true
|
|
26
29
|
|
|
@@ -20,6 +20,9 @@
|
|
|
20
20
|
# ОТКАЗ В ПОЛЬЗУ РАБОТЫ: при любой ошибке, нехватке `jq`, отсутствии записи хода и повторном
|
|
21
21
|
# заходе ход РАЗРЕШАЕТСЯ (exit 0). Сломанный гард не имеет права заклинить разговор.
|
|
22
22
|
|
|
23
|
+
# Своё имя в наблюдениях: отбой пишет общий хвост отказа, а не сам гард.
|
|
24
|
+
RT_GUARD_NAME=proposal-guard
|
|
25
|
+
|
|
23
26
|
. "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/utf8.sh" 2>/dev/null || true
|
|
24
27
|
. "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/hook-input.sh" 2>/dev/null || true
|
|
25
28
|
|
|
@@ -12,6 +12,9 @@
|
|
|
12
12
|
#
|
|
13
13
|
# FAIL-OPEN: нет узла, нет проверки, чужой инструмент, не `.md` → пропуск.
|
|
14
14
|
|
|
15
|
+
# Своё имя в наблюдениях: отбой пишет общий хвост отказа, а не сам гард.
|
|
16
|
+
RT_GUARD_NAME=prose-style-guard
|
|
17
|
+
|
|
15
18
|
. "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/utf8.sh" 2>/dev/null || true
|
|
16
19
|
. "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/hook-input.sh" 2>/dev/null || true
|
|
17
20
|
|
|
@@ -25,6 +25,9 @@
|
|
|
25
25
|
#
|
|
26
26
|
# ОТКАЗ В ПОЛЬЗУ РАБОТЫ: нет разборщика, битый ввод, чужой инструмент — пропуск.
|
|
27
27
|
|
|
28
|
+
# Своё имя в наблюдениях: отбой пишет общий хвост отказа, а не сам гард.
|
|
29
|
+
RT_GUARD_NAME=qa-dataid-guard
|
|
30
|
+
|
|
28
31
|
. "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/utf8.sh" 2>/dev/null || true
|
|
29
32
|
. "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/hook-input.sh" 2>/dev/null || true
|
|
30
33
|
|
|
@@ -20,6 +20,9 @@
|
|
|
20
20
|
# похож на перезапуск — пропуск. Гард без номера судить не берётся: перезапуск последнего
|
|
21
21
|
# упавшего прогона зовут и без него, а угадывать, о каком задании речь, значит отбивать наугад.
|
|
22
22
|
|
|
23
|
+
# Своё имя в наблюдениях: отбой пишет общий хвост отказа, а не сам гард.
|
|
24
|
+
RT_GUARD_NAME=rerun-guard
|
|
25
|
+
|
|
23
26
|
. "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/utf8.sh" 2>/dev/null || true
|
|
24
27
|
. "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/hook-input.sh" 2>/dev/null || true
|
|
25
28
|
|
|
@@ -32,6 +32,9 @@
|
|
|
32
32
|
#
|
|
33
33
|
# ОТКАЗ В ПОЛЬЗУ РАБОТЫ: нет разборщика, битый ввод, чужой инструмент — пропуск.
|
|
34
34
|
|
|
35
|
+
# Своё имя в наблюдениях: отбой пишет общий хвост отказа, а не сам гард.
|
|
36
|
+
RT_GUARD_NAME=reuse-first-guard
|
|
37
|
+
|
|
35
38
|
. "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/utf8.sh" 2>/dev/null || true
|
|
36
39
|
. "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/hook-input.sh" 2>/dev/null || true
|
|
37
40
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
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, hooks/write-targets.sh
|
|
3
|
+
# Требует: hooks/profile-check.sh, hooks/deny-tail.sh, hooks/write-targets.sh, hooks/guard-note.sh
|
|
4
4
|
# Гард места правки: слой правил чинится там, где сломано, а не там, где виден.
|
|
5
5
|
#
|
|
6
6
|
# Слой правил правит тот же исполнитель, которым слой правил управляет, и разницы между
|
|
@@ -20,6 +20,9 @@
|
|
|
20
20
|
# ОТКАЗ В ПОЛЬЗУ РАБОТЫ: нет `jq`, битый ввод, чужой инструмент, файла нет, шапки в нём нет,
|
|
21
21
|
# не git-репозиторий — правка РАЗРЕШАЕТСЯ. Сломанный гард не имеет права заклинить работу.
|
|
22
22
|
|
|
23
|
+
# Своё имя в наблюдениях: отбой пишет общий хвост отказа, а не сам гард.
|
|
24
|
+
RT_GUARD_NAME=rule-source-guard
|
|
25
|
+
|
|
23
26
|
. "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/utf8.sh" 2>/dev/null || true
|
|
24
27
|
. "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/hook-input.sh" 2>/dev/null || true
|
|
25
28
|
|
|
@@ -43,9 +46,6 @@ command -v rt_needs >/dev/null 2>&1 || rt_needs() { command -v "$1" >/dev/null 2
|
|
|
43
46
|
command -v rt_deny_tail >/dev/null 2>&1 || rt_deny_tail() { printf ''; }
|
|
44
47
|
|
|
45
48
|
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
49
|
|
|
50
50
|
reason="$1 $(rt_deny_tail "$2")"
|
|
51
51
|
jq -n --arg r "$reason" '{hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"deny",permissionDecisionReason:$r}}' 2>/dev/null \
|
|
@@ -40,6 +40,9 @@
|
|
|
40
40
|
# пропускаются целиком — иначе слова «drop» и «update» в описании коммита читались бы как
|
|
41
41
|
# запрос. Доставки запроса внутри такой команды не бывает, так что цена послабления нулевая.
|
|
42
42
|
|
|
43
|
+
# Своё имя в наблюдениях: отбой пишет общий хвост отказа, а не сам гард.
|
|
44
|
+
RT_GUARD_NAME=sql-guard
|
|
45
|
+
|
|
43
46
|
. "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/utf8.sh" 2>/dev/null || true
|
|
44
47
|
. "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/hook-input.sh" 2>/dev/null || true
|
|
45
48
|
|
|
@@ -139,7 +139,7 @@ done
|
|
|
139
139
|
cat "$PROGRESS"
|
|
140
140
|
else
|
|
141
141
|
# Раздел «Где стоим» перезаписывается каждым заходом и переживает любой объём.
|
|
142
|
-
awk '/^## Где стоим/{f=1} f&&/^## /&&!/^## Где стоим/{exit} f' "$PROGRESS"
|
|
142
|
+
LC_ALL=C awk '/^## Где стоим/{f=1} f&&/^## /&&!/^## Где стоим/{exit} f' "$PROGRESS"
|
|
143
143
|
printf '\n<обрезано по объёму. Последние записи:>\n\n'
|
|
144
144
|
tail -40 "$PROGRESS"
|
|
145
145
|
printf '\n<читается целиком: %s>\n' "$PROGRESS"
|
|
@@ -23,6 +23,9 @@
|
|
|
23
23
|
# FAIL-OPEN: нет jq, не git-репозиторий, битый ввод, чужой инструмент → пропуск. Сломанный
|
|
24
24
|
# гард не должен мешать работать.
|
|
25
25
|
|
|
26
|
+
# Своё имя в наблюдениях: отбой пишет общий хвост отказа, а не сам гард.
|
|
27
|
+
RT_GUARD_NAME=task-flow-draft-guard
|
|
28
|
+
|
|
26
29
|
rt_hooks_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
27
30
|
|
|
28
31
|
# Разбор вызова — общий у обоих гардов хода работы. Файл может быть не разложен: тогда судить
|
|
@@ -22,6 +22,9 @@
|
|
|
22
22
|
# FAIL-OPEN: нет jq, не git-репозиторий, битый ввод, чужой инструмент → пропуск. Сломанный
|
|
23
23
|
# гард не должен мешать работать.
|
|
24
24
|
|
|
25
|
+
# Своё имя в наблюдениях: отбой пишет общий хвост отказа, а не сам гард.
|
|
26
|
+
RT_GUARD_NAME=task-flow-guard
|
|
27
|
+
|
|
25
28
|
rt_hooks_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
26
29
|
|
|
27
30
|
# Разбор вызова — общий у обоих гардов хода работы. Файл может быть не разложен: тогда судить
|
|
@@ -39,11 +39,36 @@ map="$rt_hooks_dir/../rt-kit/defaults/turn-map.md"
|
|
|
39
39
|
[ -f "$map" ] || map="$ROOT/.claude/rt-kit/defaults/turn-map.md"
|
|
40
40
|
|
|
41
41
|
# Передача берётся по имени текущей ветки. Чужая, поданная как своя, описывает работу, которой
|
|
42
|
-
# в этом дереве нет, — поэтому
|
|
42
|
+
# в этом дереве нет, — поэтому и раздел, и файл ищутся по ветке, а не выбираются из каталога.
|
|
43
|
+
#
|
|
44
|
+
# Мест два, и порядок между ними один. Передача ветки с папкой задачи лежит разделом её хода
|
|
45
|
+
# работы: он коммитится и едет в ветку, поэтому переживает переход на другую машину. Файл вне
|
|
46
|
+
# дерева — запасной путь: у работы без папки задачи разделу лечь некуда. Найдя раздел, файл не
|
|
47
|
+
# читают: это одна и та же передача, и вторая копия старше первой.
|
|
48
|
+
tasks_dir="${RT_TASKS_DIR:-docs/tasks}"
|
|
49
|
+
progress=''
|
|
50
|
+
[ -n "$branch" ] && progress="$ROOT/$tasks_dir/$branch/progress.md"
|
|
51
|
+
|
|
52
|
+
section=''
|
|
53
|
+
if [ -n "$progress" ] && [ -r "$progress" ]; then
|
|
54
|
+
# Сравнение байтами: под локалью с национальными настройками `awk` этой системы считает
|
|
55
|
+
# разные кириллические заголовки равными, и вход читал бы разделом передачи первый попавшийся.
|
|
56
|
+
section="$(LC_ALL=C awk '
|
|
57
|
+
$0 == "## Передача захода" { skip = 1; next }
|
|
58
|
+
skip && /^## / { skip = 0 }
|
|
59
|
+
skip { print }
|
|
60
|
+
' "$progress" 2>/dev/null)"
|
|
61
|
+
fi
|
|
62
|
+
|
|
43
63
|
handoff=''
|
|
44
64
|
[ -n "$branch" ] && handoff="$ROOT/$handoff_dir/$branch.md"
|
|
45
65
|
|
|
46
|
-
if [ -n "$
|
|
66
|
+
if [ -n "$section" ]; then
|
|
67
|
+
printf 'ПЕРЕДАЧА ПРОШЛОГО ЗАХОДА — раздел «Передача захода» в `%s/%s/progress.md`, ниже целиком.\n\n' "$tasks_dir" "$branch"
|
|
68
|
+
printf 'Она написана прошлым заходом и описывает минуту, когда её собрали. Всё, что в ней\n'
|
|
69
|
+
printf 'стоит о дереве, проверяется деревом: команда этого хода главнее записанной вчера.\n\n'
|
|
70
|
+
printf '%s\n\n' "$section"
|
|
71
|
+
elif [ -n "$handoff" ] && [ -r "$handoff" ]; then
|
|
47
72
|
printf 'ПЕРЕДАЧА ПРОШЛОГО ЗАХОДА — `%s/%s.md`, ниже целиком.\n\n' "$handoff_dir" "$branch"
|
|
48
73
|
printf 'Она написана прошлым заходом и описывает минуту, когда её собрали. Всё, что в ней\n'
|
|
49
74
|
printf 'стоит о дереве, проверяется деревом: команда этого хода главнее записанной вчера.\n\n'
|
|
@@ -34,6 +34,9 @@
|
|
|
34
34
|
# или строки состояния ход РАЗРЕШАЕТСЯ (exit 0). Сломанный страж не имеет права заклинить
|
|
35
35
|
# разговор.
|
|
36
36
|
|
|
37
|
+
# Своё имя в наблюдениях: отбой пишет общий хвост отказа, а не сам гард.
|
|
38
|
+
RT_GUARD_NAME=turn-exit-guard
|
|
39
|
+
|
|
37
40
|
. "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/utf8.sh" 2>/dev/null || true
|
|
38
41
|
. "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/hook-input.sh" 2>/dev/null || true
|
|
39
42
|
|
|
@@ -19,9 +19,10 @@
|
|
|
19
19
|
# Спросить хостинг было бы точнее, но сетевой вызов на завершении хода падает вместе со связью и
|
|
20
20
|
# отбивал бы работу вместо промаха.
|
|
21
21
|
#
|
|
22
|
-
# Что считается действием: заведение задачи, заведение ветки, перевод
|
|
23
|
-
# заведение папки задачи.
|
|
24
|
-
#
|
|
22
|
+
# Что считается действием: заведение задачи, заведение ветки, перевод задачи в колонку работы,
|
|
23
|
+
# заведение папки задачи. Перевод в колонку разбора и снятие папки сюда не входят — это шаги
|
|
24
|
+
# закрытия прежней работы, а не начало следующей. Набор открыт и пополняется правкой — полнота
|
|
25
|
+
# его открытый вопрос, а не обещание.
|
|
25
26
|
#
|
|
26
27
|
# Чего гард не судит. Ход, в котором о чужом шаге не сказано ничего, — здесь он молчит: пустой
|
|
27
28
|
# ход неотличим от хода, которому нечего было делать. Это известная его граница. Зелёный прогон
|
|
@@ -30,6 +31,9 @@
|
|
|
30
31
|
# ОТКАЗ В ПОЛЬЗУ РАБОТЫ: при любой ошибке, нехватке `jq`, отсутствии записи хода и повторном
|
|
31
32
|
# заходе ход РАЗРЕШАЕТСЯ (exit 0). Сломанный гард не имеет права заклинить разговор.
|
|
32
33
|
|
|
34
|
+
# Своё имя в наблюдениях: отбой пишет общий хвост отказа, а не сам гард.
|
|
35
|
+
RT_GUARD_NAME=waiting-turn-guard
|
|
36
|
+
|
|
33
37
|
. "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/utf8.sh" 2>/dev/null || true
|
|
34
38
|
. "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/hook-input.sh" 2>/dev/null || true
|
|
35
39
|
|
|
@@ -58,10 +62,23 @@ opened_re='gh[^|;&]*pr[[:space:]]+create|api[^|;&]*-X[[:space:]]+POST[^|;&]*/pul
|
|
|
58
62
|
read_re='run[[:space:]]+(list|view|watch)|pipelines[[:space:]]+runs'
|
|
59
63
|
red_re='completed[[:space:]]+failure|"conclusion"[[:space:]]*:[[:space:]]*"failure"|conclusion:[[:space:]]*failure|completed with .failure.|run[[:space:]]+failed'
|
|
60
64
|
|
|
61
|
-
# Первое действие по следующей задаче.
|
|
62
|
-
#
|
|
63
|
-
#
|
|
64
|
-
|
|
65
|
+
# Первое действие по следующей задаче. Перечисляется то, чем взятие БЫВАЕТ, а не то, что
|
|
66
|
+
# действием считается: список шагов закрытия работы открыт и растёт, а список действий по
|
|
67
|
+
# следующей задаче закрыт — у перечня «что бывает» дыры закрывающего шага нет.
|
|
68
|
+
#
|
|
69
|
+
# Перевода закрываемой задачи в колонку разбора и снятия её папки здесь нет намеренно: оба —
|
|
70
|
+
# обязательные шаги закрытия работы, и оба стоят в том же ходе, которым открыт PR. Пока признак
|
|
71
|
+
# перечислял их наравне со взятием, он совпадал всегда, и гард отпускал ход, ради которого
|
|
72
|
+
# заведён, — работа так вставала трижды за один заход.
|
|
73
|
+
#
|
|
74
|
+
# Правка кода в ветке того же PR действием по следующей задаче не считается — она чинит
|
|
75
|
+
# прежнюю, а не двигает работу дальше.
|
|
76
|
+
taken_re='task:new|checkout[[:space:]]+-b|task:move[^|;&]*in-progress|(cp|mkdir)[^|;&]*/tasks/'
|
|
77
|
+
|
|
78
|
+
# Заведение папки задачи идёт и не оболочкой: инструмент правки файла пишет её первым файлом, и
|
|
79
|
+
# командной строки за этим нет вовсе. Поэтому рядом с командами судится путь правки — у него
|
|
80
|
+
# свой образец, потому что имени команды в нём нет.
|
|
81
|
+
taken_path_re='/tasks/'
|
|
65
82
|
|
|
66
83
|
# Чем ход показывает, что отданную работу он довёл до конца, а не бросил черновиком. Снятие
|
|
67
84
|
# черновика — очевидный случай; чтение прогона — тот, где снимать ещё нечего, но исполнитель
|
|
@@ -81,8 +98,8 @@ vow_re='дальше беру|дальше возьму|дальше иду|сл
|
|
|
81
98
|
#
|
|
82
99
|
# Хвост в 400 строк: запись хода растёт всю сессию, а судится только последний ход.
|
|
83
100
|
verdict="$(tail -n 400 "$transcript" 2>/dev/null | jq -s -r \
|
|
84
|
-
--arg opened "$opened_re" --arg
|
|
85
|
-
--arg ready "$ready_re" --arg vow "$vow_re" '
|
|
101
|
+
--arg opened "$opened_re" --arg taken "$taken_re" --arg taken_path "$taken_path_re" \
|
|
102
|
+
--arg read "$read_re" --arg red "$red_re" --arg ready "$ready_re" --arg vow "$vow_re" '
|
|
86
103
|
def is_input:
|
|
87
104
|
.type == "user"
|
|
88
105
|
and (((.message.content // []) | if type == "array"
|
|
@@ -93,6 +110,10 @@ verdict="$(tail -n 400 "$transcript" 2>/dev/null | jq -s -r \
|
|
|
93
110
|
| (if $i == null then [] else .[$i:] end) as $turn
|
|
94
111
|
| [$turn[] | select(.type == "assistant") | (.message.content // [])[] | select(.type == "tool_use")] as $uses
|
|
95
112
|
| ($uses | map((.input.command // "")) | join("\n")) as $ran
|
|
113
|
+
# Путь правки — второй способ завести папку задачи. Судятся только инструменты письма: у
|
|
114
|
+
# чтения путь тот же, а работой оно не является.
|
|
115
|
+
| ($uses | map(select(.name == "Write" or .name == "Edit" or .name == "MultiEdit")
|
|
116
|
+
| (.input.file_path // "")) | join("\n")) as $wrote
|
|
96
117
|
# Ответ инструмента берётся текстом, а не в записи JSON: у той таб и кавычка уезжают
|
|
97
118
|
# экранированными, и образец по ним не попадает вовсе. Приходит он и строкой, и списком
|
|
98
119
|
# блоков — обе формы сводятся к одному тексту.
|
|
@@ -103,7 +124,7 @@ verdict="$(tail -n 400 "$transcript" 2>/dev/null | jq -s -r \
|
|
|
103
124
|
else tostring end] | join("\n")) as $out
|
|
104
125
|
| ($ran | test($opened; "i")) as $opened_pr
|
|
105
126
|
| (($ran | test($read; "i")) and ($out | test($red; "i"))) as $red_run
|
|
106
|
-
| ($ran | test($
|
|
127
|
+
| (($ran | test($taken; "i")) or ($wrote | test($taken_path; "i"))) as $went_on
|
|
107
128
|
| ($ran | test($ready; "i")) as $checked
|
|
108
129
|
| ([$turn[] | select(.type == "assistant") | (.message.content // [])[]
|
|
109
130
|
| select(.type == "text") | .text] | join("\n")) as $said
|
|
@@ -29,6 +29,9 @@
|
|
|
29
29
|
# ОТКАЗ В ПОЛЬЗУ РАБОТЫ: нет размера окна, нет записи захода, нет разборщика, битый разбор —
|
|
30
30
|
# работа РАЗРЕШАЕТСЯ (exit 0). Сломанный гард не имеет права заклинить работу.
|
|
31
31
|
|
|
32
|
+
# Своё имя в наблюдениях: отбой пишет общий хвост отказа, а не сам гард.
|
|
33
|
+
RT_GUARD_NAME=window-fill-guard
|
|
34
|
+
|
|
32
35
|
. "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/utf8.sh" 2>/dev/null || true
|
|
33
36
|
. "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/hook-input.sh" 2>/dev/null || true
|
|
34
37
|
|
|
@@ -28,6 +28,9 @@
|
|
|
28
28
|
# ОТКАЗ В ПОЛЬЗУ РАБОТЫ: нет `jq`, нет записи хода, нет признака дерева, повторный заход, любая
|
|
29
29
|
# своя ошибка — ход РАЗРЕШАЕТСЯ (exit 0). Сломанный гард не имеет права заклинить работу.
|
|
30
30
|
|
|
31
|
+
# Своё имя в наблюдениях: отбой пишет общий хвост отказа, а не сам гард.
|
|
32
|
+
RT_GUARD_NAME=work-start-guard
|
|
33
|
+
|
|
31
34
|
. "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/utf8.sh" 2>/dev/null || true
|
|
32
35
|
. "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/hook-input.sh" 2>/dev/null || true
|
|
33
36
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
# Цели записи, названные командой оболочки прямо: перенаправление, `tee`, правка на месте,
|
|
3
|
-
# копирование
|
|
3
|
+
# копирование поверх, а у интерпретатора — пути из его тела. Печатает по одной в строке.
|
|
4
4
|
#
|
|
5
5
|
# Строки `# rt-hook:` здесь нет намеренно: это помощник, а не хук. Он ничего не решает сам и
|
|
6
6
|
# зовётся оттуда, где судят правку файла, — гардом места правки и гардом экзамена. Общий он
|
|
@@ -10,15 +10,64 @@
|
|
|
10
10
|
# Общий признак записи здесь не берётся намеренно — он широк, в нём и имя интерпретатора, и
|
|
11
11
|
# запуск разложенной проверки читался бы как правка её самой. Снятие копии сюда тоже не входит:
|
|
12
12
|
# снятый файл раскладка кладёт заново, и так чинят копию, которую переписал форматтер.
|
|
13
|
+
#
|
|
14
|
+
# Интерпретатор с кодом в доводе или в теле — особый случай, и без него у гардов была дыра
|
|
15
|
+
# ровно в ту сторону, которой правят чаще всего. Путь записи стоит там внутри кода — в вызове
|
|
16
|
+
# записи файла, — и ни одним из образцов выше не ловится: команда снаружи выглядит запуском
|
|
17
|
+
# интерпретатора, а не записью. Разложенную копию так правили молча, и ловила это уже раскладка
|
|
18
|
+
# — на сборке, когда правка написана целиком и переносить её приходится руками.
|
|
19
|
+
#
|
|
20
|
+
# Разбирать чужой язык нечем, и здесь не разбирают: из тела берётся всё путеподобное, а решает
|
|
21
|
+
# по нему зовущий — гард места правки отбивает лишь то, что несёт шапку раскладки. Цена названа
|
|
22
|
+
# прямо: чтение файла интерпретатором судится наравне с записью в него. Читают в оболочке
|
|
23
|
+
# другим — `cat`, `grep`, `sed -n`, — а интерпретатор, которому назвали путь разложенной копии,
|
|
24
|
+
# почти всегда её и правит.
|
|
13
25
|
|
|
14
26
|
rt_write_targets() {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
s
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
27
|
+
rt_wt_text="$(cat)"
|
|
28
|
+
|
|
29
|
+
{
|
|
30
|
+
printf '%s' "$rt_wt_text" \
|
|
31
|
+
| tr "\"'\`" ' ' \
|
|
32
|
+
| sed -E 's/>>?/\n>/g' \
|
|
33
|
+
| sed -nE '
|
|
34
|
+
s/^>[[:space:]]*([^[:space:]|&;]+).*/\1/p
|
|
35
|
+
s/(^|.*[[:space:]])tee[[:space:]]+(-a[[:space:]]+)?([^[:space:]|&;]+).*/\3/p
|
|
36
|
+
s/(^|.*[[:space:]])sed[[:space:]]+-i[[:space:]]+([^[:space:]]+[[:space:]]+)*([^[:space:]|&;]+)$/\3/p
|
|
37
|
+
s/(^|.*[[:space:]])(cp|mv|install)[[:space:]]+([^[:space:]]+[[:space:]]+)+([^[:space:]|&;]+).*/\4/p
|
|
38
|
+
'
|
|
39
|
+
|
|
40
|
+
# Тело интерпретатора: код приходит доводом `-c`, доводом `-e` либо телом heredoc, и
|
|
41
|
+
# путь записи стоит внутри него. Берётся именно тело, а не вся команда: составная
|
|
42
|
+
# строка, в которой heredoc пишет один файл, а рядом запускается проверка, отдала бы
|
|
43
|
+
# путь этой проверки как цель записи — и запуск разложенной проверки снова читался бы
|
|
44
|
+
# правкой её самой.
|
|
45
|
+
printf '%s\n' "$rt_wt_text" | awk '
|
|
46
|
+
function emit(s, n, i, parts) {
|
|
47
|
+
n = split(s, parts, /[^A-Za-z0-9_.@\/-]+/)
|
|
48
|
+
for (i = 1; i <= n; i++) {
|
|
49
|
+
if (parts[i] ~ /\// && parts[i] ~ /\.[A-Za-z0-9]+$/) { print parts[i] }
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
function trim(s) { sub(/^[ \t]+/, "", s); sub(/[ \t]+$/, "", s); return s }
|
|
53
|
+
tag != "" {
|
|
54
|
+
if (trim($0) == tag) { tag = ""; next }
|
|
55
|
+
if (keep) { emit($0) }
|
|
56
|
+
next
|
|
57
|
+
}
|
|
58
|
+
{
|
|
59
|
+
# Код доводом: всё, что стоит за `-c` или `-e` у интерпретатора.
|
|
60
|
+
if ($0 ~ /(^|[|;&(]|[ \t])(python3?|node|ruby|perl|php|deno|bun)([ \t]|$)/ \
|
|
61
|
+
&& match($0, /[ \t]-[ce][ \t]/)) {
|
|
62
|
+
emit(substr($0, RSTART + RLENGTH))
|
|
63
|
+
}
|
|
64
|
+
# Тело heredoc: путь записи интерпретатора стоит именно там.
|
|
65
|
+
if (match($0, /<<-?[ \t]*[\047\"]?[A-Za-z_][A-Za-z0-9_]*/)) {
|
|
66
|
+
t = substr($0, RSTART, RLENGTH)
|
|
67
|
+
sub(/^<<-?[ \t]*[\047\"]?/, "", t)
|
|
68
|
+
tag = t
|
|
69
|
+
keep = ($0 ~ /(^|[|;&(]|[ \t])(python3?|node|ruby|perl|php|deno|bun)([ \t]|$)/)
|
|
70
|
+
}
|
|
71
|
+
}'
|
|
72
|
+
} | sort -u
|
|
24
73
|
}
|
package/assets/laws/delivery.md
CHANGED
|
@@ -63,6 +63,11 @@
|
|
|
63
63
|
ни умолчания: работа с очередью отказывает и говорит, где он задаётся. Пустой ключ хуже
|
|
64
64
|
отсутствующей проверки — от имени остаётся огрызок, которому ничто не отвечает, и правильно
|
|
65
65
|
названной не выглядит ни одна задача.
|
|
66
|
+
- **Заведённое в очереди работ видно той стороне, для которой заводилось.** Учётная запись, чьи
|
|
67
|
+
объекты хостинг показывает не всем, оставляет за собой работу, которой для остальных не
|
|
68
|
+
существует, — и снаружи это неотличимо от сделанного: задача заведена, PR открыт, а владелец
|
|
69
|
+
видит пустую очередь. Видимость проверяется чужими глазами — чтением очереди тем, кому она
|
|
70
|
+
адресована, — а не успешным ответом команды заведения.
|
|
66
71
|
- **У задачи есть исполнитель с момента её заведения.** Задача без исполнителя выглядит ничьей: по
|
|
67
72
|
очереди работ не видно, кто её взял, и заведённая по ходу правка теряется среди чужих.
|
|
68
73
|
- **Состояние задачи в очереди работ отвечает тому, что с ней происходит.** Взятая в работу видна
|
|
@@ -32,6 +32,11 @@
|
|
|
32
32
|
«здесь нет», «этого не заводили», «такого файла не бывает» — произносится только как вывод
|
|
33
33
|
команды. Не проверенное отрицание опаснее вопроса: вопрос владелец поправит, а факт от
|
|
34
34
|
исполнителя примет на веру, потому что тот в дерево смотрит.
|
|
35
|
+
- **Пустой вывод утверждением не бывает.** Он значит либо «ноль строк», либо «спрошено не то», и
|
|
36
|
+
различает эти два только повторный вызов — без конвейера и без глушения потока ошибок: в
|
|
37
|
+
конвейере код возврата принадлежит последнему звену, а заглушённый поток уносит текст промаха.
|
|
38
|
+
Пока такой вызов не сделан, о дереве не сказано ничего, а «ничего нет» — самая дорогая из
|
|
39
|
+
возможных ошибок: она отменяет работу, которую владелец велел сделать.
|
|
35
40
|
- **Отрицание, полученное одним источником, отрицанием не является.** Ответ «не найдено», отказ в
|
|
36
41
|
доступе и пустой список говорят о правах спрашивающего, а не о предмете: тот же вопрос, заданный
|
|
37
42
|
оттуда, где предмет виден, отвечает обратным. Сказать «этого нет» можно только после второго
|
|
@@ -54,15 +54,26 @@ npx nx build admin --base-href=/
|
|
|
54
54
|
Angular DevTools, нужна ещё и dev-конфигурация (`--configuration=development`): прод-сборка не
|
|
55
55
|
публикует `window.ng`. Выводы о размере бандла и минификации на такой сборке делать нельзя.
|
|
56
56
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
57
|
+
Собранное отдаётся прокси дерева с его настоящим конфигом, а не статическим сервером. Причин
|
|
58
|
+
две, и обе всплывают в первую минуту: приложение ходит за процедурами на свой же origin, а по
|
|
59
|
+
глубокой ссылке статический сервер отвечает `404` — маршрутов одностраничного приложения он не
|
|
60
|
+
знает и на его точку входа не откатывается. Гард дев-серверов такой сервер отбивает наравне с
|
|
61
|
+
`nx serve`, и это указание на верный ход, а не помеха: готовый вызов — раздел «Стенд под
|
|
62
|
+
настоящим nginx» ниже, он и отдаёт собранное, и проксирует процедуры.
|
|
63
|
+
|
|
64
|
+
Вход делается формой, как у человека. Учётная запись местной базы разработки — не секрет
|
|
65
|
+
владельца: её кладёт туда засев того же дерева, а пароль лежит в файле окружения рядом.
|
|
66
|
+
Разрешения на неё не спрашивают — спрашивают про боевую запись, а её на местном стенде не
|
|
67
|
+
бывает вовсе. Там, где приёмника рядом нет, ответ входа подменяет сам стенд: путь у приложения
|
|
68
|
+
остаётся тот же, что у человека.
|
|
69
|
+
|
|
70
|
+
Токен руками не подписывается и в хранилище браузера руками не кладётся: положенный так, он
|
|
71
|
+
проверяет экран, миновав живой путь входа, — то есть отвечает не на тот вопрос. Просьба к
|
|
72
|
+
человеку набрать пароль, открыть вкладку или нажать кнопку означает неверно выбранный путь, а
|
|
73
|
+
не нехватку прав у исполнителя.
|
|
74
|
+
|
|
75
|
+
Стенд владельца запасным путём не бывает: он собирает главную ветку и о правке в рабочем дереве
|
|
76
|
+
не говорит ничего.
|
|
66
77
|
|
|
67
78
|
## Стенд API
|
|
68
79
|
|
|
@@ -163,6 +174,10 @@ done
|
|
|
163
174
|
это по её длительности.
|
|
164
175
|
- Свой дев-сервер не поднимать: сайт на {{sitePort}}, админка на {{adminPort}}, API на {{apiPort}} уже подняты
|
|
165
176
|
владельцем, и второй экземпляр отбивается гардом.
|
|
177
|
+
- **Отбитый статический сервер читается как запрет проверки, а он указание на верный ход.**
|
|
178
|
+
Отдавать собранное им нельзя не из осторожности: приложение ходит на свой origin, а глубокая
|
|
179
|
+
ссылка отвечает `404`. Отсюда идут не к обходу гарда, а к прокси дерева с его настоящим
|
|
180
|
+
конфигом.
|
|
166
181
|
- **Дев-сервер владельца — место, где смотрят, а не место, где проверяют.** Вывод о том, что
|
|
167
182
|
экран верен, делается на прод-сборке за настоящим прокси: разметка от сервера, локали, кэш,
|
|
168
183
|
перенаправления и заголовки на дев-сервере либо другие, либо отсутствуют вовсе. Взгляд на
|