@su-record/vibe 3.2.0 → 3.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +3 -2
- package/README.md +4 -1
- package/dist/cli/design/design-md-parser.test.js +2 -2
- package/dist/cli/design/design-md-parser.test.js.map +1 -1
- package/dist/cli/setup/Provisioner.d.ts.map +1 -1
- package/dist/cli/setup/Provisioner.js +23 -7
- package/dist/cli/setup/Provisioner.js.map +1 -1
- package/dist/cli/utils/cli-detector.d.ts +3 -3
- package/dist/cli/utils/cli-detector.d.ts.map +1 -1
- package/dist/cli/utils/cli-detector.js +7 -27
- package/dist/cli/utils/cli-detector.js.map +1 -1
- package/dist/cli/utils/cli-detector.test.js +3 -2
- package/dist/cli/utils/cli-detector.test.js.map +1 -1
- package/dist/infra/lib/llm-availability.d.ts.map +1 -1
- package/dist/infra/lib/llm-availability.js +2 -10
- package/dist/infra/lib/llm-availability.js.map +1 -1
- package/dist/infra/lib/llm-availability.test.js +3 -2
- package/dist/infra/lib/llm-availability.test.js.map +1 -1
- package/dist/infra/lib/utils.d.ts +6 -0
- package/dist/infra/lib/utils.d.ts.map +1 -1
- package/dist/infra/lib/utils.js +33 -0
- package/dist/infra/lib/utils.js.map +1 -1
- package/dist/tools/convention/validateCodeQuality.d.ts +1 -0
- package/dist/tools/convention/validateCodeQuality.d.ts.map +1 -1
- package/dist/tools/convention/validateCodeQuality.js +3 -2
- package/dist/tools/convention/validateCodeQuality.js.map +1 -1
- package/dist/tools/index.d.ts +2 -2
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +2 -0
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/spec/executionPacket.d.ts +101 -0
- package/dist/tools/spec/executionPacket.d.ts.map +1 -0
- package/dist/tools/spec/executionPacket.js +394 -0
- package/dist/tools/spec/executionPacket.js.map +1 -0
- package/dist/tools/spec/executionPacket.test.d.ts +2 -0
- package/dist/tools/spec/executionPacket.test.d.ts.map +1 -0
- package/dist/tools/spec/executionPacket.test.js +360 -0
- package/dist/tools/spec/executionPacket.test.js.map +1 -0
- package/dist/tools/spec/index.d.ts +2 -0
- package/dist/tools/spec/index.d.ts.map +1 -1
- package/dist/tools/spec/index.js +2 -0
- package/dist/tools/spec/index.js.map +1 -1
- package/dist/tools/spec/specGenerator.d.ts +12 -0
- package/dist/tools/spec/specGenerator.d.ts.map +1 -1
- package/dist/tools/spec/specGenerator.js +96 -14
- package/dist/tools/spec/specGenerator.js.map +1 -1
- package/dist/tools/spec/specGenerator.test.d.ts +2 -0
- package/dist/tools/spec/specGenerator.test.d.ts.map +1 -0
- package/dist/tools/spec/specGenerator.test.js +130 -0
- package/dist/tools/spec/specGenerator.test.js.map +1 -0
- package/hooks/scripts/__tests__/.vibe/command-log.txt +3 -3
- package/hooks/scripts/__tests__/llm-orchestrate-antigravity.test.js +5 -2
- package/hooks/scripts/__tests__/run-ledger-verify-required.test.js +7 -2
- package/hooks/scripts/__tests__/run-ledger.test.js +161 -10
- package/hooks/scripts/__tests__/utils-npm-root.test.js +5 -4
- package/hooks/scripts/lib/run-ledger.js +123 -21
- package/hooks/scripts/verify-ledger.js +23 -3
- package/package.json +1 -1
- package/skills/spec/SKILL.md +7 -0
- package/skills/vibe.run/SKILL.md +29 -6
- package/skills/vibe.trace/SKILL.md +3 -3
- package/skills/vibe.verify/SKILL.md +10 -4
- package/vibe/rules/loop-contract.md +7 -3
- package/vibe/templates/spec-template.md +32 -5
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Run Ledger — vibe.run 실행 및 vibe.verify 결과 추적.
|
|
3
3
|
*
|
|
4
4
|
* 파일 위치: <projectDir>/.vibe/metrics/run-ledger.json
|
|
5
|
-
* 형식: { runStarted, runFeature, verifyPassed, verifyAt, stopWarned,
|
|
5
|
+
* 형식: { runId, runStarted, runFeature, verifyPassed, verifyAt, stopWarned,
|
|
6
6
|
* verifyRequired, verifyRequiredReason }
|
|
7
7
|
*
|
|
8
8
|
* 모든 함수는 fail-open (try/catch, 오류 시 null/false 반환).
|
|
@@ -10,14 +10,56 @@
|
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
import fs from 'fs';
|
|
13
|
-
import
|
|
13
|
+
import { randomUUID } from 'crypto';
|
|
14
14
|
import path from 'path';
|
|
15
15
|
|
|
16
|
+
const EVIDENCE_SCHEMA_VERSION = '1.0.0';
|
|
17
|
+
const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
18
|
+
|
|
16
19
|
/** 레저 파일 경로 */
|
|
17
20
|
function ledgerPath(projectDir) {
|
|
18
21
|
return path.join(projectDir, '.vibe', 'metrics', 'run-ledger.json');
|
|
19
22
|
}
|
|
20
23
|
|
|
24
|
+
function evidencePath(projectDir, runId) {
|
|
25
|
+
if (!UUID_PATTERN.test(runId)) return null;
|
|
26
|
+
const runsDir = path.resolve(projectDir, '.vibe', 'runs');
|
|
27
|
+
const target = path.resolve(runsDir, runId, 'evidence.json');
|
|
28
|
+
return target.startsWith(`${runsDir}${path.sep}`) ? target : null;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function writeJsonAtomic(targetPath, data) {
|
|
32
|
+
if (!targetPath) return false;
|
|
33
|
+
const dir = path.dirname(targetPath);
|
|
34
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
35
|
+
const tmp = path.join(dir, `.${path.basename(targetPath)}.${process.pid}.${Date.now()}.tmp`);
|
|
36
|
+
try {
|
|
37
|
+
fs.writeFileSync(tmp, JSON.stringify(data, null, 2), 'utf-8');
|
|
38
|
+
fs.renameSync(tmp, targetPath);
|
|
39
|
+
return true;
|
|
40
|
+
} catch {
|
|
41
|
+
try { fs.rmSync(tmp, { force: true }); } catch { /* fail-open */ }
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function withLedgerLock(projectDir, operation) {
|
|
47
|
+
const lockPath = path.join(projectDir, '.vibe', 'metrics', 'run-ledger.lock');
|
|
48
|
+
let descriptor;
|
|
49
|
+
try {
|
|
50
|
+
fs.mkdirSync(path.dirname(lockPath), { recursive: true });
|
|
51
|
+
descriptor = fs.openSync(lockPath, 'wx');
|
|
52
|
+
return operation();
|
|
53
|
+
} catch {
|
|
54
|
+
return false;
|
|
55
|
+
} finally {
|
|
56
|
+
if (descriptor !== undefined) {
|
|
57
|
+
fs.closeSync(descriptor);
|
|
58
|
+
try { fs.rmSync(lockPath, { force: true }); } catch { /* fail-open */ }
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
21
63
|
/**
|
|
22
64
|
* 레저 파일 읽기.
|
|
23
65
|
* @param {string} projectDir
|
|
@@ -41,12 +83,61 @@ export function readLedger(projectDir) {
|
|
|
41
83
|
*/
|
|
42
84
|
function writeLedger(projectDir, data) {
|
|
43
85
|
try {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
86
|
+
return writeJsonAtomic(ledgerPath(projectDir), data);
|
|
87
|
+
} catch {
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function verificationResults(results) {
|
|
93
|
+
if (Array.isArray(results)) {
|
|
94
|
+
return results
|
|
95
|
+
.filter(item => item
|
|
96
|
+
&& typeof item === 'object'
|
|
97
|
+
&& typeof item.command === 'string'
|
|
98
|
+
&& Number.isInteger(item.exitCode))
|
|
99
|
+
.map(item => ({
|
|
100
|
+
command: item.command,
|
|
101
|
+
exitCode: item.exitCode,
|
|
102
|
+
}));
|
|
103
|
+
}
|
|
104
|
+
return [];
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function resolveSpecPath(projectDir, feature) {
|
|
108
|
+
if (typeof feature !== 'string' || feature.includes('..') || /[\\/]/.test(feature)) {
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
const flatPath = `.vibe/specs/${feature}.md`;
|
|
112
|
+
if (fs.existsSync(path.join(projectDir, flatPath))) return flatPath;
|
|
113
|
+
const splitPath = `.vibe/specs/${feature}/_index.md`;
|
|
114
|
+
return fs.existsSync(path.join(projectDir, splitPath)) ? splitPath : null;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function buildEvidence(projectDir, ledger, passed, generatedAt) {
|
|
118
|
+
return {
|
|
119
|
+
schemaVersion: EVIDENCE_SCHEMA_VERSION,
|
|
120
|
+
runId: ledger.runId,
|
|
121
|
+
specPath: resolveSpecPath(projectDir, ledger.runFeature),
|
|
122
|
+
generatedAt,
|
|
123
|
+
judges: {
|
|
124
|
+
deterministic: {
|
|
125
|
+
authority: 'blocking',
|
|
126
|
+
verifyPassed: Boolean(passed),
|
|
127
|
+
verificationResults: verificationResults(ledger.verificationResults),
|
|
128
|
+
},
|
|
129
|
+
model: { authority: 'advisory-only', canComplete: false },
|
|
130
|
+
humanTaste: { authority: 'release-only', canComplete: false },
|
|
131
|
+
},
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function writeEvidence(projectDir, ledger, passed, generatedAt) {
|
|
136
|
+
try {
|
|
137
|
+
return writeJsonAtomic(
|
|
138
|
+
evidencePath(projectDir, ledger.runId),
|
|
139
|
+
buildEvidence(projectDir, ledger, passed, generatedAt),
|
|
140
|
+
);
|
|
50
141
|
} catch {
|
|
51
142
|
return false;
|
|
52
143
|
}
|
|
@@ -59,10 +150,12 @@ function writeLedger(projectDir, data) {
|
|
|
59
150
|
* @returns {boolean} 성공 여부
|
|
60
151
|
*/
|
|
61
152
|
export function recordRunStart(projectDir, feature) {
|
|
62
|
-
|
|
153
|
+
return withLedgerLock(projectDir, () => {
|
|
63
154
|
const existing = readLedger(projectDir) || {};
|
|
155
|
+
const { verificationResults: _results, verificationCommands: _commands, ...retained } = existing;
|
|
64
156
|
const next = {
|
|
65
|
-
...
|
|
157
|
+
...retained,
|
|
158
|
+
runId: randomUUID(),
|
|
66
159
|
runStarted: new Date().toISOString(),
|
|
67
160
|
runFeature: feature || null,
|
|
68
161
|
verifyPassed: false,
|
|
@@ -70,9 +163,7 @@ export function recordRunStart(projectDir, feature) {
|
|
|
70
163
|
stopWarned: false,
|
|
71
164
|
};
|
|
72
165
|
return writeLedger(projectDir, next);
|
|
73
|
-
}
|
|
74
|
-
return false;
|
|
75
|
-
}
|
|
166
|
+
});
|
|
76
167
|
}
|
|
77
168
|
|
|
78
169
|
/**
|
|
@@ -80,25 +171,36 @@ export function recordRunStart(projectDir, feature) {
|
|
|
80
171
|
* pass 시 verifyRequired 상태를 클리어한다.
|
|
81
172
|
* @param {string} projectDir
|
|
82
173
|
* @param {boolean} passed - 검증 통과 여부
|
|
174
|
+
* @param {{runId?: string, verificationResults?: object[]}} options
|
|
83
175
|
* @returns {boolean} 성공 여부
|
|
84
176
|
*/
|
|
85
|
-
export function recordVerify(projectDir, passed) {
|
|
86
|
-
|
|
177
|
+
export function recordVerify(projectDir, passed, options = {}) {
|
|
178
|
+
return withLedgerLock(projectDir, () => {
|
|
87
179
|
const existing = readLedger(projectDir) || {};
|
|
180
|
+
const existingRunId = UUID_PATTERN.test(existing.runId || '') ? existing.runId : null;
|
|
181
|
+
if (existing.runId !== undefined && !existingRunId) return false;
|
|
182
|
+
if (existingRunId && options.runId !== existingRunId) return false;
|
|
183
|
+
const runId = existingRunId || randomUUID();
|
|
184
|
+
const results = verificationResults(options.verificationResults);
|
|
185
|
+
if (passed && (results.length === 0 || results.some(result => result.exitCode !== 0))) {
|
|
186
|
+
return false;
|
|
187
|
+
}
|
|
188
|
+
const generatedAt = new Date().toISOString();
|
|
88
189
|
const next = {
|
|
89
190
|
...existing,
|
|
191
|
+
runId,
|
|
90
192
|
verifyPassed: Boolean(passed),
|
|
91
|
-
verifyAt:
|
|
193
|
+
verifyAt: generatedAt,
|
|
194
|
+
verificationResults: results,
|
|
92
195
|
};
|
|
93
196
|
// pass 시 verifyRequired 클리어
|
|
94
197
|
if (passed) {
|
|
95
198
|
next.verifyRequired = false;
|
|
96
199
|
next.verifyRequiredReason = null;
|
|
97
200
|
}
|
|
201
|
+
if (!writeEvidence(projectDir, next, passed, generatedAt)) return false;
|
|
98
202
|
return writeLedger(projectDir, next);
|
|
99
|
-
}
|
|
100
|
-
return false;
|
|
101
|
-
}
|
|
203
|
+
});
|
|
102
204
|
}
|
|
103
205
|
|
|
104
206
|
/**
|
|
@@ -144,9 +246,9 @@ export function markStopWarned(projectDir) {
|
|
|
144
246
|
*/
|
|
145
247
|
export function extractRunFeature(prompt) {
|
|
146
248
|
try {
|
|
147
|
-
const m = prompt.match(/(?:\/|\$)vibe\.run\s+([^\s]+)/i);
|
|
249
|
+
const m = prompt.match(/(?:\/|\$)vibe\.run\s+(?:"([^"]+)"|'([^']+)'|([^\s]+))/i);
|
|
148
250
|
if (!m) return null;
|
|
149
|
-
const token = m[1];
|
|
251
|
+
const token = m[1] || m[2] || m[3];
|
|
150
252
|
// 플래그(-- 시작)나 키워드는 기능명이 아님
|
|
151
253
|
if (token.startsWith('-')) return null;
|
|
152
254
|
return token;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* verify-ledger CLI — vibe.verify 결과를 run-ledger에 기록.
|
|
4
4
|
*
|
|
5
|
-
* 사용법: node hooks/scripts/verify-ledger.js pass|fail
|
|
5
|
+
* 사용법: node hooks/scripts/verify-ledger.js pass|fail <run-id> <results-json-path>
|
|
6
6
|
*
|
|
7
7
|
* vibe.verify/SKILL.md 에서 검증 완료 시 호출.
|
|
8
8
|
* stdout 출력은 에이전트가 Bash로 실행하는 컨텍스트이므로 허용.
|
|
@@ -10,13 +10,33 @@
|
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
import { recordVerify } from './lib/run-ledger.js';
|
|
13
|
+
import fs from 'fs';
|
|
13
14
|
|
|
14
15
|
const arg = process.argv[2];
|
|
16
|
+
const runId = process.argv[3];
|
|
17
|
+
const resultsPath = process.argv[4];
|
|
15
18
|
const passed = arg === 'pass';
|
|
16
19
|
const projectDir = process.env.CLAUDE_PROJECT_DIR || process.cwd();
|
|
17
20
|
|
|
18
|
-
|
|
21
|
+
function readResults(filePath) {
|
|
22
|
+
try {
|
|
23
|
+
if (!filePath) return [];
|
|
24
|
+
const parsed = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
|
|
25
|
+
return Array.isArray(parsed) ? parsed : [];
|
|
26
|
+
} catch {
|
|
27
|
+
return [];
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const recorded = recordVerify(projectDir, passed, {
|
|
32
|
+
runId,
|
|
33
|
+
verificationResults: readResults(resultsPath),
|
|
34
|
+
});
|
|
19
35
|
|
|
20
36
|
const status = passed ? 'pass' : 'fail';
|
|
21
|
-
|
|
37
|
+
if (recorded) {
|
|
38
|
+
process.stdout.write(`[verify-ledger] recorded: verifyPassed=${passed} (${status})\n`);
|
|
39
|
+
} else {
|
|
40
|
+
process.stderr.write('[verify-ledger] WARNING: run-ledger or evidence.json write failed\n');
|
|
41
|
+
}
|
|
22
42
|
process.exit(0);
|
package/package.json
CHANGED
package/skills/spec/SKILL.md
CHANGED
|
@@ -54,10 +54,15 @@ Task(subagent_type="Explore",
|
|
|
54
54
|
`vibe/templates/spec-template.md` 구조로 `.vibe/specs/{feature-name}.md` 를 작성한다. 핵심 요건:
|
|
55
55
|
|
|
56
56
|
- **Overview / Goal** — 무엇을, 왜. 1-3 문장.
|
|
57
|
+
- **Context Sources** — 입력으로 사용한 파일·문서·URL·관측 상태. 출처 없는 추정은 Assumptions 로 분리.
|
|
58
|
+
- **Requirements** — `REQ-{feature}-NNN` ID와 연결된 Done Criteria를 표로 명시.
|
|
57
59
|
- **Done Criteria** — 결정론적 게이트만. 각 항목은 "명령/관찰로 pass·fail 판정 가능"해야 한다 (테스트 exit code, 빌드 성공, 특정 동작 관찰). "잘 동작한다" 류 서술 금지 — 이것이 루프의 JUDGE 입력이 된다.
|
|
60
|
+
- **Evidence Required** — Done 을 증명할 명령 결과·테스트 리포트·로그·스크린샷·코드 위치.
|
|
61
|
+
- **Human Taste (Non-Blocking)** — UX·브랜드·제품 감각처럼 release 시 사람이 판단할 기준. 완료 게이트로 쓰지 않는다.
|
|
58
62
|
- **Scenarios** — Given-When-Then. Happy path + 주요 edge case. 각 시나리오는 Done Criteria 중 하나에 매핑.
|
|
59
63
|
- **Out of Scope** — 이번에 하지 않는 것을 명시 (비어 있으면 스코프 팽창 신호).
|
|
60
64
|
- **Assumptions** — 3단계에서 채택한 기본값 전부.
|
|
65
|
+
- **Constraints** — 구현·보안·호환성 경계. execution packet으로 압축돼도 반드시 보존한다.
|
|
61
66
|
- **API Contract** (해당 시에만) — 엔드포인트/요청/응답 형태. 이 섹션이 있으면 이후 `/vibe.contract` 가 drift 를 검사한다.
|
|
62
67
|
|
|
63
68
|
이어서 `.vibe/features/{feature-name}.feature` 를 생성한다: 시나리오 섹션을 gherkin 으로 변환 (Done Criteria ↔ Scenario 매핑 유지). `/vibe.run` 이 이 파일을 구현·검증 단위로 사용한다.
|
|
@@ -71,6 +76,8 @@ Task(subagent_type="Explore",
|
|
|
71
76
|
작성 직후, 아래 체크리스트로 자기 SPEC 을 **1회** 점검하고 걸리는 항목을 즉시 고친다. 외부 LLM 리뷰 없음, 수렴 루프 없음 — 한 번 고치면 끝.
|
|
72
77
|
|
|
73
78
|
- [ ] 모든 Done Criteria 가 명령/관찰로 판정 가능한가 (모델 자기 보고가 아닌)
|
|
79
|
+
- [ ] Context Sources 와 Assumptions 가 분리됐고, 각 Done Criteria 의 Evidence Required 가 있는가
|
|
80
|
+
- [ ] Human Taste 가 결정론적 완료 게이트에 섞이지 않았는가
|
|
74
81
|
- [ ] 모든 시나리오가 Done Criteria 에 매핑되는가 (고아 시나리오 없음)
|
|
75
82
|
- [ ] 수치가 필요한 곳에 수치가 있는가 (제한·타임아웃·크기 — 없으면 기본값 + Assumptions)
|
|
76
83
|
- [ ] Out of Scope 가 비어 있지 않은가
|
package/skills/vibe.run/SKILL.md
CHANGED
|
@@ -201,18 +201,41 @@ Step 3: If neither → Error: "Run /vibe.spec first"
|
|
|
201
201
|
|
|
202
202
|
**Split structure:** Load `_index.md` first, then phase files in order. Execute phases sequentially (or per `--phase` flag).
|
|
203
203
|
|
|
204
|
+
### 1-0. Compile + validate execution packet (MANDATORY)
|
|
205
|
+
|
|
206
|
+
For a monolithic SPEC, compile it after resolving the canonical path. For a split SPEC, do not compile `_index.md`; defer this step until each active phase file is loaded in Phase Isolation Step B. Compile with `writeExecutionPacket`, then immediately verify the saved artifact with `validateExecutionPacket`.
|
|
207
|
+
|
|
208
|
+
```bash
|
|
209
|
+
node -e "import('file://{{VIBE_PATH}}/dist/tools/index.js').then(t => {
|
|
210
|
+
const projectPath=process.cwd(), specPath='.vibe/specs/{feature-name}.md';
|
|
211
|
+
const profile='{codex-or-claude-code}';
|
|
212
|
+
const written=t.writeExecutionPacket({projectPath,specPath,profile});
|
|
213
|
+
if(!written.ok){console.error(JSON.stringify(written.errors));process.exit(1)}
|
|
214
|
+
const checked=t.validateExecutionPacket({projectPath,specPath,packetPath:written.packetPath});
|
|
215
|
+
if(!checked.valid){console.error(checked.code);process.exit(1)}
|
|
216
|
+
console.log(written.packetPath);
|
|
217
|
+
})"
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
- Codex uses profile `codex`; Claude Code uses `claude-code`.
|
|
221
|
+
- Split SPECs compile each active `phase-N-*.md` immediately before that phase runs; `_index.md` remains the overview ANCHOR and is not treated as a phase contract.
|
|
222
|
+
- Use the packet only when validation returns `valid: true`.
|
|
223
|
+
- `STALE_PACKET`, invalid packet, preservation-audit failure, or budget failure is blocking: recompile from the canonical SPEC and never silently fall back to an unvalidated packet.
|
|
224
|
+
- The packet is a derived execution view. The canonical SPEC remains the ANCHOR and source of truth.
|
|
225
|
+
|
|
204
226
|
### 1-1. Phase Isolation Protocol (Large SPEC Guard, MANDATORY for 3+ phases)
|
|
205
227
|
|
|
206
228
|
```
|
|
207
229
|
Step A: Read _index.md (overview only — phase list, REQ IDs)
|
|
208
230
|
Step B: For each Phase N:
|
|
209
231
|
1. RE-READ Phase N SPEC section (every time, no memory)
|
|
210
|
-
2.
|
|
211
|
-
3.
|
|
212
|
-
4.
|
|
213
|
-
5.
|
|
214
|
-
6.
|
|
215
|
-
7.
|
|
232
|
+
2. Compile + validate Phase N execution packet using the phase file path
|
|
233
|
+
3. RE-READ Phase N Feature scenarios
|
|
234
|
+
4. Extract Phase N scope: files, scenarios, requirements
|
|
235
|
+
5. Implement Phase N scenarios
|
|
236
|
+
6. Verify Phase N
|
|
237
|
+
7. Write Phase Checkpoint → .vibe/checkpoints/
|
|
238
|
+
8. DISCARD Phase N details from working memory
|
|
216
239
|
Step C: Next Phase
|
|
217
240
|
```
|
|
218
241
|
|
|
@@ -213,18 +213,18 @@ RTM status === 'empty'
|
|
|
213
213
|
|
|
214
214
|
### Run-ledger flow
|
|
215
215
|
|
|
216
|
-
`/vibe.verify` records its outcome
|
|
216
|
+
`/vibe.verify` records its outcome through `hooks/scripts/verify-ledger.js`, binding the current run ID and command-result evidence. This writes `verifyPassed` and `verifyAt` into `.vibe/metrics/run-ledger.json`. Downstream gates consume this record:
|
|
217
217
|
|
|
218
218
|
| Gate | Behavior |
|
|
219
219
|
|------|----------|
|
|
220
220
|
| `auto-commit` | Commits only when `verifyPassed === true` AND `verifyAt > runStarted` |
|
|
221
221
|
| Stop hook | Warns when `runStarted && !verifyPassed`; blocks once if `verifyGate.mode === 'block'` |
|
|
222
222
|
|
|
223
|
-
**To register a passing trace as verified**, run `/vibe.verify` after `/vibe.trace` reports acceptable coverage. The verify skill
|
|
223
|
+
**To register a passing trace as verified**, run `/vibe.verify` after `/vibe.trace` reports acceptable coverage. The verify skill records the current run ID and command evidence internally — you do not invoke the ledger CLI manually.
|
|
224
224
|
|
|
225
225
|
```
|
|
226
226
|
/vibe.trace "login" → RTM: 9/9 (100%)
|
|
227
|
-
/vibe.verify "login" → runs checks →
|
|
227
|
+
/vibe.verify "login" → runs checks → records pass + run ID + command results
|
|
228
228
|
→ .vibe/metrics/run-ledger.json updated
|
|
229
229
|
auto-commit / Stop gate → verifyPassed=true, gate clears
|
|
230
230
|
```
|
|
@@ -104,7 +104,7 @@ Load skill `contract` with: check "{feature}"
|
|
|
104
104
|
|
|
105
105
|
### 7. Metrics + Ledger update (MANDATORY final step)
|
|
106
106
|
|
|
107
|
-
Record run metrics, then write the verify result to the run ledger. This is the machine-readable JUDGE record consumed by the Stop-hook verify gate, auto-commit verify gate, and loop-contract gates.
|
|
107
|
+
Record run metrics, then write the verify result to the run ledger. This is the machine-readable deterministic JUDGE record consumed by the Stop-hook verify gate, auto-commit verify gate, and loop-contract gates. `recordVerify` also writes `.vibe/runs/{run-id}/evidence.json`; Model Judge findings remain advisory-only and Human Taste remains release-only.
|
|
108
108
|
|
|
109
109
|
```bash
|
|
110
110
|
# Append step-count history (ok if current-run.json missing)
|
|
@@ -113,15 +113,21 @@ const fs=require('fs'),p='.vibe/metrics';
|
|
|
113
113
|
try{const c=JSON.parse(fs.readFileSync(p+'/current-run.json','utf-8'));
|
|
114
114
|
fs.appendFileSync(p+'/history.jsonl',JSON.stringify({verifiedAt:new Date().toISOString(),feature:c.feature,startedAt:c.startedAt,steps:c.steps||0})+'\n');}catch{}"
|
|
115
115
|
|
|
116
|
-
#
|
|
116
|
+
# Write the exact commands run in steps 2-3 and their exit codes.
|
|
117
|
+
mkdir -p .vibe/metrics
|
|
118
|
+
# Write `.vibe/metrics/verification-results.json` as:
|
|
119
|
+
# [{"command":"npm test","exitCode":0}, ...]
|
|
120
|
+
|
|
121
|
+
# Bind the result to the current run and its command evidence.
|
|
117
122
|
HOOKS_DIR="${VIBE_PATH:-$(npm root -g 2>/dev/null)/@su-record/vibe}/hooks/scripts"
|
|
118
|
-
|
|
123
|
+
RUN_ID=$(node -p "JSON.parse(require('fs').readFileSync('.vibe/metrics/run-ledger.json','utf8')).runId")
|
|
124
|
+
[ -f "$HOOKS_DIR/verify-ledger.js" ] && node "$HOOKS_DIR/verify-ledger.js" pass "$RUN_ID" .vibe/metrics/verification-results.json # or: fail
|
|
119
125
|
|
|
120
126
|
# Recipe extraction (best-effort, silent)
|
|
121
127
|
[ -f "$HOOKS_DIR/recipe-extractor.js" ] && node "$HOOKS_DIR/recipe-extractor.js" 2>/dev/null || true
|
|
122
128
|
```
|
|
123
129
|
|
|
124
|
-
Use `pass` only when the summary in step 4 is PASS; otherwise `fail`.
|
|
130
|
+
Use `pass` only when the summary in step 4 is PASS; otherwise `fail`. A passing record requires at least one command result and every exit code must be zero. A stale run ID, missing evidence, or mismatched result leaves `verifyPassed` unset and downstream gates treat the run as unverified.
|
|
125
131
|
|
|
126
132
|
## Failure escalation (convergence-based, no retry cap)
|
|
127
133
|
|
|
@@ -17,15 +17,19 @@
|
|
|
17
17
|
→ 루프:
|
|
18
18
|
ANCHOR 디스크에서 재고정: SPEC + run-ledger + scope.json (+ 직전 인박스)
|
|
19
19
|
ACT 파이프라인 실행 (스킬 체인)
|
|
20
|
-
JUDGE
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
JUDGE Deterministic Judge(blocking): run-ledger verifyPassed │ 테스트 exit code │ RTM status
|
|
21
|
+
Model Judge(advisory-only): 발견을 제안하지만 완료 권한 없음
|
|
22
|
+
Human Taste(release-only): UX·브랜드·제품 감각을 판단하지만 루프 완료 권한 없음
|
|
23
|
+
RECORD run-ledger + `.vibe/runs/{run-id}/evidence.json` + loop-history.jsonl
|
|
23
24
|
→ 종료(EXIT): 게이트 전부 통과 │ stuck │ max_iterations │ 예산 상한
|
|
24
25
|
```
|
|
25
26
|
|
|
26
27
|
### ANCHOR가 컨텍스트 오염 방어인 이유
|
|
27
28
|
루프 상태는 컨텍스트가 아니라 디스크에 산다. 매 회전이 아티팩트에서 다시 시작하므로 컨텍스트가 오염되거나 compact로 소실돼도 루프는 깨지지 않으며, 회전마다 fresh 컨텍스트(서브에이전트)로 돌려도 된다.
|
|
28
29
|
|
|
30
|
+
### Judge 권한 경계
|
|
31
|
+
종료 권한은 테스트 exit code·run-ledger·RTM 같은 **결정론적 Judge**에만 있다. Model Judge는 누락·모순·위험을 발견하는 보조 수단이며, 발견을 테스트나 관측 가능한 기준으로 내리기 전에는 차단 근거가 아니다. Human Taste는 공개·배포 시점의 사람 판단으로 남고 루프의 완료 상태를 변경하지 않는다.
|
|
32
|
+
|
|
29
33
|
### stuck (결정론)
|
|
30
34
|
연속 2회 회전의 발견(discover/findings) 해시가 동일 → 중단하고 사람에게 (`loop-ledger.js check-stuck`이 판정·기록). "다시 해보면 될 것 같다"는 모델 판단으로 무시 금지.
|
|
31
35
|
|
|
@@ -10,13 +10,29 @@
|
|
|
10
10
|
|
|
11
11
|
{What and why — 1-3 sentences.}
|
|
12
12
|
|
|
13
|
+
### Context Sources
|
|
14
|
+
|
|
15
|
+
- {File, document, URL, or observed system state used as input}
|
|
16
|
+
|
|
13
17
|
### Assumptions
|
|
14
18
|
|
|
15
19
|
- {Default adopted without asking — e.g., session expiry 24h}
|
|
16
20
|
|
|
21
|
+
### Constraints
|
|
22
|
+
|
|
23
|
+
- {Invariant or implementation boundary that every execution packet must preserve}
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## 2. Requirements
|
|
28
|
+
|
|
29
|
+
| ID | Requirement | Done Criteria |
|
|
30
|
+
|----|-------------|---------------|
|
|
31
|
+
| REQ-{feature}-001 | {Observable functional requirement} | D1, D2 |
|
|
32
|
+
|
|
17
33
|
---
|
|
18
34
|
|
|
19
|
-
##
|
|
35
|
+
## 3. Done Criteria (deterministic gates)
|
|
20
36
|
|
|
21
37
|
> Each criterion must be judgeable by a command or observable behavior — never by self-report.
|
|
22
38
|
> These are the JUDGE inputs of the loop (`vibe/rules/loop-contract.md`); `/vibe.verify` records the result in `.vibe/metrics/run-ledger.json`.
|
|
@@ -26,9 +42,18 @@
|
|
|
26
42
|
| D1 | {e.g., all scenarios in the feature file pass} | {e.g., `npx vitest run` exit 0} |
|
|
27
43
|
| D2 | {e.g., build succeeds with no type errors} | {e.g., `npm run build` exit 0} |
|
|
28
44
|
|
|
45
|
+
### Evidence Required
|
|
46
|
+
|
|
47
|
+
- D1 → {Command result, test report, log, screenshot, or verified code location}
|
|
48
|
+
- D2 → {Evidence required for this specific criterion}
|
|
49
|
+
|
|
50
|
+
### Human Taste (Non-Blocking)
|
|
51
|
+
|
|
52
|
+
- {UX, brand, or product-quality review reserved for the release decision; never a loop completion gate}
|
|
53
|
+
|
|
29
54
|
---
|
|
30
55
|
|
|
31
|
-
##
|
|
56
|
+
## 4. Scenarios
|
|
32
57
|
|
|
33
58
|
> Mirrored to `.vibe/features/{feature}.feature` (gherkin). Every scenario maps to a Done criterion.
|
|
34
59
|
|
|
@@ -46,13 +71,13 @@ Scenario: {Edge case title} # → D1
|
|
|
46
71
|
|
|
47
72
|
---
|
|
48
73
|
|
|
49
|
-
##
|
|
74
|
+
## 5. Out of Scope
|
|
50
75
|
|
|
51
76
|
- {Explicitly not doing this time — must not be empty}
|
|
52
77
|
|
|
53
78
|
---
|
|
54
79
|
|
|
55
|
-
##
|
|
80
|
+
## 6. API Contract (only if the feature exposes an API)
|
|
56
81
|
|
|
57
82
|
> Presence of this section enables `/vibe.contract` drift detection.
|
|
58
83
|
|
|
@@ -64,8 +89,10 @@ Response: 201 {...}
|
|
|
64
89
|
|
|
65
90
|
---
|
|
66
91
|
|
|
67
|
-
##
|
|
92
|
+
## 7. Verification
|
|
68
93
|
|
|
69
94
|
- `/vibe.run "{feature}"` implements scenario-by-scenario, verifying each immediately.
|
|
70
95
|
- `/vibe.verify "{feature}"` judges the Done Criteria and sets `verifyPassed` in the run-ledger.
|
|
96
|
+
- Verification writes `.vibe/runs/{run-id}/evidence.json`; only deterministic Judge results can complete the loop.
|
|
97
|
+
- Model Judge findings are advisory-only. Human Taste is release-only.
|
|
71
98
|
- Gate = all Done Criteria pass (exit codes / observed behavior) — loop continues until gates pass, stuck, or max iterations.
|