oh-my-customcode 1.1.47 → 1.1.49

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.
Files changed (38) hide show
  1. package/README.md +7 -6
  2. package/dist/cli/index.js +1 -1
  3. package/dist/index.js +1 -1
  4. package/package.json +1 -1
  5. package/templates/.claude/agents/agora-runner.md +114 -0
  6. package/templates/.claude/hooks/scripts/agent-teams-advisor.sh +6 -1
  7. package/templates/.claude/hooks/scripts/r007-r008-drift-advisor.sh +41 -3
  8. package/templates/.claude/hooks/scripts/session-env-check.sh +29 -3
  9. package/templates/.claude/rules/MAY-optimization.md +6 -0
  10. package/templates/.claude/rules/MUST-agent-design.md +2 -0
  11. package/templates/.claude/rules/MUST-completion-verification.md +33 -2
  12. package/templates/.claude/rules/MUST-enforcement-policy.md +3 -3
  13. package/templates/.claude/rules/MUST-intent-transparency.md +7 -2
  14. package/templates/.claude/rules/MUST-orchestrator-coordination.md +50 -5
  15. package/templates/.claude/rules/MUST-parallel-execution.md +56 -7
  16. package/templates/.claude/rules/MUST-sync-verification.md +21 -2
  17. package/templates/.claude/rules/MUST-tool-identification.md +22 -4
  18. package/templates/.claude/skills/agora/SKILL.md +325 -0
  19. package/templates/.claude/skills/agora/scripts/agora.sh +761 -0
  20. package/templates/.claude/skills/agora/scripts/anonymize.sh +492 -0
  21. package/templates/.claude/skills/agora/scripts/judge.sh +427 -0
  22. package/templates/.claude/skills/agora/scripts/response-schema.json +26 -0
  23. package/templates/.claude/skills/agora/scripts/reviewers.sh +312 -0
  24. package/templates/.claude/skills/agora/scripts/verdict-schema.json +34 -0
  25. package/templates/.claude/skills/hada-scout/SKILL.md +1 -1
  26. package/templates/.claude/skills/help/SKILL.md +1 -1
  27. package/templates/.claude/skills/pipeline/workflows/auto-dev.yaml +47 -6
  28. package/templates/.claude/skills/sauron-watch/SKILL.md +1 -1
  29. package/templates/.claude/skills/status/SKILL.md +3 -3
  30. package/templates/.claude/skills/token-efficiency-audit/SKILL.md +1 -1
  31. package/templates/.github/workflows/wiki-sync.yml +1 -1
  32. package/templates/CLAUDE.md +3 -3
  33. package/templates/CLAUDE.md.en +3 -3
  34. package/templates/CLAUDE.md.ko +3 -3
  35. package/templates/README.md +5 -5
  36. package/templates/guides/agent-eval/README.md +1 -1
  37. package/templates/manifest.json +3 -3
  38. package/templates/workflows/auto-dev.yaml +47 -6
