oh-my-customcode 1.1.59 → 1.1.60
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/dist/cli/index.js +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/templates/.claude/hooks/scripts/stuck-detector.sh +170 -9
- package/templates/.claude/rules/MUST-agent-identification.md +3 -0
- package/templates/.claude/rules/MUST-orchestrator-coordination.md +16 -0
- package/templates/.claude/skills/fsd/SKILL.md +8 -0
- package/templates/.claude/skills/pipeline/workflows/auto-dev.yaml +11 -0
- package/templates/manifest.json +1 -1
- package/templates/workflows/auto-dev.yaml +11 -0
package/dist/cli/index.js
CHANGED
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -38,6 +38,18 @@ is_readonly_single_command() {
|
|
|
38
38
|
return 0
|
|
39
39
|
fi
|
|
40
40
|
|
|
41
|
+
# A NEWLINE is a statement separator, so this function — which classifies a
|
|
42
|
+
# SINGLE statement — must never see one: "read -ra" below stops at the first
|
|
43
|
+
# newline, so "git status\nrm -rf build" used to be judged on "git status"
|
|
44
|
+
# alone and came back read-only (#1647 M-5). Callers split on newlines
|
|
45
|
+
# (Step 6); anything still multi-line here is ambiguous => write.
|
|
46
|
+
case "$cmd" in
|
|
47
|
+
*$'\n'*)
|
|
48
|
+
echo "false"
|
|
49
|
+
return 0
|
|
50
|
+
;;
|
|
51
|
+
esac
|
|
52
|
+
|
|
41
53
|
local parts=()
|
|
42
54
|
read -ra parts <<< "$cmd"
|
|
43
55
|
local w1="${parts[0]:-}"
|
|
@@ -48,8 +60,28 @@ is_readonly_single_command() {
|
|
|
48
60
|
# A bare assignment has no side effect outside the shell; when followed by a
|
|
49
61
|
# command, classify that command instead. Command substitutions inside the
|
|
50
62
|
# RHS were already validated and replaced by is_readonly_bash_command (#1641).
|
|
63
|
+
#
|
|
64
|
+
# EXCEPTION (#1647 L-1): a few variables change WHICH program runs, or how it
|
|
65
|
+
# is loaded, so the trailing command's NAME no longer describes what the
|
|
66
|
+
# command does — "LD_PRELOAD=./evil.so ls" is not "ls", and "PATH=/tmp ls"
|
|
67
|
+
# runs an arbitrary /tmp/ls. Assignments to those names are writes whether or
|
|
68
|
+
# not a command follows them; an ordinary "FOO=1 ls" still defers to "ls".
|
|
69
|
+
#
|
|
70
|
+
# The git entry is the GLOB "GIT_*", not the three names GIT_DIR /
|
|
71
|
+
# GIT_WORK_TREE / GIT_INDEX_FILE it replaced: git has many more variables
|
|
72
|
+
# that run an arbitrary program (GIT_EXTERNAL_DIFF, GIT_PAGER, GIT_EDITOR,
|
|
73
|
+
# GIT_SSH, GIT_*_COMMAND, ...) or relocate the repository, and enumerating
|
|
74
|
+
# them one at a time keeps losing that race. GLOBIGNORE (changes which files
|
|
75
|
+
# a glob expands to) and BASH_XTRACEFD (sends trace output to an arbitrary
|
|
76
|
+
# descriptor, i.e. a file) are writes for the same reason.
|
|
51
77
|
case "$w1" in
|
|
52
78
|
[A-Za-z_]*=*)
|
|
79
|
+
case "${w1%%=*}" in
|
|
80
|
+
PATH|LD_*|DYLD_*|BASH_ENV|ENV|IFS|PROMPT_COMMAND|SHELLOPTS|BASHOPTS|GLOBIGNORE|BASH_XTRACEFD|GIT_*)
|
|
81
|
+
echo "false"
|
|
82
|
+
return 0
|
|
83
|
+
;;
|
|
84
|
+
esac
|
|
53
85
|
if [ "${#parts[@]}" -le 1 ]; then
|
|
54
86
|
echo "true"
|
|
55
87
|
return 0
|
|
@@ -62,13 +94,40 @@ is_readonly_single_command() {
|
|
|
62
94
|
esac
|
|
63
95
|
|
|
64
96
|
case "$w1" in
|
|
65
|
-
ls|cat|head|tail|grep|rg|wc|jq|md5|md5sum|type|
|
|
97
|
+
ls|cat|head|tail|grep|rg|wc|jq|md5|md5sum|type|which|echo|printf|pwd|date)
|
|
66
98
|
echo "true"
|
|
67
99
|
return 0
|
|
68
100
|
;;
|
|
101
|
+
command)
|
|
102
|
+
# "command" used to sit in the whitelist above, which made
|
|
103
|
+
# "command rm -rf x" read-only (#1647 L-5) — it is a PREFIX around an
|
|
104
|
+
# arbitrary program, not a program. Only the lookup forms ("command -v",
|
|
105
|
+
# "command -V") are read-only. "builtin" and "exec" are deliberately
|
|
106
|
+
# absent from the whitelist for the same reason: they fall through to the
|
|
107
|
+
# "*)" write default below.
|
|
108
|
+
case "$w2" in
|
|
109
|
+
-v|-V)
|
|
110
|
+
echo "true"
|
|
111
|
+
;;
|
|
112
|
+
*)
|
|
113
|
+
echo "false"
|
|
114
|
+
;;
|
|
115
|
+
esac
|
|
116
|
+
return 0
|
|
117
|
+
;;
|
|
69
118
|
find)
|
|
119
|
+
# Writing actions (#1647 L-5): -delete/-exec/-execdir remove or run
|
|
120
|
+
# things, -ok/-okdir are the interactive forms of -exec, and
|
|
121
|
+
# -fprint/-fprint0/-fprintf/-fls all CREATE a file. Each pattern is
|
|
122
|
+
# wrapped in "*" on BOTH sides, so it matches the fragment ANYWHERE in
|
|
123
|
+
# the command, not just as an option prefix: that is how the "dir"/"0"/
|
|
124
|
+
# "f" variants ("-execdir", "-fprint0", "-okdir") are covered by a single
|
|
125
|
+
# entry, and it also means an operand that merely CONTAINS the fragment
|
|
126
|
+
# (e.g. "find . -name '*-exec*'") is classified as a write. That
|
|
127
|
+
# over-match is the safe direction — a write verdict only ever tightens
|
|
128
|
+
# blocking.
|
|
70
129
|
case "$cmd" in
|
|
71
|
-
*-delete*|*-exec*|*-
|
|
130
|
+
*-delete*|*-exec*|*-fprint*|*-fls*|*-ok*)
|
|
72
131
|
echo "false"
|
|
73
132
|
;;
|
|
74
133
|
*)
|
|
@@ -133,6 +192,61 @@ is_readonly_single_command() {
|
|
|
133
192
|
esac
|
|
134
193
|
}
|
|
135
194
|
|
|
195
|
+
# Remove heredoc BODIES ("<<DELIM", "<<-'DELIM'") from a command, keeping the
|
|
196
|
+
# header line. A heredoc body is DATA, not statements: without this, the
|
|
197
|
+
# newline splitting in Step 6 (#1647 M-5) would classify every data line as a
|
|
198
|
+
# command and turn "cat <<'EOF' ... EOF" — read-only before #1647 — into a
|
|
199
|
+
# write. Herestrings ("<<<") are single-line and are NOT heredocs. An
|
|
200
|
+
# UNTERMINATED heredoc is unparseable, so a sentinel word is emitted instead
|
|
201
|
+
# and the caller classifies the command as a write. The same sentinel route is
|
|
202
|
+
# taken when the "<<" turns out to sit INSIDE a quoted string (see below).
|
|
203
|
+
_strip_heredoc_bodies() {
|
|
204
|
+
local text="$1"
|
|
205
|
+
local out="" line tok delim="" in_body=0
|
|
206
|
+
local before_op dq sq
|
|
207
|
+
while IFS= read -r line; do
|
|
208
|
+
if [ "$in_body" -eq 1 ]; then
|
|
209
|
+
# "<<-" allows leading tabs before the terminator; accept leading
|
|
210
|
+
# whitespace for either form.
|
|
211
|
+
tok="$(printf '%s' "$line" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')"
|
|
212
|
+
if [ "$tok" = "$delim" ]; then
|
|
213
|
+
in_body=0
|
|
214
|
+
fi
|
|
215
|
+
continue
|
|
216
|
+
fi
|
|
217
|
+
out="${out}${line}"$'\n'
|
|
218
|
+
# A DELIMITER FORGERY guard: the extraction below has no idea whether the
|
|
219
|
+
# "<<" it finds is shell syntax or literal text inside a quoted string, so
|
|
220
|
+
# `echo "a << EOF"` followed by `rm -rf x` and a line reading `EOF` used to
|
|
221
|
+
# hand the rm to the heredoc BODY — where it was dropped as data — and the
|
|
222
|
+
# command came back read-only. Quote state is not tracked here, so rather
|
|
223
|
+
# than guess, count the quotes preceding the "<<": an ODD number of " or '
|
|
224
|
+
# means the "<<" is quoted, the command is unparseable by this function,
|
|
225
|
+
# and the sentinel makes the CALLER classify the whole multi-line command
|
|
226
|
+
# as a write. "${line%<<*}" strips the shortest matching suffix, i.e. it
|
|
227
|
+
# yields the text before the LAST "<<" — the same occurrence the greedy
|
|
228
|
+
# extraction regex below settles on.
|
|
229
|
+
if [ "$line" != "${line%<<*}" ]; then
|
|
230
|
+
before_op="${line%<<*}"
|
|
231
|
+
dq="$(printf '%s' "$before_op" | tr -cd '"' | wc -c | tr -d '[:space:]')"
|
|
232
|
+
sq="$(printf '%s' "$before_op" | tr -cd "'" | wc -c | tr -d '[:space:]')"
|
|
233
|
+
if [ $((dq % 2)) -eq 1 ] || [ $((sq % 2)) -eq 1 ]; then
|
|
234
|
+
printf '%s' "${out}__QUOTED_HEREDOC_OPENER__"$'\n'
|
|
235
|
+
return 0
|
|
236
|
+
fi
|
|
237
|
+
fi
|
|
238
|
+
delim="$(printf '%s' "$line" \
|
|
239
|
+
| sed -n -E "s/^.*[^<]<<-?[[:space:]]*[\"']?([A-Za-z_][A-Za-z0-9_]*)[\"']?.*\$/\\1/p")"
|
|
240
|
+
if [ -n "$delim" ]; then
|
|
241
|
+
in_body=1
|
|
242
|
+
fi
|
|
243
|
+
done <<< "$text"
|
|
244
|
+
if [ "$in_body" -eq 1 ]; then
|
|
245
|
+
out="${out}__UNTERMINATED_HEREDOC__"$'\n'
|
|
246
|
+
fi
|
|
247
|
+
printf '%s' "$out"
|
|
248
|
+
}
|
|
249
|
+
|
|
136
250
|
# Maximum recursion depth for nested command substitution / loop bodies.
|
|
137
251
|
_RO_MAX_DEPTH=5
|
|
138
252
|
# Maximum command length that is parsed at all (see Step 0 below).
|
|
@@ -235,9 +349,14 @@ is_readonly_bash_command() {
|
|
|
235
349
|
# --- Step 3: strip harmless redirections BEFORE the ">" write check.
|
|
236
350
|
# "2>&1", ">/dev/null", "2>/dev/null", "1>/dev/null", "&>/dev/null" produce no
|
|
237
351
|
# observable side effect. Any OTHER ">" (a real file write) remains a write.
|
|
352
|
+
# ">&N" is stripped ONLY for N in {1,2} (stdout/stderr). An arbitrary
|
|
353
|
+
# descriptor writes wherever that fd was opened, so "cat x >&3" stays a write
|
|
354
|
+
# (#1647 L-2). The trailing "[^0-9]" / "$" guard stops ">&10" from being read
|
|
355
|
+
# as ">&1" plus a stray "0"; the two expressions differ only in whether the
|
|
356
|
+
# redirection is followed by another character or ends the command.
|
|
238
357
|
local stripped
|
|
239
358
|
stripped="$(printf '%s' "$cmd" \
|
|
240
|
-
| sed -E 's/[0-9]?>&[0-9]/ /g; s#[0-9]?&?>[[:space:]]*/dev/null# #g')"
|
|
359
|
+
| sed -E 's/[0-9]?>&[12]([^0-9])/ \1/g; s/[0-9]?>&[12]$/ /; s#[0-9]?&?>[[:space:]]*/dev/null# #g')"
|
|
241
360
|
case "$stripped" in
|
|
242
361
|
*'>'*)
|
|
243
362
|
echo "false"
|
|
@@ -296,10 +415,38 @@ is_readonly_bash_command() {
|
|
|
296
415
|
;;
|
|
297
416
|
esac
|
|
298
417
|
|
|
299
|
-
# --- Step 6: compound command — split on "&&", ";",
|
|
300
|
-
# require EVERY segment to be read-only (#1629
|
|
301
|
-
#
|
|
302
|
-
|
|
418
|
+
# --- Step 6: compound command — split on "&&", ";", a single "|", or a
|
|
419
|
+
# NEWLINE and require EVERY segment to be read-only (#1629, #1647 M-5).
|
|
420
|
+
# A newline separates statements exactly like ";"; before #1647 it was not a
|
|
421
|
+
# separator at all, so a multi-line command fell through to
|
|
422
|
+
# is_readonly_single_command, whose "read -ra" only ever saw the FIRST line
|
|
423
|
+
# ("git status\nrm -rf build" => read-only).
|
|
424
|
+
#
|
|
425
|
+
# Order matters: the ">" (Step 3) and "||" (Step 4) checks above still ran
|
|
426
|
+
# against the FULL text including any heredoc body, so those verdicts are
|
|
427
|
+
# unchanged. Heredoc bodies are dropped only here, right before the split.
|
|
428
|
+
# CR / CRLF are newlines too.
|
|
429
|
+
cmd="${cmd//$'\r\n'/$'\n'}"
|
|
430
|
+
cmd="${cmd//$'\r'/$'\n'}"
|
|
431
|
+
if [[ "$cmd" == *$'\n'* ]]; then
|
|
432
|
+
# A heredoc body needs a following line, so only a MULTI-LINE command can
|
|
433
|
+
# have one — checking "<<" on a single-line command would misread a literal
|
|
434
|
+
# "<<" (e.g. `echo "a << b"`) as an opener.
|
|
435
|
+
case "$cmd" in
|
|
436
|
+
*'<<'*)
|
|
437
|
+
cmd="$(_strip_heredoc_bodies "$cmd")"
|
|
438
|
+
;;
|
|
439
|
+
esac
|
|
440
|
+
# A BLANK line is not a statement, so drop blank lines before splitting;
|
|
441
|
+
# otherwise they would look like an empty — i.e. ambiguous — segment. A
|
|
442
|
+
# genuinely empty segment from a trailing ";" or "&&" still means write.
|
|
443
|
+
cmd="$(printf '%s' "$cmd" | sed -e '/^[[:space:]]*$/d')"
|
|
444
|
+
if [ -z "$cmd" ]; then
|
|
445
|
+
echo "false"
|
|
446
|
+
return 0
|
|
447
|
+
fi
|
|
448
|
+
fi
|
|
449
|
+
if [[ "$cmd" == *'&&'* || "$cmd" == *';'* || "$cmd" == *'|'* || "$cmd" == *$'\n'* ]]; then
|
|
303
450
|
local normalized="$cmd"
|
|
304
451
|
normalized="${normalized//&&/$'\n'}"
|
|
305
452
|
normalized="${normalized//;/$'\n'}"
|
|
@@ -360,11 +507,25 @@ else
|
|
|
360
507
|
file_path_full=$(printf '%s' "$input" | jq -r '.tool_input.file_path // ""')
|
|
361
508
|
# file_path is display/guard only (basename in the messages); target_key is
|
|
362
509
|
# the value matched against history, so only it carries the length tag.
|
|
363
|
-
|
|
510
|
+
# "|| true": same broken-pipe class as output_preview below (#1647 M-6) —
|
|
511
|
+
# an over-cap file_path would make head close the pipe and take the hook
|
|
512
|
+
# down through pipefail. The captured bytes are unaffected.
|
|
513
|
+
file_path=$(printf '%s' "$file_path_full" | head -c "$TARGET_KEY_CAP" || true)
|
|
364
514
|
target_key=$(truncate_key "$file_path_full")
|
|
365
515
|
fi
|
|
366
516
|
is_error=$(printf '%s' "$input" | jq -r '.tool_output.is_error // false')
|
|
367
|
-
|
|
517
|
+
# "head -c 200" closes the pipe after 200 bytes. Once tool_output.output is
|
|
518
|
+
# larger than the pipe buffer (~64 KB), jq keeps writing, dies of SIGPIPE
|
|
519
|
+
# (exit 141), and "set -o pipefail" + "set -e" aborted the ENTIRE hook before
|
|
520
|
+
# the history entry was ever appended — a 300 KB tool output silently erased
|
|
521
|
+
# the record it was supposed to create (#1647 M-6). Two independent guards:
|
|
522
|
+
# (a) slice inside jq so nothing large is piped at all, and (b) "|| true" so a
|
|
523
|
+
# broken pipe can never again take the hook down. The byte cap is still
|
|
524
|
+
# enforced by head (jq slices by codepoint, head by byte, and 200 codepoints
|
|
525
|
+
# are always >= 200 bytes, so the resulting bytes are unchanged).
|
|
526
|
+
output_preview=$(printf '%s' "$input" \
|
|
527
|
+
| jq -r '(.tool_output.output // "" | tostring)[0:200]' \
|
|
528
|
+
| head -c 200 || true)
|
|
368
529
|
raw_command=$(printf '%s' "$input" | jq -r '.tool_input.command // ""')
|
|
369
530
|
|
|
370
531
|
# Distinguish "the same file edited 3 times with DIFFERENT content" (a normal
|
|
@@ -117,6 +117,9 @@ Reference issue: #1096.
|
|
|
117
117
|
| Anti-pattern | Required |
|
|
118
118
|
|--------------|----------|
|
|
119
119
|
| 도구 결과 수신 후 응답 텍스트 없이 turn 종료 ("(no content)") | 최소 1줄 진행 상태 + R007 헤더 출력 후 종료/계속 |
|
|
120
|
+
| 백그라운드 에이전트 완료 통지·task-notification 등 **system-reminder 통지 수신 후** 도구만 호출하고 텍스트 없이 turn 종료 | 통지 처리 턴에도 헤더 + 상태 1줄 출력 후 도구 호출 |
|
|
121
|
+
|
|
122
|
+
v1.1.58 세션 트랜스크립트 실측 — 응답 22건 중 3건이 텍스트 블록 없이 tool_use만 있었고 전부 통지 수신 직후 턴이었다(#1646).
|
|
120
123
|
|
|
121
124
|
<!-- Reference issues: #1188 item #2, #1198 item #2, #1409. -->
|
|
122
125
|
|
|
@@ -338,6 +338,22 @@ Origin: #1563 찐빠 #3 — `gh issue edit --assignee`가 gh 2.86.0에 없는
|
|
|
338
338
|
|
|
339
339
|
Origin: #1584 #5 (v1.1.45 세션 — 커밋 위임서에 `develop @ 96ef8f85`로 적었으나 실측은 `f6d3f518`; #1572 머지 후 pull 미반영). R017 「메모리 TODO를 위임 전제로 쓸 때」의 **세션 내 축소판** — 스냅샷의 수명이 세션 간이 아니라 **턴 간**이라는 차이만 있다.
|
|
340
340
|
|
|
341
|
+
**사전 실측 리터럴 (Origin: #1646)**: 저장소 상태를 기재한 위임서를 작성하기 **직전에** 아래 3줄을 그대로 실행한다.
|
|
342
|
+
|
|
343
|
+
```
|
|
344
|
+
git rev-parse --abbrev-ref HEAD
|
|
345
|
+
git rev-parse --short HEAD
|
|
346
|
+
git branch --list
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
이 3줄의 **출력 원문**을 위임서 '사전 실측' 절에 그대로 붙여 넣고, 브랜치 존재·SHA를 예측형으로 적지 않는다 — 도구의 부수효과(예: `gh pr merge --delete-branch`가 체크아웃된 브랜치를 base로 전환·pull·삭제하는 부수효과)를 모른 채 이전 관측을 그대로 옮기면 상태가 어긋난다.
|
|
350
|
+
|
|
351
|
+
| Anti-pattern | Required |
|
|
352
|
+
|--------------|----------|
|
|
353
|
+
| `gh pr merge --delete-branch` 등 부수효과가 있는 명령 실행 후, 그 이전에 관측한 브랜치/SHA를 위임서에 그대로 기재 | 위임 직전 위 3줄을 재실행하고 출력 원문을 위임서에 붙여 넣음 |
|
|
354
|
+
|
|
355
|
+
Origin: #1646 (v1.1.58 세션 — 커밋 위임서 `12e74a76` vs 실제 `e101a0b4`, 정리 위임서 '로컬 브랜치 2개 존재' vs 실제 부재; 원인은 `gh pr merge --delete-branch`가 체크아웃된 브랜치를 base로 전환·pull·삭제하는 부수효과였음 — 도구 부수효과를 모른 채 상태를 예측한 사례).
|
|
356
|
+
|
|
341
357
|
#### 참인 전제 ≠ 참인 함의 — 브랜치 전환 위임 (Origin: #1595 #2)
|
|
342
358
|
|
|
343
359
|
uncommitted 변경이 있는 상태의 브랜치 전환을 위임할 때, 위임서에 **전환의 결과를 확정형으로 예측해 적지 않는다**. "대상 브랜치에 그 파일이 없다"는 **사실**에서 "전환해도 안전하다"는 **함의**는 도출되지 않는다 — 파일이 **없기 때문에** checkout이 그 파일을 삭제해야 하고, modified 상태면 거부된다.
|
|
@@ -69,6 +69,14 @@ All PR merge operations are delegated to **mgr-gitnerd** (R010). After merging,
|
|
|
69
69
|
|
|
70
70
|
Do NOT merge PRs that require user approval for architectural decisions. Surface them with a short summary and wait.
|
|
71
71
|
|
|
72
|
+
### `.claude/hooks/**` 이슈 — 무인 루프 분리 스코프
|
|
73
|
+
|
|
74
|
+
FSD는 사용자가 실시간 응답할 수 없는 무인 루프이므로, scope-selection의 R010 approval-required
|
|
75
|
+
path pre-check(`auto-dev.yaml`)가 `.claude/hooks/**` 대상 이슈를 발견하면 매 이터레이션마다
|
|
76
|
+
즉시 승인을 요청하지 않는다. 대신 그 이슈를 이번 스코프에서 **분리·이월**하고 나머지 적격 이슈로
|
|
77
|
+
계속 진행하며, `/homework` 반복 경계(게이트)에서 이월된 hooks 이슈들을 **1회 묶어** 승인 질문으로
|
|
78
|
+
제시한다. 승인을 받으면 R015 directive persistence로 세션 내 동일 카테고리에 지속 적용한다.
|
|
79
|
+
|
|
72
80
|
### Iteration Flow (per iteration)
|
|
73
81
|
|
|
74
82
|
```
|
|
@@ -164,6 +164,13 @@ steps:
|
|
|
164
164
|
If approval was already granted THIS session for the same category+target, do not
|
|
165
165
|
re-request (R015 directive persistence).
|
|
166
166
|
|
|
167
|
+
무인 실행(`/fsd` 등 사용자가 실시간 응답할 수 없는 루프)에서는 `.claude/hooks/**` 대상 이슈를
|
|
168
|
+
이번 스코프에서 **분리·이월**하고 나머지 적격 이슈로 진행하며, 반복 경계(homework 게이트)에서
|
|
169
|
+
승인 질문을 **1회** 묶어 제시한다. 승인을 받으면 R015 directive persistence로 세션 내 동일
|
|
170
|
+
카테고리에 지속 적용. 유인 모드에서는 기존대로 즉시 승인 요청. 무인 판별 신호는 "오케스트레이터가
|
|
171
|
+
`/fsd`로 진입했거나 세션 지침이 autonomous임을 명시한 경우"로 판단한다 — 결정론적 판별 신호
|
|
172
|
+
확립은 후속 과제로 남긴다.
|
|
173
|
+
|
|
167
174
|
Output approval_required_paths (list, empty if none) as pipeline state.
|
|
168
175
|
|
|
169
176
|
Assign scoped issues to milestone.
|
|
@@ -194,6 +201,10 @@ steps:
|
|
|
194
201
|
- plan step: skip release-plan skill; single-response plan instead
|
|
195
202
|
- deep-plan step: skip deep-plan skill; single-response implementation notes instead
|
|
196
203
|
- deep-verify step: skip deep-verify skill; perform self-review checklist instead
|
|
204
|
+
— 단, 변경 집합에 `.claude/rules/**`(또는 agents/skills frontmatter 등 구조 표면)가
|
|
205
|
+
포함되면 self-review로 대체하지 않고 mgr-sauron R017 검증을 **단일 목표 위임으로 필수
|
|
206
|
+
실행**한다(Tier 2 lite의 R017 조항과 동일 원칙; Origin: v1.1.58 세션 — docs-only 티어였으나
|
|
207
|
+
sauron이 R002/R010 모순 advisory 1건을 포착).
|
|
197
208
|
- Log: "[compression-mode] docs-only compression activated (scope={n}, all docs/yaml labels)"
|
|
198
209
|
- ⚠ Analysis artifacts only. Every step's state-change side effect (milestone create/assign,
|
|
199
210
|
label add/remove, issue assign/comment/close) still runs in full — see "Cross-tier —
|
package/templates/manifest.json
CHANGED
|
@@ -164,6 +164,13 @@ steps:
|
|
|
164
164
|
If approval was already granted THIS session for the same category+target, do not
|
|
165
165
|
re-request (R015 directive persistence).
|
|
166
166
|
|
|
167
|
+
무인 실행(`/fsd` 등 사용자가 실시간 응답할 수 없는 루프)에서는 `.claude/hooks/**` 대상 이슈를
|
|
168
|
+
이번 스코프에서 **분리·이월**하고 나머지 적격 이슈로 진행하며, 반복 경계(homework 게이트)에서
|
|
169
|
+
승인 질문을 **1회** 묶어 제시한다. 승인을 받으면 R015 directive persistence로 세션 내 동일
|
|
170
|
+
카테고리에 지속 적용. 유인 모드에서는 기존대로 즉시 승인 요청. 무인 판별 신호는 "오케스트레이터가
|
|
171
|
+
`/fsd`로 진입했거나 세션 지침이 autonomous임을 명시한 경우"로 판단한다 — 결정론적 판별 신호
|
|
172
|
+
확립은 후속 과제로 남긴다.
|
|
173
|
+
|
|
167
174
|
Output approval_required_paths (list, empty if none) as pipeline state.
|
|
168
175
|
|
|
169
176
|
Assign scoped issues to milestone.
|
|
@@ -194,6 +201,10 @@ steps:
|
|
|
194
201
|
- plan step: skip release-plan skill; single-response plan instead
|
|
195
202
|
- deep-plan step: skip deep-plan skill; single-response implementation notes instead
|
|
196
203
|
- deep-verify step: skip deep-verify skill; perform self-review checklist instead
|
|
204
|
+
— 단, 변경 집합에 `.claude/rules/**`(또는 agents/skills frontmatter 등 구조 표면)가
|
|
205
|
+
포함되면 self-review로 대체하지 않고 mgr-sauron R017 검증을 **단일 목표 위임으로 필수
|
|
206
|
+
실행**한다(Tier 2 lite의 R017 조항과 동일 원칙; Origin: v1.1.58 세션 — docs-only 티어였으나
|
|
207
|
+
sauron이 R002/R010 모순 advisory 1건을 포착).
|
|
197
208
|
- Log: "[compression-mode] docs-only compression activated (scope={n}, all docs/yaml labels)"
|
|
198
209
|
- ⚠ Analysis artifacts only. Every step's state-change side effect (milestone create/assign,
|
|
199
210
|
label add/remove, issue assign/comment/close) still runs in full — see "Cross-tier —
|