@su-record/vibe 2.13.0 → 2.14.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 (42) hide show
  1. package/CLAUDE.md +18 -15
  2. package/README.en.md +5 -3
  3. package/README.md +6 -4
  4. package/dist/cli/postinstall/constants.d.ts.map +1 -1
  5. package/dist/cli/postinstall/constants.js +1 -0
  6. package/dist/cli/postinstall/constants.js.map +1 -1
  7. package/dist/cli/setup/ProjectSetup.d.ts.map +1 -1
  8. package/dist/cli/setup/ProjectSetup.js +4 -3
  9. package/dist/cli/setup/ProjectSetup.js.map +1 -1
  10. package/dist/tools/index.d.ts +2 -0
  11. package/dist/tools/index.d.ts.map +1 -1
  12. package/dist/tools/index.js +2 -0
  13. package/dist/tools/index.js.map +1 -1
  14. package/dist/tools/loop/index.d.ts +6 -0
  15. package/dist/tools/loop/index.d.ts.map +1 -0
  16. package/dist/tools/loop/index.js +5 -0
  17. package/dist/tools/loop/index.js.map +1 -0
  18. package/dist/tools/loop/validateLoopDefinition.d.ts +38 -0
  19. package/dist/tools/loop/validateLoopDefinition.d.ts.map +1 -0
  20. package/dist/tools/loop/validateLoopDefinition.js +224 -0
  21. package/dist/tools/loop/validateLoopDefinition.js.map +1 -0
  22. package/dist/tools/loop/validateLoopDefinition.test.d.ts +14 -0
  23. package/dist/tools/loop/validateLoopDefinition.test.d.ts.map +1 -0
  24. package/dist/tools/loop/validateLoopDefinition.test.js +229 -0
  25. package/dist/tools/loop/validateLoopDefinition.test.js.map +1 -0
  26. package/hooks/scripts/__tests__/.vibe/command-log.txt +21 -0
  27. package/hooks/scripts/__tests__/.vibe/memories/memories.db-shm +0 -0
  28. package/hooks/scripts/__tests__/.vibe/memories/memories.db-wal +0 -0
  29. package/hooks/scripts/__tests__/keyword-detector.test.js +26 -18
  30. package/hooks/scripts/__tests__/loop-ledger.test.js +321 -0
  31. package/hooks/scripts/keyword-detector.js +22 -22
  32. package/hooks/scripts/lib/hook-context.js +31 -2
  33. package/hooks/scripts/lib/loop-ledger.js +118 -0
  34. package/hooks/scripts/loop-ledger.js +56 -0
  35. package/package.json +1 -1
  36. package/skills/vibe/SKILL.md +40 -23
  37. package/skills/vibe.loop/SKILL.md +116 -0
  38. package/skills/vibe.run/SKILL.md +22 -18
  39. package/skills/vibe.run/references/ralph-loop.md +18 -17
  40. package/skills/vibe.run/references/ultrawork-mode.md +36 -33
  41. package/vibe/rules/loop-contract.md +54 -0
  42. package/vibe/templates/loop-template.md +69 -0