@@ -0,0 +1,312 @@
1
+ #!/usr/bin/env bash
2
+ # reviewers.sh — parallel 3-vendor adapter.
3
+ # Spec: docs/superpowers/plans/2026-08-15-agora-anonymous-consensus-design.md §3 §11
4
+ set -uo pipefail
5
+
6
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
7
+ SCHEMA_PATH="$SCRIPT_DIR/response-schema.json"
8
+
9
+ # Ruling 12 (controller): keep the bare binary names as defaults — `claude`
10
+ # and `agy` are the user's shell ALIASES, not distinct binaries, and the
11
+ # alias-appended flags below are added explicitly in invoke_vendor() because
12
+ # aliases do not expand under non-interactive `bash script.sh` execution.
13
+ # omx has no alias (spec §2 measured: /opt/homebrew/bin/omx), so its default
14
+ # stays the absolute path.
15
+ AGORA_CLAUDE_BIN="${AGORA_CLAUDE_BIN:-claude}"
16
+ AGORA_OMX_BIN="${AGORA_OMX_BIN:-/opt/homebrew/bin/omx}"
17
+ AGORA_AGY_BIN="${AGORA_AGY_BIN:-agy}"
18
+ AGORA_TIMEOUT_SECS="${AGORA_TIMEOUT_SECS:-300}"
19
+
20
+ # ---------------------------------------------------------------------------
21
+ # _child_env_strip — the `env -u NAME` list that removes every AGORA_*
22
+ # variable from a vendor CLI's environment.
23
+ #
24
+ # The anonymity of this process rests on the vendors and the judge never
25
+ # reaching the sealed session material. Two guards already covered that:
26
+ # agora.sh exports nothing of its own, and no session path is ever passed in
27
+ # argv. Neither covers what the OPERATOR's shell exported before agora.sh
28
+ # ever ran. Measured: with AGORA_OUTPUT_ROOT set in the invoking shell, that
29
+ # value is inherited straight through agora.sh into every vendor CLI spawned
30
+ # here, and the whole session tree — sealed round records included — sits one
31
+ # glob below it. "We do not pass it" is no defence against a value that is
32
+ # already in the environment.
33
+ #
34
+ # Prefix rule rather than a hardcoded name list, deliberately: a name list
35
+ # leaks silently the day someone adds AGORA_SOMETHING_NEW, whereas the prefix
36
+ # fails CLOSED — a new variable is stripped unless someone argues it back in.
37
+ # Nothing outside the prefix is touched, so PATH, HOME, the vendors' own auth
38
+ # tokens and proxy settings all survive; `env -i` would leave the CLIs unable
39
+ # to authenticate at all.
40
+ #
41
+ # Read time and pass time are separate: the config reads above have already
42
+ # happened, so this script goes on using AGORA_CLAUDE_BIN, AGORA_TIMEOUT_SECS
43
+ # and friends as ordinary shell variables. Only the CHILD's copy is removed,
44
+ # which is why test-injected configuration still works.
45
+ #
46
+ # Built from the exported names actually present (compgen -e), so a prefixed
47
+ # variable this script has never heard of is covered too.
48
+ # ---------------------------------------------------------------------------
49
+ _child_env_strip=()
50
+ while IFS= read -r _agora_env_name; do
51
+ [ -n "$_agora_env_name" ] || continue
52
+ _child_env_strip+=(-u "$_agora_env_name")
53
+ done < <(compgen -e 2>/dev/null | grep '^AGORA_' || true)
54
+ unset _agora_env_name
55
+
56
+ # ---------------------------------------------------------------------------
57
+ # run_with_timeout <seconds> <cmd>... — R005: macOS ships no GNU timeout.
58
+ # Returns 124 on timeout, otherwise the command's own exit code.
59
+ #
60
+ # AGORA_FORCE_TIMEOUT_FALLBACK=1 is a TEST-ONLY switch (default off, never
61
+ # set in production) that forces the wait-based fallback branch even when
62
+ # gtimeout is installed locally, so both the gtimeout path and the fallback
63
+ # path can be exercised deterministically regardless of what happens to be
64
+ # on the machine running the tests (dev boxes have coreutils; GitHub-hosted
65
+ # macos CI runners may not).
66
+ #
67
+ # F1 (review round 2, controller-measured): the fallback watchdog kills the
68
+ # WHOLE PROCESS GROUP of the backgrounded command, not just its direct PID.
69
+ # `kill -TERM "$pid"` alone only reaches the direct child — a vendor CLI
70
+ # that forks a grandchild (common in Node-wrapped CLIs) orphans it, and the
71
+ # orphan keeps running (and, against a real vendor, keeps making API calls)
72
+ # after this function has already reported the vendor as timed out/missing.
73
+ # Measured: `kill -TERM "$pid"` left 2/3 descendants of a forking stub
74
+ # alive; `kill -TERM -- -"$pid"` (negative PID = process group) left 0.
75
+ # `gtimeout`'s own default (non `--foreground`) mode already targets the
76
+ # whole group, so only this fallback branch needed the fix.
77
+ #
78
+ # Getting a process group AT ALL requires job control (`set -m`) to be on —
79
+ # without it, `"$@" &` inherits this shell's own process group, and a
80
+ # negative-PID kill would hit this script's group, not just the timed-out
81
+ # command's. macOS ships no `setsid`, so `set -m` is the only portable way
82
+ # to get the backgrounded command its own group here. Scoped strictly to
83
+ # this function: only turned on if not already on, and turned back off
84
+ # before returning, so the rest of reviewers.sh (and any caller) is
85
+ # unaffected by the job-control side effects (e.g. "Terminated" job
86
+ # notifications on stray fds).
87
+ # ---------------------------------------------------------------------------
88
+ run_with_timeout() {
89
+ local secs="$1"; shift
90
+ if [ "${AGORA_FORCE_TIMEOUT_FALLBACK:-0}" != "1" ] && command -v gtimeout >/dev/null 2>&1; then
91
+ gtimeout "$secs" "$@"
92
+ return $?
93
+ fi
94
+
95
+ local restore_job_control=0
96
+ case $- in
97
+ *m*) ;;
98
+ *) set -m; restore_job_control=1 ;;
99
+ esac
100
+
101
+ "$@" &
102
+ local pid=$!
103
+ # Negative PID targets the whole process group (job-control-assigned pgid
104
+ # == the group leader's pid), so grandchildren the command forks are
105
+ # reaped too, not just the direct child.
106
+ ( sleep "$secs"; kill -TERM -- "-$pid" 2>/dev/null ) &
107
+ local watcher=$!
108
+
109
+ local rc=0
110
+ # 2>/dev/null: suppresses this shell's own job-control "Terminated"
111
+ # notification, which (only under `set -m`) prints as a side effect of
112
+ # `wait` reaping a signal-killed background job — noise, not a diagnostic
113
+ # this script itself emits.
114
+ wait "$pid" 2>/dev/null || rc=$?
115
+ # F1 (review round 3, measured): the watchdog must be torn down by PROCESS
116
+ # GROUP for exactly the reason the command itself is — `kill -TERM
117
+ # "$watcher"` reaches only the subshell, and its `sleep` child is orphaned
118
+ # and re-parented to init, where it lingers for the full AGORA_TIMEOUT_SECS
119
+ # (default 300). This is the SUCCESS path: when the command finishes first
120
+ # the watchdog's sleep is still running, so every completed vendor call
121
+ # leaked one. Measured before the fix: 1 orphaned `sleep` per call — up to
122
+ # 6 per round (3 reviewers + 3 judge attempts). On timeout nothing leaked,
123
+ # because the sleep had already elapsed by definition, which is why the
124
+ # existing timeout-path tests never saw it.
125
+ # Safe under `set -m` (guaranteed on in this branch by the block above):
126
+ # measured pgid(watcher) == pid(watcher) != pgid(this script), so the
127
+ # negative-PID kill cannot reach this script's own group. The pid also
128
+ # cannot have been recycled onto an unrelated group, because $watcher is
129
+ # still an unreaped child here — its zombie holds both the pid and the
130
+ # group until the `wait` below.
131
+ kill -TERM -- "-$watcher" 2>/dev/null || true
132
+ wait "$watcher" 2>/dev/null || true
133
+
134
+ [ "$restore_job_control" -eq 1 ] && set +m
135
+
136
+ # 143 = SIGTERM from the watchdog; normalize to the GNU timeout convention.
137
+ [ "$rc" -eq 143 ] && rc=124
138
+ return "$rc"
139
+ }
140
+
141
+ # ---------------------------------------------------------------------------
142
+ # run_sanitized <seconds> <cmd>... — run_with_timeout with the AGORA_*
143
+ # variables removed from the command's environment (see _child_env_strip).
144
+ # Wrapping the command in `env` rather than unsetting the variables in this
145
+ # shell is what keeps the read/pass split above intact. `env` execs the
146
+ # command in place, so the pid and process group that run_with_timeout's
147
+ # watchdog targets are the command's own, exactly as before.
148
+ # ---------------------------------------------------------------------------
149
+ run_sanitized() {
150
+ local secs="$1"; shift
151
+ run_with_timeout "$secs" env ${_child_env_strip[@]+"${_child_env_strip[@]}"} "$@"
152
+ }
153
+
154
+ # ---------------------------------------------------------------------------
155
+ # invoke_vendor <slug> <prompt_file> — dispatches to the vendor-specific CLI
156
+ # argument shape (spec §2 REQ-1) and applies run_sanitized uniformly, so
157
+ # every vendor is launched under both the timeout and the env strip.
158
+ # ---------------------------------------------------------------------------
159
+ invoke_vendor() {
160
+ local slug="$1" prompt_file="$2"
161
+ local prompt; prompt=$(cat "$prompt_file")
162
+ case "$slug" in
163
+ claude)
164
+ # --enable-auto-mode: the user's shell `claude` alias appends this
165
+ # flag (Ruling 12 measured); aliases do not expand under
166
+ # non-interactive `bash script.sh` execution, so it must be passed
167
+ # explicitly here or the CLI runs without it.
168
+ run_sanitized "$AGORA_TIMEOUT_SECS" "$AGORA_CLAUDE_BIN" \
169
+ -p --model claude-opus-4-8 --enable-auto-mode "$prompt"
170
+ ;;
171
+ omx)
172
+ # omx is a plain binary, not a shell alias (spec §2 measured) — no
173
+ # extra flag is needed.
174
+ run_sanitized "$AGORA_TIMEOUT_SECS" "$AGORA_OMX_BIN" exec "$prompt"
175
+ ;;
176
+ agy)
177
+ # --dangerously-skip-permissions: the user's shell `agy` alias appends
178
+ # this flag (Ruling 12 measured); aliases do not expand under
179
+ # non-interactive `bash script.sh` execution, so it must be passed
180
+ # explicitly here or the CLI may block on a permission prompt.
181
+ run_sanitized "$AGORA_TIMEOUT_SECS" "$AGORA_AGY_BIN" \
182
+ -p --model gemini-3.1-pro-high --output-format json \
183
+ --json-schema "$SCHEMA_PATH" --dangerously-skip-permissions "$prompt"
184
+ ;;
185
+ *) return 65 ;;
186
+ esac
187
+ }
188
+
189
+ # ---------------------------------------------------------------------------
190
+ # _emit_vendor_stderr_tail <slug> <err_file> — F2 (review round 2): print the
191
+ # last 20 lines of a vendor's captured stderr as part of THIS script's own
192
+ # diagnostic output, so a failure's actual cause (auth, rate-limit, model
193
+ # deprecation, network) is debuggable instead of silently discarded. A
194
+ # vendor's session/hook logs can be long (omx in particular), so only the
195
+ # tail is surfaced, not the whole file.
196
+ # ---------------------------------------------------------------------------
197
+ _emit_vendor_stderr_tail() {
198
+ local slug="$1" err_file="$2"
199
+ if [ -s "$err_file" ]; then
200
+ printf '[agora] %s stderr (last 20 lines):\n' "$slug" >&2
201
+ tail -n 20 "$err_file" >&2
202
+ fi
203
+ }
204
+
205
+ # ---------------------------------------------------------------------------
206
+ # call_vendor <slug> <prompt_file> <out_file> — one retry, then missing
207
+ # (spec §11). Writes <out_file> ONLY on a verified-parseable success; a
208
+ # missing vendor leaves no file at all (silent-failure avoidance: callers
209
+ # distinguish "responded" from "missing" by file existence, never by an
210
+ # empty/partial file).
211
+ #
212
+ # F2 (review round 2): each attempt's vendor stderr is captured to
213
+ # <out_dir>/<slug>.attempt-<N>.stderr.log — inside SEALED/raw/round-N/, the
214
+ # same trust boundary as the raw response itself (never under anon/). On
215
+ # success the log is removed (no noise from chatty vendors); on failure it
216
+ # is left on disk for audit AND its tail is folded into this script's own
217
+ # diagnostic stderr via _emit_vendor_stderr_tail.
218
+ # ---------------------------------------------------------------------------
219
+ call_vendor() {
220
+ local slug="$1" prompt_file="$2" out_file="$3"
221
+ local attempt rc tmp err_file out_dir_path
222
+ tmp=$(mktemp)
223
+ out_dir_path="$(dirname "$out_file")"
224
+
225
+ for attempt in 1 2; do
226
+ rc=0
227
+ err_file="$out_dir_path/$slug.attempt-$attempt.stderr.log"
228
+ invoke_vendor "$slug" "$prompt_file" > "$tmp" 2> "$err_file" || rc=$?
229
+
230
+ if [ "$rc" -eq 124 ]; then
231
+ printf '[agora] %s timeout on attempt %s\n' "$slug" "$attempt" >&2
232
+ _emit_vendor_stderr_tail "$slug" "$err_file"
233
+ elif [ "$rc" -ne 0 ]; then
234
+ printf '[agora] %s exited %s on attempt %s\n' "$slug" "$rc" "$attempt" >&2
235
+ _emit_vendor_stderr_tail "$slug" "$err_file"
236
+ elif ! jq -e . "$tmp" >/dev/null 2>&1; then
237
+ printf '[agora] %s returned unparsable output on attempt %s\n' "$slug" "$attempt" >&2
238
+ _emit_vendor_stderr_tail "$slug" "$err_file"
239
+ rc=65
240
+ else
241
+ # Success is written to the final path ONLY after the response has
242
+ # been confirmed valid JSON — never before the check is settled.
243
+ rm -f "$err_file"
244
+ mv "$tmp" "$out_file"
245
+ return 0
246
+ fi
247
+
248
+ [ "$attempt" -eq 1 ] && printf '[agora] %s retry\n' "$slug" >&2
249
+ done
250
+
251
+ rm -f "$tmp"
252
+ printf '[agora] %s missing after retry\n' "$slug" >&2
253
+ return 1
254
+ }
255
+
256
+ # ---------------------------------------------------------------------------
257
+ # run_reviewers --session-dir <dir> --round <N> --prompt-file <file>
258
+ # Fans out call_vendor to all three vendors in parallel, then aborts the
259
+ # round (exit 3) once two or more are missing (spec §11: a single opinion is
260
+ # not a consensus process).
261
+ # ---------------------------------------------------------------------------
262
+ run_reviewers() {
263
+ local dir='' round='' prompt_file=''
264
+ while [ "$#" -gt 0 ]; do
265
+ case "$1" in
266
+ --session-dir) dir="$2"; shift 2 ;;
267
+ --round) round="$2"; shift 2 ;;
268
+ --prompt-file) prompt_file="$2"; shift 2 ;;
269
+ *) printf 'reviewers.sh: unknown option %s\n' "$1" >&2; return 64 ;;
270
+ esac
271
+ done
272
+ [ -n "$dir" ] && [ -n "$round" ] && [ -n "$prompt_file" ] || {
273
+ printf 'reviewers.sh: --session-dir, --round and --prompt-file are required\n' >&2
274
+ return 64
275
+ }
276
+
277
+ local out_dir="$dir/SEALED/raw/round-$round"
278
+ mkdir -p "$out_dir"
279
+
280
+ local pids=() slugs=(claude omx agy) slug
281
+ for slug in "${slugs[@]}"; do
282
+ call_vendor "$slug" "$prompt_file" "$out_dir/$slug.json" &
283
+ pids+=("$!")
284
+ done
285
+
286
+ local i missing=0
287
+ for i in "${!pids[@]}"; do
288
+ wait "${pids[$i]}" || missing=$(( missing + 1 ))
289
+ done
290
+
291
+ if [ "$missing" -ge 2 ]; then
292
+ printf '[agora] 2 or more reviewers missing (%s) — aborting round %s\n' "$missing" "$round" >&2
293
+ return 3
294
+ fi
295
+ printf '[agora] round %s reviewers: %s responded\n' "$round" "$(( 3 - missing ))" >&2
296
+ return 0
297
+ }
298
+
299
+ main() {
300
+ case "${1:---help}" in
301
+ --run) shift; run_reviewers "$@" ;;
302
+ --help | -h)
303
+ cat <<'USAGE'
304
+ Usage:
305
+ reviewers.sh --run --session-dir <dir> --round <N> --prompt-file <file>
306
+ USAGE
307
+ ;;
308
+ *) printf 'reviewers.sh: unknown option %s\n' "$1" >&2; return 64 ;;
309
+ esac
310
+ }
311
+
312
+ main "$@"
@@ -0,0 +1,34 @@
1
+ {
2
+ "type": "object",
3
+ "required": ["round", "judge", "consensus", "verdict", "resolved", "unresolved", "agenda", "draft", "new_findings", "notes"],
4
+ "properties": {
5
+ "round": { "type": "number" },
6
+ "judge": { "type": "string" },
7
+ "consensus": { "type": "string", "enum": ["UNANIMOUS", "MAJORITY", "SPLIT", "NONE"] },
8
+ "verdict": { "type": "string", "enum": ["BUILD", "BUILD_WITH_CHANGES", "REDESIGN", "ABANDON"] },
9
+ "resolved": {
10
+ "type": "array",
11
+ "items": {
12
+ "type": "object",
13
+ "required": ["id", "resolution"],
14
+ "properties": { "id": { "type": "string" }, "resolution": { "type": "string" } }
15
+ }
16
+ },
17
+ "unresolved": {
18
+ "type": "array",
19
+ "items": {
20
+ "type": "object",
21
+ "required": ["id", "severity", "positions"],
22
+ "properties": {
23
+ "id": { "type": "string" },
24
+ "severity": { "type": "string", "enum": ["CRITICAL", "HIGH", "MEDIUM", "LOW"] },
25
+ "positions": { "type": "string" }
26
+ }
27
+ }
28
+ },
29
+ "agenda": { "type": "array", "items": { "type": "string" } },
30
+ "draft": { "type": "string" },
31
+ "new_findings": { "type": "number" },
32
+ "notes": { "type": "string" }
33
+ }
34
+ }
@@ -50,7 +50,7 @@ The haiku agent receives the following system prompt:
50
50
 
