@su-record/vibe 3.2.12 → 3.2.14

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 (53) hide show
  1. package/CLAUDE.md +15 -14
  2. package/README.en.md +1 -1
  3. package/README.md +1 -1
  4. package/dist/__tests__/engines-contract.test.d.ts +2 -0
  5. package/dist/__tests__/engines-contract.test.d.ts.map +1 -0
  6. package/dist/__tests__/engines-contract.test.js +72 -0
  7. package/dist/__tests__/engines-contract.test.js.map +1 -0
  8. package/dist/__tests__/instruction-drift.test.d.ts +2 -0
  9. package/dist/__tests__/instruction-drift.test.d.ts.map +1 -0
  10. package/dist/__tests__/instruction-drift.test.js +116 -0
  11. package/dist/__tests__/instruction-drift.test.js.map +1 -0
  12. package/dist/__tests__/stakes-contract.test.js +7 -0
  13. package/dist/__tests__/stakes-contract.test.js.map +1 -1
  14. package/dist/__tests__/stuck-semantics.test.js +52 -0
  15. package/dist/__tests__/stuck-semantics.test.js.map +1 -1
  16. package/dist/cli/commands/upgrade.d.ts.map +1 -1
  17. package/dist/cli/commands/upgrade.js +7 -7
  18. package/dist/cli/commands/upgrade.js.map +1 -1
  19. package/dist/cli/postinstall/fs-utils.d.ts.map +1 -1
  20. package/dist/cli/postinstall/fs-utils.js +53 -4
  21. package/dist/cli/postinstall/fs-utils.js.map +1 -1
  22. package/dist/cli/postinstall/fs-utils.test.js +48 -0
  23. package/dist/cli/postinstall/fs-utils.test.js.map +1 -1
  24. package/dist/cli/setup/ProjectSetup.d.ts +6 -0
  25. package/dist/cli/setup/ProjectSetup.d.ts.map +1 -1
  26. package/dist/cli/setup/ProjectSetup.js +19 -13
  27. package/dist/cli/setup/ProjectSetup.js.map +1 -1
  28. package/hooks/hooks.json +1 -1
  29. package/hooks/scripts/__tests__/.vibe/command-log.txt +3 -3
  30. package/hooks/scripts/__tests__/anchor-inbox.test.js +119 -0
  31. package/hooks/scripts/__tests__/code-check-false-positive.test.js +74 -0
  32. package/hooks/scripts/__tests__/fixtures/seq-harness.js +13 -0
  33. package/hooks/scripts/__tests__/fixtures/seq-step.js +22 -0
  34. package/hooks/scripts/__tests__/stop-dispatcher-sequential.test.js +74 -0
  35. package/hooks/scripts/code-check.js +60 -50
  36. package/hooks/scripts/lib/anchor.js +74 -0
  37. package/hooks/scripts/lib/console-allow.js +66 -0
  38. package/hooks/scripts/lib/dispatcher.js +28 -16
  39. package/hooks/scripts/lib/inbox.js +60 -0
  40. package/hooks/scripts/loop-ledger.js +24 -1
  41. package/hooks/scripts/post-edit-dispatcher.js +4 -3
  42. package/hooks/scripts/post-edit.js +2 -2
  43. package/package.json +5 -3
  44. package/skills/vibe/SKILL.md +2 -1
  45. package/skills/vibe.clone/references/verification-loops.md +6 -3
  46. package/skills/vibe.loop/SKILL.md +7 -4
  47. package/skills/vibe.review/SKILL.md +5 -1
  48. package/skills/vibe.run/references/e2e-and-autofix.md +1 -1
  49. package/skills/vibe.run/references/process-steps.md +1 -1
  50. package/vibe/constitution.md +1 -1
  51. package/vibe/rules/loop-contract.md +40 -3
  52. package/vibe/rules/quality/checklist.md +3 -3
  53. package/vibe/templates/constitution-template.md +1 -1
@@ -178,10 +178,11 @@ Phase 4: /vibe.verify → 검증
178
178
  스킬 **이름과 인자**가 계약이고, 그것을 실제 호출로 바꾸는 것은 각 하네스의 몫이다.
179
179
 