@@ -0,0 +1,118 @@
1
+ /**
2
+ * Loop Ledger 라이브러리 — 루프 실행 이력 추적 및 stuck 감지.
3
+ *
4
+ * 파일 위치: <projectDir>/.vibe/metrics/loop-history.jsonl
5
+ * 형식: JSON Lines — 각 줄이 독립적인 루프 이벤트 JSON 객체
6
+ *
7
+ * 모든 함수는 fail-open (try/catch, 오류 시 무시하거나 안전한 기본값 반환).
8
+ * isStuck: 같은 루프의 가장 최근 discover 이벤트의 discoverHash가
9
+ * 신규 hash와 같으면 stuck으로 판정한다 (2회 연속 동일 발견).
10
+ * discover 이벤트는 CLI check-stuck이 판정 직후 스스로 기록한다 —
11
+ * 기록 없는 판정은 다음 실행의 비교 기준을 잃는다.
12
+ */
13
+
14
+ import fs from 'fs';
15
+ import path from 'path';
16
+ import crypto from 'crypto';
17
+
18
+ /** 루프 이력 파일 경로 */
19
+ function historyPath(projectDir) {
20
+ return path.join(projectDir, '.vibe', 'metrics', 'loop-history.jsonl');
21
+ }
22
+
23
+ /**
24
+ * discover 산출물 텍스트를 sha256 hex 해시로 변환한다.
25
+ * 공백/줄바꿈을 정규화해 동등한 출력이 동일 해시를 갖도록 한다.
26
+ *
27
+ * @param {string} text
28
+ * @returns {string} sha256 hex
29
+ */
30
+ export function hashDiscoverOutput(text) {
31
+ const normalized = text.replace(/\s+/g, ' ').trim();
32
+ return crypto.createHash('sha256').update(normalized, 'utf-8').digest('hex');
33
+ }
34
+
35
+ /**
36
+ * 루프 이벤트를 jsonl 파일에 append한다.
37
+ *
38
+ * @param {string} projectDir
39
+ * @param {{ loop: string, event: 'start'|'discover'|'end', result?: 'ok'|'fail'|'stuck', summary?: string, discoverHash?: string }} opts
40
+ * @returns {boolean} 성공 여부
41
+ */
42
+ export function appendLoopEvent(projectDir, opts) {
43
+ try {
44
+ const p = historyPath(projectDir);
45
+ fs.mkdirSync(path.dirname(p), { recursive: true });
46
+ const entry = {
47
+ ts: new Date().toISOString(),
48
+ loop: opts.loop,
49
+ event: opts.event,
50
+ ...(opts.result !== undefined ? { result: opts.result } : {}),
51
+ ...(opts.summary !== undefined ? { summary: opts.summary } : {}),
52
+ ...(opts.discoverHash !== undefined ? { discoverHash: opts.discoverHash } : {}),
53
+ };
54
+ fs.appendFileSync(p, JSON.stringify(entry) + '\n', 'utf-8');
55
+ return true;
56
+ } catch {
57
+ return false;
58
+ }
59
+ }
60
+
61
+ /**
62
+ * 지정 루프의 특정 이벤트 목록을 최신순으로 읽는다.
63
+ * 손상된 줄은 건너뛴다 (fail-open).
64
+ *
65
+ * @param {string} projectDir
66
+ * @param {string} loop
67
+ * @param {string} eventType
68
+ * @returns {{ ts: string, discoverHash?: string }[]}
69
+ */
70
+ function readEventsOfType(projectDir, loop, eventType) {
71
+ try {
72
+ const p = historyPath(projectDir);
73
+ if (!fs.existsSync(p)) return [];
74
+ const lines = fs.readFileSync(p, 'utf-8').split('\n').filter(Boolean);
75
+ const events = [];
76
+ for (const line of lines) {
77
+ try {
78
+ const obj = JSON.parse(line);
79
+ if (obj.loop === loop && obj.event === eventType) {
80
+ events.push(obj);
81
+ }
82
+ } catch {
83
+ // 손상된 줄 무시
84
+ }
85
+ }
86
+ // 최신순 정렬 (ts 기준)
87
+ return events.sort((a, b) => (a.ts < b.ts ? 1 : a.ts > b.ts ? -1 : 0));
88
+ } catch {
89
+ return [];
90
+ }
91
+ }
92
+
93
+ /**
94
+ * 신규 discoverHash가 stuck 조건을 충족하는지 판정한다.
95
+ *
96
+ * stuck 조건: 신규 hash가 non-empty이고, 해당 루프의 가장 최근
97
+ * discover 이벤트에 동일한 discoverHash가 있을 때.
98
+ * (직전 실행의 발견 + 이번 발견 = 2회 연속 동일이 되는 시점)
99
+ *
100
+ * 주의: 판정만 하고 기록하지 않으면 다음 실행이 비교할 기준이 없다 —
101
+ * 호출자는 판정 직후 event:'discover'로 해시를 기록해야 한다 (CLI check-stuck이 수행).
102
+ *
103
+ * @param {string} projectDir
104
+ * @param {string} loop
105
+ * @param {string} discoverHash
106
+ * @returns {boolean}
107
+ */
108
+ export function isStuck(projectDir, loop, discoverHash) {
109
+ try {
110
+ if (!discoverHash) return false;
111
+ const discoverEvents = readEventsOfType(projectDir, loop, 'discover');
112
+ if (discoverEvents.length === 0) return false;
113
+ const lastHash = discoverEvents[0].discoverHash;
114
+ return Boolean(lastHash && lastHash === discoverHash);
115
+ } catch {
116
+ return false;
117
+ }
118
+ }
@@ -0,0 +1,56 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * loop-ledger CLI — 루프 실행 이력 기록 및 stuck 감지.
4
+ *
5
+ * 사용법:
6
+ * node hooks/scripts/loop-ledger.js start <name>
7
+ * node hooks/scripts/loop-ledger.js end <name> <ok|fail|stuck> [summary]
8
+ * node hooks/scripts/loop-ledger.js check-stuck <name> <discoverHash>
9
+ *
10
+ * check-stuck: 'stuck' 또는 'ok'를 stdout에 출력하고 항상 exit 0.
11
+ * 항상 exit 0 (fail-open).
12
+ */
13
+
14
+ import { appendLoopEvent, isStuck } from './lib/loop-ledger.js';
15
+
16
+ const [, , subcommand, ...args] = process.argv;
17
+ const projectDir = process.env.CLAUDE_PROJECT_DIR || process.cwd();
18
+
19
+ if (subcommand === 'start') {
20
+ const loop = args[0];
21
+ if (!loop) {
22
+ process.stdout.write('[loop-ledger] error: start 에 루프 이름이 필요합니다\n');
23
+ process.exit(0);
24
+ }
25
+ appendLoopEvent(projectDir, { loop, event: 'start' });
26
+ process.stdout.write(`[loop-ledger] start recorded: loop=${loop}\n`);
27
+
28
+ } else if (subcommand === 'end') {
29
+ const [loop, result, ...summaryParts] = args;
30
+ if (!loop || !result) {
31
+ process.stdout.write('[loop-ledger] error: end 에 루프 이름과 결과(ok|fail|stuck)가 필요합니다\n');
32
+ process.exit(0);
33
+ }
34
+ const summary = summaryParts.length > 0 ? summaryParts.join(' ') : undefined;
35
+ appendLoopEvent(projectDir, { loop, event: 'end', result, summary });
36
+ process.stdout.write(`[loop-ledger] end recorded: loop=${loop} result=${result}\n`);
37
+
38
+ } else if (subcommand === 'check-stuck') {
39
+ const [loop, discoverHash] = args;
40
+ if (!loop || !discoverHash) {
41
+ process.stdout.write('[loop-ledger] error: check-stuck 에 루프 이름과 discoverHash가 필요합니다\n');
42
+ process.stdout.write('ok\n');
43
+ process.exit(0);
44
+ }
45
+ const stuck = isStuck(projectDir, loop, discoverHash);
46
+ // 판정 직후 이번 발견 해시를 기록 — 다음 실행의 비교 기준이 된다
47
+ appendLoopEvent(projectDir, { loop, event: 'discover', discoverHash });
48
+ process.stdout.write(stuck ? 'stuck\n' : 'ok\n');
49
+
50
+ } else {
51
+ process.stdout.write(
52
+ '[loop-ledger] 사용법: start <name> | end <name> <ok|fail|stuck> [summary] | check-stuck <name> <hash>\n'
53
+ );
54
+ }
55
+
56
+ process.exit(0);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@su-record/vibe",
3
- "version": "2.13.0",
3
+ "version": "2.14.0",
4
4
  "description": "AI Coding Framework for Claude Code — 60+ agents, 69 skills, multi-LLM orchestration",