51
51
  ```
52
52
  You are a relevance filter for the oh-my-customcode project — an AI agent harness/orchestration
53
- system built on Claude Code CLI with 49 agents, 114 skills.
53
+ system built on Claude Code CLI with 50 agents, 115 skills.
54
54
 
55
55
  Project domains (HIGH relevance):
56
56
  - AI agent orchestration, multi-agent systems, agent design patterns
@@ -95,7 +95,7 @@ Infra Engineer:
95
95
  infra-docker-expert Docker containerization
96
96
  infra-aws-expert AWS architecture
97
97
 
98
- Total: 49 agents
98
+ Total: 50 agents
99
99
  ```
100
100
 
101
101
  ### Rules List
@@ -5,6 +5,14 @@ version: "2.1.0"
5
5
  # Design rule: each pipeline run produces ONE bounded release unit (3-7 issues), not all open issues.
6
6
  # This pipeline is a project-agnostic template. Project-specific overrides belong in
7
7
  # the project's own workflows/auto-dev.yaml (e.g., AgentNav, second-brain).
8
+ #
9
+ # Design rule (R020 「위임 경계를 Phase 개수로 설계」 배선, Origin: #1595 #4):
10
+ # 아래 `skill:` 스텝 중 다중 Phase 스킬(deep-plan = research→plan→verify, deep-verify = 다각 검증)은
11
+ # 스킬을 그대로 spawn하지 않는다. Phase 경계가 곧 종료 유혹 지점이므로, 발주 전에 스킬의 Phase를
12
+ # 읽고 **단일 목표 1개짜리 위임**으로 분할해 순차 발주한다. 실측(v1.1.48): `skill: deep-plan`을
13
+ # 그대로 호출한 결과 tool_uses=0 / 6.9초 / 산출물 0으로 종료했고, 단일 목표 Plan 에이전트로
14
+ # 재설계하니 1/1 완주했다. `skill:` 값은 **분할의 근거가 되는 스킬 정의**를 가리키는 것이지
15
+ # "이 스킬을 1회 호출하라"는 뜻이 아니다.
8
16
 