180
180
  각 phase 종료 후 JUDGE 단계:
181
- - 게이트 통과 (P1=0 ∧ verifyPassed) → 루프 종료, Phase 5 보고
181
+ - 게이트 통과 (**측정된** P1=0 ∧ verifyPassed) → 루프 종료, Phase 5 보고. 판정된 P1(리뷰어 findings)은 단독으로 게이트를 막지 않는다 — SSOT: `vibe/rules/loop-contract.md` Judge 권한 경계
182
182
  - 게이트 미통과 → RECORD(run-ledger + loop-history.jsonl) 후 다음 ANCHOR로
183
183
  - stuck(연속 2회 동일 findings 해시) → **어느 automationLevel 에서도 루프를 종료한다.** `confirm`이면 사용자 질문, `autonomous`이면 질문 없이 TODO 기록 후 다음 독립 단위로. 미달을 완료로 기록하지 않는다 (SSOT: `vibe/rules/loop-contract.md` stuck 절)
184
184
  - max_iterations(기본 10) 도달 → 잔여를 인박스로 이월
185
+ - **실행 실패(error)** — 스킬 미설치·도구 부재·파일 없음·명령 비정상 종료는 stuck 이 아니다(해시 비교로 안 잡힌다). 같은 방식으로 재시도하지 않고 루프를 종료한다: `confirm` 이면 원인을 제시하고 조치/건너뛰기/중단을 묻고, `autonomous` 이면 `loop-ledger.js inbox <name> fail "<원인>"` 기록 후 다음 독립 단위로. 실행 실패도 완료로 기록하지 않는다 (SSOT: `vibe/rules/loop-contract.md` 실행 실패 절)
185
186
 
186
187
  ### Phase 5: 종료 보고
187
188
 
@@ -6,7 +6,8 @@
6
6
  ## Phase 4: Compile Gate
7
7
 
8
8
  ```
9
- No round cap. Loop until compile succeeds (or stuck → ask user).
9
+ No round cap. Loop until compile succeeds (or stuck → end loop; automationLevel decides
10
+ whether the user is asked — see Termination below).
10
11
 
11
12
  0. Capture baseline (before Phase 3): record existing tsc + build errors
12
13
  → Phase 4 only fixes NEW errors
@@ -33,7 +34,8 @@ Termination:
33
34
  **⛔ Skipping Phase 5 makes the entire clone "incomplete".**
34
35
 
35
36
  ```
36
- No round cap. Loop until P1=0 (or stuck → ask user).
37
+ No round cap. Loop until P1=0 (or stuck → end loop; automationLevel decides whether the
38
+ user is asked — see Termination below).
37
39
  Infrastructure: src/infra/lib/browser/ (Puppeteer + CDP) — same as figma Phase 6.
38
40
 
39
41
  1. Render scaffolded page in dev server at matching viewport
@@ -52,7 +54,8 @@ Narrowing scope:
52
54
  Termination:
53
55
  ✅ P1=0 AND no new findings → complete
54
56
  ⚠️ Stuck: same findings → ask user (resolve / proceed / abort)
55
- automationLevel: autonomous → on stuck, record TODO without prompting and complete
57
+ automationLevel: autonomous → on stuck, record TODO without prompting and end the loop
58
+ as `stuck` — never record unresolved P1 as complete (SSOT: vibe/rules/loop-contract.md)
56
59
 
57
60
  Responsive: after MO verification → change viewport → repeat against PC screenshot