5
5
  "type": "module",
6
6
  "main": "dist/cli/index.js",
@@ -7,7 +7,9 @@ user-invocable: true
7
7
 
8
8
  # /vibe
9
9
 
10
- **vibe의 메인 진입점.** 사용자는 "무엇을 만들지 / 무엇을 할지"만 자연어로 말한다. vibe가 의도를 분석해 어떤 `/vibe.*` 스킬을 어떤 순서로 호출할지 파이프라인을 동적으로 설계하고, **1회 승인 후 실행**한다.
10
+ **vibe의 메인 진입점.** 사용자는 "무엇을 만들지 / 무엇을 할지"만 자연어로 말한다. vibe가 의도를 분석해 어떤 `/vibe.*` 스킬을 어떤 순서로 호출할지 파이프라인을 동적으로 설계하고, **SPEC 확정 1회 승인 후 게이트 통과까지 루프 실행**한다.
11
+
12
+ > **루프 시맨틱 SSOT**: `vibe/rules/loop-contract.md` — 이 문서가 ANCHOR→ACT→JUDGE→RECORD 계약과 모든 파라미터(exit, stuck, max_iterations, automationLevel)를 정의한다.
11
13
 
12
14
  ## Usage
13
15
 
@@ -17,7 +19,8 @@ user-invocable: true
17
19
  /vibe "로그인 회귀 테스트 다시 돌려서 통과시켜줘"
18
20
  /vibe "이 SPEC 리뷰만 한번 봐줘" + 📎 .vibe/specs/login.md
19
21
  /vibe "PRD 문서 기반으로 진행" + 📎 docs/prd.pdf
20
- /vibe "..." ultrawork # 승인 게이트 없이 자동 전 흐름 실행
22
+ /vibe "..." --interactive # 단계별 확인 모드 (회전마다 승인)
23
+ /vibe "..." --max-iter 1 # 1회 시도만
21
24
  ```
22
25
 
23
26
  ## Philosophy
@@ -29,7 +32,7 @@ user-invocable: true
29
32
  - **무제한 라우팅**: 라우팅 표는 빠른 경로일 뿐 닫힌 화이트리스트가 아니다. 설치된 모든 `vibe.*` 스킬이 라우팅 후보이며, 표에 없는 요구사항도 description 기반 의미 매칭으로 처리한다 (Catch-all).
30
33
  - **하네스 정규화 (추론 앞단)**: vibe는 CC(추론)·Codex(직역) 어느 하네스의 암묵적 동작에도 의존하지 않는다. `/vibe`가 모호한 NL을 **명시적·직역 가능한 지시로 먼저 전개**하고, 하위 skill은 모호한 입력을 받지 않는다. 이로써 모든 하네스에서 동일 결과 + CC급 편의를 제공한다. 전문: `vibe/rules/principles/dual-harness-doctrine.md`.
31
34
  - **Smart Resume**: `.vibe/{interviews,plans,specs,features}/` 감지하여 "이어서 진행?" 자동 제안.
32
- - **1회 승인 게이트**: 파이프라인을 설계해서 사용자에게 보여주고 OK 받은 실행. `ultrawork` 키워드 있으면 skip.
35
+ - **SPEC 확정 1회 승인 루프**: SPEC(Done의 정의 + 수용 기준)을 사용자와 확정하는 것이 유일한 의무적 사람 개입. 승인 후에는 `vibe/rules/loop-contract.md`의 ANCHOR→ACT→JUDGE→RECORD 루프를 게이트 전부 통과할 때까지 자동 반복한다. 루프 종료 = 게이트 통과 또는 stuck 또는 max_iterations 도달.
33
36
  - **위임자 역할**: `/vibe` 본인은 코드를 직접 쓰지 않는다. 라우팅·설계·실행 위임만 한다.
34
37
 
35
38
  ## Process
@@ -45,7 +48,7 @@ user-invocable: true
45
48
  | 자연어 텍스트 | 의도 추출 (요구 종류, 도메인, 키워드) |
46
49
  | 첨부 파일 (`📎 path`) | 확장자로 분류 (md/feature/pdf/png/jpg/...) |
47
50
  | URL | 도메인으로 분류 (figma.com / github.com / 기타) |
48
- | 키워드 | `ultrawork`, `ralph`, `quick`, `verify`magic keyword 추출 |
51
+ | 옵션 플래그 | `--interactive`, `--max-iter N` 등 루프 파라미터 추출; `ultrawork`/`ralph`/`quick` 등 deprecated 별칭은 loop-contract 매핑표로 변환 |
49
52
 
50
53
  ### Phase 1: Intent 분류
51
54
 
@@ -113,7 +116,7 @@ user-invocable: true
113
116
  - **research 명시**: 조사가 필요하면 파이프라인에 명시적 탐색 단계를 넣는다. planning mode 같은 하네스 스위치에 의존하지 않는다.
114
117
  - **도메인 지식 흡수**: 사용자가 준 라이브러리·함수·파일 위치를 SPEC 입력으로 전달한다.
115
118
 
116
- 이어서 분류된 의도 + resume 상태 + magic keyword 를 종합해 실행 계획 작성:
119
+ 이어서 분류된 의도 + resume 상태 + 루프 파라미터(automationLevel, --max-iter 등)를 종합해 실행 계획 작성:
117
120
 
118
121
  ```