9
17
  steps:
10
18
  - name: pre-triage
@@ -270,7 +278,7 @@ steps:
270
278
 
271
279
  - name: deep-plan
272
280
  skill: deep-plan
273
- description: "Research-validated implementation plan (research → plan → verify) — skipped if docs-only, integrated-analysis allowed if lite"
281
+ description: "Research-validated implementation plan (research → plan → verify) — skipped if docs-only, integrated-analysis allowed if lite. MULTI-PHASE: 스킬을 그대로 spawn하지 말고 R020 「위임 경계를 Phase 개수로 설계」에 따라 단일 목표 위임으로 분할해 순차 발주한다 (#1595 #4)."
274
282
  depends_on: plan
275
283
 
276
284
  - name: implement
@@ -357,7 +365,7 @@ steps:
357
365
 
358
366
  - name: deep-verify
359
367
  skill: deep-verify
360
- description: "Multi-angle release quality verification — self-review checklist if docs-only; mgr-sauron R017 + core self-check if lite"
368
+ description: "Multi-angle release quality verification — self-review checklist if docs-only; mgr-sauron R017 + core self-check if lite. MULTI-PHASE: 스킬을 그대로 spawn하지 말고 R020 「위임 경계를 Phase 개수로 설계」에 따라 단일 목표 위임으로 분할해 순차 발주한다 (#1595 #4)."
361
369
  depends_on: verify-build
