oh-my-customcode 1.1.44 → 1.1.46
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 +56 -13
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/templates/.claude/hooks/scripts/r007-r008-drift-advisor.sh +11 -1
- package/templates/.claude/hooks/scripts/session-env-check.sh +33 -5
- package/templates/.claude/hooks/scripts/session-reflection.sh +7 -1
- package/templates/.claude/hooks/scripts/user-prompt-preprocessor.sh +74 -15
- package/templates/.claude/rules/MAY-optimization.md +5 -1
- package/templates/.claude/rules/MUST-agent-design.md +3 -1
- package/templates/.claude/rules/MUST-agent-teams.md +2 -0
- package/templates/.claude/rules/MUST-completion-verification.md +25 -4
- package/templates/.claude/rules/MUST-continuous-improvement.md +1 -1
- package/templates/.claude/rules/MUST-enforcement-policy.md +7 -5
- package/templates/.claude/rules/MUST-orchestrator-coordination.md +25 -1
- package/templates/.claude/rules/MUST-parallel-execution.md +4 -0
- package/templates/.claude/rules/MUST-permissions.md +11 -1
- package/templates/.claude/rules/MUST-safety.md +2 -0
- package/templates/.claude/rules/MUST-sync-verification.md +10 -0
- package/templates/.claude/rules/SHOULD-memory-integration.md +5 -1
- package/templates/.claude/rules/SHOULD-verification-ladder.md +13 -1
- package/templates/.claude/skills/pipeline/workflows/auto-dev.yaml +19 -1
- package/templates/manifest.json +1 -1
- package/templates/workflows/auto-dev.yaml +19 -1
package/dist/cli/index.js
CHANGED
|
@@ -241,7 +241,7 @@ var init_package = __esm(() => {
|
|
|
241
241
|
workspaces: [
|
|
242
242
|
"packages/*"
|
|
243
243
|
],
|
|
244
|
-
version: "1.1.
|
|
244
|
+
version: "1.1.46",
|
|
245
245
|
description: "Batteries-included agent harness for Claude Code",
|
|
246
246
|
type: "module",
|
|
247
247
|
bin: {
|
|
@@ -25737,6 +25737,7 @@ var i18n = {
|
|
|
25737
25737
|
var DEFAULT_PACKAGE_NAME = "oh-my-customcode";
|
|
25738
25738
|
var DEFAULT_CACHE_TTL_MS = 24 * 60 * 60 * 1000;
|
|
25739
25739
|
var DEFAULT_CACHE_PATH = join2(homedir2(), ".oh-my-customcode", "self-update-cache.json");
|
|
25740
|
+
var MS_EPOCH_THRESHOLD = 1000000000000;
|
|
25740
25741
|
function normalizeVersion(version) {
|
|
25741
25742
|
return version.trim().replace(/^v/i, "").split("-")[0] || "";
|
|
25742
25743
|
}
|
|
@@ -25779,22 +25780,57 @@ function isNpxInvocation(argv = process.argv, env = process.env) {
|
|
|
25779
25780
|
const npmCommand = env.npm_command || "";
|
|
25780
25781
|
return argv1.includes("/_npx/") || argv1.includes("\\_npx\\") || npmExecPath.includes("npx") || npmCommand === "exec" || env.npm_lifecycle_event === "npx";
|
|
25781
25782
|
}
|
|
25782
|
-
function
|
|
25783
|
+
function isJsonRecord(value) {
|
|
25784
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
25785
|
+
}
|
|
25786
|
+
function readNonEmptyString(value) {
|
|
25787
|
+
if (typeof value !== "string") {
|
|
25788
|
+
return null;
|
|
25789
|
+
}
|
|
25790
|
+
const trimmed = value.trim();
|
|
25791
|
+
return trimmed === "" ? null : trimmed;
|
|
25792
|
+
}
|
|
25793
|
+
function epochToIsoString(value) {
|
|
25794
|
+
let numeric = Number.NaN;
|
|
25795
|
+
if (typeof value === "number") {
|
|
25796
|
+
numeric = value;
|
|
25797
|
+
} else if (typeof value === "string") {
|
|
25798
|
+
numeric = Number(value.trim());
|
|
25799
|
+
}
|
|
25800
|
+
if (!Number.isFinite(numeric) || numeric <= 0) {
|
|
25801
|
+
return null;
|
|
25802
|
+
}
|
|
25803
|
+
const milliseconds = numeric >= MS_EPOCH_THRESHOLD ? numeric : numeric * 1000;
|
|
25804
|
+
const date = new Date(milliseconds);
|
|
25805
|
+
if (Number.isNaN(date.getTime())) {
|
|
25806
|
+
return null;
|
|
25807
|
+
}
|
|
25808
|
+
return date.toISOString();
|
|
25809
|
+
}
|
|
25810
|
+
function readSelfUpdateCache(cachePath) {
|
|
25783
25811
|
if (!existsSync(cachePath)) {
|
|
25784
25812
|
return null;
|
|
25785
25813
|
}
|
|
25814
|
+
let parsed;
|
|
25786
25815
|
try {
|
|
25787
|
-
|
|
25788
|
-
if (!parsed.checkedAt || !parsed.latestVersion) {
|
|
25789
|
-
return null;
|
|
25790
|
-
}
|
|
25791
|
-
return {
|
|
25792
|
-
checkedAt: parsed.checkedAt,
|
|
25793
|
-
latestVersion: parsed.latestVersion
|
|
25794
|
-
};
|
|
25816
|
+
parsed = JSON.parse(readFileSync(cachePath, "utf-8"));
|
|
25795
25817
|
} catch {
|
|
25796
25818
|
return null;
|
|
25797
25819
|
}
|
|
25820
|
+
if (!isJsonRecord(parsed)) {
|
|
25821
|
+
return null;
|
|
25822
|
+
}
|
|
25823
|
+
const cliVersion = readNonEmptyString(parsed.latestVersion);
|
|
25824
|
+
const cliCheckedAt = readNonEmptyString(parsed.checkedAt);
|
|
25825
|
+
if (cliVersion && cliCheckedAt) {
|
|
25826
|
+
return { checkedAt: cliCheckedAt, latestVersion: cliVersion, schema: "cli" };
|
|
25827
|
+
}
|
|
25828
|
+
const hookVersion = readNonEmptyString(parsed.version);
|
|
25829
|
+
const hookCheckedAt = epochToIsoString(parsed.timestamp);
|
|
25830
|
+
if (hookVersion && hookCheckedAt) {
|
|
25831
|
+
return { checkedAt: hookCheckedAt, latestVersion: hookVersion, schema: "hook" };
|
|
25832
|
+
}
|
|
25833
|
+
return null;
|
|
25798
25834
|
}
|
|
25799
25835
|
function writeCache(cachePath, latestVersion, now) {
|
|
25800
25836
|
const dir2 = dirname(cachePath);
|
|
@@ -25803,7 +25839,10 @@ function writeCache(cachePath, latestVersion, now) {
|
|
|
25803
25839
|
}
|
|
25804
25840
|
const payload = {
|
|
25805
25841
|
checkedAt: new Date(now).toISOString(),
|
|
25806
|
-
latestVersion
|
|
25842
|
+
latestVersion,
|
|
25843
|
+
version: latestVersion,
|
|
25844
|
+
timestamp: Math.floor(now / 1000),
|
|
25845
|
+
source: "omcustom-cli"
|
|
25807
25846
|
};
|
|
25808
25847
|
writeFileSync(cachePath, `${JSON.stringify(payload, null, 2)}
|
|
25809
25848
|
`, "utf-8");
|
|
@@ -25813,7 +25852,11 @@ function isCacheFresh(cache, now, cacheTtlMs) {
|
|
|
25813
25852
|
if (Number.isNaN(checkedAt)) {
|
|
25814
25853
|
return false;
|
|
25815
25854
|
}
|
|
25816
|
-
|
|
25855
|
+
const age = now - checkedAt;
|
|
25856
|
+
if (age < -cacheTtlMs) {
|
|
25857
|
+
return false;
|
|
25858
|
+
}
|
|
25859
|
+
return age < cacheTtlMs;
|
|
25817
25860
|
}
|
|
25818
25861
|
function fetchLatestVersionFromNpm(packageName = DEFAULT_PACKAGE_NAME) {
|
|
25819
25862
|
try {
|
|
@@ -25958,7 +26001,7 @@ function checkSelfUpdate(options) {
|
|
|
25958
26001
|
}
|
|
25959
26002
|
let latestVersion = null;
|
|
25960
26003
|
let usedCache = false;
|
|
25961
|
-
const cache =
|
|
26004
|
+
const cache = readSelfUpdateCache(cachePath);
|
|
25962
26005
|
if (cache && isCacheFresh(cache, now, cacheTtlMs)) {
|
|
25963
26006
|
const cachedVersion = normalizeVersion(cache.latestVersion);
|
|
25964
26007
|
if (isVersionPlausible(currentVersion, cachedVersion)) {
|
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -74,6 +74,16 @@
|
|
|
74
74
|
# R008 explicitly exempts from the `[N]` prefix). Excluding this notation would recreate
|
|
75
75
|
# exactly the false positive this fix removes (N Agent tool_use blocks, 0 `Tool:` lines).
|
|
76
76
|
#
|
|
77
|
+
# Tool calls EXCLUDED from the denominator:
|
|
78
|
+
# * `Skill` — R008 §"Tier-3 Interaction Tool Prefix" exempts it verbatim:
|
|
79
|
+
# "Skill | NO separate R008 prefix — identified via R007 `claude → {skill-name}`
|
|
80
|
+
# integrated header instead"
|
|
81
|
+
# A compliant skill invocation therefore emits a `┌─ Agent: claude → homework` header and
|
|
82
|
+
# NO `→ Tool:` line, so counting the Skill tool_use scored `R008 접두사=1` against a turn
|
|
83
|
+
# that follows the rule exactly (verified against the R008 table, #1569). The exclusion
|
|
84
|
+
# lives in the SHARED verdict, so session-reflection.sh carries the identical select or
|
|
85
|
+
# the next copy re-contaminates.
|
|
86
|
+
#
|
|
77
87
|
# R007 detection (first line of the turn's first text block) is unchanged.
|
|
78
88
|
#
|
|
79
89
|
# ── Performance ───────────────────────────────────────────────────────────────────────
|
|
@@ -182,7 +192,7 @@ split("\n")
|
|
|
182
192
|
| ([ $lines[] | select(test("^[[:space:]]*\\[[0-9]+\\][[:space:]].*(→|->|—>)")) ] | length) as $an_spawn_item
|
|
183
193
|
| ([ $lines[] | select(test("\\[.+\\]\\[.+\\] ?(→|->|—>) ?Spawning:")) ] | length) as $an_spawn_hdr
|
|
184
194
|
| ($an_tool + (if $an_spawn_item > 0 then $an_spawn_item else $an_spawn_hdr end)) as $announce
|
|
185
|
-
| ([ $blocks[] | select(.type? == "tool_use") ] | length) as $ntools
|
|
195
|
+
| ([ $blocks[] | select((.type? == "tool_use") and ((.name? // "") != "Skill")) ] | length) as $ntools
|
|
186
196
|
| (if $ntools > $announce then $ntools - $announce else 0 end) as $r008
|
|
187
197
|
| [$tuuid, ($r007 | tostring), ($r008 | tostring)] | @tsv
|
|
188
198
|
end
|
|
@@ -82,8 +82,10 @@ if command -v git >/dev/null 2>&1 && git rev-parse --is-inside-work-tree >/dev/n
|
|
|
82
82
|
mkdir -p "$SESSION_STATE_DIR"
|
|
83
83
|
|
|
84
84
|
PROJECT_HASH=$(echo "$(pwd)" | md5 2>/dev/null || echo "$(pwd)" | md5sum 2>/dev/null | cut -c1-8)
|
|
85
|
-
# md5 on macOS outputs "MD5 (stdin) = <hash>", extract just the hash
|
|
86
|
-
|
|
85
|
+
# md5 on macOS outputs "MD5 (stdin) = <hash>", extract just the hash.
|
|
86
|
+
# Guarded for the same reason as json_string_field below: on a platform where the fallback
|
|
87
|
+
# already yields an 8-char digest this grep matches nothing and would abort the hook.
|
|
88
|
+
PROJECT_HASH=$(echo "$PROJECT_HASH" | grep -oE '[a-f0-9]{32}' | cut -c1-8 || printf '')
|
|
87
89
|
STATE_FILE="${SESSION_STATE_DIR}/${PROJECT_HASH}.last-head"
|
|
88
90
|
|
|
89
91
|
CURRENT_HEAD=$(git log -1 --format="%H" 2>/dev/null || echo "")
|
|
@@ -126,15 +128,41 @@ OMCUSTOM_UPDATE_STATUS="unknown"
|
|
|
126
128
|
INSTALLED_VERSION=""
|
|
127
129
|
CACHED_LATEST=""
|
|
128
130
|
|
|
131
|
+
# Extract a "<key>": "<value>" pair from a JSON file using grep only (no jq dependency).
|
|
132
|
+
# EVERY grep here is guarded with `|| printf ''`: this script runs under `set -euo pipefail`,
|
|
133
|
+
# where an unmatched grep exits 1 and (via pipefail) kills the ENTIRE SessionStart hook —
|
|
134
|
+
# measured as exit 1 with 0 bytes of stdout and 11 failing tests, including the script's own
|
|
135
|
+
# "should always exit with code 0" contract (#1570).
|
|
136
|
+
json_string_field() {
|
|
137
|
+
local file="$1" key="$2"
|
|
138
|
+
grep -o "\"${key}\"[[:space:]]*:[[:space:]]*\"[^\"]*\"" "$file" 2>/dev/null \
|
|
139
|
+
| head -1 \
|
|
140
|
+
| grep -o '"[^"]*"$' \
|
|
141
|
+
| tr -d '"' \
|
|
142
|
+
|| printf ''
|
|
143
|
+
}
|
|
144
|
+
|
|
129
145
|
# Read installed version from .omcustomrc.json
|
|
130
146
|
if [ -f ".omcustomrc.json" ]; then
|
|
131
|
-
INSTALLED_VERSION=$(
|
|
147
|
+
INSTALLED_VERSION=$(json_string_field ".omcustomrc.json" "version")
|
|
132
148
|
fi
|
|
133
149
|
|
|
134
|
-
# Read cached latest version (no network call)
|
|
150
|
+
# Read cached latest version (no network call).
|
|
151
|
+
#
|
|
152
|
+
# Schema note (MEASURED 2026-08-10, #1570): TWO writers produce this exact path with
|
|
153
|
+
# DIFFERENT key names, and BOTH are current — neither is a legacy schema:
|
|
154
|
+
# * .claude/hooks/scripts/omcustom-auto-update.sh → {"version","timestamp","source"}
|
|
155
|
+
# * src/core/self-update.ts writeCache() → {"checkedAt","latestVersion"}
|
|
156
|
+
# The live cache on this machine was the auto-update shape ({source,timestamp,version}), so
|
|
157
|
+
# the hard-coded "latestVersion" lookup matched nothing. Read `latestVersion` first, then fall
|
|
158
|
+
# back to `version`. The `"version"` pattern includes the opening quote, so it cannot
|
|
159
|
+
# accidentally match the tail of `"latestVersion"`.
|
|
135
160
|
CACHE_FILE="$HOME/.oh-my-customcode/self-update-cache.json"
|
|
136
161
|
if [ -f "$CACHE_FILE" ]; then
|
|
137
|
-
CACHED_LATEST=$(
|
|
162
|
+
CACHED_LATEST=$(json_string_field "$CACHE_FILE" "latestVersion")
|
|
163
|
+
if [ -z "$CACHED_LATEST" ]; then
|
|
164
|
+
CACHED_LATEST=$(json_string_field "$CACHE_FILE" "version")
|
|
165
|
+
fi
|
|
138
166
|
fi
|
|
139
167
|
|
|
140
168
|
if [ -n "$INSTALLED_VERSION" ] && [ -n "$CACHED_LATEST" ]; then
|
|
@@ -105,6 +105,12 @@ ISO8601="$(date -u +"%Y-%m-%dT%H:%M:%SZ")"
|
|
|
105
105
|
# 없음)도 포함 — 에이전트당 단위는 번호 라인이고, 번호 라인이 없을 때만(단일 spawn) 헤더를 1로 센다.
|
|
106
106
|
# 위반 샘플은 announce가 모자란 만큼 턴의 마지막 tool_use들을 보고한다(결손 개수 기준).
|
|
107
107
|
#
|
|
108
|
+
# 분모에서 제외하는 도구: `Skill` (#1569). R008 §"Tier-3 Interaction Tool Prefix" 표는 Skill을
|
|
109
|
+
# 명시 면제한다 — "Skill | NO separate R008 prefix — identified via R007 `claude → {skill-name}`
|
|
110
|
+
# integrated header instead". 즉 준수한 스킬 호출은 `┌─ Agent: claude → homework` 통합 헤더만
|
|
111
|
+
# 남기고 `→ Tool:` 라인을 쓰지 않으므로, Skill tool_use를 세면 규칙을 정확히 지킨 턴에
|
|
112
|
+
# `R008 접두사=1` 오탐이 찍힌다.
|
|
113
|
+
#
|
|
108
114
|
# 이 판정은 r007-r008-drift-advisor.sh와 공유된다 — 한쪽만 고치면 재오염된다.
|
|
109
115
|
#
|
|
110
116
|
# 성능: 줄마다 jq를 포크하던 구조를 jq 1회 포크로 교체.
|
|
@@ -137,7 +143,7 @@ split("\n")
|
|
|
137
143
|
| ([ $lines[] | select(test("^[[:space:]]*\\[[0-9]+\\][[:space:]].*(→|->|—>)")) ] | length) as $an_spawn_item
|
|
138
144
|
| ([ $lines[] | select(test("\\[.+\\]\\[.+\\] ?(→|->|—>) ?Spawning:")) ] | length) as $an_spawn_hdr
|
|
139
145
|
| ($an_tool + (if $an_spawn_item > 0 then $an_spawn_item else $an_spawn_hdr end)) as $announce
|
|
140
|
-
| ([ $blocks[] | select(.type? == "tool_use") ]) as $tus
|
|
146
|
+
| ([ $blocks[] | select((.type? == "tool_use") and ((.name? // "") != "Skill")) ]) as $tus
|
|
141
147
|
| (if ($tus | length) > $announce then ($tus | length) - $announce else 0 end) as $r008n
|
|
142
148
|
| if $r008n > 0
|
|
143
149
|
then [ $tus[(($tus | length) - $r008n):][]
|
|
@@ -1,32 +1,91 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# UserPromptSubmit hook
|
|
3
|
-
#
|
|
4
|
-
# Advisory
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# user-prompt-preprocessor.sh — UserPromptSubmit hook: advisory hints from the user's prompt
|
|
3
|
+
#
|
|
4
|
+
# Advisory-only: ALWAYS exits 0, NEVER blocks prompt submission.
|
|
5
|
+
#
|
|
6
|
+
# ── Two measured defects this file fixes (#1568) ──────────────────────────────────────
|
|
7
|
+
#
|
|
8
|
+
# 1. SELECTOR. The previous implementation read `.user_input`, but the Claude Code
|
|
9
|
+
# UserPromptSubmit payload carries the text in `prompt`. The `[ -z "$user_input" ]` guard
|
|
10
|
+
# on the next line therefore ALWAYS took the early-return branch, so the detection blocks
|
|
11
|
+
# below were unreachable — the hook was a pure pass-through.
|
|
12
|
+
# Live probe (2026-08-10):
|
|
13
|
+
# platform-shaped {"prompt":"끝"} → NO hint (defect)
|
|
14
|
+
# script-shaped {"user_input":"끝"} → "[Hook] Session-end signal detected"
|
|
15
|
+
# `.prompt` is now read first, with `.user_input` retained as a fallback so any
|
|
16
|
+
# script-shaped caller (and the pre-existing test corpus) keeps working.
|
|
17
|
+
#
|
|
18
|
+
# 2. DELIVERY CHANNEL. The hints were written to stderr only. Per the official hook spec,
|
|
19
|
+
# stderr on exit 0 is NEVER fed into the model's context for ANY event — it is visible in
|
|
20
|
+
# transcript debug mode, i.e. to a human, not to Claude. Fixing the selector alone would
|
|
21
|
+
# have left the hook functionally silent. Hints are now delivered through
|
|
22
|
+
# `hookSpecificOutput.additionalContext` (JSON on stdout, exit 0), the same contract used
|
|
23
|
+
# by the sibling UserPromptSubmit advisors r007-r008-drift-advisor.sh and
|
|
24
|
+
# fail-axis-cause-advisor.sh. The stderr line is kept purely as a human audit trail.
|
|
25
|
+
#
|
|
26
|
+
# `hookEventName` MUST echo the ACTUAL firing event — a wrong value invalidates the
|
|
27
|
+
# output — so a missing `hook_event_name` is a hard `exit 0` with no guessed default.
|
|
28
|
+
# A top-level `decision` field is NEVER emitted: `"decision": "block"` would turn this
|
|
29
|
+
# advisory into an enforcement gate, exactly what R021 (advisory-first) forbids.
|
|
30
|
+
#
|
|
31
|
+
# ── Why the stdin pass-through (`echo "$input"`) was removed ──────────────────────────
|
|
32
|
+
# It was never part of the UserPromptSubmit contract. For UserPromptSubmit, plain stdout on
|
|
33
|
+
# exit 0 is injected into the model's context, so echoing the payload back would inject the
|
|
34
|
+
# raw hook JSON as context noise. The repo's own convention agrees: of the four scripts wired
|
|
35
|
+
# to UserPromptSubmit in .claude/hooks/hooks.json, the three that were written or repaired
|
|
36
|
+
# against the measured spec — r007-r008-drift-advisor.sh, session-autofix-prompt.sh,
|
|
37
|
+
# fail-axis-cause-advisor.sh — all exit 0 WITHOUT echoing stdin. This file was the only
|
|
38
|
+
# hold-out. Pass-through echo is a Stop-hook idiom (see session-reflection.sh, which chains),
|
|
39
|
+
# not a UserPromptSubmit one.
|
|
5
40
|
|
|
41
|
+
set -euo pipefail
|
|
42
|
+
|
|
43
|
+
# ── stdin 읽기 ──
|
|
6
44
|
input=$(cat)
|
|
7
|
-
|
|
45
|
+
|
|
46
|
+
# ── jq 의존성 체크 (graceful degrade) ──
|
|
47
|
+
if ! command -v jq >/dev/null 2>&1; then
|
|
48
|
+
exit 0
|
|
49
|
+
fi
|
|
50
|
+
|
|
51
|
+
# ── 입력 필드 추출 ──
|
|
52
|
+
# `.prompt`(플랫폼 실제 필드)를 우선 읽고 `.user_input`(스크립트형 호출자)로 폴백한다.
|
|
53
|
+
# prompt 본문은 탭/개행을 포함할 수 있으므로 @tsv 로 묶지 않고 개별 추출한다.
|
|
54
|
+
user_input=$(printf '%s' "$input" | jq -r '(.prompt // .user_input // "")' 2>/dev/null) || exit 0
|
|
55
|
+
hook_event_name=$(printf '%s' "$input" | jq -r '(.hook_event_name // "")' 2>/dev/null) || exit 0
|
|
8
56
|
|
|
9
57
|
if [ -z "$user_input" ]; then
|
|
10
|
-
echo "$input"
|
|
11
58
|
exit 0
|
|
12
59
|
fi
|
|
13
60
|
|
|
14
|
-
#
|
|
61
|
+
# ── 패턴 탐지 ──
|
|
15
62
|
hints=""
|
|
16
63
|
|
|
17
64
|
# Korean session-end signals
|
|
18
|
-
if
|
|
19
|
-
hints="${hints}[Hook] Session-end signal detected — R011 memory saves will be triggered\n
|
|
65
|
+
if printf '%s' "$user_input" | grep -qiE '(끝|종료|마무리|done|wrap up|end session)'; then
|
|
66
|
+
hints="${hints}[Hook] Session-end signal detected — R011 memory saves will be triggered"$'\n'
|
|
20
67
|
fi
|
|
21
68
|
|
|
22
69
|
# Workflow invocation
|
|
23
|
-
if
|
|
24
|
-
hints="${hints}[Hook] Slash command detected\n
|
|
70
|
+
if printf '%s\n' "$user_input" | grep -qE '^/'; then
|
|
71
|
+
hints="${hints}[Hook] Slash command detected"$'\n'
|
|
25
72
|
fi
|
|
26
73
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
printf "%b" "$hints" >&2
|
|
74
|
+
if [ -z "$hints" ]; then
|
|
75
|
+
exit 0
|
|
30
76
|
fi
|
|
31
77
|
|
|
32
|
-
|
|
78
|
+
# 사람이 보는 감사 추적용 (exit 0에서는 모델에 전달되지 않음 — 위 주석 2번 참고)
|
|
79
|
+
printf '%s' "$hints" >&2
|
|
80
|
+
|
|
81
|
+
# hook_event_name 이 없으면 hookSpecificOutput.hookEventName 을 정확히 채울 수 없다.
|
|
82
|
+
# 잘못된 기본값은 출력을 무효화하므로 추측하지 않는다.
|
|
83
|
+
if [ -z "$hook_event_name" ]; then
|
|
84
|
+
exit 0
|
|
85
|
+
fi
|
|
86
|
+
|
|
87
|
+
# 실제 전달 경로: additionalContext. decision 필드는 절대 포함하지 않는다 (R021).
|
|
88
|
+
jq -cn --arg event "$hook_event_name" --arg ctx "$hints" \
|
|
89
|
+
'{hookSpecificOutput: {hookEventName: $event, additionalContext: $ctx}}'
|
|
90
|
+
|
|
91
|
+
exit 0
|
|
@@ -34,12 +34,16 @@
|
|
|
34
34
|
|
|
35
35
|
<!-- RETIRED (은퇴 릴리즈 v1.1.44, 보존 기준 v2.1.212 미만): > **v2.1.208+**: Fixed several tool-reliability bugs: env vars like `CLAUDE_CODE_MAX_OUTPUT_TOKENS` silently used only the mantissa of scientific-notation values (`1e6` became `1`); Edit now succeeds on a file modified after being read, as long as the target text still matches uniquely; Read no longer misreports empty files as "shorter than offset"; Grep no longer silently returns "No files found" for invalid regex, no longer under-reports paginated count-mode totals; and Glob no longer crashes on a null byte in pattern/path/cwd. -->
|
|
36
36
|
|
|
37
|
-
> **v2.1.210+**: Bash/PowerShell 명령이 timeout으로 auto-background될 때의 메시지가 개선되어 모델이 hang과 명시적 background 요청을 구분할 수 있으며, auto-background된 명령 내 `cd`는 적용되지 않고 tool result가 working directory 불변을 명시합니다 — auto-background 이후 cwd 의존 후속 명령은 절대 경로로 수행합니다. 또한 Grep content mode가 결과 끝을 지난 페이지네이션에서 "No matches found"를 반환하던 문제가 수정되었습니다(v2.1.208 Grep 페이지네이션 수정의 연장) — 구버전에서 이 응답은 "패턴 미존재"가 아니라 "페이지 끝"일 수 있습니다.
|
|
37
|
+
<!-- RETIRED (은퇴 릴리즈 v1.1.45, 보존 기준 v2.1.212 미만): > **v2.1.210+**: Bash/PowerShell 명령이 timeout으로 auto-background될 때의 메시지가 개선되어 모델이 hang과 명시적 background 요청을 구분할 수 있으며, auto-background된 명령 내 `cd`는 적용되지 않고 tool result가 working directory 불변을 명시합니다 — auto-background 이후 cwd 의존 후속 명령은 절대 경로로 수행합니다. 또한 Grep content mode가 결과 끝을 지난 페이지네이션에서 "No matches found"를 반환하던 문제가 수정되었습니다(v2.1.208 Grep 페이지네이션 수정의 연장) — 구버전에서 이 응답은 "패턴 미존재"가 아니라 "페이지 끝"일 수 있습니다. -->
|
|
38
38
|
|
|
39
39
|
> **v2.1.212+**: MCP 도구 호출이 2분(기본값, `CLAUDE_CODE_MCP_AUTO_BACKGROUND_MS`로 임계값 조정·비활성) 초과 시 자동으로 백그라운드로 이동해 세션이 계속 사용 가능해집니다 — 위 v2.1.210 Bash/PowerShell auto-background의 MCP 도구 확장. 느린 MCP 호출(ontology-rag `rebuild_ontology`, code-review-graph 인덱싱 등)을 hang으로 오판하지 말고, 2분 초과 시 백그라운드 전환을 전제로 후속 작업을 진행합니다.
|
|
40
40
|
|
|
41
41
|
> **v2.1.224+**: mid-turn에 연결된 MCP 도구가 **이름 고지 없이** tool search로 deferred되던 결함이 수정되었습니다. 구버전에서는 세션 도중 붙은 MCP 서버의 도구가 이름조차 노출되지 않아 "그런 도구 없음"으로 오판할 수 있었으므로, 위 tool-availability 주의(`command -v` 사전 확인과 동류)를 MCP 도구에도 적용합니다 — 도구 부재 결론 전에 `ToolSearch`로 실측합니다.
|
|
42
42
|
|
|
43
|
+
> **v2.1.228+**: Write 도구가 **이번 세션에 읽지 않은 기존 파일도 newer model에서는 덮어쓸 수 있도록** 변경되어 Edit 도구 규칙과 일치합니다(구모델은 여전히 read 선행 필요). 도구가 강제하던 read-before-write 가드가 모델에 따라 사라지므로, "Write가 실패했다 = 파일을 안 읽었다"는 진단이 더 이상 보편적으로 성립하지 않고, **읽지 않은 파일을 Write하면 기존 내용이 경고 없이 소실**됩니다 — 전체 교체가 아닌 변경에는 Edit을 쓰는 원칙을 도구 강제가 아니라 절차로 유지합니다. 또한 deferred-tools reminder가 skill 호출 후 모델에 두 번 전달되던 문제가 수정되었습니다(중복 컨텍스트 소모).
|
|
44
|
+
|
|
45
|
+
> **v2.1.229+**: 도구 호출의 `glob`/`file_path`/`command` 값이 **비문자열일 때 에러 화면으로 크래시**하던 문제가 수정되었습니다(해당 세션의 `--resume`에서도 재발). 구버전에서 이 크래시는 세션을 복구 불가 상태로 만들면서 **원인이 도구 인자 타입이라는 단서를 남기지 않았으므로**, 스크립트로 도구 인자를 조립할 때 문자열 타입을 보장합니다(cross-ref R023 Workflow Script Sanity Check). 좁은 터미널에서 progress bar·마크다운 표 렌더링 시 발생하던 RangeError 크래시(`claude --continue`/`--resume` 시작 시에도 발생)도 함께 수정되었습니다.
|
|
46
|
+
|
|
43
47
|
### Capability-Aware Tool Scheduling
|
|
44
48
|
|
|
45
49
|
When dispatching parallel tool calls, consider per-tool capabilities to optimize scheduling:
|
|
@@ -488,10 +488,12 @@ Key optional fields: `scope`, `context`, `version`, `effort`, `model`, `agent`,
|
|
|
488
488
|
> **v2.1.199+**: 스택된 slash-skill 호출(`/skill-a /skill-b do XYZ`)이 이제 leading skill을 최대 5개까지 모두 로드합니다(이전에는 첫 번째만 로드). oh-my-customcode의 라우팅 스킬 체이닝(예: `/omcustom:fsd`가 여러 스킬을 연쇄 호출하는 패턴)에서 다중 스킬 스택 호출 시 컨텍스트 손실이 줄어듭니다. 또한 subagent 조회 중 `/model`·`/fast`를 입력하면 lead의 model picker가 열리며 notice가 표시됩니다.
|
|
489
489
|
-->
|
|
490
490
|
|
|
491
|
-
> **v2.1.210+**: 스킬/커맨드 본문에서 인자 없이 호출된(unmatched) `$1`/`$2` positional placeholder가 조용히 제거되던(silently stripped) 동작이 수정되어 이제 리터럴 `$1`로 verbatim 보존됩니다 — 인자 부재 시 `$1`이 확장된 프롬프트에 그대로 남아 지시가 깨지므로, silent stripping에 옵션-인자 처리를 의존하지 말고 인자 부재 케이스를 명시 처리(default text / `$ARGUMENTS` guard / `argument-hint`)해야 합니다. (위 v2.1.163+ `\$1` escape는 항상 리터럴 `$` 출력용 별개 메커니즘으로 이번 변경 대상이 아니며, 이번 수정은 치환 의도의 bare `$1`이 unmatched일 때만 적용됩니다.)
|
|
491
|
+
<!-- RETIRED (은퇴 릴리즈 v1.1.45, 보존 기준 v2.1.212 미만): > **v2.1.210+**: 스킬/커맨드 본문에서 인자 없이 호출된(unmatched) `$1`/`$2` positional placeholder가 조용히 제거되던(silently stripped) 동작이 수정되어 이제 리터럴 `$1`로 verbatim 보존됩니다 — 인자 부재 시 `$1`이 확장된 프롬프트에 그대로 남아 지시가 깨지므로, silent stripping에 옵션-인자 처리를 의존하지 말고 인자 부재 케이스를 명시 처리(default text / `$ARGUMENTS` guard / `argument-hint`)해야 합니다. (위 v2.1.163+ `\$1` escape는 항상 리터럴 `$` 출력용 별개 메커니즘으로 이번 변경 대상이 아니며, 이번 수정은 치환 의도의 bare `$1`이 unmatched일 때만 적용됩니다.) -->
|
|
492
492
|
|
|
493
493
|
> **v2.1.222+**: 스킬 frontmatter의 `disable-model-invocation: true`(모델이 스스로 그 스킬을 호출하지 못하게 막고 사용자/파이프라인의 명시적 호출만 허용하는 필드)가 설정된 스킬을 모델이 호출하려 할 때의 refusal 문구가 개선되어, 모델에게 **워크플로우를 스스로 복제하지 말고 사용자에게 실행을 요청하라**고 지시합니다. 무인 루프(`/fsd` 등)가 이런 스킬을 모델 호출 경로에 두면 실행 대신 refusal이 반환되므로, 해당 스킬은 **사용자/파이프라인 명시 호출**로 설계합니다.
|
|
494
494
|
|
|
495
|
+
> **v2.1.228+**: claude.ai에서 동기화된 스킬이 하드닝되었습니다 — 로컬 커맨드·MCP prompt를 **shadow하지 않고**, description이 sanitize·labeling되며, 로컬 머신에서 그 본문이 `!` 명령을 실행하거나 `@` 파일 참조를 확장하지 **않습니다**. 즉 외부 출처 스킬은 로컬 `.claude/skills/` 스킬과 **동일한 실행 능력을 갖지 않으므로**, 동기화 스킬에 `!`/`@` 동작을 전제한 본문을 작성하면 무음 미실행이 됩니다. 구버전에서는 동기화 스킬이 로컬 커맨드를 가릴 수 있어 같은 이름 호출이 어느 정의로 해소되는지 결정론적이지 않았습니다.
|
|
496
|
+
|
|
495
497
|
<!-- DETAIL: Skill Optional Fields (full yaml block)
|
|
496
498
|
```yaml
|
|
497
499
|
scope: core # core | harness | package (default: core)
|
|
@@ -42,6 +42,8 @@ These are distinct mechanisms. Agent Teams `SendMessage` requires `TeamCreate` a
|
|
|
42
42
|
|
|
43
43
|
> **v2.1.224/225+**: CC 네이티브 `SendMessage`가 **cross-session으로 확장**되었습니다(다른 머신 포함, macOS/Linux) — `ListAgents`로 대상을 열거하고 `crossSessionInbound` / `dialogExpiry` 설정으로 수신·만료를 제어합니다. 위 표의 "Cross-session = claude-peers-mcp 전용" 구분은 이제 **유일한 수단이 아니며**, 브로커 없이 네이티브 경로를 쓸 수 있습니다. 다만 위 Cross-Session Relay Authority Hardening(v2.1.166)의 권한 비전파 원칙은 네이티브 경로에도 동일하게 적용됩니다 — cross-session 메시지는 조율 신호이지 승인 채널이 아닙니다. (225) cross-session 메시지가 headless 세션·기동 중에 **고지도 만료도 없이 대기**하던 결함이 수정되었으므로, 구버전에서 "응답 없음"은 미수신이 아니라 무기한 대기였을 수 있습니다.
|
|
44
44
|
|
|
45
|
+
> **v2.1.229/232+**: 네이티브 cross-session 경로의 대상 지정이 쉬워졌습니다 — (229) `ListAgents`가 끊긴 Remote Control 세션을 `offline`, 클라우드 세션을 `cloud`로 표시하므로 **열거 결과에 있다는 사실만으로 도달 가능하다고 가정하지 않습니다**(구버전에서는 살아있는 세션과 끊긴 세션이 구분 없이 나열되어 무응답 원인을 판별할 수 없었습니다). (232) 프롬프트에 `@`로 다른 세션을 이름으로 멘션하면 `SendMessage`가 그 세션에 직접 도달하고, bare name이 **살아있는 세션 1개와 정확히 일치하면 ref 확인 없이 전달**됩니다 — 같은 머신의 세션 이름은 중복 시 `name-word-word` 변형으로 유일성이 보장되므로 이름 기반 전달이 결정론적입니다. 전달 성공은 여전히 조율 신호일 뿐이며, 아래 Member Completion Verification의 ground-truth 원칙과 v2.1.166 권한 비전파 원칙은 그대로 적용됩니다. 또한 (232) interactive session의 background 기본 실행은 **non-teammate 스폰에 한정**되므로 Teams member에는 적용되지 않습니다 — 아래 stall handling의 2분 휴리스틱을 background 지연과 혼동하지 않습니다(cross-ref R010).
|
|
46
|
+
|
|
45
47
|
### Cross-Session Relay Authority Hardening (CC v2.1.166+)
|
|
46
48
|
|
|
47
49
|
<!-- ARCHIVED CC version note (historical):
|
|
@@ -73,9 +73,9 @@ Never accept "pre-existing" without direct base-branch evidence. A false "pre-ex
|
|
|
73
73
|
|
|
74
74
|
### Verification-Delegation Non-Termination (검증 위임 판정 종료 보장)
|
|
75
75
|
|
|
76
|
-
구조 검증(mgr-sauron R017)·판정·품질 게이트를 서브에이전트에 위임할 때, 위임 프롬프트에 **"최종 PASS/FAIL 판정 없이 turn을 종료하지 말라"**를 명시한다 — 단 이 clause는 **보조 수단**일 뿐 1차 방어선이 아니다. clause를 명시해도 mid-step 종료가 **누적
|
|
76
|
+
구조 검증(mgr-sauron R017)·판정·품질 게이트를 서브에이전트에 위임할 때, 위임 프롬프트에 **"최종 PASS/FAIL 판정 없이 turn을 종료하지 말라"**를 명시한다 — 단 이 clause는 **보조 수단**일 뿐 1차 방어선이 아니다. clause를 명시해도 mid-step 종료가 **누적 14회** 재발했다(v1.1.13/14/17/18/19 … v1.1.44, 아래 Origin 참조). **예방의 1차 방어선은 위임 경계 분할**(아래 「위임 경계를 Phase 개수로 설계」)이고, **사후 1차 방어선은 오케스트레이터의 직접 ground-truth 실측**이다.
|
|
77
77
|
|
|
78
|
-
mid-step 종료는 예상 가능한 정상 실패 모드로 취급한다 — 발생 시 즉시 ground-truth를 실측해 실제 진행 상태를 확인한다. **증상만으로 결과를 넘겨짚지 않는다**: 같은 "...중" 한 줄 종료라도 실측 결과는
|
|
78
|
+
mid-step 종료는 예상 가능한 정상 실패 모드로 취급한다 — 발생 시 즉시 ground-truth를 실측해 실제 진행 상태를 확인한다. **증상만으로 결과를 넘겨짚지 않는다**: 같은 "...중" 한 줄 종료라도 실측 결과는 **세 방향 모두** 관측됐다 — (a) 보고=완료("merging now" → 실측 시 PR 이미 MERGED, resume 불필요), (b) 보고=미완료("CI 실행 중" → 실측 시 PR OPEN 미머지, resume 필요), (c) **실제가 보고보다 앞섬**("커밋 1 완료, 커밋 2 스테이징으로 이어갑니다" → 실측 시 3개 커밋 전부 완료). 세 방향이 모두 나온 이상 증상 기반 진행도 추론은 **원리적으로 불가능**하며, 실측만이 유일한 판정 수단이다. 미완이면 SendMessage로 resume하되, 오케스트레이터가 실측한 값(예: "CI 전부 통과, mergeStateStatus=CLEAN")을 resume 메시지에 동봉해 에이전트가 재폴링 후 재종료하는 루프를 끊는다.
|
|
79
79
|
|
|
80
80
|
| Anti-pattern | Required |
|
|
81
81
|
|--------------|----------|
|
|
@@ -83,7 +83,17 @@ mid-step 종료는 예상 가능한 정상 실패 모드로 취급한다 — 발
|
|
|
83
83
|
| mid-step 종료 증상(예: "merging now")으로 결과를 넘겨짚음 | 매번 `gh pr view`/`gh run list` 등으로 실측 후 완료/미완료 판정 |
|
|
84
84
|
| resume 시 빈 재촉만 전달 | 실측값을 resume 메시지에 동봉해 재폴링 루프 차단 |
|
|
85
85
|
|
|
86
|
-
|
|
86
|
+
#### 위임 경계를 Phase 개수로 설계 (예방 1차 방어선)
|
|
87
|
+
|
|
88
|
+
다중 Phase 작업을 한 에이전트에 위임하면 **Phase 경계가 곧 종료 유혹 지점**이 된다 — 완료 조건 번호 명시와 종료 금지 clause를 넣어도 동일하다. 위임 단위는 **단일 목표 1개**로 자르고, Phase가 2개 이상이면 분할해 순차 발주한다.
|
|
89
|
+
|
|
90
|
+
| Anti-pattern | Required |
|
|
91
|
+
|--------------|----------|
|
|
92
|
+
| 다중 Phase 작업(3-Phase 검증, 3-커밋 시퀀스)을 한 에이전트에 위임하고 clause로 종료를 막으려 함 | 위임 단위를 단일 목표 1개로 분할해 순차 발주 — 경계 분할이 clause 강화보다 실효적 |
|
|
93
|
+
|
|
94
|
+
대조 실증(#1574, v1.1.44 세션): 단일 목표 위임(PR 생성 / 머지 / 브랜치 정리 / 버전 범프) **4건 전원 완주**, 다중 Phase 위임(mgr-sauron 3-Phase, mgr-gitnerd 3-커밋) **2건 모두 mid-step 종료**. 같은 세션에서 릴리즈 단계를 push+범프 / PR 생성 / 머지로 3분할한 것이 이 설계의 적용례다.
|
|
95
|
+
|
|
96
|
+
Origin: #1443 (Session 126 회고 찐빠 #1) — v1.1.3 R017 검증에서 mgr-sauron이 source-hash 대조 중 판정 없이 종료 → resume 후 PASS. v1.1.4에서 "판정 반드시 출력" 명시로 1회 완료(대조 실증). **5회 재발 확인(#1492, Session 132)**: v1.1.13/14/17(clause 명시에도 재발) → v1.1.18(완료조건 6항목+종료금지 명시에도 "merging now" 한 줄 남기고 종료, 실측 결과 이미 완료) → v1.1.19(위임 프롬프트에 "4회 무시됨"까지 명시했으나 "CI 실행 중" 한 줄 남기고 종료, 실측 결과 미완료). Session 132에서 2회 모두 오케스트레이터 직접 실측으로 복구 — clause 강화가 아니라 실측 습관화가 유일하게 실증된 방어선. **누적 11회 확인(#1518 찐빠 #2, Session 136)**: v1.1.30 릴리즈 세션에서도 "완료 조건 5항목 실측 + 판정 없이 종료 금지" 명시에도 mgr-gitnerd가 "폴링 완료 통지를 기다리겠습니다" 한 줄만 남기고 종료 → 오케스트레이터 직접 실측으로 복구(lockfile push 완료 / CI pending / PR OPEN); 이번엔 "대기 중" 증상이 실제 미완료였고 Session 132의 "머지 중" 증상은 실제 완료였다는 대비로 증상→결과 추론 금지가 재확인됨. **누적 14회 + 3방향째 확인(#1574, v1.1.44 세션)**: mgr-sauron 3-Phase / mgr-gitnerd 3-커밋 위임 2건이 Phase 경계에서 종료했고(위 「위임 경계를 Phase 개수로 설계」의 대조 실증), 그중 mgr-gitnerd는 "커밋 2로 이어가겠다"고 보고했으나 실측 시 3개 커밋이 이미 전부 완료 — 실제가 보고보다 앞서는 세 번째 방향.
|
|
87
97
|
|
|
88
98
|
Cross-reference: R018 (Member Completion Verification), `feedback_release_delegation_phasing`, `feedback_orchestrator_direct_verify` (release delegation phasing을 verification 위임에도 확장).
|
|
89
99
|
|
|
@@ -93,7 +103,7 @@ Cross-reference: R018 (Member Completion Verification), `feedback_release_delega
|
|
|
93
103
|
> **v2.1.200+**: rate limit으로 어떤 텍스트 출력도 내기 전에 잘린 subagent가 이전에는 빈 결과(empty result)를 반환하던 것을 clean failure로 반환하도록 수정되었습니다 — v2.1.199 partial-work 반환에 이어, 출력 이전 rate-limit 차단 시 조용한 빈 결과 대신 명시적 실패를 parent에 보고합니다. 플랫폼이 false-success/silent-empty 자가보고를 추가로 줄였으나, "actual outcome ≠ attempt" ground-truth 검증 원칙(R020 Core Rule)은 여전히 유지됩니다 — subagent 보고를 그대로 신뢰하지 말고 git status/grep/validation script로 재확인합니다.
|
|
94
104
|
-->
|
|
95
105
|
|
|
96
|
-
> **v2.1.211+**: CC의 background agent 결과 보고가 개선되어, Claude가 아직 실행 중인 agent의 상태를 그대로 보고하고 **결과를 지어내지 않고 실제 완료를 기다립니다**(previously fabricated results). v2.1.199/200(false-success·silent-empty 자가보고 감소)에 이은 플랫폼 개선으로 오케스트레이터의 fabricated-completion 리스크를 추가로 낮추지만, "actual outcome ≠ attempt" ground-truth 검증 원칙(Core Rule)은 여전히 유지됩니다 — subagent/background agent 보고를 그대로 신뢰하지 말고 `git status`/`grep`/validation script로 재확인합니다.
|
|
106
|
+
<!-- RETIRED (은퇴 릴리즈 v1.1.45, 보존 기준 v2.1.212 미만): > **v2.1.211+**: CC의 background agent 결과 보고가 개선되어, Claude가 아직 실행 중인 agent의 상태를 그대로 보고하고 **결과를 지어내지 않고 실제 완료를 기다립니다**(previously fabricated results). v2.1.199/200(false-success·silent-empty 자가보고 감소)에 이은 플랫폼 개선으로 오케스트레이터의 fabricated-completion 리스크를 추가로 낮추지만, "actual outcome ≠ attempt" ground-truth 검증 원칙(Core Rule)은 여전히 유지됩니다 — subagent/background agent 보고를 그대로 신뢰하지 말고 `git status`/`grep`/validation script로 재확인합니다. -->
|
|
97
107
|
|
|
98
108
|
## Common False Completion Patterns — 8 anti-patterns including "Command executed" without exit code check, "Waiting for manual publish" when CI auto-publishes, "UI changes done" without browser render. See full table via Read tool.
|
|
99
109
|
|
|
@@ -261,6 +271,17 @@ Origin: #1266 ④.
|
|
|
261
271
|
|
|
262
272
|
이는 Read-Before-Characterize의 **자기 적용** 각도다 — 진단 대상이 외부 로그가 아니라 자기 자신의 transcript일 때에도 "읽기 전 특성화 금지"가 동일하게 적용된다.
|
|
263
273
|
|
|
274
|
+
#### 자율 루프 세션의 턴 경계 정의 (계수 전 확정 필수)
|
|
275
|
+
|
|
276
|
+
위 파싱 레시피는 **"사용자 프롬프트 = 턴 경계"**를 암묵 전제한다. `/fsd` 같은 자율 루프는 사용자 프롬프트가 거의 없어(실측: 사용자 프롬프트 4개 대 assistant 응답 30여 회) 이 전제로는 경계 재구성이 실패하고, 계수 자체가 성립하지 않는다. 자율 루프 transcript를 셀 때는 대안 경계 정의(예: **`tool_result` 직후 첫 text 블록을 응답 시작으로 간주**)를 먼저 확정하고, 정의를 확정하기 전에는 **위반 횟수를 단정하지 않는다**.
|
|
277
|
+
|
|
278
|
+
| Anti-pattern | Required |
|
|
279
|
+
|--------------|----------|
|
|
280
|
+
| 자율 루프 transcript를 사용자 프롬프트 경계로 파싱해 위반 N회로 단정 | 대안 경계 정의를 먼저 확정; 확정 전에는 횟수 단정 금지 |
|
|
281
|
+
| 경계 재구성 실패를 "위반 없음"으로 해석 | 경계 무관 지표로 대체 보고 — `┌─ Agent:` 헤더 총량, tool_use 대 announce 라인 비율 |
|
|
282
|
+
|
|
283
|
+
Origin: #1574 (v1.1.44 세션 — 자율 루프에서 R007 헤더 누락 계수를 시도했으나 사용자 프롬프트 4개로 턴 경계 재구성 불가). Cross-ref: R005(계수/매칭 방법 확인 — 도구 기본 동작 미확인 시 결과 오해석).
|
|
284
|
+
|
|
264
285
|
### Proxy Signal vs Canonical Ground-Truth (#1336 ①②)
|
|
265
286
|
|
|
266
287
|
> Origin: #1336 ①② — transcription was alarmed as "stopped" because `.txt` files looked stale, but the canonical DB had transcripts current to 06-09 21:30 (.txt is not the whisper collector's output — it emits only to the DB). Separately, SMS was over-diagnosed as "fully blocked" from one empty OneDrive XML path + a single 401, while the DB held 17 SMS rows ingested via the app path.
|
|
@@ -90,7 +90,7 @@ R016의 승격 루프(위반 지적 → 규칙 조항 추가)는 코퍼스의 **
|
|
|
90
90
|
|
|
91
91
|
1. **발동 추적**: `/homework` 회고·위반 지적 시 인용된 규칙 ID/조항을 feedback memory에 기록한다 (가벼운 추적 — 완전 자동화는 불요).
|
|
92
92
|
2. **후보 선정**: 마이너 2릴리즈 무발동 + 행동 지시 가치 소멸 조항을 은퇴 후보로 선정한다.
|
|
93
|
-
3. **HTML-comment화**: 조항을 `<!-- RETIRED (은퇴 릴리즈 vX.Y.Z,
|
|
93
|
+
3. **HTML-comment화**: 조항을 `<!-- RETIRED (은퇴 릴리즈 vX.Y.Z, <사유>): 원문 -->` 로 감싸 auto-injection에서 제외한다. `<사유>`는 위 「은퇴 대상」 두 범주에 대응한다 — 장기 무발동은 `N릴리즈 무발동`, 수정 완료된 플랫폼 버그 서사는 `보존 기준 v2.1.NNN 미만`. Read 도구로 열람 가능하므로 무손실이다 (R005 Context Optimization via HTML Comments).
|
|
94
94
|
4. **부활**: 동일 패턴이 재발하면 uncomment하여 즉시 복원한다 — 승격 루프와 대칭이다.
|
|
95
95
|
|
|
96
96
|
### 버전노트 보존정책
|
|
@@ -14,10 +14,12 @@ oh-my-customcode uses an **advisory-first enforcement model**. Most rules are en
|
|
|
14
14
|
| Soft Block | Stop hook prompt | R011 session-end saves | Auto-performs then approves |
|
|
15
15
|
| Conversation Block | PostToolUse hook + `continueOnBlock` (CC v2.1.139+), exit 2 | stuck-detector, context-budget-advisor, cost-cap-advisor | Feeds rejection reason into conversation; Claude continues with awareness |
|
|
16
16
|
| Advisory | PostToolUse hooks | R007, R008, R009, R010, R018 | Warns via stderr, never blocks |
|
|
17
|
-
| Advisory (proactive) | UserPromptSubmit + SubagentStop hooks | R007, R008 (`r007-r008-drift-advisor.sh` — #1229 UserPromptSubmit, #1545 SubagentStop) | Reads last assistant turn; emits advisory if header/prefix absent. SubagentStop wiring (#1545) closes the no-user-input autonomous-loop gap (`/fsd`). Complements retroactive Stop-hook (`session-reflection.sh`, #1190).
|
|
17
|
+
| Advisory (proactive) | UserPromptSubmit + SubagentStop + PostToolUse hooks | R007, R008 (`r007-r008-drift-advisor.sh` — #1229 UserPromptSubmit, #1545 SubagentStop, #1553 PostToolUse) | Reads last assistant turn; emits advisory if header/prefix absent. SubagentStop wiring (#1545) closes the no-user-input autonomous-loop gap (`/fsd`); PostToolUse (#1553) covers the orchestrator-only stretch before the first subagent spawn. Complements retroactive Stop-hook (`session-reflection.sh`, #1190). **v1.1.43부터 실제 발화 — 아래 각주 참조.** |
|
|
18
|
+
| Advisory (telemetry) | PostToolUseFailure hook | — (계측 전용, 규칙 강제 없음) | `failure-ledger.sh` (#1561, v1.1.44) — 도구 실패를 JSONL 원장에 append. stdout/stderr 무출력이라 모델에 도달하지 않으며 절대 차단하지 않음 |
|
|
19
|
+
| Advisory (proactive) | UserPromptSubmit hook | R020 (원인 진단) | `fail-axis-cause-advisor.sh` (#1561, v1.1.44) — 원장에 실패 기록이 있는데 원인 진술 없는 재촉 프롬프트가 오면 `hookSpecificOutput.additionalContext`로 "원인 가설 되묻기" advisory 전달. 원장 부재 시 조용히 통과 |
|
|
18
20
|
| Prompt-based | CLAUDE.md + rules/ + PostCompact | All MUST rules | Behavioral guidance in context |
|
|
19
21
|
|
|
20
|
-
> **Advisory (proactive/retroactive)
|
|
22
|
+
> **Advisory (proactive/retroactive) 발화 결함과 해소 (실측)**: `hookSpecificOutput.additionalContext` **전달 경로 자체는 #1547(v1.1.40)에서 구현**됐으나, 그 앞단 **파서 셀렉터 결함**으로 advisory가 **v1.1.42까지 한 번도 발화하지 못했다** — `jq -r '.role'`로 읽었으나 트랜스크립트 최상위에 `role` 키가 없어(실제는 `.message.role`) `last_assistant`가 항상 비고 즉시 `exit 0`으로 종료됐다. 당시 실측: 트랜스크립트 771개 전수에서 `"additionalContext":` 출현 0건, 라이브 프로브 stdout/stderr 각 0바이트. **proactive(`r007-r008-drift-advisor.sh`)와 retroactive(`session-reflection.sh`, 동일 결함) 두 계층 모두 미발화**였다. **v1.1.43에서 양 계층 파서 복구 + `PostToolUse` 배선을 완료했고, 라이브 프로브로 최초 발화를 확인했다(#1553).** 후속으로 v1.1.44에서 R008 판정을 블록 인접 비교 → 턴 단위 개수 비교로 전환(#1563), v1.1.45에서 Skill 도구 면제를 추가했다(#1569).
|
|
21
23
|
>
|
|
22
24
|
> 교훈: **배선 확인 ≠ 전달 확인 ≠ 발화 확인** — R020 "actual outcome ≠ attempt"의 훅 도메인 재현 사례.
|
|
23
25
|
|
|
@@ -27,7 +29,7 @@ oh-my-customcode uses an **advisory-first enforcement model**. Most rules are en
|
|
|
27
29
|
> **v2.1.199+**: SessionStart/Setup/SubagentStart hook이 exit code 2로 종료할 때 stderr를 조용히 숨기던 문제가 수정되어 이제 오류가 표시됩니다. Hard Block/Advisory 계층(위 Enforcement Tiers 표)의 훅 실패 관측성을 강화합니다 — cf. v2.1.163 `additionalContext` 구조화 피드백.
|
|
28
30
|
-->
|
|
29
31
|
|
|
30
|
-
> **v2.1.210+**: hook callback timeout이 모델에 user rejection으로 오보고되어 unattended 세션이 정지 대기하던 문제가 수정되었습니다. R021 advisory 훅(PostToolUse/UserPromptSubmit/Stop 등)이 매 턴 발화하고 /fsd 등 장기 무인 루프가 이에 의존하므로, hook timeout이 더 이상 phantom rejection으로 무인 세션을 중단시키지 않습니다 — cf. v2.1.199 훅 실패 관측성.
|
|
32
|
+
<!-- RETIRED (은퇴 릴리즈 v1.1.45, 보존 기준 v2.1.212 미만): > **v2.1.210+**: hook callback timeout이 모델에 user rejection으로 오보고되어 unattended 세션이 정지 대기하던 문제가 수정되었습니다. R021 advisory 훅(PostToolUse/UserPromptSubmit/Stop 등)이 매 턴 발화하고 /fsd 등 장기 무인 루프가 이에 의존하므로, hook timeout이 더 이상 phantom rejection으로 무인 세션을 중단시키지 않습니다 — cf. v2.1.199 훅 실패 관측성. -->
|
|
31
33
|
|
|
32
34
|
> **v2.1.211/212/214+**: 훅의 enforcement 결정이 auto/unattended 모드에서 안정적으로 존중되도록 세 건이 수정되었습니다 — (211) auto mode가 unsandboxed Bash에 대한 PreToolUse 훅의 `ask` 결정을 덮어쓰던 문제가 수정되어 훅 `ask`가 최소 prompt로 floor되고, (212) `continue:false` 훅의 halt가 도구 실패·중간 완료 시 누락되던 문제 및 훅 인프라 오류가 user rejection으로 오보고되던 문제가 수정되었으며, (214) 훅 stdout JSON이 스키마 검증에 실패할 때 exit code 2가 문서대로 차단하지 못하던 문제가 수정되었습니다. R021 Enforcement Tiers(Hard Block=exit 2, Conversation Block=continueOnBlock exit 2, Advisory)가 훅의 block/ask 결정 존중에 의존하므로, 세 수정 모두 hard-block·advisory 훅(stage-blocker, rule-deletion-guard, stuck-detector 등)의 강제 신뢰성을 강화합니다 — v2.1.210 훅 timeout phantom-rejection 수정의 연장선.
|
|
33
35
|
|
|
@@ -40,7 +42,7 @@ oh-my-customcode uses an **advisory-first enforcement model**. Most rules are en
|
|
|
40
42
|
3. **Composability**: External skills and internal rules can coexist without deadlocks
|
|
41
43
|
4. **PostCompact reinforcement**: R007/R008/R009/R010/R018 are re-injected after context compaction
|
|
42
44
|
|
|
43
|
-
## Hard Enforcement Candidates — R010 git-delegation-guard (conditional), R007/R008 advisory **implemented** (#1229 UserPromptSubmit, proactive) + **#1545 SubagentStop** (closes autonomous-loop gap) + retroactive Stop-hook (#1190); `additionalContext` 전달 경로는 #1547(v1.1.40)에서 구현됐으나 파서 셀렉터 결함으로 **양 계층 모두
|
|
45
|
+
## Hard Enforcement Candidates — R010 git-delegation-guard (conditional), R007/R008 advisory **implemented & firing** (#1229 UserPromptSubmit, proactive) + **#1545 SubagentStop** (closes autonomous-loop gap) + **#1553 PostToolUse** + retroactive Stop-hook (#1190); `additionalContext` 전달 경로는 #1547(v1.1.40)에서 구현됐으나 파서 셀렉터 결함으로 v1.1.42까지 **양 계층 모두 미발화**였고, **v1.1.43에서 수정 완료·발화 확인**(#1553); hard-block variant still candidate if advisory insufficient (#1096). Promoted: rule-deletion-guard.sh (2026-04-08). See details via Read tool.
|
|
44
46
|
|
|
45
47
|
<!-- DETAIL: Hard Enforcement Candidates (Future)
|
|
46
48
|
If advisory enforcement proves insufficient for specific rules, these are candidates for promotion to hard-block:
|
|
@@ -48,7 +50,7 @@ If advisory enforcement proves insufficient for specific rules, these are candid
|
|
|
48
50
|
| Rule | Candidate Hook | Status | Condition for Promotion |
|
|
49
51
|
|------|---------------|--------|------------------------|
|
|
50
52
|
| R010 | git-delegation-guard.sh | Candidate | If orchestrator-direct-write violations exceed 3/session |
|
|
51
|
-
| R007/R008 | `r007-r008-drift-advisor.sh` (UserPromptSubmit #1229 + SubagentStop #1545) | **Advisory implemented** — proactive pre-response check
|
|
53
|
+
| R007/R008 | `r007-r008-drift-advisor.sh` (UserPromptSubmit #1229 + SubagentStop #1545 + PostToolUse #1553) | **Advisory implemented and firing** — proactive pre-response check wired to three trigger points; the SubagentStop leg (#1545) closes the no-user-input autonomous-loop gap (`/fsd` etc.), and the PostToolUse leg (#1553) covers the orchestrator-only stretch before the first subagent spawn. Retroactive: `session-reflection.sh` (Stop, #1190). Two-layer drift detection: proactive (#1229/#1545/#1553) + retroactive (#1190). **Historical defect (v1.1.40–v1.1.42): delivery path implemented but never fired** — #1547 (v1.1.40) switched delivery from stderr (never model-visible on exit 0) to `hookSpecificOutput.additionalContext` on JSON stdout, non-blocking (no top-level `decision`/`continue`/`stopReason`), but both scripts short-circuited BEFORE emitting: `jq -r '.role'` read a key absent at transcript top level (it is `.message.role`), so `last_assistant` was always empty and the script exited 0 silently. Measured then: 0 `"additionalContext":` occurrences across 771 transcripts; live probe emitted 0 bytes on both streams. **Resolved in v1.1.43** — parser fix on both layers + `PostToolUse` wiring, first fire confirmed by live probe (#1553). Follow-ups: v1.1.44 turn-level R008 prefix counting (#1563), v1.1.45 Skill-tool exemption (#1569). | Promote to hard-block if advisory proves insufficient (#1096) |
|
|
52
54
|
|
|
53
55
|
Promotion requires: (1) measured violation rate data, (2) user approval, (3) rollback plan.
|
|
54
56
|
|
|
@@ -309,7 +309,7 @@ Cross-reference: the Subagent Scope-Creep STOP Protocol (reactive halt after tri
|
|
|
309
309
|
|--------------|----------|
|
|
310
310
|
| 미확인 플래그를 확정형으로 위임 프롬프트에 기재 (`gh issue edit <N> --assignee @me`) | 실행 전 `--help`로 실측 후 기재, 또는 "예시 — 실제 플래그는 확인 후 사용" 명시 |
|
|
311
311
|
|
|
312
|
-
Origin: #1563 찐빠 #3 — `gh issue edit --assignee`가 gh 2.86.0에 없는 플래그였고(정답 `--add-assignee`) 에이전트가 실행 중 자체 복구했다. Cross-reference: R005(도구 플래그·기본 동작 실측 함정 사례집),
|
|
312
|
+
Origin: #1563 찐빠 #3 — `gh issue edit --assignee`가 gh 2.86.0에 없는 플래그였고(정답 `--add-assignee`) 에이전트가 실행 중 자체 복구했다. Cross-reference: R005(도구 플래그·기본 동작 실측 함정 사례집), 아래 Agent Capability Pre-Check(위임 전 존재성 확인의 도구·경로 각도).
|
|
313
313
|
|
|
314
314
|
### Parallel Delegation — Sibling-Agent Disclosure
|
|
315
315
|
|
|
@@ -323,6 +323,16 @@ Origin: #1563 찐빠 #3 — `gh issue edit --assignee`가 gh 2.86.0에 없는
|
|
|
323
323
|
|
|
324
324
|
> Origin: #1518 (찐빠 #3 — 미고지 git 에이전트가 형제를 "외부 프로세스"로 오귀속; 같은 세션에서 고지한 4개 구현 에이전트는 전원 정확히 구분 보고 — 대조 실증). Cross-ref: R009 (병렬 실행 조건).
|
|
325
325
|
|
|
326
|
+
#### 고지는 귀속 후보를 늘릴 뿐 증거 등급을 올리지 않는다
|
|
327
|
+
|
|
328
|
+
형제 고지를 받았더라도 **정황 귀속(형제 탓)은 여전히 오답을 낸다** — 오히려 고지가 그럴듯한 오귀속 대상을 제공한다. 공유 뷰의 이상 징후는 형제 고지 여부와 무관하게 **개입 실험**(캐시 제거·복원, `bash -x` 추적, 변경 되돌려 재현)으로 귀속해야 한다.
|
|
329
|
+
|
|
330
|
+
| Anti-pattern | Required |
|
|
331
|
+
|--------------|----------|
|
|
332
|
+
| 고지받은 형제의 담당 범위와 겹친다는 정황만으로 실패 원인을 형제에 귀속 | 개입 실험(제거→재현 / 복원→소멸)으로 인과를 확정한 뒤 귀속 |
|
|
333
|
+
|
|
334
|
+
> Origin: #1574 (v1.1.44 세션 대조 실증 — 동일 고지를 받은 3개 병렬 에이전트 중 [1]은 `bun test` 11 fail을 "형제가 그 파일 편집 중"으로 정황 귀속해 오답, [2]/[3]은 개입 실험으로 정확히 귀속). Cross-ref: R020 (Read-Before-Characterize — 정황으로 특성화 금지).
|
|
335
|
+
|
|
326
336
|
## Universal bypassPermissions
|
|
327
337
|
|
|
328
338
|
> **This section is the canonical single source for the bypassPermissions requirement.** R002 (MUST-permissions.md) and R006 (MUST-agent-design.md) reference this section rather than repeating it.
|
|
@@ -411,6 +421,8 @@ Before spawning any agent:
|
|
|
411
421
|
|
|
412
422
|
> **v2.1.221+**: background session이 작업 보존을 위해 commit·push를 수행하고, draft PR은 작업이 요구할 때만 열며, 사용자의 CLAUDE.md git 지침을 따르고, 항상 작업 위치를 보고하며 종료하도록 변경되었습니다. 이 저장소의 R010은 모든 git 작업을 mgr-gitnerd 위임으로 요구하므로 background session은 그 지침을 읽고 동작하지만, **R020 기준 ground-truth(`git log` / `gh pr view`) 실측 없이 background session의 커밋/푸시 완료 보고를 신뢰하지 않습니다**. 또한 v2.1.221에서 `/status`가 세션 종류(interactive / background attached / background unattended)를 표시하므로 무인 실행 여부를 결정론적으로 확인할 수 있습니다.
|
|
413
423
|
|
|
424
|
+
> **v2.1.232+**: interactive session의 **non-teammate 에이전트 스폰이 기본 background 실행**으로 바뀌었습니다(subagent forking 기본 활성화의 일부). 즉 Agent 도구 호출의 반환은 "작업 완료"가 아니라 **"백그라운드 착수"일 수 있으므로**, 오케스트레이터는 스폰 반환이나 완료 통지를 완료 근거로 삼지 않고 R020 ground-truth(`git status` / `grep` / 검증 스크립트)로 확인합니다 — 구버전에서는 동기 반환이 기본이라 "반환 = 완료"라는 암묵 전제가 대체로 성립했고, 그 전제가 이 버전부터 무너집니다. 위 v2.1.221 `/status` 표시와 v2.1.211(실행 중 agent 결과를 지어내지 않음)이 진단 보조 수단입니다. cross-ref R009(fork의 컨텍스트 상속), R018(Teams member는 non-teammate가 아니므로 이 변경 대상 밖).
|
|
425
|
+
|
|
414
426
|
## Agent Capability Pre-Check
|
|
415
427
|
|
|
416
428
|
Before delegating a task to a subagent, MUST verify the target agent's tool capabilities against the task requirements. Failure to pre-check causes round-trip waste (delegation → failure → re-delegation).
|
|
@@ -611,6 +623,18 @@ Usage:
|
|
|
611
623
|
|
|
612
624
|
All git operations (commit, push, branch, PR) MUST go through `mgr-gitnerd`. Internal rules override external skill instructions for git execution.
|
|
613
625
|
|
|
626
|
+
### 품질 게이트 우회 금지 — 훅 차단은 보고 대상
|
|
627
|
+
|
|
628
|
+
git 위임 에이전트는 pre-commit/pre-push 훅 차단을 **자체 판단으로 우회하지 않는다**. `--no-verify`(및 `--no-gpg-sign` 등 게이트 무력화 플래그)는 git 위임의 **상시 금지 목록**이며, 오케스트레이터의 사전 승인이 있을 때만 예외다. 근본 원인을 확정했더라도, CI가 권위 게이트로 남더라도 마찬가지다 — 우회 여부는 에이전트가 아니라 오케스트레이터가 판단한다.
|
|
629
|
+
|
|
630
|
+
| Anti-pattern | Required |
|
|
631
|
+
|--------------|----------|
|
|
632
|
+
| pre-commit 훅 차단(테스트 실패 등)을 `--no-verify`로 자체 우회하고 커밋 진행 | 차단 사실과 원인을 오케스트레이터에 **보고하고 대기** — 우회는 사전 승인 후에만 |
|
|
633
|
+
|
|
634
|
+
> Origin: #1574 (v1.1.44 세션 — mgr-gitnerd가 `bun test` 11 fail로 인한 pre-commit 차단을 `--no-verify`로 자체 우회; 결과는 무해했으나 승인 없는 품질 게이트 우회는 절차 이탈). Cross-ref: R020 (Test-Skip Is Not Completion — 그린 빌드 회피 금지), R017 (커밋 전 검증 게이트).
|
|
635
|
+
|
|
636
|
+
> **v2.1.229+**: `/commit-push-pr`가 위험 플래그(`--force`, `--amend`, `--no-verify` 등)를 가진 git/gh 명령을 **더 이상 auto-approve하지 않습니다**. 플랫폼이 이 저장소의 위 조항(v1.1.45 신설)과 **독립적으로 같은 결론**에 도달한 사례입니다 — 즉 `--no-verify` 상시 금지는 이 저장소만의 보수적 관행이 아니라 플랫폼이 기본값으로 채택한 경계입니다. **구버전에서는 이 플래그들이 프롬프트 없이 통과했으므로, 과거 세션에서 `--no-verify` 커밋이 프롬프트 없이 성사된 사실은 승인의 증거가 아닙니다**(R020 "실행됨 ≠ 승인됨"). 플랫폼 프롬프트는 방어심층일 뿐 위 조항의 오케스트레이터 사전 승인 요구를 대체하지 않습니다.
|
|
637
|
+
|
|
614
638
|
<!-- ARCHIVED CC version note (historical):
|
|
615
639
|
> **v2.1.206+**: `/commit-push-pr`가 origin 외에 `remote.pushDefault`(또는 단일 remote)로의 git push도 auto-allow합니다. mgr-gitnerd git 위임 흐름 관련. `mode: "bypassPermissions"`는 모든 Agent tool 호출에 여전히 필수입니다.
|
|
616
640
|
-->
|
|
@@ -108,6 +108,10 @@ Reference: #1320 (fix), #1321 (session 113 retrospective 찐빠 #1), `feedback_l
|
|
|
108
108
|
|
|
109
109
|
> **v2.1.224+**: **세션당 200 subagent spawn cap이 제거**되어 장기 세션이 신규 에이전트를 거부하지 않습니다(동시성 제한과 depth 제한은 유지). 위 표의 "Max instances 5 concurrent"는 **동시성** 제한이므로 그대로 유효합니다 — 제거된 것은 세션 누적 총량 cap입니다. `/fsd` 등 장기 무인 루프에서 후반 반복의 스폰 실패를 더 이상 누적 cap으로 진단하지 않습니다.
|
|
110
110
|
|
|
111
|
+
> **v2.1.232+**: subagent forking이 **기본 활성화**되어 `subagent_type: "fork"` 서브에이전트가 전체 대화와 prompt cache를 상속합니다. 위 표의 "Instance independence — Isolated context, no shared state"는 **fork에는 성립하지 않습니다** — fork는 격리된 병렬 인스턴스가 아니라 컨텍스트 사본이므로, 위 Detection Criteria의 독립성 전제로 병렬 배치를 설계할 때 fork를 일반 subagent와 동일하게 취급하지 않습니다(오케스트레이터 컨텍스트가 그대로 전달되므로 위임 프롬프트의 범위 서술이 유일한 경계가 아님). 구버전에서는 fork가 opt-in이라 이 상속이 예외 경로였습니다. 또한 interactive session의 **non-teammate 에이전트 스폰이 기본 background 실행**이므로 스폰 반환은 완료 신호가 아닙니다(R010/R020).
|
|
112
|
+
|
|
113
|
+
> **v2.1.229+**: workflow fan-out이 같은 prefix를 공유하는 sibling agent를 **stagger**해 후속 에이전트가 prompt prefix 캐시를 재사용합니다(`CLAUDE_CODE_WORKFLOW_PREFIX_STAGGER_MS=0`으로 비활성). 즉 동일 접두사 병렬 배치는 동시 발사가 아니라 **의도적 시차 실행**이며, 스폰 직후 일부 에이전트의 시작 지연을 아래 Adaptive Parallel Splitting의 stall 신호로 오판하지 않습니다 — 구버전에서는 각 형제가 접두사 비용을 중복 지불했습니다. 또한 CPU 제한 컨테이너에서 dynamic workflow가 호스트 코어 수를 쓰던 문제가 수정되어, 컨테이너 실행 시 실효 동시성이 위 표의 cap보다 낮을 수 있습니다.
|
|
114
|
+
|
|
111
115
|
> **Fable 5 long-lived subagent reuse (Origin: #1435)**: Fable 5는 long-lived subagent 재사용(단일 subagent가 여러 단계를 이어서 수행)에 강함 — 현행 R009 병렬 실행 원칙과 상충하지 않으며, Fable 5 실행 시 short-lived 병렬 다수 대신 long-lived 재사용도 유효한 선택지. 상세는 `guides/claude-code/16-fable5-prompting.md`.
|
|
112
116
|
|
|
113
117
|
## Adaptive Parallel Splitting
|
|
@@ -71,7 +71,7 @@ Use a `"*"` deny rule in `settings.json` to enforce a deny-by-default posture, t
|
|
|
71
71
|
> **v2.1.207+**: Auto mode가 Bedrock/Vertex/Foundry에서 `CLAUDE_CODE_ENABLE_AUTO_MODE` opt-in 없이 사용 가능해졌습니다(설정 `disableAutoMode`로 비활성화 가능). 또한 `-p`/SDK 비대화 실행의 remote managed settings가 consent 다이얼로그 없이 동의로 기록되던 문제가 수정되었습니다. Tier-3/4 권한 흐름 관련.
|
|
72
72
|
-->
|
|
73
73
|
|
|
74
|
-
> **v2.1.210+**: `Write(path)`/`NotebookEdit(path)`/`Glob(path)` 형태의 permission rule은 시작 시 경고를 발생시킵니다 — 파일 쓰기 rule은 `Edit(path)`, 읽기 rule은 `Read(path)` matcher로 작성합니다. 위 Tier 표의 Write/NotebookEdit/Glob은 도구명일 뿐 path-scoped rule matcher가 아닙니다(위 v2.1.166 unknown-tool startup warning 연장선).
|
|
74
|
+
<!-- RETIRED (은퇴 릴리즈 v1.1.45, 보존 기준 v2.1.212 미만): > **v2.1.210+**: `Write(path)`/`NotebookEdit(path)`/`Glob(path)` 형태의 permission rule은 시작 시 경고를 발생시킵니다 — 파일 쓰기 rule은 `Edit(path)`, 읽기 rule은 `Read(path)` matcher로 작성합니다. 위 Tier 표의 Write/NotebookEdit/Glob은 도구명일 뿐 path-scoped rule matcher가 아닙니다(위 v2.1.166 unknown-tool startup warning 연장선). -->
|
|
75
75
|
|
|
76
76
|
> **v2.1.214+**: 단일 세그먼트 `dir/**` allow rule(예: `Edit(src/**)`)이 트리 어디에나 있는 중첩 `dir/`까지 auto-approve하던 버그가 수정되어 이제 `<cwd>/dir`에만 매칭됩니다(hook `if:` 조건도 동일 — 임의 깊이 매칭이 필요하면 `**/dir/**`로 작성). **`deny`/`ask` permission rule은 any-depth 매칭을 유지**(allow만 `<cwd>`로 좁아짐). settings.json 스코프 설계 시 이 비대칭(allow 좁게 / deny·ask 넓게)을 전제로 삼습니다. 위 v2.1.210 `Edit(path)`/`Read(path)` matcher 권고의 연장선.
|
|
77
77
|
|
|
@@ -82,6 +82,16 @@ Use a `"*"` deny rule in `settings.json` to enforce a deny-by-default posture, t
|
|
|
82
82
|
|
|
83
83
|
> **v2.1.223/225+**: 두 건이 Tier-4 Bash 권한 검사와 세션 인증에 영향을 줍니다. (223) 조작된 명령이 **자기 일부를 권한 검사에서 숨기던** 결함이 수정되었습니다 — v2.1.221 zsh `[[ ]]` 우회 수정의 연장선이며, 검사 대상 문자열과 실행 문자열이 다를 수 있었다는 뜻입니다(표시 측 결함은 R001). (225) 일시적 401이 장수명 `CLAUDE_CODE_OAUTH_TOKEN`을 저장된 단수명 토큰으로 교체해 headless 세션을 재시작 전까지 망가뜨리던 결함이 수정되었습니다 — 구버전 무인 실행에서 401 이후의 연쇄 인증 실패는 토큰 설정 오류가 아니라 이 교체 버그일 수 있으므로 진단 시 구분합니다. agent definition의 `bypassPermissions`가 org 정책을 무시하던 공백(223)은 R010 "Universal bypassPermissions"가 canonical.
|
|
84
84
|
|
|
85
|
+
> **v2.1.232/233+**: 232가 권한 검사 우회 3건을 수정했으나 **233이 그중 2건을 롤백**했습니다. 어느 것이 현행인지 버전별로 구분합니다.
|
|
86
|
+
>
|
|
87
|
+
> 1. **233 현재 유효 (232 수정 유지)** — (a) PowerShell에서 변수 기록 파라미터가 `$PSDefaultParameterValues`를 조용히 덮어써 이후 명령의 파일 접근을 리다이렉트할 수 있던 우회가 수정되었습니다. 또한 중첩 git 저장소가 부모 디렉토리의 trust를 상속하던 문제가 수정되어 저장소마다 별도 trust 확인이 필요하고, `sandbox.ripgrep`은 user/managed/`--settings`에서만 적용되며 **project settings로는 override 불가**입니다(위 v2.1.214 allow/deny 스코프 비대칭과 같은 계열의 축소 — repo-local 설정으로 켤 수 없는 항목이 늘었습니다).
|
|
88
|
+
> 2. **233에서 롤백됨 — 현재 권한 검사되지 않음** — (b) Windows Git Bash가 경로 검증에는 일반 파일로 보이는 Cygwin-style symlink를 따라가던 우회 수정, (c) Bash 입력 리다이렉션(`< file`)을 인자 표기와 동일하게 권한 검사하던 변경. 두 건 모두 232에서 도입됐다가 233에서 되돌려졌습니다(CHANGELOG v2.1.233: "Reverted the 2.1.232 Bash permission changes for Cygwin-style symlinks on Windows and for input redirections (`< file`); a narrower version will return in a later release"). **이 두 경로는 현행 233에서 권한 검사를 거치지 않으므로, 검사된다고 가정하고 경로 스코프 규칙을 설계하면 우회됩니다.**
|
|
89
|
+
> 3. **재도입 예정** — "a narrower version will return in a later release"이므로 좁힌 형태로 돌아옵니다. 재도입 시 적용 범위가 232 원본과 다를 수 있으므로 그때 다시 확인합니다.
|
|
90
|
+
>
|
|
91
|
+
> **`< file` 회고적 함의(버전 무관 유지)**: `< file`은 232 한 릴리즈를 제외하면 경로 스코프 규칙을 우회해 왔고 233에서 다시 그 상태이므로, **프롬프트 없이 읽힌 사실을 allow 규칙의 증거로 삼지 않습니다**. 위 v2.1.221/223 "검사 대상 문자열 ≠ 실행 문자열" 계열의 연장선입니다.
|
|
92
|
+
>
|
|
93
|
+
> **일반 교훈**: 단일 릴리즈의 플랫폼 권한 개선은 롤백될 수 있으므로 **항구적 보호막으로 간주하지 않습니다**. 스코프 규칙은 개선 이전 상태를 기준으로 설계하고 플랫폼 개선은 defense-in-depth로만 취급합니다(`feedback_platform_claim_staleness` 계열 — 플랫폼 주장의 시효성).
|
|
94
|
+
|
|
85
95
|
## Agent Tool Permission Mode
|
|
86
96
|
|
|
87
97
|
> Canonical source: R010 (MUST-orchestrator-coordination.md) "Universal bypassPermissions" owns the full requirement, rationale, self-check, and version history. Core rule: always pass `mode: "bypassPermissions"` explicitly on every Agent tool call — the Agent tool's default `mode` (`acceptEdits`) overrides agent frontmatter `permissionMode` and causes prompts during unattended execution. Skills that spawn agents MUST include this in their Agent tool call instructions. See R010 for details.
|
|
@@ -123,6 +123,8 @@ Cross-reference: R010 Subagent Scope-Creep STOP Protocol, R002 (permission tiers
|
|
|
123
123
|
> **v2.1.205+**: auto mode가 session transcript 파일 변조(tampering)를 차단하는 규칙이 추가되었습니다 — transcript 의존 스킬(homework/episodic-memory) 무결성 보호. 또한 Windows worktree 제거가 NTFS junction/symlink 존재 시 worktree 밖 파일을 삭제하던 문제가 수정되었습니다.
|
|
124
124
|
-->
|
|
125
125
|
|
|
126
|
+
> **v2.1.232+**: 시크릿·격리 보호가 확장되었습니다 — GitLab 토큰 계열(`glrt-`/`gloas-`/`glptt-`/`glagent-`/`glimt-`/`glsoat-`/`glcbt-`/`glft-`/`glffct-`) redaction 추가와 routable `glpat-`/`gldt-` 전체 redaction, `glab` CLI config가 `gh`와 동일한 샌드박스·자격증명 경로 보호를 받습니다. 또한 공유 `/tmp`의 cross-session messaging 소켓 디렉토리가 사전에 심어진 symlink나 타 사용자 소유 디렉토리를 **사용 대신 거부**하도록, Linux 파일시스템 샌드박스가 protected-path 우회에 대해 하드닝되었습니다. **구버전에서 GitLab 토큰은 redaction 대상이 아니었으므로 트랜스크립트·에이전트 출력에 원문 노출이 가능했습니다** — 과거 세션 로그를 공유하기 전 이 점을 전제합니다. 위 표의 "자격증명 저장소 덤프 금지"는 플랫폼 redaction과 무관하게 유지합니다(redaction은 최후 방어선이지 1차 방어선이 아님).
|
|
127
|
+
|
|
126
128
|
## Required Before Destructive Operations
|
|
127
129
|
|
|
128
130
|
Verify target, assess impact scope, check recoverability, get user approval.
|
|
@@ -166,6 +166,16 @@ Origin: #1492 (Session 132) — cc-release-monitor 워크플로우 삭제(#1454,
|
|
|
166
166
|
|
|
167
167
|
Origin: #1457 (Session 128 회고 찐빠 #1) — 오케스트레이터가 stale 메모리(npm 1.1.6→target v1.1.7 추정)로 implement를 위임 → v1.1.7이 이미 배포된 closed milestone임을 에이전트가 STOP으로 감지 → v1.1.8 재위임 왕복 1회. 기존 `feedback_session_memory_git_stale`(브랜치 분기 전 pull)의 릴리즈-버전-선정 각도 확장. Cross-ref: R020 (Diagnostic Hypothesis Verification — 영구 변경/위임 전 전제 실측 확정).
|
|
168
168
|
|
|
169
|
+
### 일반화 — 메모리 TODO 를 위임 전제로 쓸 때 (Origin: #1574)
|
|
170
|
+
|
|
171
|
+
위 게이트는 **버전**에 대한 규정이지만 원리는 세션 메모리 항목 전반에 적용된다. `MEMORY.md`의 "선재 항목 / Next Session TODO"는 **직전 세션 종료 시점의 스냅샷**이므로 이후 해소·변경됐을 수 있다. 이를 위임 브리핑(예: mgr-sauron 검증 스코프)의 전제로 넘기기 전, 각 항목을 실측으로 재확인하거나 **측정 시점을 함께 표기**해 전달한다.
|
|
172
|
+
|
|
173
|
+
| Anti-pattern | Required |
|
|
174
|
+
|--------------|----------|
|
|
175
|
+
| 메모리 TODO를 현재 상태로 간주해 위임 브리핑에 전제로 기재 | 위임 전 항목별 실측 재확인, 또는 "vX.Y.Z 시점 스냅샷 — 직접 확인하라"를 명시 |
|
|
176
|
+
|
|
177
|
+
Origin: #1574 (v1.1.44 세션 — mgr-sauron 브리핑의 "선재 항목" 4건 중 3건이 부정확: 이미 해소된 항목, 의도적 차이를 결함으로 오인, 규모 과대). **완화 요인**: 프롬프트에 "그대로 믿지 말고 직접 확인하라"를 명시해 3건 전부 에이전트가 정정 — #1443의 "실측값 기준으로 동기화하라" 방어선과 동일 효과. Cross-ref: R011(메모리 신뢰도·Temporal Decay), R020(Diagnostic Hypothesis Verification).
|
|
178
|
+
|
|
169
179
|
## Post-Gate Scope-Expansion Re-Run (Origin: #1433 #2)
|
|
170
180
|
|
|
171
181
|
R017 게이트(mgr-sauron) 통과 선언 후 신규 결함 발견 등으로 스코프가 확장되면(추가 파일 편집), 커밋 전 게이트를 **최종 상태에서 재실행**한다. 게이트 통과 시점 이후의 변경은 형식적으로 미검증이므로, 확장분 미검증 커밋은 R017이 최종 산출물을 커버하지 못하게 만든다.
|
|
@@ -482,4 +482,8 @@ References: #1226 (item 3), #1227.
|
|
|
482
482
|
- Memory write failure is **non-blocking**: MUST NOT prevent session from ending
|
|
483
483
|
- If sys-memory-keeper fails to write MEMORY.md: log warning, confirm to user anyway
|
|
484
484
|
|
|
485
|
-
> **v2.1.
|
|
485
|
+
> **v2.1.228+**: **session cleanup이 프로젝트 memory 폴더 내부 내용을 삭제하던 결함**이 수정되었습니다. 구버전에서는 MEMORY.md·archive 파일이 세션 정리 단계에서 소실될 수 있었으므로, 메모리 누락을 위 Failure Policy의 쓰기 실패로만 진단하지 않습니다 — 쓰기는 성공했으나 정리에 삭제된 경우일 수 있습니다. 위 Memory Scopes 표대로 `project` 스코프(`.claude/agent-memory/`)는 git tracked라 복구 가능하지만 `user`/`local` 스코프는 복구 수단이 없습니다.
|
|
486
|
+
|
|
487
|
+
> **v2.1.232+**: Cowork 세션이 **user-scope 메모리 파일의 외부 @-import를 인라인하지 않습니다**. 즉 `~/.claude/agent-memory/`의 MEMORY.md가 @-import로 외부 파일을 끌어오는 구조라면 세션 종류에 따라 그 내용이 컨텍스트에 없을 수 있으므로, 항상 로드되어야 하는 내용은 import 참조가 아니라 **MEMORY.md 본문**에 둡니다(위 200줄 예산 내 Hot/Warm 배치 원칙과 정합).
|
|
488
|
+
|
|
489
|
+
<!-- RETIRED (은퇴 릴리즈 v1.1.45, 보존 기준 v2.1.212 미만): > **v2.1.210+**: MEMORY.md 인덱스가 read limit을 초과하게 만드는 memory write는 이제 silent truncation 대신 명시적 오류를 반환합니다. write 실패는 여전히 non-blocking이지만, 오류 수신 시 log-warning으로 끝내지 말고 예산 초과 처리(Attention-Weight Tiering — Cold 항목 archive 이동)로 축소 후 재시도합니다 — 이전의 silent truncation을 가정하고 oversize write를 던지면 업데이트가 반영되지 않습니다. -->
|
|
@@ -15,6 +15,8 @@
|
|
|
15
15
|
| **3: Expensive LLM** | sonnet/opus skills (`deep-verify`, `adversarial-review`, `multi-model-verification`, `evaluator-optimizer`) | $$$ | 1-5분 | Pre-commit, PR review |
|
|
16
16
|
| **4: Human** | maintainer review | time | hours-days | Final gate, contested decisions |
|
|
17
17
|
|
|
18
|
+
> **v2.1.232+**: `/code-review`가 high·xhigh·max effort에서도 **background agent로 실행**됩니다(다른 레벨과 동일). 즉 Tier 3 검증 호출의 반환은 착수 신호이지 검증 결과가 아니므로, 다음 tier 진행이나 통과 판정 전에 background agent의 실제 완료와 결과를 실측합니다 — 구버전에서는 고effort 레벨만 전경 실행이라 "반환 = 결과"가 성립했고, 그 전제로 ladder를 진행하면 미완료 검증을 통과로 오판합니다(R020 "attempt ≠ outcome", cross-ref R010 non-teammate background 기본 실행).
|
|
19
|
+
|
|
18
20
|
## Shift-left 원칙
|
|
19
21
|
|
|
20
22
|
결정론적 단계가 잡을 수 있는 문제는 LLM에 보내지 않는다. LLM 검증은 ambiguous/semantic 문제에 집중한다.
|
|
@@ -103,6 +105,16 @@ staleness/audit 검증은 model ID·placeholder·TBD뿐 아니라 **폐기된
|
|
|
103
105
|
|
|
104
106
|
Origin: #1455 #1 (Session 127 회고 찐빠 #1) — cc-release-monitor PR #1449 머지 후 workflow_dispatch 실검증에서 issue_body의 `<details>`·릴리즈 요약에 12칸 리터럴 들여쓰기 발견 → PR #1451 재작업. 첫 위임이 문법 검증만 지시하고 샘플 값 출력 조립 검증을 누락. `textwrap.dedent` + 멀티라인 변수 함정이 문법 검증만으로는 미노출. R020(문법 통과 ≠ 출력 정상)과 정합.
|
|
105
107
|
|
|
108
|
+
## Delegated Verification Floor — CI 잡 목록에서 도출 (Origin: #1574)
|
|
109
|
+
|
|
110
|
+
위임 프롬프트의 검증 항목은 "변경 파일의 영향 범위"만으로 정하면 부족하다. **하한선은 CI가 실제로 돌리는 잡 전체**다 — 워크플로 YAML의 잡 목록을 읽어 대응하는 로컬 명령(`lint` / `test` / `validate-docs` / sync 검사)을 열거하고, 그중 로컬 실행 가능한 것을 위임 완료 조건에 포함한다. 로컬에서 통과시키지 않은 CI 잡은 병합 시점에 halt로 돌아와 수정 에이전트 추가 발주를 강제한다.
|
|
111
|
+
|
|
112
|
+
| Anti-pattern | Required |
|
|
113
|
+
|--------------|----------|
|
|
114
|
+
| "변경분 영향 범위"만 보고 검증 항목을 정해 위임 → CI 전용 잡(lint 등) 누락 | 워크플로 잡 목록을 하한선으로 삼아 로컬 대응 명령을 완료 조건에 열거 |
|
|
115
|
+
|
|
116
|
+
Origin: #1574 (v1.1.44 세션 — 병렬 위임 3건 모두 `bun run lint`를 누락해 verify-build halt, 수정 에이전트 1회 추가 발주). 기존 `feedback_delegation_verify_scope_by_impact`("영향 범위 기준")의 하한선을 명문화한 것이다. Cross-reference: R020(완료 검증 — 선언 전 실제 게이트 통과 확인), R017(커밋 전 검증 게이트).
|
|
117
|
+
|
|
106
118
|
## Conditional-Output Verification — Positive/Negative Pair Mandate (Origin: #1563 #2)
|
|
107
119
|
|
|
108
120
|
조건부로만 출력하는 대상(advisory 훅, 가드, 경고 emitter)의 동작을 검증하도록 위임할 때, 완료 기준은 **"출력이 나와야 하는 입력"과 "나오면 안 되는 입력"을 짝으로** 지정해야 한다. "stdout ≠ 0바이트" 같은 단일 프록시는 검증이 아니다 — 침묵이 정답인 입력에서도 통과를 요구하게 되어 기준 자체가 틀리고, 반대로 오탐(준수 턴에서 발화)을 통과시킨다.
|
|
@@ -111,7 +123,7 @@ Origin: #1455 #1 (Session 127 회고 찐빠 #1) — cc-release-monitor PR #1449
|
|
|
111
123
|
|--------------|----------|
|
|
112
124
|
| "지정 입력에서 stdout ≠ 0바이트"를 단일 완료 기준으로 위임 | 양성 케이스(발화해야 함)와 음성 케이스(침묵해야 함)를 짝으로 명시 |
|
|
113
125
|
|
|
114
|
-
Origin: #1563 찐빠 #2 — R007/R008 advisor 발화 검증에 단일 "0바이트 아님" 프록시를 제시했으나, advisor는 준수 턴에서 침묵하는 것이 정상 동작이라 기준이 성립하지 않았다. Cross-reference: R020(Proxy Signal vs Canonical Ground-Truth — 프록시로 상태를 특성화하지 말 것),
|
|
126
|
+
Origin: #1563 찐빠 #2 — R007/R008 advisor 발화 검증에 단일 "0바이트 아님" 프록시를 제시했으나, advisor는 준수 턴에서 침묵하는 것이 정상 동작이라 기준이 성립하지 않았다. Cross-reference: R020(Proxy Signal vs Canonical Ground-Truth — 프록시로 상태를 특성화하지 말 것), 아래 Detection Guard Delegation Standard(positive-match vs negative-context 구분의 가드 설계 각도).
|
|
115
127
|
|
|
116
128
|
## Detection Guard Delegation Standard (Origin: #1438 #3)
|
|
117
129
|
|
|
@@ -116,12 +116,30 @@ steps:
|
|
|
116
116
|
4. Cap at 7 issues; if priority issues < 7, stop at that priority (don't mix tiers)
|
|
117
117
|
5. Minimum 1 issue; if 0 eligible, halt with "no eligible issues for auto-dev run"
|
|
118
118
|
|
|
119
|
+
## Step 3 — Approval-required path pre-check (R010)
|
|
120
|
+
|
|
121
|
+
For each scoped issue, extract target file/directory paths from its title+body (explicit
|
|
122
|
+
paths, backtick-quoted paths, or clearly named targets).
|
|
123
|
+
|
|
124
|
+
Check against `.claude/rules/MUST-orchestrator-coordination.md` "Protected Paths":
|
|
125
|
+
- `.claude/hooks/**` → EXCLUDED from mgr-creator routing; requires EXPLICIT USER APPROVAL
|
|
126
|
+
(security-critical). If any scoped issue targets this path, request approval for the
|
|
127
|
+
FULL scoped set NOW, before proceeding — do not let a later step (e.g. compression-mode-
|
|
128
|
+
eval) discover it mid-run after scope is already committed (#1574 찐빠 #5).
|
|
129
|
+
- `.claude/agents/*.md`, `.claude/skills/*/SKILL.md`, `guides/*/` (new dirs) → route to
|
|
130
|
+
mgr-creator at implement time (delegation requirement, not a HALT).
|
|
131
|
+
|
|
132
|
+
If approval was already granted THIS session for the same category+target, do not
|
|
133
|
+
re-request (R015 directive persistence).
|
|
134
|
+
|
|
135
|
+
Output approval_required_paths (list, empty if none) as pipeline state.
|
|
136
|
+
|
|
119
137
|
Assign scoped issues to milestone.
|
|
120
138
|
Output markdown release manifest:
|
|
121
139
|
| order | # | title | prerequisite | effort | labels |
|
|
122
140
|
|
|
123
141
|
Persist manifest as pipeline state for subsequent steps.
|
|
124
|
-
description: "Milestone state pre-check, label filter, pick 3-7 issues as v{X.Y.Z} scope"
|
|
142
|
+
description: "Milestone state pre-check, label filter, pick 3-7 issues as v{X.Y.Z} scope, R010 approval-required path pre-check"
|
|
125
143
|
depends_on: pre-triage
|
|
126
144
|
|
|
127
145
|
- name: compression-mode-eval
|
package/templates/manifest.json
CHANGED
|
@@ -116,12 +116,30 @@ steps:
|
|
|
116
116
|
4. Cap at 7 issues; if priority issues < 7, stop at that priority (don't mix tiers)
|
|
117
117
|
5. Minimum 1 issue; if 0 eligible, halt with "no eligible issues for auto-dev run"
|
|
118
118
|
|
|
119
|
+
## Step 3 — Approval-required path pre-check (R010)
|
|
120
|
+
|
|
121
|
+
For each scoped issue, extract target file/directory paths from its title+body (explicit
|
|
122
|
+
paths, backtick-quoted paths, or clearly named targets).
|
|
123
|
+
|
|
124
|
+
Check against `.claude/rules/MUST-orchestrator-coordination.md` "Protected Paths":
|
|
125
|
+
- `.claude/hooks/**` → EXCLUDED from mgr-creator routing; requires EXPLICIT USER APPROVAL
|
|
126
|
+
(security-critical). If any scoped issue targets this path, request approval for the
|
|
127
|
+
FULL scoped set NOW, before proceeding — do not let a later step (e.g. compression-mode-
|
|
128
|
+
eval) discover it mid-run after scope is already committed (#1574 찐빠 #5).
|
|
129
|
+
- `.claude/agents/*.md`, `.claude/skills/*/SKILL.md`, `guides/*/` (new dirs) → route to
|
|
130
|
+
mgr-creator at implement time (delegation requirement, not a HALT).
|
|
131
|
+
|
|
132
|
+
If approval was already granted THIS session for the same category+target, do not
|
|
133
|
+
re-request (R015 directive persistence).
|
|
134
|
+
|
|
135
|
+
Output approval_required_paths (list, empty if none) as pipeline state.
|
|
136
|
+
|
|
119
137
|
Assign scoped issues to milestone.
|
|
120
138
|
Output markdown release manifest:
|
|
121
139
|
| order | # | title | prerequisite | effort | labels |
|
|
122
140
|
|
|
123
141
|
Persist manifest as pipeline state for subsequent steps.
|
|
124
|
-
description: "Milestone state pre-check, label filter, pick 3-7 issues as v{X.Y.Z} scope"
|
|
142
|
+
description: "Milestone state pre-check, label filter, pick 3-7 issues as v{X.Y.Z} scope, R010 approval-required path pre-check"
|
|
125
143
|
depends_on: pre-triage
|
|
126
144
|
|
|
127
145
|
- name: compression-mode-eval
|