119
122
  📋 Pipeline Plan
@@ -143,9 +146,10 @@ Phase 6: /vibe.trace → RTM 출력
143
146
  ```
144
147
 
145
148
  **Skip 조건:**
146
- - 입력에 `ultrawork` 또는 `ulw` 키워드자동 진행
147
- - `ralph` 키워드자동 진행 + 100% 수렴까지 반복
148
- - `quick` 키워드 → 최소 phase 만 자동 진행 (review/verify/contract/trace skip)
149
+ - `automationLevel: autonomous` 설정 (`.vibe/config.json`) 또는 `ultrawork`/`ulw` 별칭SPEC 승인 게이트만 skip, 루프는 정상 동작
150
+ - `--max-iter 1` 또는 `quick` 별칭1회 시도 종료
151
+
152
+ > 참고: Phase 4의 승인 게이트는 **파이프라인 설계 승인**이다. SPEC 확정 시점의 승인(loop-contract의 "유일한 의무 개입")과 구별된다.
149
153
 
150
154
  ### Phase 5: 체인 실행
151
155
 
@@ -161,10 +165,11 @@ Phase 6: /vibe.trace → RTM 출력
161
165
  ...
162
166
  ```
163
167
 
164
- 각 phase 종료 시:
165
- - 성공다음 phase
166
- - 실패중단하고 사용자에게 보고 (재시도 / skip / abort 선택)
167
- - `ultrawork` 모드실패도 TODO 기록하고 계속 진행 (non-interactive)
168
+ 각 phase 종료 후 JUDGE 단계:
169
+ - 게이트 통과 (P1=0 ∧ verifyPassed) 루프 종료, Phase 6 보고
170
+ - 게이트 미통과 RECORD(run-ledger + loop-history.jsonl) 다음 ANCHOR로
171
+ - stuck(연속 2회 동일 findings 해시) `automationLevel: confirm`이면 사용자 질문; `autonomous`이면 TODO 기록 루프 종료
172
+ - max_iterations(기본 10) 도달 → 잔여를 인박스로 이월
168
173
 
169
174
  ### Phase 6: 종료 보고
170
175
 
@@ -228,31 +233,43 @@ Phase 6: /vibe.trace → RTM 출력
228
233
  1. /vibe.review (단일 phase)
229
234
  ```
230
235
 
231
- ### 예시 4: ultrawork
236
+ ### 예시 4: automationLevel autonomous (ultrawork 별칭)
232
237
 
233
238
  ```
234
239
  입력: /vibe "결제 API 만들어줘" ultrawork
235
240
 