58
61
  Post-merge (Phase 3C): re-run at BOTH viewports (375×812 vs mo/screenshot.png,
@@ -69,6 +69,9 @@ node -e "import('{{VIBE_PATH_URL}}/node_modules/@su-record/vibe/dist/tools/index
69
69
  `status: paused` 루프는 즉시 종료. 실행 순서는 **전부 의무**이며 생략 불가:
70
70
 
71
71
  ```
72
+ 0. ANCHOR node "$HOOKS_DIR/loop-ledger.js" anchor [feature]
73
+ → missing[] 이 비어 있지 않으면 없는 아티팩트를 기억으로 메우지 않는다.
74
+ 재고정 실패를 인박스에 남기고 종료한다.
72
75
  1. 검증 validateLoopDefinition 통과 확인 (위 design 3의 명령) — 실패 시 인박스에 기록 후 종료
73
76
  2. 시작 기록 node "$HOOKS_DIR/loop-ledger.js" start <name>
74
77
  3. DISCOVER 정의의 discover 지시 실행 → 일거리 목록 산출
@@ -84,10 +87,10 @@ node -e "import('{{VIBE_PATH_URL}}/node_modules/@su-record/vibe/dist/tools/index
84
87
  · tests: 정의의 test_command 실행 → exit 0 만 성공
85
88
  · none: 판정 생략(보고만). "코드를 보니 잘 된 것 같다"는 판정이 아니다.
86
89
  7. 종료 기록 node "$HOOKS_DIR/loop-ledger.js" end <name> <ok|fail|stuck> "<한 줄 요약>"
87
- 8. 인박스 $INBOX 상단에 결과 블록 prepend:
88
- ## <name> <ISO 시각> <ok|fail|stuck>
89
- - 발견: N건 / 처리: M건 / 검증: <기준과 결과>
90
- - 리뷰 필요: <항목들 없으면 "없음">
90
+ 8. 인박스 node "$HOOKS_DIR/loop-ledger.js" inbox <name> <ok|fail|stuck> \
91
+ "발견: N건 / 처리: M건 / 검증: <기준과 결과>" \
92
+ "리뷰 필요: <항목들 없으면 없음>"
93
+ 블록 형식과 최신순 정렬은 명령이 보장한다. 손으로 마크다운을 쓰지 않는다.
91
94
  ```
92
95
 
93
96
  **금지**: `git push`, `gh pr merge`, `npm publish`, 버전 범프, 릴리즈 — 루프는 커밋까지만 가며(auto-commit verify 게이트 통과 시), 그 이상은 인박스를 본 사람이 한다.
@@ -79,7 +79,11 @@ user-invocable: true
79
79
 
80
80
  - **P1 = 0 means MERGE READY** — mergeable even with remaining P2/P3
81
81
  - **P1 = 0 after auto-fix means DONE** — record P2 auto-fix failures as TODO and stop
82
- - **Final P1 list unchanged after Review Debate → DONE** — no new findings = converged
82
+ - **P1 = 0 AND final P1 list unchanged after Review Debate → DONE** — converged
83
+ - **P1 > 0 AND final P1 list unchanged → STUCK, not DONE** — 같은 발견이 2회 연속이면
84
+ 루프는 종료하되 **완료로 기록하지 않는다**. `confirm` 이면 사용자에게 묻고, `autonomous`
85
+ 이면 TODO 로 남긴다 (SSOT: `vibe/rules/loop-contract.md` stuck 절). "목록이 안 바뀌었으니
86
+ 수렴했다" 는 남은 P1 을 완료로 포장하는 것이다
83
87
 
84
88
  ### Anti-Patterns (FORBIDDEN)
85
89
 
@@ -34,7 +34,7 @@ Scenario verification failed
34
34
 
35
35
  **Stakes 프로파일 (SSOT: `vibe/rules/loop-contract.md` Stakes 표):**
36
36
  - `demo`/`prototype` → max_iterations 1, 리뷰 1패스, **검증 스크립트 신규 생성 금지** — 검증은 기존 테스트 러너·브라우저 게이트만 사용한다. 새 verify_*.py / 검증 전용 스크립트 파일을 만들지 않는다.
37
- - JUDGE 검증 산출물 절제 (모든 stakes): 이번 feature 신규 검증 코드 바이트 합이 신규 구현 코드 바이트 합을 초과하면 (`git diff --numstat` 기준) P2 경고를 run-ledger 기록한다. advisory — 게이트 통과 여부는 불변.
37
+ - JUDGE 검증 산출물 절제 (모든 stakes): 이번 feature 신규 검증 코드 수가 신규 구현 코드 수를 초과하면 (`git diff --numstat` 기준) **최종 보고에 P2 경고 1줄**을 적는다. run-ledger 에는 적재하지 않는다 (경고 필드 없음). advisory — 게이트 통과 여부는 불변.
38
38
 
39
39
  ---
40
40
 
@@ -173,7 +173,7 @@ node -e "import('{{VIBE_PATH_URL}}/node_modules/@su-record/vibe/dist/tools/index
173
173
 
174
174
  > Default SPEC path is `.vibe/specs/<feature>.md`. `status === 'empty'` must be treated as failed/not-applicable — never as 100% pass.
175
175
 
176
- JUDGE: `coveragePercent === 100` → 루프 종료. stuck(연속 2회 동일 커버리지) → automationLevel confirm이면 사용자 질문; autonomous이면 TODO + done.
176
+ JUDGE: `coveragePercent === 100` → 루프 종료. stuck(연속 2회 동일 발견 해시 — `loop-ledger.js check-stuck`; 커버리지 수치가 아니라 발견으로 판정한다) → **어느 automationLevel 에서도 루프를 종료한다**; confirm이면 사용자 질문, autonomous이면 TODO 기록 후 다음 독립 단위로. 미달 커버리지를 완료로 기록하지 않는다 (SSOT: `vibe/rules/loop-contract.md`).
177
177
 
178
178
  ---
179
179
 
@@ -81,7 +81,7 @@ All reference documents are stored globally and specified in `.vibe/config.json`
81
81
  - **DRY**: Don't Repeat Yourself
82
82
  - **SRP**: Single Responsibility Principle
83
83
  - **YAGNI**: You Aren't Gonna Need It
84
- - **Functions ≤30 lines** (recommended), ≤50 lines (allowed)
84
+ - **Functions ≤50 lines** (SSOT: `CLAUDE.md` Complexity Limits)
85
85
  - **Cyclomatic Complexity ≤10**
86
86
  - **Cognitive Complexity ≤15**
87
87
 
@@ -21,15 +21,35 @@
21
21
  Model Judge(advisory-only): 발견을 제안하지만 완료 권한 없음
22
22
  Human Taste(release-only): UX·브랜드·제품 감각을 판단하지만 루프 완료 권한 없음
23
23
  RECORD run-ledger + `.vibe/runs/{run-id}/evidence.json` + loop-history.jsonl
24
- → 종료(EXIT): 게이트 전부 통과 │ stuck │ max_iterations │ 예산 상한
24
+ → 종료(EXIT): 게이트 전부 통과 │ stuck │ max_iterations │ 예산 상한 │ 실행 실패(error)
25
25
  ```
26
26
 
27
27
  ### ANCHOR가 컨텍스트 오염 방어인 이유
28
28
  루프 상태는 컨텍스트가 아니라 디스크에 산다. 매 회전이 아티팩트에서 다시 시작하므로 컨텍스트가 오염되거나 compact로 소실돼도 루프는 깨지지 않으며, 회전마다 fresh 컨텍스트(서브에이전트)로 돌려도 된다.
29
29
 
30
+ **재고정은 명령으로 한다** — 모델의 기억이 아니라 디스크가 답한다:
31
+
32
+ ```bash
33
+ node "$HOOKS_DIR/loop-ledger.js" anchor [feature]
34
+ # → { feature, spec, scope, ledger, latestInbox, missing[] }
35
+ ```
36
+
37
+ `missing` 이 비어 있지 않으면 그 회전은 재고정에 실패한 것이다 — 없는 아티팩트를 기억으로 메우지 않는다. JUDGE·RECORD·stuck 이 전부 명령으로 판정되는데 ANCHOR만 산문 지시로 남아 있으면, 정작 오염 방어의 근거가 되는 단계가 가장 약해진다.
38
+
30
39
  ### Judge 권한 경계
31
40
  종료 권한은 테스트 exit code·run-ledger·RTM 같은 **결정론적 Judge**에만 있다. Model Judge는 누락·모순·위험을 발견하는 보조 수단이며, 발견을 테스트나 관측 가능한 기준으로 내리기 전에는 차단 근거가 아니다. Human Taste는 공개·배포 시점의 사람 판단으로 남고 루프의 완료 상태를 변경하지 않는다.
32
41
 
42
+ #### P1 은 출처가 둘이다 — exit 기준은 이를 구분한다
43
+
44
+ "P1" 이 가리키는 것이 두 가지이고, 위 권한 경계는 그중 하나에만 적용된다.
45
+
46
+ | 출처 | 예 | 성격 | exit 게이트 |
47
+ |---|---|---|---|
48
+ | **측정된 P1** | clone Phase 5 `pixelmatch diffRatio > 0.05`, computed CSS delta > 2px, contract drift, 테스트 실패 | 결정론 | **차단한다** — 게이트 통과 = 측정 P1 0 |
49
+ | **판정된 P1** | `vibe.review` 리뷰어 findings | Model Judge | **단독으로 차단하지 않는다** — 테스트·관측 기준으로 내려야 게이트가 된다 |
50
+
51
+ 판정된 P1 이 남았는데 내릴 기준이 없으면, 그것은 게이트 실패가 아니라 **인박스로 가는 리뷰 항목**이다. 동일한 판정 P1 이 2회 연속 반복되면 stuck 이며 — 완료가 아니다 (아래 stuck 절).
52
+
33
53
  ### stuck (결정론)
34
54
  연속 2회 회전의 발견(discover/findings) 해시가 동일 → **그 루프는 종료한다** (`loop-ledger.js check-stuck`이 판정·기록). "다시 해보면 될 것 같다"는 모델 판단으로 무시 금지.
35
55
 
@@ -42,12 +62,25 @@
42
62
 
43
63
  > `autonomous` 의 "계속" 은 **stuck 난 루프를 더 돌린다는 뜻이 아니다** — 2회 연속 동일 발견은 정의상 재시도가 무의미하다. 같은 목표를 붙잡지 않고 다음 단위로 넘어간다는 뜻이며, 미달은 TODO/인박스에 남는다. 미달 상태를 **완료로 기록하지 않는다.**
44
64
 
65
+ ### 실행 실패 (error) — stuck 과 다른 종료 사유
66
+
67
+ stuck 은 **같은 발견이 반복되는** 상태다. 스킬이 로드되지 않거나, 도구가 없거나, 파일이 없거나, 명령이 비정상 종료하는 것은 stuck 이 아니라 **실행 실패**이며 해시 비교로는 잡히지 않는다. 재시도 대상도 아니다 — 환경이 바뀌지 않는 한 결과가 같다.
68
+
69
+ | | 루프 | 사람에게 질문 | 그 다음 |
70
+ |---|---|---|---|
71
+ | `confirm` | 종료 | **한다** (원인 제시 + 조치 요청 / 건너뛰기 / 중단) | 사용자 응답에 따름 |
72
+ | `autonomous` | 종료 | 하지 않음 | 인박스에 원인 기록 후 **다음 독립 단위로** |
73
+
74
+ - 실패한 단계를 **같은 방식으로 재시도하지 않는다.** 재시도가 의미 있으려면 무엇이 달라지는지 말할 수 있어야 한다 (`vibe.review` 의 escalation ladder 가 그 예 — 재시도 1회 → 다른 하네스 1회 → TODO).
75
+ - 실행 실패도 **완료가 아니다.** stuck 과 동일하게, 미달 상태를 완료로 기록하지 않는다.
76
+ - 원인은 인박스에 남긴다: `loop-ledger.js inbox <name> fail "<원인 한 줄>"`.
77
+
45
78
  ## 파라미터 (기본값)
46
79
 
47
80
  | 파라미터 | 기본 | 의미 |
48
81
  |---|---|---|
49
82
  | `max_iterations` | 10 | 회전 상한. 도달 시 잔여를 인박스로 이월 |
50
- | `exit` | 게이트 통과 (P1=0 ∧ verifyPassed) | 종료 기준. coverage 100% 등으로 상향 가능 |
83
+ | `exit` | 게이트 통과 (**측정된** P1=0 ∧ verifyPassed) | 종료 기준. coverage 100% 등으로 상향 가능. 판정된 P1 은 위 Judge 권한 경계 표를 따른다 |
51
84
  | `--interactive` | off | 단계별 확인 모드 (회전마다 사람 승인 — 과거의 기본값) |
52
85
  | `--max-iter N` | — | 회전 상한 명시 (N=1이면 1회 시도) |
53
86
  | `automationLevel` | `confirm` | `confirm`(SPEC·stuck에서 질문) / `autonomous`(질문 없이 TODO 기록 후 다음 단위로, 비대화형) — `.vibe/config.json`. **어느 값에서도 stuck 은 루프를 종료한다** (위 stuck 절) |
@@ -69,7 +102,11 @@
69
102
 
70
103
  ### JUDGE 검증 산출물 절제 (모든 stakes 공통)
71
104
 
72
- JUDGE는 이번 feature의 **신규 생성 파일** 기준으로 검증 코드 총량(테스트·검증 스크립트)과 구현 코드 총량을 `git diff --numstat` 비교한다. 검증 코드 바이트 합 > 구현 코드 바이트 합이면 **P2 경고**를 run-ledger 에 기록한다 (restraint 원칙의 프로세스 적용). 경고는 advisory — 게이트 통과 여부를 바꾸지 않는다.
105
+ 검증은 실패 비용보다 싸야 한다 검증 코드가 구현 코드보다 커지면 루프는 남는 장사가 아니다.
106
+
107
+ JUDGE는 이번 feature의 **신규 생성 파일** 기준으로 검증 코드 총량(테스트·검증 스크립트)과 구현 코드 총량을 `git diff --numstat` 로 비교하고, 검증 코드 줄 수가 구현 코드 줄 수를 넘으면 **최종 보고에 P2 경고 1줄**을 적는다 (restraint 원칙의 프로세스 적용).
108
+
109
+ > ⚠️ 이 경고는 **보고용이며 어디에도 적재되지 않는다.** run-ledger 스키마(`runId`·`runStarted`·`runFeature`·`verifyPassed`·`verifyAt`·`stopWarned`·`verifyRequired`·`verifyRequiredReason`)에는 경고 필드가 없다 — 기록을 지시하면 갈 곳 없는 지시가 된다. 게이트 통과 여부를 바꾸지 않는다.
73
110
 
74
111
  ## 금지 (루프 권한 경계)
75
112
 
@@ -28,7 +28,7 @@ const typeSafety = {
28
28
  ```typescript
29
29
  const codeStructure = {
30
30
  singleResponsibility: true, // ✅ Single Responsibility Principle
31
- functionUnder30Lines: true, // ✅ Functions ≤30 lines (recommended), 50 allowed
31
+ functionUnder50Lines: true, // ✅ Functions ≤50 lines
32
32
  maxNesting3Levels: true, // ✅ Max nesting 3 levels
33
33
  cyclomaticComplexity: 10, // ✅ Cyclomatic complexity ≤ 10
34
34
  cognitiveComplexity: 15, // ✅ Cognitive complexity ≤ 15
@@ -171,7 +171,7 @@ const bundleOptimization = {
171
171
 
172
172
  ```text
173
173
  [ ] Follow Single Responsibility Principle
174
- [ ] Keep function length ≤30 lines (max 50)
174
+ [ ] Keep function length ≤50 lines
175
175
  [ ] Nesting depth ≤3 levels
176
176
  [ ] Extract magic numbers to constants
177
177
  [ ] Ensure type safety
@@ -266,7 +266,7 @@ npm run format:check
266
266
  ```text
267
267
  ✅ Only modified requested scope?
268
268
  ✅ No any types?
269
- ✅ Functions ≤30 lines? (max 50)
269
+ ✅ Functions ≤50 lines?
270
270
  ✅ Nesting ≤3 levels?
271
271
  ✅ Error handling implemented?
272
272
  ✅ Magic numbers extracted to constants?
@@ -99,7 +99,7 @@ All reference documents are stored globally and specified in `.vibe/config.json`
99
99
  - **DRY**: Don't Repeat Yourself
100
100
  - **SRP**: Single Responsibility Principle
101
101
  - **YAGNI**: You Aren't Gonna Need It
102
- - **Functions ≤30 lines** (recommended), ≤50 lines (allowed)
102
+ - **Functions ≤50 lines** (SSOT: `CLAUDE.md` Complexity Limits)
103
103
  - **Cyclomatic Complexity ≤10**
104
104
  - **Cognitive Complexity ≤15**
105
105