@uzysjung/agent-harness 26.146.0 → 26.147.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/dist/{chunk-OEIGA5IL.js → chunk-GPH5AWLX.js} +450 -53
  2. package/dist/chunk-GPH5AWLX.js.map +1 -0
  3. package/dist/index.js +607 -530
  4. package/dist/index.js.map +1 -1
  5. package/dist/trust-tier-drift.js +1 -1
  6. package/package.json +4 -2
  7. package/templates/CLAUDE.md +14 -5
  8. package/templates/agents/implementer.md +1 -1
  9. package/templates/codex/AGENTS.md.template +11 -0
  10. package/templates/opencode/AGENTS.md.template +8 -1
  11. package/templates/opencode/opencode.json.template +0 -1
  12. package/templates/rules/cli-development.md +2 -2
  13. package/templates/rules/doc-governance.md +3 -1
  14. package/templates/rules/test-policy.md +5 -6
  15. package/templates/scripts/check-absence.sh +205 -0
  16. package/templates/settings.json +0 -9
  17. package/templates/skills/gh-issue-workflow/ISSUE.template.md +111 -35
  18. package/templates/skills/gh-issue-workflow/SKILL.md +268 -138
  19. package/templates/skills/gh-issue-workflow/references/hierarchy.md +261 -0
  20. package/templates/skills/gh-issue-workflow/references/operating-model.md +132 -0
  21. package/templates/skills/gh-issue-workflow/references/readiness.md +121 -0
  22. package/templates/skills/ui-visual-review/SKILL.md +17 -3
  23. package/dist/chunk-OEIGA5IL.js.map +0 -1
  24. package/templates/commands/ecc/checkpoint.md +0 -32
  25. package/templates/commands/ecc/e2e.md +0 -105
  26. package/templates/commands/ecc/eval.md +0 -88
  27. package/templates/commands/ecc/evolve.md +0 -7
  28. package/templates/commands/ecc/harness-audit.md +0 -73
  29. package/templates/commands/ecc/instinct-status.md +0 -8
  30. package/templates/commands/ecc/promote.md +0 -10
  31. package/templates/commands/ecc/security-scan.md +0 -10
  32. package/templates/hooks/mcp-pre-exec.sh +0 -122
  33. package/templates/mcp-allowlist.example +0 -24
  34. package/templates/rules/playwright-launch.md +0 -16
@@ -15,7 +15,7 @@ import {
15
15
  init_esm_shims,
16
16
  residentCost,
17
17
  resolveBundleRoot
18
- } from "./chunk-OEIGA5IL.js";
18
+ } from "./chunk-GPH5AWLX.js";
19
19
 
20
20
  // src/trust-tier-drift.ts
21
21
  init_esm_shims();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uzysjung/agent-harness",
3
- "version": "26.146.0",
3
+ "version": "26.147.0",
4
4
  "description": "Curate vetted AI-coding skills & plugins by your tech stack — install only what you need, across Claude Code, Codex, OpenCode & Antigravity",
5
5
  "type": "module",
6
6
  "publishConfig": {
@@ -33,7 +33,9 @@
33
33
  "ci": "npm run typecheck && npm run lint && npm run test:coverage && npm run build",
34
34
  "prepare": "[ -d dist ] || npm run build",
35
35
  "cost:report": "npm run build && node scripts/context-cost-report.mjs",
36
- "cost:baseline": "npm run build && node scripts/context-cost-baseline.mjs"
36
+ "cost:baseline": "npm run build && node scripts/context-cost-baseline.mjs",
37
+ "security": "npm audit --omit=dev --audit-level=high && npx --yes ecc-agentshield@1.4.0 scan --baseline security-baseline.json --gate",
38
+ "security:baseline": "npx --yes ecc-agentshield@1.4.0 scan --save-baseline security-baseline.json"
37
39
  },