236
- Approval gate SKIP
237
- 자동 phase 실행
238
- 실패는 TODO 기록만 하고 계속
241
+ automationLevel: autonomous 설정 → SPEC 승인 게이트 skip
242
+ 병렬 ACT 활성화
243
+ ANCHOR→ACT→JUDGE→RECORD 루프 (게이트 통과까지 자동 반복)
244
+ → stuck 시 TODO 기록 후 루프 종료 (사용자 질문 없음)
239
245
  ```
240
246
 
241
247
  ## ⛔ 하지 않는 것
242
248
 
243
249
  - 직접 코드 작성 / 파일 수정 (위임만)
244
- - 파이프라인 미리보기 없이 즉시 실행 (`ultrawork` )
250
+ - SPEC 확정 없이 루프 진입 (SPEC = Done의 정의, 유일한 의무 승인 지점)
245
251
  - 사용자가 명시한 phase 를 임의로 추가/제거
246
252
  - Resume 상태를 무시하고 처음부터 다시 시작
247
253
 
248
- ## Magic Keywords (재정의)
254
+ ## 루프 옵션
255
+
256
+ | 옵션 | 효과 |
257
+ |---|---|
258
+ | `--interactive` | 매 회전마다 사람 승인 (과거 기본값) |
259
+ | `--max-iter N` | 회전 상한 N 으로 설정 (N=1이면 1회 시도) |
260
+ | `automationLevel: autonomous` (`.vibe/config.json`) | stuck/SPEC 게이트 외 모든 확인 skip |
261
+
262
+ ## Deprecated 별칭 (하위 호환 — 새 코드에서 사용하지 말 것)
263
+
264
+ > 아래 키워드는 계속 동작하지만 loop-contract 파라미터로 환원된다. 새 문서나 예시에서 가르치지 않는다. 전문: `vibe/rules/loop-contract.md` Deprecated 별칭 매핑표.
249
265
 
250
- | Keyword | Effect |
266
+ | 별칭 | 환원 |
251
267
  |---|---|
252
- | `ultrawork` / `ulw` | 승인 게이트 skip + non-interactive |
253
- | `ralph` | 100% 수렴까지 자동 반복 |
254
- | `quick` | 최소 phase (review/verify/contract/trace skip) |
255
- | `verify` | 강한 검증 (verify + contract + trace 강제 포함) |
268
+ | `ultrawork` / `ulw` | `automationLevel: autonomous` + 병렬 ACT |
269
+ | `ralph` | 기본 동작과 동일 (no-op); exit=coverage-100으로 해석 가능 |
270
+ | `quick` | `--max-iter 1` + 최소 JUDGE |
271
+ | `verify` | 기본 동작과 동일 (no-op) JUDGE는 항상 결정론 검증 |
272
+ | `ralplan` | 동일 loop-contract를 계획 단계에 적용 |
256
273
 
257
274
  ## Output
258
275
 
@@ -0,0 +1,116 @@
1
+ ---
2
+ name: vibe.loop
3
+ description: Loop engineering — design, install, and run autonomous goal loops whose completion is judged by deterministic gates, not self-report
4
+ argument-hint: "design | install | run | status | list [loop-name]"
5
+ user-invocable: true
6
+ ---
7
+
8
+ # /vibe.loop
9
+
10
+ **Loop Engineering** — 사람이 프롬프트하는 대신, 에이전트에게 프롬프트하는 루프를 설계한다.
11
+
12
+ > 루프의 완료 판정은 모델의 자기 보고가 아니라 **결정론적 게이트**(run-ledger `verifyPassed`, 테스트 exit code)가 내린다.
13
+ > 루프는 push·release·배포를 절대 수행하지 않는다 — 결과는 항상 사람 리뷰 큐(인박스)로 간다.
14
+
15
+ ## Usage
16
+
17
+ ```
18
+ /vibe.loop design [name] # 인터뷰 → .vibe/loops/<name>.md 정의 생성
19
+ /vibe.loop install <name> # 실행 환경(CC/Codex/cron)에 스케줄 연결
20
+ /vibe.loop run <name> # 1회 반복 실행 (스케줄러가 호출하는 진입점)
21
+ /vibe.loop status [name] # 실행 이력 + 인박스 요약
22
+ /vibe.loop list # 정의된 루프 목록과 상태
23
+ ```
24
+
25
+ ## 경로 상수 (모든 서브커맨드 공통)
26
+
27
+ ```bash
28
+ HOOKS_DIR="${VIBE_PATH:-$(npm root -g 2>/dev/null)/@su-record/vibe}/hooks/scripts"
29
+ LOOPS_DIR=".vibe/loops"
30
+ INBOX="$LOOPS_DIR/inbox.md"
31
+ HISTORY=".vibe/metrics/loop-history.jsonl"
32
+ ```
33
+
34
+ ---
35
+
36
+ ## design — 루프 정의 생성
37
+
38
+ 1. 다음 5가지를 사용자에게 묻는다 (이미 인자로 답이 있으면 생략):
39
+ - **목표(goal)**: 루프가 0으로 만들려는 것 (예: "open 회귀 0건", "P1 lint 0건")
40
+ - **트리거**: `scheduled`(cron) / `manual` / `on-event`
41
+ - **발견(discover)**: 일거리를 어떻게 찾는가 — 명령 또는 지시
42
+ - **검증(verify)**: `ledger`(vibe.verify 경유, 기본) / `tests`(테스트 명령 exit code) / `none`(보고만)
43
+ - **격리(isolation)**: 병렬 항목이 파일을 수정하면 `worktree`, 아니면 `none`
44
+ 2. `{{VIBE_PATH}}/vibe/templates/loop-template.md`를 읽어 `.vibe/loops/<name>.md`를 생성한다.
45
+ 3. **생성 직후 반드시 검증한다** — 실패 시 정의를 고치고 재검증, 통과 전에는 install/run 금지:
46
+
47
+ ```bash
48
+ node -e "import('{{VIBE_PATH_URL}}/node_modules/@su-record/vibe/dist/tools/index.js').then(t => { const fs = require('fs'); const r = t.validateLoopDefinition(fs.readFileSync('.vibe/loops/<name>.md', 'utf-8')); console.log(JSON.stringify(r.errors.length ? r.errors : 'valid')); })"
49
+ ```
50
+
51
+ **기본 제안 레시피 — nightly-triage** (사용자가 목표를 정하지 못하면 이것을 제안):
52
+ - discover: ① `.vibe/regressions/`에서 `status: open` 항목 ② `npx vitest run` 실패 ③ `.vibe/contracts/` drift 의심 항목 → 우선순위 목록(P1 먼저)
53
+ - pipeline: `[vibe.run, vibe.verify]` — 항목별로 SPEC 범위 내 수정 → 검증
54
+ - verify: `ledger` / trigger: `scheduled` / schedule: `0 6 * * *` / isolation: `worktree`
55
+
56
+ ## install — 스케줄 연결
57
+
58
+ 루프 정의는 하네스 중립이다. 현재 환경을 감지해 정확한 설치 명령을 **사용자에게 제시**한다 (직접 등록하지 않는다 — 스케줄 등록은 사용자 결정):
59
+
60
+ | 환경 | 명령 |
61
+ |------|------|
62
+ | Claude Code (세션 루프) | `/loop <interval> "/vibe.loop run <name>"` |
63
+ | Claude Code (클라우드 루틴) | `/schedule` 로 cron `<schedule>` + 프롬프트 `/vibe.loop run <name>` 등록 |
64
+ | OS cron 폴백 | `<schedule> cd <project> && claude -p "/vibe.loop run <name>" --permission-mode acceptEdits` |
65
+ | Codex | Automations 탭에 `<schedule>` + `$vibe.loop run <name>` 등록 |
66
+
67
+ `trigger: manual` 루프는 install 불필요 — run만 안내한다.
68
+
69
+ ## run — 1회 반복 실행 (핵심)
70
+
71
+ `status: paused` 루프는 즉시 종료. 실행 순서는 **전부 의무**이며 생략 불가:
72
+
73
+ ```
74
+ 1. 검증 validateLoopDefinition 통과 확인 (위 design 3의 명령) — 실패 시 인박스에 기록 후 종료
75
+ 2. 시작 기록 node "$HOOKS_DIR/loop-ledger.js" start <name>
76
+ 3. DISCOVER 정의의 discover 지시 실행 → 일거리 목록 산출
77
+ 4. STUCK 검사 DISCOVER_HASH=$(node -e "...hashDiscoverOutput...") # 발견 목록 텍스트의 해시
78
+ node "$HOOKS_DIR/loop-ledger.js" check-stuck <name> "$DISCOVER_HASH"
79
+ → 'stuck'이면: end <name> stuck 기록 + 인박스에 "동일 발견 2회 연속 — 사람 개입 필요" 후 종료.
80
+ stuck 판단은 이 명령의 출력만 따른다. 모델이 "다시 해보면 될 것 같다"고 무시하는 것 금지.
81
+ 5. ACT 발견 항목을 우선순위순으로, max_iterations 한도 내에서 pipeline 체인 실행.
82
+ isolation: worktree면 항목별 worktree에서 (git-worktree 스킬 패턴).
83
+ scope-guard·verifyGate 등 기존 게이트는 그대로 적용된다 — 루프라고 우회하지 않는다.
84
+ 6. VERIFY 완료 판정 — verify 모드별 결정론 기준:
85
+ · ledger: cat .vibe/metrics/run-ledger.json → verifyPassed === true 만 성공
86
+ · tests: 정의의 test_command 실행 → exit 0 만 성공
87
+ · none: 판정 생략(보고만). "코드를 보니 잘 된 것 같다"는 판정이 아니다.
88
+ 7. 종료 기록 node "$HOOKS_DIR/loop-ledger.js" end <name> <ok|fail|stuck> "<한 줄 요약>"
89
+ 8. 인박스 $INBOX 상단에 결과 블록 prepend:
90
+ ## <name> — <ISO 시각> — <ok|fail|stuck>
91
+ - 발견: N건 / 처리: M건 / 검증: <기준과 결과>
92
+ - 리뷰 필요: <항목들 — 없으면 "없음">
93
+ ```
94
+
95
+ **금지**: `git push`, `gh pr merge`, `npm publish`, 버전 범프, 릴리즈 — 루프는 커밋까지만 가며(auto-commit verify 게이트 통과 시), 그 이상은 인박스를 본 사람이 한다.
96
+
97
+ ## status / list
98
+
99
+ - `list`: `$LOOPS_DIR/*.md`의 frontmatter(name, trigger, status, goal) 테이블.
100
+ - `status [name]`: `$HISTORY`에서 해당 루프(생략 시 전체)의 최근 10개 이벤트 + 인박스의 미처리 블록 수.
101
+
102
+ ## Auto-integration
103
+
104
+ - `/vibe.regress`의 open 항목, `/vibe.contract`의 drift는 nightly-triage discover의 1차 입력이다.
105
+ - 루프 안에서 verify 실패 시 기존 규칙대로 회귀 자동 등록(vibe.verify 경유)이 그대로 동작한다.
106
+
107
+ ## 설계 원칙 (요약)
108
+
109
+ | 원칙 | 구현 |
110
+ |------|------|
111
+ | 완료는 게이트가 판정 | run-ledger `verifyPassed` / 테스트 exit code (REQ-005) |
112
+ | stuck은 해시가 판정 | loop-ledger check-stuck, 동일 발견 2회 연속 (REQ-009) |
113
+ | 이해 부채 가드 | 인박스 리뷰 큐 + push/release 금지 (REQ-008) |
114
+ | 기록 없는 실행 없음 | loop-history.jsonl start/end 의무 (REQ-006) |
115
+
116
+ SPEC: `.vibe/specs/loop-engineering.md`
@@ -14,10 +14,12 @@ Execute **Scenario-Driven Implementation** with automatic quality verification.
14
14
  ## Usage
15
15
 
16
16
  ```
17
- /vibe.run "feature-name" # Full implementation
17
+ /vibe.run "feature-name" # Full implementation (loops to convergence)
18
18
  /vibe.run "feature-name" --phase 1 # Specific Phase only
19
- /vibe.run "feature-name" ultrawork # ULTRAWORK mode (recommended)
20
- /vibe.run "feature-name" ulw # Short alias for ultrawork
19
+ /vibe.run "feature-name" --interactive # Step-by-step confirmation per iteration
20
+ /vibe.run "feature-name" --max-iter 1 # Single-pass (no loop)
21
+ /vibe.run "feature-name" ultrawork # deprecated alias: automationLevel autonomous + parallel
22
+ /vibe.run "feature-name" ulw # deprecated alias: same as ultrawork
21
23
  ```
22
24
 
23
25
  ---
@@ -47,7 +49,7 @@ Execute **Scenario-Driven Implementation** with automatic quality verification.
47
49
  Load skill `regress` with: list --feature "{feature-name}"
48
50
  ```
49
51
 
50
- - Open regressions exist → interactive: ask user; ultrawork: auto-invoke `/vibe.regress generate <slug>`
52
+ - Open regressions exist → automationLevel confirm: ask user; autonomous: auto-invoke `/vibe.regress generate <slug>`
51
53
  - No open regressions → silently continue
52
54
 
53
55
  Also load `.vibe/contracts/{feature-name}.md` if present — use it as the contract reference during implementation.
@@ -60,8 +62,8 @@ test -f DESIGN.md
60
62
 
61
63
  - **DESIGN.md present OR no UI stack** → silently continue
62
64
  - **DESIGN.md absent AND UI stack present**:
63
- - interactive: 한 줄 안내 — "UI 작업에 `DESIGN.md` 시각 SSOT 가 없습니다. `/vibe.design init` 으로 생성하면 시각 드리프트가 자동 검출됩니다. (생략 가능 — 1 회만 안내)"
64
- - ultrawork: 무음 스킵
65
+ - automationLevel confirm: 한 줄 안내 — "UI 작업에 `DESIGN.md` 시각 SSOT 가 없습니다. `/vibe.design init` 으로 생성하면 시각 드리프트가 자동 검출됩니다. (생략 가능 — 1 회만 안내)"
66
+ - automationLevel autonomous: 무음 스킵
65
67
 
66
68
  > **권유 > 강제**. DESIGN.md 부재는 절대 vibe.run 을 블록하지 않는다.
67
69
 
@@ -89,7 +91,7 @@ test -f DESIGN.md
89
91
  └─────────────────────────────────────────────────────────────────┘
90
92
  ```
91
93
 
92
- > **하네스-안전 증분 (Dual-Harness Doctrine)**: 시나리오는 **가장 작은 검증 단위**다. 한 시나리오 구현 → 즉시 검증 → 다음. `ultrawork` 모드라도 이 단위는 무너뜨리지 않는다 (병렬은 시나리오 간, 검증은 시나리오별). 전문: `vibe/rules/principles/dual-harness-doctrine.md`.
94
+ > **하네스-안전 증분 (Dual-Harness Doctrine)**: 시나리오는 **가장 작은 검증 단위**다. 한 시나리오 구현 → 즉시 검증 → 다음. `automationLevel: autonomous`이라도 이 단위는 무너뜨리지 않는다 (병렬은 시나리오 간, 검증은 시나리오별). 전문: `vibe/rules/principles/dual-harness-doctrine.md`.
93
95
 
94
96
  ### Automated Verification (Closed Loop)
95
97
 
@@ -129,20 +131,22 @@ Scenario verification failed
129
131
  Repeat until pass (stuck 감지로 종료)
130
132
  ```
131
133
 
132
- **Termination conditions:**
134
+ **Termination conditions (loop-contract JUDGE):**
133
135
  - PASS → 다음 scenario
134
- - Stuck (같은 failure가 이전 라운드와 동일) → ask user or ultrawork: TODO + next scenario
136
+ - stuck (같은 failure가 이전 라운드와 동일, `loop-ledger.js check-stuck`) → automationLevel confirm: 사용자 질문; autonomous: TODO + next scenario
135
137
 
136
138
  ---
137
139
 
138
- ## **ULTRAWORK Mode** (ulw)
140
+ ## **ULTRAWORK Mode** (ulw) — deprecated alias
139
141
 
140
- > Read `references/ultrawork-mode.md` for the full Boulder Loop diagram, automation level definitions, and confirmation matrix.
142
+ > 루프 시맨틱은 `vibe/rules/loop-contract.md`를 따른다. `ultrawork`/`ulw`는 `automationLevel: autonomous` + 병렬 ACT의 deprecated 별칭이다.
143
+ > 전체 Boulder Loop 다이어그램, automation level 정의, confirmation matrix: `references/ultrawork-mode.md`
141
144
 
142
- When `ultrawork` or `ulw` is included:
143
- - Parallel Exploration + Boulder Loop + No Pause + Error Recovery (auto-TODO on stuck)
144
- - Race Review (GPT+Antigravity) enabled by default
145
- - Ralph Loop runs after all phases
145
+ `ultrawork` 또는 `ulw` 포함 시 vibe.run-specific 동작:
146
+ - 병렬 탐색 (3+ Task agents 동시)
147
+ - 비대화형 (중단점 없음)
148
+ - Race Review (GPT+Antigravity) 기본 활성화
149
+ - stuck 시 TODO 기록 후 다음 시나리오로 (사용자 질문 없음)
146
150
 
147
151
  ---
148
152
 
@@ -316,9 +320,9 @@ After all scenarios: GPT + Antigravity review in parallel. ULTRAWORK enables thi
316
320
 
317
321
  Auto-update scenario status with `Last verified` timestamp and quality score.
318
322
 
319
- ### 8. Ralph Loop (Coverage Verification)
323
+ ### 8. Coverage Verification Loop (RTM)
320
324
 
321
- > Read `references/ralph-loop.md` for the full RTM loop diagram, output format, and iteration rules.
325
+ > 루프 시맨틱은 `vibe/rules/loop-contract.md`를 따른다. 여기서의 exit 기준은 `coveragePercent === 100`. RTM 다이어그램, 출력 형식, 반복 규칙: `references/ralph-loop.md`
322
326
 
323
327
  After ALL phases complete:
324
328
 
@@ -329,7 +333,7 @@ node -e "import('{{VIBE_PATH_URL}}/node_modules/@su-record/vibe/dist/tools/index
329
333
 
330
334
  > Default SPEC path is `.vibe/specs/<feature>.md`. `status === 'empty'` must be treated as failed/not-applicable — never as 100% pass.
331
335
 
332
- Loop until `coveragePercent === 100` or stuck. Stuckinteractive: ask user; ultrawork TODO + done.
336
+ JUDGE: `coveragePercent === 100` 루프 종료. stuck(연속 2회 동일 커버리지) automationLevel confirm이면 사용자 질문; autonomous이면 TODO + done.
333
337
 
334
338
  ---
335
339
 
@@ -1,18 +1,20 @@
1
- # Ralph Loop — Full Reference
1
+ # Ralph Loop — Coverage Verification Reference
2
2
 
3
- > Loaded by vibe.run SKILL.md when Ralph Loop or ULTRAWORK is active (coverage verification).
3
+ > Loaded by vibe.run SKILL.md for RTM/coverage verification. **루프 시맨틱(ANCHOR/ACT/JUDGE/RECORD/stuck/max_iterations)의 SSOT는 `vibe/rules/loop-contract.md`다.** 파일은 vibe.run-specific한 RTM 기반 커버리지 검증 메커니즘을 문서화한다.
4
+ >
5
+ > `ralph`는 loop-contract의 deprecated 별칭으로, "exit: coverage-100"이 설정된 기본 루프와 동일하다. 더 이상 별도 모드가 아니다.
4
6
 
5
- ## Ralph Loop (Completion Verification)
7
+ ## Coverage Verification Loop
6
8
 
7
9
  > **Inspired by [ghuntley.com/ralph](https://ghuntley.com/ralph)**: "Deterministically bad in an undeterministic world" — Keep iterating until TRULY complete.
8
10
 
9
11
  **Problem**: AI often claims "complete" when implementation is partial.
10
12
 
11
- **Solution**: RTM-based automated coverage verification with iteration tracking.
13
+ **Solution**: RTM-based automated coverage verification loop-contract의 JUDGE 기준은 `coveragePercent === 100`.
12
14
 
13
15
  ```
14
16
  ┌─────────────────────────────────────────────────────────────────┐
15
- RALPH LOOP (Mandatory)
17
+ COVERAGE VERIFICATION LOOP (loop-contract + RTM)
16
18
  │ │
17
19
  │ After ALL phases complete: │
18
20
  │ │
@@ -50,15 +52,15 @@
50
52
  │ │
51
53
  │ │ │
52
54
  │ ↓ │
53
- Stuck? (coverage unchanged from prev iteration)
55
+ stuck? (loop-ledger.js check-stuck: 연속 2회 동일 커버리지)
54
56
  │ │ │
55
- │ ├─ Interactive: Ask user
57
+ │ ├─ automationLevel confirm: Ask user
56
58
  │ │ 1. Provide resolution → retry │
57
59
  │ │ 2. "proceed" → TODO + done │
58
60
  │ │ 3. "abort" → stop │
59
- │ └─ ultrawork: TODO + done
61
+ │ └─ automationLevel autonomous: TODO + done
60
62
  │ │
61
- NO iteration caploop until 100% OR stuck
63
+ max_iterations 기본 10도달 잔여 인박스 이월
62
64
  │ ZERO TOLERANCE for silent scope reduction │
63
65
  └─────────────────────────────────────────────────────────────────┘
64
66
  ```
@@ -84,16 +86,15 @@ node -e "import('{{VIBE_PATH_URL}}/node_modules/@su-record/vibe/dist/tools/index
84
86
  | `coveragePercent` | Overall coverage percentage |
85
87
  | `uncoveredRequirements` | List of missing REQ-* IDs |
86
88
 
87
- ## Ralph Loop Rules
89
+ ## Coverage Loop Rules
88
90
 
89
91
  | Rule | Description |
90
92
  |------|-------------|
91
93
  | **No Scope Reduction** | Never say "simplified" or "basic version" — implement FULL request |
92
- | **Iteration Tracking** | Display `[{{ITER}}]` to show progress (no max loop until done) |
94
+ | **Iteration Tracking** | Display `[{{ITER}}]` to show progress; max_iterations 기본 10 |
93
95
  | **RTM-Based Gap List** | Use `uncoveredRequirements` array — no manual comparison |
94
- | **Coverage Threshold** | Must reach 100% coverage to complete |
95
- | **No Iteration Cap** | Loop until 100% coverage OR stuck (convergence detected) |
96
- | **Stuck Handling** | If coverage % unchanged between iterations → ask user (proceed/abort/fix), or ultrawork → TODO + done |
96
+ | **Coverage Threshold** | JUDGE: `coveragePercent === 100` exit (loop-contract exit=coverage-100) |
97
+ | **Stuck Handling** | `loop-ledger.js check-stuck`: 연속 2회 동일 커버리지 confirm이면 질문; autonomous이면 TODO + done |
97
98
  | **Diminishing Returns** | Iteration 3+ → focus on core requirements (REQ-*-001~003) first; P2/P3 continue but lower priority |
98
99
 
99
100
  ## Ralph Loop Output Format
@@ -155,11 +156,11 @@ RTM saved: .vibe/rtm/login-rtm.md
155
156
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
156
157
  ```
157
158
 
158
- ## When to Trigger Ralph Loop
159
+ ## When to Trigger Coverage Loop
159
160
 
160
- 1. After all phases complete
161
+ 1. After all phases complete (기본 동작 — 별도 키워드 불필요)
161
162
  2. Before final quality report
162
- 3. Whenever user says "ultrawork" or "ralph"
163
+ 3. `ralph` 별칭 감지 기본 동작과 동일, exit=coverage-100으로 해석
163
164
 
164
165
  ## Forbidden Responses (VIOLATIONS)
165
166