362
370
 
363
371
  - name: release
@@ -398,8 +406,18 @@ steps:
398
406
  # Edit ONLY the .version field — do NOT overwrite templates/manifest.json wholesale (e.g. a source-hash path→hash map).
399
407
  # Recover a corrupted manifest: git show HEAD:templates/manifest.json | jq '.version="<NEW>"' > templates/manifest.json (#1423/#1154).
400
408
  d. templates/manifest.json: jq '.version = "<NEW>"' templates/manifest.json > templates/manifest.json.tmp && mv templates/manifest.json.tmp templates/manifest.json [on release/v{NEW}]
401
- e. bun run build — run standalone (NO pipe), read exit code directly ($? after a pipe is the last command's, R005/#1492).
402
- This regenerates .omcustom.lock.json (generatorVersion/templateVersion) to <NEW> via scripts/sync-source-lockfile.ts. [on release/v{NEW}]
409
+ e. ⚠ PREREQUISITE: steps c (package.json) AND d (templates/manifest.json) MUST BOTH be completed
410
+ before running this step. bun run build — run standalone (NO pipe), read exit code directly
411
+ ($? after a pipe is the last command's, R005/#1492).
412
+ Actual mechanism (corrected, #1593): `bun run build` invokes `scripts/sync-source-lockfile.ts`
413
+ (a 10-line wrapper with no version literals of its own), which calls
414
+ `generateAndWriteLockfileForDir` (`src/core/lockfile.ts`). That function reads versions via
415
+ `loadVersions()` (`src/core/sync.ts`) — `generatorVersion` FROM package.json, `templateVersion`
416
+ FROM templates/manifest.json — and writes both into .omcustom.lock.json. If step d has not yet
417
+ landed when this runs, the lockfile silently records the PREVIOUS templateVersion — no warning,
418
+ no error. Observed contamination: v1.1.47 shipped generatorVersion=1.1.47 / templateVersion=1.1.46
419
+ because the bump commit created package.json + lockfile together and manifest.json was bumped in
420
+ a later commit. Do NOT run this step until BOTH c and d are staged. [on release/v{NEW}]
403
421
  f. git status --short — enumerate ALL tracked drift (`^ M`) the build produced; stage EVERY one (esp. .omcustom.lock.json) before commit.
404
422
  (#1531 — .omcustom.lock.json missed staging across 4 consecutive releases after v1.1.29; root cause was no build step between version bump and commit.) [on release/v{NEW}]
405
423
  g. mgr-gitnerd commit: "chore(release): bump to v<NEW>" — MUST include the drift from step f.
@@ -411,12 +429,27 @@ steps:
411
429
  i. mandatory verification (with existence guard for partial-update safety), run on release/v{NEW}:
412
430
  [ -f scripts/verify-version-sync.sh ] && bash scripts/verify-version-sync.sh || echo "::warning::verify-version-sync.sh not found, version sync verification skipped"
413
431
  (verify-version-sync.sh 가 exit 1 시 release 단계 halt)
432
+ j. lockfile 3-way assertion (#1593 제안3, closes the gap step i's 2-way check misses) — run standalone
433
+ (NO pipe), read exit code directly, on release/v{NEW}:
434
+ jq -e --arg v "<NEW>" '.generatorVersion==$v and .templateVersion==$v' .omcustom.lock.json
435
+ 실패 시 release 단계 halt — cause is almost always step e run before step d landed; fix by
436
+ re-running step d then step e, re-staging, and re-running this assertion.
414
437
 
415
438
  Version decision (semver) — PATCH-PREFERRED policy (post-1.0.0):
416
439
  - No existing tags → v0.1.0
417
440
  - Previous tag exists → DEFAULT to patch. When in doubt, patch — do NOT reflexively bump minor for rule/doc/skill edits.
418
441
  - patch (DEFAULT): rule/doc/skill/config/wiki/workflow changes, bugfixes, hardening, CC-compat notes — the vast majority of this project's changes
419
- - minor: ONLY a genuinely NEW user-facing capability (a new skill/agent/command adding user-visible surface), not a refinement of an existing one
442
+ - minor: ONLY a new user-facing capability that changes HOW the harness is used — a new
443
+ workflow axis, a new command surface users must learn, or a contract other components
444
+ depend on. NOT "a file was added under .claude/skills/ or .claude/agents/".
445
+ ⚠ Counter-example (Origin: v1.1.49 세션, 사용자 지적): adding one skill + one agent
446
+ (agora: skills 114→115, agents 49→50) was initially scoped as v1.2.0 minor by reading
447
+ the old wording literally. That was WRONG — this repo adds skills routinely, and
448
+ skills reached 115 while the version stayed v1.1.48, so skill/agent addition is
449
+ ESTABLISHED as patch (target was corrected to v1.1.49). Count growth is this repo's
450
+ baseline rate of change, NOT a minor signal. If a version bump would follow
451
+ mechanically from "a new file exists", it is patch. Ask instead: does a user's
452
+ workflow change because of this?
420
453
  - major: breaking changes to user-facing contracts (CLI flags, public skill/command names, public API), or a deliberate milestone declaration (e.g., v1.0.0)
421
454
  - Previous tag is ahead of source version (e.g., tag v0.136.1, package.json 0.136.0): use next available skip-version (0.136.2)
422
455
 
@@ -428,7 +461,15 @@ steps:
428
461
  b. mgr-gitnerd creates PR: gh pr create --base develop --head release/v{NEW} --title "chore(release): bump to v{NEW}" --body "<MUST include 'Closes #N' for EVERY issue this release resolves>"
429
462
  ⚠ auto-tag.yml closes issues by grep'ing Closes/Fixes/Resolves keywords in the PR BODY — NOT by milestone membership NOR by commit message trailers. If the keywords are missing, no issues close and the workflow still reports success (#1531). Commit-message close keywords are explicitly forbidden during `implement` and step 1.g above (#1542) — the PR body is the ONLY place a close keyword should appear.
430
463
  Before creating the PR (whether via inline --body or --body-file), confirm the body text actually contains a Closes line per issue.
431
- c. mgr-gitnerd merges PR (admin merge required due to branch protection)
464
+ c. mgr-gitnerd merges PR: gh pr merge {n} --merge --delete-branch (plain merge — NOT --admin).
465
+ Ground-truth (measured 2026-08-15, `gh api repos/{owner}/{repo}/branches/develop/protection`):
466
+ required status checks are exactly 6 — Test, Lint, Template Sync, Version Sync,
467
+ Dependency Security Audit, Rust Tests (strict=true); enforce_admins=false; NO
468
+ required_pull_request_reviews (no reviewer-approval requirement exists). v1.1.47 merged with a
469
+ plain `gh pr merge 1585 --merge --delete-branch` — no --admin needed. A plain merge succeeds
470
+ once all 6 checks report green. If the merge is rejected, do NOT reflexively add --admin —
471
+ re-run the protection query above to re-measure current requirements and report the actual
472
+ blocker (R010 bypass-flag pre-check: name what is bypassed and why before ever using --admin).
432
473
  d. DO NOT manually git tag or gh release create.
433
474
  auto-tag.yml fires on release/v* PR merge → creates tag → closes issues linked via PR-body Closes/Fixes/Resolves keywords → closes milestone → deletes release branch.
434
475
  release.yml fires on the tag → npm publish + GitHub Release creation.
@@ -198,7 +198,7 @@ Starting full R017 verification...
198
198
  ═══════════════════════════════════════════════════════════
199
199
 
200
200
  [Round 1/5] mgr-supplier:audit
201
- ✓ 49 agents checked, 0 issues
201
+ ✓ 50 agents checked, 0 issues
202
202
 
203
203
  [Round 2/5] mgr-updater:docs
204
204
  ✓ Documentation sync: OK
@@ -33,10 +33,10 @@ Agents:
33
33
  Database: 4
34
34
  Infra Engineer: 2
35
35
  Other: 10 (security, architect, QA, system)
36
- Total: 49 agents
36
+ Total: 50 agents
37
37
 
38
38
  Skills:
39
- Total: 114 skills
39
+ Total: 115 skills
40
40
 
41
41
  Guides: 56 loaded
42
42
  Commands: 60+ available
@@ -131,7 +131,7 @@ status --health
131
131
  Health Checks:
132
132
 
133
133
  Agents:
134
- ✓ 49/49 agents valid
134
+ ✓ 50/50 agents valid
135
135
  ✓ All agent files exist in .claude/agents/
136
136
 
137
137
  Dependencies:
@@ -125,7 +125,7 @@ Applies CI/Worker-Only Levers. These disable core oh-my-customcode functionality
125
125
 
126
126
  These settings disable core oh-my-customcode functionality:
127
127
  • CLAUDE_CODE_DISABLE_CLAUDE_MDS=1 → ALL rules and routing offline (R010 disabled)
128
- • CLAUDE_AGENT_SDK_DISABLE_BUILTIN_AGENTS=1 → All 49 agents unavailable
128
+ • CLAUDE_AGENT_SDK_DISABLE_BUILTIN_AGENTS=1 → All 50 agents unavailable
129
129
  • ENABLE_CLAUDEAI_MCP_SERVERS=false → MCP-dependent skills unavailable
130
130
  • CLAUDE_CODE_DISABLE_AUTO_MEMORY=1 → No persistent memory across sessions
131
131
 
@@ -127,6 +127,6 @@ jobs:
127
127
  TOTAL_GUIDES=$(find guides -mindepth 1 -maxdepth 1 -type d 2>/dev/null | wc -l | tr -d ' ')
128
128
  TOTAL_WIKI=$(find wiki -name "*.md" ! -name "index.md" ! -name "log.md" 2>/dev/null | wc -l | tr -d ' ')
129
129
 
130
- echo "Source entities: agents=$TOTAL_AGENTS skills=$TOTAL_SKILLS rules=$TOTAL_RULES guides=$TOTAL_GUIDES"
130
+ echo "Source entities: agents=$TOTAL_AGENTS skills=$TOTAL_SKILLS rules=$TOTAL_RULES guide_topics=$TOTAL_GUIDES"
131
131
  echo "Wiki pages: $TOTAL_WIKI"
132
132
  echo "All wiki pages present — sync OK"
@@ -117,8 +117,8 @@ oh-my-customcode로 구동됩니다.
117
117
  project/
118
118
  +-- CLAUDE.md # 진입점
119
119
  +-- .claude/
120
- | +-- agents/ # 서브에이전트 정의 (49 파일)
121
- | +-- skills/ # 스킬 (114 디렉토리)
120
+ | +-- agents/ # 서브에이전트 정의 (50 파일)
121
+ | +-- skills/ # 스킬 (115 디렉토리)
122
122
  | +-- rules/ # 전역 규칙 (R000-R023)
123
123
  | +-- hooks/ # 훅 스크립트 (보안, 검증, HUD)
124
124
  | +-- contexts/ # 컨텍스트 파일 (ecomode)
@@ -180,7 +180,7 @@ oh-my-customcode는 소프트웨어 컴파일과 동일한 구조를 따릅니
180
180
  | System | 4 | sys-memory-keeper, sys-naggy, tracker-checkpoint, wiki-curator |
181
181
  -->
182
182
 
183
- 총 **49개** 에이전트 (타입별 상세는 `/omcustom:lists` 또는 wiki/agents/ 참조)
183
+ 총 **50개** 에이전트 (타입별 상세는 `/omcustom:lists` 또는 wiki/agents/ 참조)
184
184
 
185
185
  ## Agent Teams (MUST when enabled)
186
186
 
@@ -129,8 +129,8 @@ NO EXCEPTIONS. NO EXCUSES.
129
129
  project/
130
130
  +-- CLAUDE.md # Entry point
131
131
  +-- .claude/
132
- | +-- agents/ # Subagent definitions (49 files)
133
- | +-- skills/ # Skills (114 directories)
132
+ | +-- agents/ # Subagent definitions (50 files)
133
+ | +-- skills/ # Skills (115 directories)
134
134
  | +-- rules/ # Global rules (R000-R023)
135
135
  | +-- hooks/ # Hook scripts (security, validation, HUD)
136
136
  | +-- contexts/ # Context files (ecomode)
@@ -174,7 +174,7 @@ This is the core oh-my-customcode philosophy: **"No expert? CREATE one, connect
174
174
  | QA Team | 3 | qa-planner, qa-writer, qa-engineer |
175
175
  | Manager | 6 | mgr-creator, mgr-updater, mgr-supplier, mgr-gitnerd, mgr-sauron, mgr-claude-code-bible |
176
176
  | System | 4 | sys-memory-keeper, sys-naggy, tracker-checkpoint, wiki-curator |
177
- | **Total** | **49** | |
177
+ | **Total** | **50** | |
178
178
 
179
179
  ## Agent Teams (MUST when enabled)
180
180
 
@@ -129,8 +129,8 @@ oh-my-customcode로 구동됩니다.
129
129
  project/
130
130
  +-- CLAUDE.md # 진입점
131
131
  +-- .claude/
132
- | +-- agents/ # 서브에이전트 정의 (49 파일)
133
- | +-- skills/ # 스킬 (114 디렉토리)
132
+ | +-- agents/ # 서브에이전트 정의 (50 파일)
133
+ | +-- skills/ # 스킬 (115 디렉토리)
134
134
  | +-- rules/ # 전역 규칙 (R000-R023)
135
135
  | +-- hooks/ # 훅 스크립트 (보안, 검증, HUD)
136
136
  | +-- contexts/ # 컨텍스트 파일 (ecomode)
@@ -174,7 +174,7 @@ project/
174
174
  | QA Team | 3 | qa-planner, qa-writer, qa-engineer |
175
175
  | Manager | 6 | mgr-creator, mgr-updater, mgr-supplier, mgr-gitnerd, mgr-sauron, mgr-claude-code-bible |
176
176
  | System | 4 | sys-memory-keeper, sys-naggy, tracker-checkpoint, wiki-curator |
177
- | **총계** | **49** | |
177
+ | **총계** | **50** | |
178
178
 
179
179
  ## Agent Teams (MUST when enabled)
180
180
 
@@ -47,8 +47,8 @@ templates/
47
47
  ├── CLAUDE.md # 에이전트 시스템 진입점
48
48
  ├── manifest.json # 배포 컴포넌트 카운트 및 메타데이터
49
49
  ├── .claude/
50
- │ ├── agents/ # 에이전트 정의 파일 (*.md, 49개)
51
- │ ├── skills/ # 스킬 모듈 (각 디렉토리에 SKILL.md, 114개)
50
+ │ ├── agents/ # 에이전트 정의 파일 (*.md, 50개)
51
+ │ ├── skills/ # 스킬 모듈 (각 디렉토리에 SKILL.md, 115개)
52
52
  │ ├── rules/ # 전역 규칙 (R000–R023, 23개)
53
53
  │ ├── hooks/
54
54
  │ │ ├── hooks.json # 훅 이벤트 설정 (PreToolUse/PostToolUse 등)
@@ -65,7 +65,7 @@ templates/
65
65
  아래 카운트는 `templates/manifest.json`과 동기화됩니다.
66
66
  CI의 `verify-template-sync.sh`가 소스와 templates/ 간 일치를 검증합니다.
67
67
 
68
- ### Agents (49)
68
+ ### Agents (50)
69
69
 
70
70
  `.claude/agents/*.md` — 에이전트 정의 파일.
71
71
 
@@ -80,13 +80,13 @@ CI의 `verify-template-sync.sh`가 소스와 templates/ 간 일치를 검증합
80
80
  | DE Engineer | 6 |
81
81
  | SW Engineer / Database | 4 |
82
82
  | Security | 1 |
83
- | SW Architect | 2 |
83
+ | SW Architect | 3 |
84
84
  | Infra Engineer | 2 |
85
85
  | QA Team | 3 |
86
86
  | Manager | 6 |
87
87
  | System | 4 |
88
88
 
89
- ### Skills (114)
89
+ ### Skills (115)
90
90
 
91
91
  `.claude/skills/*/SKILL.md` — 재사용 가능한 스킬 모듈.
92
92
 
@@ -23,7 +23,7 @@ LangChain의 Deep Agents 평가 방법론은 "More evals ≠ better agents" 철
23
23
 
24
24
  ### oh-my-customcode 맥락
25
25
 
26
- oh-my-customcode의 에이전트 시스템은 49개 전문 에이전트와 114개 스킬로 구성된다. 이 방법론은 신규 에이전트 검증(mgr-creator), 릴리즈 품질 검증(deep-verify), 반복 개선 루프(evaluator-optimizer)에 정량 차원을 추가하는 수단으로 내재화된다.
26
+ oh-my-customcode의 에이전트 시스템은 50개 전문 에이전트와 115개 스킬로 구성된다. 이 방법론은 신규 에이전트 검증(mgr-creator), 릴리즈 품질 검증(deep-verify), 반복 개선 루프(evaluator-optimizer)에 정량 차원을 추가하는 수단으로 내재화된다.
27
27
 
28
28
  ---
29
29