38
40
  "dependencies": {
39
41
  "@clack/prompts": "^1.3.0",
@@ -132,11 +132,20 @@ handoff.
132
132
  ## Presenting a decision
133
133
 
134
134
  Present a decision or approval request as AS-IS → TO-BE with a recommendation
135
- and the trade-off, not as prose. Give the surrounding before/after context in
136
- enough detail that the reader does not have to ask, and show the choice the way
137
- they will meet it — a comparison table, a sketch, a rendered example — rather
138
- than describing it. When the reader says they don't follow, fix what the words
139
- point at before rewording; the usual cause is one name meaning two things.
135
+ and the trade-off, not as prose.
136
+
137
+ **Write it from the position of whoever lives with the result** — the person who
138
+ uses what you are building, or the operator who runs it. Name that role, and say
139
+ what they can do now that they could not before, or what stops happening to them;
140
+ a field added to a module is not something anyone outside the code can feel. When
141
+ a change has no user-visible effect, say who does benefit rather than inventing a
142
+ user.
143
+
144
+ Give the surrounding before/after context in enough detail that the reader does
145
+ not have to ask, and show the choice the way they will meet it — a comparison
146
+ table, a sketch, a rendered example — rather than describing it. When the reader
147
+ says they don't follow, fix what the words point at before rewording; the usual
148
+ cause is one name meaning two things.
140
149
 
141
150
  ## Skills that apply continuously
142
151
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: implementer
3
- description: Implementation lane — writes code for a scoped change and closes it with a test that fails without the change. Use whenever work moves from deciding to building: a feature, a bug fix, a refactor, a migration step. Prefer this over implementing on the main thread. Korean triggers: 구현해 · 고쳐 · 만들어 · 적용해 · 리팩터 · 위임.
3
+ description: "Implementation lane — writes code for a scoped change and closes it with a test that fails without the change. Use whenever work moves from deciding to building: a feature, a bug fix, a refactor, a migration step. Prefer this over implementing on the main thread. Korean triggers: 구현해 · 고쳐 · 만들어 · 적용해 · 리팩터 · 위임."
4
4
  tools: ["Read", "Write", "Edit", "Bash", "Grep", "Glob"]
5
5
  model: opus
6
6
  origin: self-authored
@@ -12,6 +12,17 @@
12
12
 
13
13
  {PROJECT_RULES}
14
14
 
15
+ ## Harness Rules
16
+
17
+ > Codex 는 룰 디렉터리가 없고 `AGENTS.md` 계층만 읽는다. 그래서 하네스가 설치한 룰이 여기 본문으로
18
+ > 들어온다. 이 프로젝트에서만 지킬 규칙을 세우면 이 문서에 이어 쓴다.
19
+ >
20
+ > 하위 디렉터리 `AGENTS.md` 로 범위를 좁히는 것도 되지만, Codex 는 **합친 크기**가
21
+ > `project_doc_max_bytes`(기본 32 KiB)에 닿으면 **거기서 읽기를 멈춘다** — 뒤에 오는 파일이
22
+ > 조용히 빠지고, 하위 파일이 뒤에 온다. 나눠 쓰기 전에 총량을 먼저 본다.
23
+
24
+ {HARNESS_RULES}
25
+
15
26
  ## Session Start
16
27
 
17
28
  매 세션 시작 시:
@@ -12,6 +12,13 @@
12
12
 
13
13
  {PROJECT_RULES}
14
14
 
15
+ ## Harness Rules
16
+
17
+ > 하네스가 설치한 룰이 여기 본문으로 들어온다. 이 프로젝트에서만 지킬 규칙을 세우면 이 문서에
18
+ > 이어 쓰거나, 하위 디렉터리에 `AGENTS.md` 를 두면 그 범위에서 더 구체적인 규칙이 된다.
19
+
20
+ {HARNESS_RULES}
21
+
15
22
  ## Session Start
16
23
 
17
24
  매 세션 시작 시:
@@ -57,7 +64,7 @@ OpenCode `permission` 설정이 1차 방어. LLM 추가 준수:
57
64
  ## Context Management
58
65
 
59
66
  - SPEC/PRD 매 세션 시작 시 재참조 (Persistent Anchor)
60
- - `instructions` 키 (`opencode.json`)로 `.claude/rules/*.md` glob 자동 merge
67
+ - 이 `AGENTS.md` 는 OpenCode 가 프로젝트 루트에서 **자동으로** 읽는다 — §Harness Rules 가 그래서 여기 있다. `opencode.json` 의 `instructions` 키는 그 위에 SPEC 문서를 더 얹는다
61
68
 
62
69
  ---
63
70
 
@@ -1,7 +1,6 @@
1
1
  {
2
2
  "$schema": "https://opencode.ai/config.json",
3
3
  "instructions": [
4
- ".claude/rules/*.md",
5
4
  "docs/SPEC.md",
6
5
  "docs/specs/*.md"
7
6
  ],
@@ -8,7 +8,7 @@ paths:
8
8
 
9
9
  - **빈 결과는 부재의 증거가 아니다.** 미지원 플래그를 만난 명령은 에러만 내고 아무것도 출력하지 않는다. `2>/dev/null` 이 그 에러를 지우면 남는 빈 출력은 "깨끗함"과 구분되지 않는다 — **부재를 확인하는 명령에 stderr 를 버리지 마라.**
10
10
  - **파이프 뒤 `$?` 는 마지막 명령의 것이다.** 파이프 없이 실행하거나 `set -o pipefail` 을 쓴다.
11
- - 처음 쓰는 플래그로 "이상 없음"을 결론내지 마라. 알려진 값이 잡히는지로 탐지기를 먼저 검증한 뒤에 빈 결과를 신뢰한다.
11
+ - 처음 쓰는 플래그로 "이상 없음"을 결론내지 마라. 알려진 값이 잡히는지로 탐지기를 먼저 검증한 뒤에 빈 결과를 신뢰한다. (부정 결론 전반의 요구와 그것을 강제하는 도구는 `doc-governance` 에 있다.)
12
12
  - macOS(BSD)와 Linux(GNU)는 `sed -i` · `date` · `readlink -f` · `realpath -m` · `find -newermt` · `stat` 포맷이 호환되지 않는다. 양쪽에서 도는 형태를 쓰거나 `command -v` 로 분기한다.
13
13
 
14
- Claude Code 훅으로 쓸 스크립트의 계약: **차단 = `exit 2` + stderr 에 사유 · 통과 = `exit 0`(출력 없음).** 다른 코드는 "비차단 오류"로 흘러가 조용히 무시된다.
14
+ 훅으로 쓸 스크립트의 **차단 계약은 실행기마다 다르다** — Claude Code · Codex 는 `exit 2` + stderr 에 사유(통과 = `exit 0`, 출력 없음) · OpenCode 플러그인은 훅 함수에서 `throw` · Antigravity 는 JSON 으로 `decision: "deny"`. 계약 밖의 형태는 "비차단 오류"로 흘러가 조용히 무시된다.
@@ -4,4 +4,6 @@
4
4
  - 그 변경을 추적하는 문서가 있으면 **머지까지** 코드와 추적 상태를 함께 동기화한다. 한쪽만 바꾸면 다음 세션이 완료분을 중복 작업하거나 미완분을 완료로 오인해 건너뛴다.
5
5
  - 착수 시점의 미완 표기는 "안 된 것"이 아니라 **"모르는 것"**이다. 코드를 먼저 확인하고 상태를 정정한다 — 심볼이 있다는 것만으로 완료로 읽지 않는다. 못 찾았다고 착수를 보류하지는 않는다.
6
6
 
7
- **이 하네스가 설치해 둔 검사기**가 있다(스스로는 존재를 알 수 없으므로 여기 적는다): `bash .uzys-agent-harness/spec-drift-check.sh` 가 추적 문서의 미완 잔존·상태 불일치를 검출한다 — 인자 없이 부르면 경고(exit 1), `ship` 인자면 차단(exit 2). **탐지 경로 밖의 문서 레이아웃은 못 본다** — 그 경우 이 룰은 프로즈로만 작동한다.
7
+ - **대조군 없이 "없다"·"안 된다"를 결론으로 쓰지 마라.** 빈 결과도 실패한 명령도 "대상이 없다"와 "내 탐지기가 틀렸다"를 구분해 주지 않는다.
8
+
9
+ **이 하네스가 설치해 둔 검사기**(스스로는 존재를 알 수 없으므로 여기 적는다): `bash .uzys-agent-harness/spec-drift-check.sh` 가 추적 문서의 미완 잔존·상태 불일치를 검출한다 — 인자 없이 경고(exit 1), `ship` 인자면 차단(exit 2). **탐지 경로 밖의 문서 레이아웃은 못 본다** — 그 구간에서는 이 룰이 프로즈로만 작동한다. `bash .uzys-agent-harness/check-absence.sh` 가 위 대조군 요구를 강제한다 — `--canary <알려진 양성> [-i] <ERE> <경로>...` 또는 `--control <되는 줄 아는 명령> --subject <대상>` (0 부재 · 1 발견 · 2 신뢰불가 · 3 사용법). 바이너리 부재를 판정할 때는 `--subject 'command -v <bin>'` 처럼 **1 을 내는 형태**로 쓴다 — 명령이 없어 127 로 죽으면 무효 처리된다.
@@ -1,17 +1,16 @@
1
1
  # Testing
2
2
 
3
- - Select the test level and technique that best exposes plausible failures.
4
- - Test observable behavior, contracts, and invariants rather than reproducing implementation details.
3
+ - Test observable behavior — contracts and invariants — at whichever level best exposes
4
+ plausible failures.
5
5
  - For high-risk changes—including authentication, authorization, payments, personal data, data
6
6
  integrity, concurrency, and migrations—cover normal, boundary, failure, misuse, and recovery
7
7
  paths, omitting one only when failure on it is not plausible.
8
- - Keep tests repeatable and deterministic. Control the sources of nondeterminism: time, randomness,
9
- shared state, execution order, network, and external services.
8
+ - Keep tests deterministic: time, randomness, shared state, execution order, network, and
9
+ external services.
10
10
  - Use production-compatible dependencies when a substitute's behavioral differences could affect
11
11
  the result. Otherwise, use explicit test doubles or contract tests.
12
12
  - Never use unauthorized production personal data, credentials, or secrets in tests.
13
13
  - Do not hide failures by weakening assertions, deleting or skipping tests, excluding coverage, or
14
14
  adding indiscriminate retries. Change tests only when the intended behavior has changed.
15
- - Treat coverage as a signal for untested risk, not as a quality target. Follow the
16
- repository-defined CI gates.
15
+ - Coverage signals untested risk; it is not a target. Follow the repository-defined CI gates.
17
16
  - If the affected scope cannot be established confidently, broaden the validation.
@@ -0,0 +1,205 @@
1
+ #!/usr/bin/env bash
2
+ # check-absence.sh — 부정 결론("없다" · "안 된다")을 **증거로** 만드는 명령.
3
+ #
4
+ # 왜 도구인가: `cli-development.md` 가 규약으로 적고 있는데도 같은 실수가 계속 났다.
5
+ # · 대소문자 구분 grep 으로 2건을 놓치고 "잔여 0" 선언
6
+ # · `grep | sort || echo` 로 exit code 를 가림 (`sort` 는 빈 입력에도 0)
7
+ # · 탐지기를 검증하지 않고 빈 출력을 신뢰
8
+ # · Docker 로 설치를 돌려 exit 1 을 보고 "설치 불가" 결론 — **대조군도 함께 실패**했고
9
+ # 원인은 컨테이너에 git 이 없던 것이었다 (실험이 무효였지 대상이 안 되는 게 아니었다)
10
+ # 사람이 매번 기억해야 하는 규약은 규약이 아니므로 도구로 내린다.
11
+ #
12
+ # 하나의 원리: **대조군이 통과해야 부정 결론이 증거다.**
13
+ # 빈 결과도, 실패한 명령도, 그 자체로는 "대상이 없다"와 "내 탐지기가 틀렸다"를 구분해 주지 않는다.
14
+ #
15
+ # 두 모드가 그 원리를 각각의 재료에 적용한다.
16
+ #
17
+ # ┌ pattern 모드 — "이 패턴이 없다"
18
+ # │ 대조군 = 합성한 알려진 양성(canary). 패턴이 그것을 물어야 부재 결과를 신뢰한다.
19
+ # │
20
+ # │ check-absence.sh --canary '<알려진 양성>' [-i] '<ERE 패턴>' <경로>...
21
+ # │ 예: ... --canary 'OldName' 'oldname|legacy_prefix' dist src templates
22
+ # │
23
+ # └ command 모드 — "이 명령이 안 된다 / 이 기능이 없다"
24
+ # 대조군 = **되는 줄 아는 대상**에 같은 절차를 돌린 명령. 그게 실패하면 실험이 무효다.
25
+ #
26
+ # check-absence.sh --control '<되는 줄 아는 명령>' \
27
+ # --subject '<판정할 명령>' [--control-exit N]
28
+ # 예: ... --control '<이미 되는 대상으로 같은 절차>' --subject '<판정할 대상으로 같은 절차>'
29
+ #
30
+ # **대상이 실행조차 안 되면(126/127) 판정이 아니라 무효다** — "대상이 안 된다"와
31
+ # "내가 친 명령이 틀렸다"는 다른 사실이고 exit 1 은 둘을 구분해 주지 않는다.
32
+ # 실행기는 `bash -c` 로 고정한다. `sh` 는 macOS 에서 bash, Linux 에서 dash 라
33
+ # 같은 입력이 플랫폼마다 반대 판정을 냈다.
34
+ # 바이너리 부재를 재려면 `--subject 'command -v <bin>'` 처럼 **1 을 내는 형태**로 쓴다.
35
+ #
36
+ # 공통 규율: stderr 를 버리지 않는다 · 파이프로 exit code 를 가리지 않는다 · 건수/코드를 명시 출력한다.
37
+ #
38
+ # exit:
39
+ # 0 부재 확인 (대조군 통과 상태에서 매치 0 또는 대상이 실행된 뒤 실패)
40
+ # 1 발견 — 부정 결론이 틀렸다 (매치 있음 또는 대상 성공)
41
+ # 2 결과 신뢰 불가 — 대조군 실패 · 탐지기 자기검증 실패 · 대상 미실행(126/127) · grep 오류 · 경로 부재
42
+ # 3 사용법 오류
43
+
44
+ set -u
45
+
46
+ usage() {
47
+ cat >&2 <<'USAGE'
48
+ usage:
49
+ pattern 모드: check-absence.sh --canary <알려진-양성> [-i] <ERE-패턴> <경로>...
50
+ command 모드: check-absence.sh --control <대조-명령> --subject <대상-명령> [--control-exit N]
51
+
52
+ 두 모드 모두 대조군이 필수다. 탐지기(또는 절차)가 실제로 무는지 보이지 않으면
53
+ "없음"·"안 됨"은 증거가 아니라 추측이다.
54
+
55
+ exit: 0 부재 확인 · 1 발견 · 2 결과 신뢰 불가 · 3 사용법
56
+ USAGE
57
+ exit 3
58
+ }
59
+
60
+ CANARY=""
61
+ CANARY_SET=0
62
+ CONTROL=""
63
+ SUBJECT=""
64
+ CONTROL_EXIT=0
65
+ CONTROL_EXIT_SET=0
66
+ # -i 는 자기검증과 실제 검사에 **동시에** 적용된다. 한쪽만 적용하면 자기검증이 통과해도
67
+ # 실제 검사가 놓치는 구멍이 생긴다 — 이 도구가 막으려는 바로 그 실패다.
68
+ IGNORE_CASE=""
69
+ IGNORE_CASE_SET=0
70
+ while [ $# -gt 0 ]; do
71
+ case "$1" in
72
+ --canary) [ $# -ge 2 ] || usage; CANARY="$2"; CANARY_SET=1; shift 2 ;;
73
+ --control) [ $# -ge 2 ] || usage; CONTROL="$2"; shift 2 ;;
74
+ --subject) [ $# -ge 2 ] || usage; SUBJECT="$2"; shift 2 ;;
75
+ --control-exit) [ $# -ge 2 ] || usage; CONTROL_EXIT="$2"; CONTROL_EXIT_SET=1; shift 2 ;;
76
+ -i|--ignore-case) IGNORE_CASE="-i"; IGNORE_CASE_SET=1; shift ;;
77
+ --) shift; break ;;
78
+ -*) usage ;;
79
+ *) break ;;
80
+ esac
81
+ done
82
+
83
+ WORK_DIR="$(mktemp -d)"
84
+ trap 'rm -rf "$WORK_DIR"' EXIT
85
+
86
+ # --- command 모드 --------------------------------------------------------------
87
+ # 모드는 배타적이다. 섞어 부르면 어느 대조군이 적용됐는지 출력만 보고는 알 수 없다.
88
+ if [ -n "$CONTROL" ] || [ -n "$SUBJECT" ]; then
89
+ { [ -n "$CONTROL" ] && [ -n "$SUBJECT" ]; } || usage
90
+ [ "$CANARY_SET" -eq 0 ] || usage
91
+ # 남는 위치 인자를 조용히 버리지 않는다 — 조용한 무시가 이 도구의 성격과 반대다.
92
+ [ $# -eq 0 ] || usage
93
+ [ "$IGNORE_CASE_SET" -eq 0 ] || usage
94
+ # 형태와 **범위**를 함께 본다. 범위를 안 보면 아래 `[ ... -ne ... ]` 가 에러(상태 2)로 끝나고
95
+ # `if` 가 그것을 거짓 = "대조군 통과" 로 읽는다 — 실패가 삼켜지는 자리다.
96
+ case "$CONTROL_EXIT" in
97
+ ''|*[!0-9]*) usage ;;
98
+ esac
99
+ # 선행 0 만 벗긴다(0255 → 255 · 000 → 0). **산술을 쓰지 않는다** — bash 산술은 오버플로에서
100
+ # 에러를 내지 않고 조용히 랩어라운드해서(`$((10#18446744073709551616))` = 0) 사용자가 적은
101
+ # 기대값이 사라진 채 다른 값으로 판정된다. `|| usage` 도 그때는 한 번도 발화하지 않는다.
102
+ while [ "${#CONTROL_EXIT}" -gt 1 ] && [ "${CONTROL_EXIT#0}" != "$CONTROL_EXIT" ]; do
103
+ CONTROL_EXIT="${CONTROL_EXIT#0}"
104
+ done
105
+ { [ "${#CONTROL_EXIT}" -le 3 ] && [ "$CONTROL_EXIT" -le 255 ]; } || usage
106
+
107
+ C_OUT="$WORK_DIR/control.out"; C_ERR="$WORK_DIR/control.err"
108
+ bash -c "$CONTROL" >"$C_OUT" 2>"$C_ERR"
109
+ C_RC=$?
110
+
111
+ if [ "$C_RC" -ne "$CONTROL_EXIT" ]; then
112
+ echo "FAIL(2): 대조군이 기대(exit $CONTROL_EXIT)와 다르게 끝났다 — exit $C_RC." >&2
113
+ echo " 이 실행에서 대상이 실패해도 '안 된다'의 증거가 아니다. 절차 자체가 틀렸을 수 있다." >&2
114
+ case "$C_RC" in
115
+ 126) echo " (126 = 실행 권한 없음 — 대조 명령 자체가 안 돌았다)" >&2 ;;
116
+ 127) echo " (127 = 명령을 찾을 수 없음 — 대조 명령 자체가 안 돌았다)" >&2 ;;
117
+ esac
118
+ echo " 대조 명령: $CONTROL" >&2
119
+ [ -s "$C_ERR" ] && sed 's/^/ control stderr: /' "$C_ERR" >&2
120
+ [ -s "$C_OUT" ] && tail -20 "$C_OUT" | sed 's/^/ control stdout: /' >&2
121
+ exit 2
122
+ fi
123
+
124
+ S_OUT="$WORK_DIR/subject.out"; S_ERR="$WORK_DIR/subject.err"
125
+ bash -c "$SUBJECT" >"$S_OUT" 2>"$S_ERR"
126
+ S_RC=$?
127
+
128
+ echo "실행기: bash -c (sh 는 플랫폼마다 방언이 달라 같은 입력의 판정이 뒤집힌다)"
129
+ echo "대조군: $CONTROL"
130
+ echo " → exit $C_RC (기대 $CONTROL_EXIT) — 절차가 작동한다"
131
+ echo "대상: $SUBJECT"
132
+ echo " → exit $S_RC"
133
+ [ -s "$S_ERR" ] && sed 's/^/ stderr: /' "$S_ERR"
134
+ [ -s "$S_OUT" ] && tail -20 "$S_OUT" | sed 's/^/ stdout: /'
135
+
136
+ # 실행조차 안 된 것은 판정이 아니다. 여기서 0 을 내주면 "대상이 안 된다"와
137
+ # "내가 친 명령이 틀렸다"가 같은 결론이 되고, 그게 이 도구가 없애려던 실패다.
138
+ case "$S_RC" in
139
+ 126|127)
140
+ echo "FAIL(2): 대상 명령이 **실행되지 않았다**(exit $S_RC) — 실패가 아니라 무효다." >&2
141
+ echo " 126 = 실행 권한 없음 · 127 = 명령을 찾을 수 없음." >&2
142
+ echo " **바이너리 부재**를 판정하려면 1 을 내는 형태로 바꿔라 — 예: --subject 'command -v <bin>'" >&2
143
+ exit 2 ;;
144
+ esac
145
+
146
+ if [ "$S_RC" -eq 0 ]; then
147
+ echo "결과: 대상이 성공했다 — '안 된다'는 결론은 틀렸다."
148
+ exit 1
149
+ fi
150
+ echo "결과: 대조군이 통과한 상태에서 대상이 실패했다 — 부정 결론이 증거를 얻었다."
151
+ exit 0
152
+ fi
153
+
154
+ # --- pattern 모드 --------------------------------------------------------------
155
+ [ -n "$CANARY" ] || usage
156
+ [ $# -ge 2 ] || usage
157
+ # pattern 모드에 command 모드 플래그를 섞으면 조용히 무시되지 않고 거절된다.
158
+ [ "$CONTROL_EXIT_SET" -eq 0 ] || usage
159
+ PATTERN="$1"; shift
160
+
161
+ # 1) 탐지기 자기검증
162
+ printf '%s\n' "$CANARY" > "$WORK_DIR/canary.txt"
163
+
164
+ if ! grep -rIqE ${IGNORE_CASE:+"$IGNORE_CASE"} "$PATTERN" "$WORK_DIR/canary.txt" 2>"$WORK_DIR/probe.err"; then
165
+ echo "FAIL(2): 탐지기 자기검증 실패 — canary '$CANARY' 가 패턴 '$PATTERN' 에 안 잡힌다." >&2
166
+ echo " 이 상태의 '매치 없음'은 부재의 증거가 아니다. 패턴이나 canary 를 고쳐라." >&2
167
+ [ -s "$WORK_DIR/probe.err" ] && sed 's/^/ grep: /' "$WORK_DIR/probe.err" >&2
168
+ exit 2
169
+ fi
170
+
171
+ # 2) 실제 검사 (stderr 보존, 파이프 없음)
172
+ # 경로는 **배열**로 넘긴다. 공백 구분 문자열로 쌓아 인용 없이 전개하면 `a b` 같은 경로가
173
+ # 단어 분할돼 엉뚱한 곳을 훑고 `매치: 0건` 을 낸다 — canary 를 통과한 뒤라
174
+ # 사용자는 최대 확신 상태에서 거짓 부재를 받는다.
175
+ OUT="$WORK_DIR/out.txt"; ERR="$WORK_DIR/err.txt"
176
+ PATHS=()
177
+ for p in "$@"; do
178
+ [ -e "$p" ] && PATHS+=("$p")
179
+ done
180
+ if [ "${#PATHS[@]}" -eq 0 ]; then
181
+ echo "FAIL(2): 검사 대상 경로가 하나도 존재하지 않는다 — '없음'이 아니라 '안 봤음'이다." >&2
182
+ exit 2
183
+ fi
184
+
185
+ grep -rInIE ${IGNORE_CASE:+"$IGNORE_CASE"} "$PATTERN" "${PATHS[@]}" >"$OUT" 2>"$ERR"
186
+ GREP_RC=$?
187
+
188
+ if [ "$GREP_RC" -ge 2 ]; then
189
+ echo "FAIL(2): grep 이 오류로 끝났다(exit $GREP_RC) — 결과를 부재로 읽으면 안 된다." >&2
190
+ [ -s "$ERR" ] && sed 's/^/ grep: /' "$ERR" >&2
191
+ exit 2
192
+ fi
193
+ [ -s "$ERR" ] && sed 's/^/ warn: /' "$ERR" >&2
194
+
195
+ COUNT=$(wc -l < "$OUT" | tr -d "[:space:]")
196
+ printf '검사 대상:'; printf ' %s' "${PATHS[@]}"; printf '\n'
197
+ echo "패턴: $PATTERN (canary '$CANARY' 검증 통과)"
198
+ echo "매치: ${COUNT}건"
199
+
200
+ if [ "$COUNT" -gt 0 ]; then
201
+ head -50 "$OUT"
202
+ [ "$COUNT" -gt 50 ] && echo " … 외 $((COUNT - 50))건"
203
+ exit 1
204
+ fi
205
+ exit 0
@@ -30,15 +30,6 @@
30
30
  "timeout": 5
31
31
  }
32
32
  ]
33
- },
34
- {
35
- "matcher": "mcp__.*",
36
- "hooks": [
37
- {
38
- "type": "command",
39
- "command": "bash \"$CLAUDE_PROJECT_DIR/.claude/hooks/mcp-pre-exec.sh\""
40
- }
41
- ]
42
33
  }
43
34
  ],
44
35
  "PostToolUse": [],
@@ -1,58 +1,134 @@
1
1
  <!--
2
- GitHub Issue body 템플릿 (gh-issue-workflow skill)
3
- - 5섹션 모두 채울 필요는 없으나, 비어있으면 그 줄을 지운다 (placeholder 남기지 말 것).
4
- - BDD 매핑: 전제(Given) → 적용 대상(When) → AC(Then).
5
- - 방향성 상태로 작업 가능 여부가 결정된다 (OPEN = 작업 차단, "YYYY-MM-DD 확정" = 작업 가능).
6
- - Labels (3-축, 권장):
7
- - type: bug | feature | refactor | docs | infra
8
- - 상태: decision-pending(방향성 OPEN) | ready(확정) | in-progress(PR open) | blocked(전제 미충족)
9
- - 우선순위: P0 | P1 | P2 (선택)
10
- - GitHub Project 연계 (선택): docs/SPEC.md에 `github_project: <URL>` 명시 시 자동 add.
2
+ GitHub Issue body 템플릿 (gh-issue-workflow skill) — 변형 2종: Task · Epic
3
+
4
+ - 빈 섹션은 통째로 지운다 (placeholder 남기지 말 것). 최소 = 배경 · 문제 · 근거 · AC.
5
+ - 등록 전 준비도 8줄을 센다 — references/readiness.md §4. 길이는 **측정된 감점 요인**이라,
6
+ 아래 칸을 채우고 나서 **남는 서술을 지운다**(칸을 지워서 짧게 만들면 자족성이 같이 떨어진다).
7
+ - 라벨·마일스톤·부모는 본문이 아니라 gh 플래그로 붙인다 — references/hierarchy.md §2.
8
+ - 층 분리(목표/이슈/묶음)와 링크 감쇠는 references/operating-model.md.
11
9
  -->
12
10
 
13
- ## 배경
11
+ # 변형 A — Task (문제 제기 · 실행 단위)
14
12
 
15
- [왜 이 작업이 필요한가. 1-3 문장. 사용자가 발견한 증상, 도달하려는 상태, 비즈니스 맥락.]
13
+ **육하원칙이 어느 칸에 들어가는가** — 칸 이름을 외우지 말고 이 대응만 본다:
16
14
 
17
- ## 전제 (Given)
15
+ | | 들어가는 칸 |
16
+ |---|---|
17
+ | **왜**(Why) · **누가**(Who) | `## 배경` |
18
+ | **무엇이**(What) · **언제·어디서**(When/Where) | `## 문제` |
19
+ | (증명) | `## 근거` |
20
+ | (출처) | `## 레퍼런스` |
21
+ | **어떻게**(How) | `## 제안` |
18
22
 
19
- [이 작업을 시작하기 전에 충족돼야 하는 조건. 다른 issue / 외부 의존성 / 의사결정 결과 / 인프라 상태. 미충족 시 작업 차단.]
23
+ ---
20
24
 
21
- - [ ] [전제 조건 1 — 예: Issue #N 완료]
22
- - [ ] [전제 조건 2 — 예: Stripe 계정 발급]
23
- - [ ] [전제 조건 3 — 예: DB 스키마 v3 마이그레이션]
25
+ ## 배경 — 왜 · 누가
24
26
 
25
- 전제 미충족 시 → 차단 사유 명시 + 충족시킬 책임자/순서 기록.
27
+ [왜 지금 이걸 다루는가. **누가** 겪는가(설치받는 사용자 / 운영자 / 리뷰어 / 다음 유지보수자).
28
+ 안 고치면 무엇이 계속 일어나는가. 2-4 문장.]
26
29
 
27
- ## 방향성 (OPEN | YYYY-MM-DD 확정)
30
+ ## 문제 — 무엇이 · 언제·어디서
28
31
 
29
- [현재 의사결정 상태. `OPEN` = 사용자 결정 대기, `YYYY-MM-DD 확정` = 결정 완료.]
32
+ [**관측된 것만** 적는다. 원인 추정은 `## 제안` 으로 내린다 — 둘을 섞으면 추정이 사실로 굳는다.]
30
33
 
31
- - 옵션 A: [설명]
32
- - 옵션 B: [설명]
33
- - **선택 (확정 시)**: [선택지 + 근거]
34
+ - **관측**: [어떤 명령 / 화면 / 버전에서 무엇이 일어났나]
35
+ - **기대**: [대신 무엇이 일어나야 하나]
36
+ - **범위**: [항상인가, 특정 조건에서만인가. 안 나는 조건을 알면 그것도 적는다]
34
37
 
35
- 방향성이 OPEN이면 본 issue로 작업 진행 금지. AI agent는 사용자 결정 대기.
38
+ ## 근거
36
39
 
37
- ## 적용 대상 / Acceptance Criteria (When → Then)
40
+ [남이 붙여넣어 재현할 수 있는 형태로. "확인했다"는 근거가 아니다.]
38
41
 
39
- [변경 범위 + 측정 가능한 완료 조건.]
42
+ ```bash
43
+ <재현 명령>
44
+ # 실제 출력 (잘라내지 말고, 실패 메시지는 stderr 까지)
45
+ ```
40
46
 
41
- - [ ] [AC 1 — 예: `/admin/activity-logs` 페이지 11 이상 페이지 정상 작동 (When 사용자 11 클릭 → Then 11페이지 데이터 표시)]
42
- - [ ] [AC 2 — 예: 디자인 시스템 토큰 사용 (When 페이지 렌더 → Then 색상/간격이 design system과 일치)]
43
- - [ ] [AC 3]
47
+ - 실측 날짜 / 환경: [OS · 버전 — 재현이 환경에 걸리면 이 줄이 판정을 가른다]
48
+ - **대조군**: [되는 줄 아는 경우도 같은 절차로 돌렸는가. 그게 함께 실패하면 위 결과는 무효다]
44
49
 
45
- AC는 검증 가능해야 함 — pass/fail 명확.
50
+ ## 레퍼런스
46
51
 
47
- ## 후속 작업 (Next)
52
+ [**링크는 죽는다** — 커밋 메시지 안 링크의 34~43% 가 접근 불가라는 측정이 있다
53
+ (arxiv.org/abs/2305.16591).
54
+ 그래서 밖의 자료는 URL 만 두지 말고 **핵심 문장을 인용해 둔다.**]
55
+
56
+ - 저장소 안: `#N` · `path/to/file.ts:123` ← 이슈·PR 은 반드시 `#N` 으로. 교차참조 **이벤트**가 남는다
57
+ - 저장소 밖: [제목](URL) — > "인용할 한 문장"
58
+ - 선행 결정: `#M` 의 `## 방향성` / ADR
59
+
60
+ ## 제안 — 어떻게
61
+
62
+ [고치는 방향. **강제가 아니라 출발점**이다 — 구현 레인이 더 나은 길을 찾으면 그쪽이 이긴다.]
63
+
64
+ - [1안: <무엇을 · 어디를>]
65
+ - **안 하기로 생각한 것**: [<대안> — 왜 아닌지 한 줄] ← 이걸 적어야 다음에 다시 논의되지 않는다
66
+
67
+ ## 전제 (Given)
68
+
69
+ [시작 전에 충족돼야 하는 조건. 미충족이면 착수 차단. 없으면 이 절을 지운다.]
70
+
71
+ - [ ] [예: #N 완료]
72
+
73
+ ## 방향성 (OPEN | YYYY-MM-DD 확정)
74
+
75
+ [`OPEN` = 사용자 결정 대기 → 착수 금지. 확정이면 **날짜와 채택 근거**를 남긴다.
76
+ 옵션이 나열만 돼 있는 것은 결정이 아니라 결정이 없다는 뜻이다.]
77
+
78
+ - **채택**: [고른 것 + 왜 다른 것을 안 골랐는지]
79
+
80
+ ## 적용 대상 / Acceptance Criteria (When → Then)
81
+
82
+ 대상: `[path/to/file.ts]` 의 `[symbol]` · `[path/to/other]`
48
83
 
49
- [본 issue 완료 후 분기되는 작업. 새 issue 번호 또는 잠정 설명.]
84
+ - [ ] [When <조건> → Then <관측 가능한 결과>]
85
+ - [ ] [경계 하나 — 예외·edge 경로]
86
+ - [ ] [위를 고정하는 회귀 테스트가 변경 없이는 red]
50
87
 
51
- - [ ] [후속 1 — 예: Issue #N으로 분리]
52
- - [ ] [후속 2]
88
+ ## 후속 작업 (Next)
53
89
 
54
- 후속 작업이 없으면 이 섹션 통째로 삭제.
90
+ - [ ] [#M 으로 분리한 것 — 이번 범위에서 뺀 것과 그 이유]
55
91
 
56
92
  ---
57
93
 
58
- <!-- PR 머지 시 본 issue 자동 close되도록 PR body에 `Closes #<this-issue-number>` 추가 -->
94
+ # 변형 B — Epic (부모 이슈 / 묶음)
95
+
96
+ <!--
97
+ 에픽은 직접 구현되지 않는다. AC 대신 "하위와 순서"를 담는다.
98
+ 하위가 3개 미만이면 에픽을 만들지 않는다 — 껍데기만 는다.
99
+ -->
100
+
101
+ ## 배경
102
+
103
+ [이 묶음이 왜 필요한가. 무엇이 지금 안 되는가. 2-4 문장.]
104
+
105
+ ## 방향성 (OPEN | YYYY-MM-DD 확정)
106
+
107
+ [하위 전체가 공유하는 결정. 하위마다 다시 정할 필요가 없어야 에픽이 값을 한다.]
108
+
109
+ - **채택**: [방침 + 근거]
110
+ - **하지 않기로 한 것**: [빼기로 한 것 + 왜]
111
+
112
+ ## 하위 작업과 순서
113
+
114
+ [부모-자식은 gh 로 붙인다(진척은 GitHub 이 표시). 여기에는 **순서와 그 이유**를 적는다.]
115
+
116
+ 1. #N — [무엇] · 먼저 하는 이유: [의존/위험]
117
+ 2. #M — [무엇] · #N 의 결과가 있어야 판정 가능
118
+ 3. #L — [무엇] · 독립. 언제든
119
+
120
+ ## 완료 판정 (에픽이 닫히는 조건)
121
+
122
+ [하위가 다 닫혔다는 사실만으로 닫지 않는다. 묶음 수준에서 관측되는 것을 적는다.]
123
+
124
+ - [ ] [When <사용자 표면 조건> → Then <관측 결과>]
125
+
126
+ ## 범위 밖 (Non-Goals)
127
+
128
+ - [이 에픽에서 다루지 않는 것 — 나중에 "왜 저건 안 했지"를 막는다]
129
+
130
+ <!--
131
+ 본문을 나중에 **교체**할 때: 사라지면 안 되는 것(사용자 원문 요청 · 기각된 대안)은 본문 안에
132
+ 남기거나 코멘트로 먼저 옮긴다. 이전 판은 GraphQL `userContentEdits` 에만 있고 타임라인 · REST ·
133
+ `gh issue view` 어디에도 안 보인다 — 보존돼 있어도 아무도 안 찾는다.
134
+ -->