@su-record/vibe 3.2.62 → 3.2.64
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/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/CLAUDE.md +27 -4
- package/dist/tools/bench/benchCompare.d.ts +78 -0
- package/dist/tools/bench/benchCompare.d.ts.map +1 -0
- package/dist/tools/bench/benchCompare.js +113 -0
- package/dist/tools/bench/benchCompare.js.map +1 -0
- package/dist/tools/bench/benchDefinition.d.ts +37 -0
- package/dist/tools/bench/benchDefinition.d.ts.map +1 -0
- package/dist/tools/bench/benchDefinition.js +69 -0
- package/dist/tools/bench/benchDefinition.js.map +1 -0
- package/dist/tools/bench/benchReport.d.ts +16 -0
- package/dist/tools/bench/benchReport.d.ts.map +1 -0
- package/dist/tools/bench/benchReport.js +64 -0
- package/dist/tools/bench/benchReport.js.map +1 -0
- package/dist/tools/bench/index.d.ts +10 -0
- package/dist/tools/bench/index.d.ts.map +1 -0
- package/dist/tools/bench/index.js +7 -0
- package/dist/tools/bench/index.js.map +1 -0
- package/dist/tools/contract/agentContract.d.ts +86 -0
- package/dist/tools/contract/agentContract.d.ts.map +1 -0
- package/dist/tools/contract/agentContract.js +140 -0
- package/dist/tools/contract/agentContract.js.map +1 -0
- package/dist/tools/contract/index.d.ts +8 -0
- package/dist/tools/contract/index.d.ts.map +1 -0
- package/dist/tools/contract/index.js +6 -0
- package/dist/tools/contract/index.js.map +1 -0
- package/dist/tools/contract/reverseDrift.d.ts +71 -0
- package/dist/tools/contract/reverseDrift.d.ts.map +1 -0
- package/dist/tools/contract/reverseDrift.js +136 -0
- package/dist/tools/contract/reverseDrift.js.map +1 -0
- package/dist/tools/index.d.ts +6 -0
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +6 -0
- package/dist/tools/index.js.map +1 -1
- package/hooks/scripts/code-check.js +7 -0
- package/hooks/scripts/lib/ephemeral-lane.js +106 -0
- package/hooks/scripts/lib/iteration-cost.js +178 -0
- package/hooks/scripts/lib/loop-ledger.js +38 -5
- package/hooks/scripts/loop-ledger.js +9 -1
- package/hooks/scripts/pre-tool-guard.js +16 -0
- package/hooks/scripts/step-counter.js +29 -0
- package/package.json +1 -1
- package/skills/vibe.contract/SKILL.md +114 -1
- package/skills/vibe.loop/SKILL.md +47 -1
- package/skills/vibe.spec/SKILL.md +2 -1
- package/vibe/rules/loop-contract.md +25 -5
- package/vibe/templates/spec-template.md +15 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vibe",
|
|
3
3
|
"displayName": "Vibe",
|
|
4
|
-
"version": "3.2.
|
|
4
|
+
"version": "3.2.64",
|
|
5
5
|
"description": "Verification harness for AI coding agents — \"done\" is decided by deterministic gates (test exit codes, run-ledger, regression memory), not the model self-report.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "su-record",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vibe",
|
|
3
|
-
"version": "3.2.
|
|
3
|
+
"version": "3.2.64",
|
|
4
4
|
"description": "Verification harness for AI coding agents — \"done\" is decided by deterministic gates (test exit codes, run-ledger, regression memory), not the model self-report.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "su-record",
|
package/CLAUDE.md
CHANGED
|
@@ -75,6 +75,19 @@ Loop semantics SSOT: `vibe/rules/loop-contract.md` (ANCHOR→ACT→JUDGE→RECOR
|
|
|
75
75
|
|
|
76
76
|
이 표 전부가 CI(`test.yml`)에서 돈다 — 아무도 안 돌리는 가드는 아무것도 잡지 못한다. **배포 순서는 PR 병합 먼저, 태그는 그다음** — 태그를 먼저 밀면 보호 브랜치에 막혀 병합이 실패해도 CI 가 이미 npm 에 게시한다(실측 v3.2.19).
|
|
77
77
|
|
|
78
|
+
**릴리스 입구는 둘이다** (`.github/workflows/release.yml`):
|
|
79
|
+
|
|
80
|
+
| 입구 | 언제 | 검사 주체 |
|
|
81
|
+
|---|---|---|
|
|
82
|
+
| `push: tags: ['v*']` | 기본 — `pnpm release` 가 미는 태그 | `scripts/release.sh` 가 태그 전에 확인 |
|
|
83
|
+
| `workflow_dispatch` (`version` 입력) | **태그를 밀 수 없는 환경** (에이전트 세션의 이그레스 정책 등) | 워크플로 안의 Guard 스텝 |
|
|
84
|
+
|
|
85
|
+
dispatch 는 `scripts/release.sh` 를 거치지 않으므로 워크플로가 스스로 같은 것을 확인한다: main 에서만 실행 · 입력 버전이 `package.json` 과 일치 · 같은 태그 부재. 태그는 **테스트 통과 뒤 publish 앞에** 워크플로가 만든다 — 깨진 빌드에 태그를 남기지도, 게시된 버전에 태그가 없게 두지도 않는다. 두 입구가 쓰는 태그 이름은 워크플로 `env.RELEASE_TAG` 한 곳에서 만든다(스텝마다 분기하면 한쪽만 조용히 틀린 태그로 게시된다).
|
|
86
|
+
|
|
87
|
+
> ⚠️ dispatch 의 태그 push 가 Release 워크플로를 **다시 부르지 않는 이유**는 `GITHUB_TOKEN` 으로 만든 push 가 워크플로를 트리거하지 않기 때문이다(GitHub 재귀 방지). PAT·App 토큰으로 바꾸면 같은 버전이 두 번 게시된다.
|
|
88
|
+
|
|
89
|
+
**버전 범프는 여전히 PR 로 병합된 뒤여야 한다** — dispatch 는 태그 push 만 대체하고 병합 순서는 바꾸지 않는다.
|
|
90
|
+
|
|
78
91
|
### Config Locations
|
|
79
92
|
| Path | Purpose |
|
|
80
93
|
|---|---|
|
|
@@ -174,10 +187,10 @@ Claude Code 는 `Workflow` 도구(dynamic workflows)를 제공한다 — 한 실
|
|
|
174
187
|
- `/vibe.run` — SPEC-driven implementation
|
|
175
188
|
- `/vibe.verify` — implementation vs SPEC verification
|
|
176
189
|
- `/vibe.regress` — regression test auto-evolution. Auto-registers on verify failure; `generate` produces preventive tests; `cluster` promotes recurring patterns.
|
|
177
|
-
- `/vibe.contract` — API contract drift detection. Compares the contract extracted from the SPEC against the implementation; P1 drift auto-propagates to regress.
|
|
190
|
+
- `/vibe.contract` — API contract drift detection. Compares the contract extracted from the SPEC against the implementation; P1 drift auto-propagates to regress. `reverse` runs the other direction (implementation → SPEC) and routes SPEC gaps to the inbox — it never blocks the loop. `agent` asserts a SPEC's Agent Contract against an agent's tool-call log — that one does block, because a logged call is observed fact rather than a model's judgement.
|
|
178
191
|
- `/vibe.trace` — Requirements Traceability Matrix
|
|
179
|
-
- `/vibe.loop` — loop engineering. Goal loops whose completion is judged by deterministic gates (run-ledger/tests), with stuck detection by discover-hash and a human triage inbox. Loops never push/release.
|
|
180
|
-
- `/vibe.test` — vibe self-test
|
|
192
|
+
- `/vibe.loop` — loop engineering. Goal loops whose completion is judged by deterministic gates (run-ledger/tests), with stuck detection by discover-hash and a human triage inbox. Loops never push/release. `bench` self-compares loop settings and reports 'inconclusive' rather than inventing a difference the sample cannot support.
|
|
193
|
+
- `/vibe.test` — vibe self-test. Probes every shipped surface (commands, skills, hooks, agents) in one install dir and writes a pass/fail report with STCV skill-quality verdicts. One command, no subcommands. Recommended before every release.
|
|
181
194
|
|
|
182
195
|
| Task Size | Approach |
|
|
183
196
|
|---|---|
|
|
@@ -211,6 +224,16 @@ Public skills use the `vibe.*` namespace and are classified as **entry** / **sta
|
|
|
211
224
|
|
|
212
225
|
**Include**: `.vibe/{plans,specs,features,todos,research,regressions,contracts,recipes,anti-patterns,loops,config.json,constitution.md}`, `CLAUDE.md`, `plugins/vibe/` (배포 트리 — 생성물이지만 커밋한다, 위 "배포 3경로" 참조)
|
|
213
226
|
**Vibe-global (not project-local)**: `~/.vibe/test-reports/` — `/vibe.test` artifacts live with the vibe install, not with the project
|
|
214
|
-
**Exclude**: `~/.claude/{rules,commands,agents,skills}/`, `.claude/settings.local.json`, `.codex/hooks.json`, `.vibe/{memories,checkpoints,metrics,gates}/`
|
|
227
|
+
**Exclude**: `~/.claude/{rules,commands,agents,skills}/`, `.claude/settings.local.json`, `.codex/hooks.json`, `.vibe/{memories,checkpoints,metrics,gates,ephemeral}/`
|
|
228
|
+
|
|
229
|
+
### 일회성 코드 레인 (`lifetime` 축)
|
|
230
|
+
|
|
231
|
+
`.vibe/ephemeral/` 은 **생성·실행 후 폐기되는 코드**의 자리다 — 분석 스크립트, 일회성 프로브. 품질 검사(`code-check` 훅)를 면제받는 대신 **커밋되지 않는다.** 면제받은 코드가 배포되면 그 면제가 곧 구멍이다.
|
|
232
|
+
|
|
233
|
+
- **판정은 모델이 아니라 경로가 한다.** "이건 일회성이라 린트 면제" 를 모델이 정하면 축이 아니라 뒷문이고, 뒷문은 바쁠 때 쓰인다. 경로가 판정하면 면제 대상이 `ls` 하나로 감사된다
|
|
234
|
+
- 정규화 후 판정한다 — `.vibe/ephemeral/../src/x.ts` 는 일회성이 **아니다**. 상위 탈출로 면제를 훔칠 수 없다
|
|
235
|
+
- **방어 순서**: `.gitignore` 가 **1차**(`git add` 는 무시된 경로를 거부한다), `pre-tool-guard` 가 `git add -f` 하나를 막는 **심층 방어**. 훅은 프로젝트 로컬이라 미설치가 흔하므로 1차가 아니다
|
|
236
|
+
- 면제 범위는 `code-check` 품질 검사뿐. 라쳇·린트·테스트는 커밋된 소스를 보므로 이 경로를 애초에 보지 않는다
|
|
237
|
+
- 판정에 실패하면 **일회성이 아니라고 답한다**(fail-safe). 잘못된 면제는 조용히 게이트를 끄지만, 잘못된 비면제는 검사가 한 번 더 도는 것뿐이다
|
|
215
238
|
|
|
216
239
|
<!-- VIBE:END -->
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 벤치 집계·판정 — **결론을 낼 수 없을 때 내지 않는 것**이 이 모듈의 목적이다.
|
|
3
|
+
*
|
|
4
|
+
* 벤치 산출물은 곧 문서의 수치가 된다. 판정을 사람의 절제에 맡기면 constitution §3.5 가
|
|
5
|
+
* 금지하는 배수·퍼센트가 바로 그 자리에서 만들어진다 — "5회 돌렸더니 A 가 30% 빨랐다" 는
|
|
6
|
+
* 표본이 5일 때 아무 의미가 없는데도 문장으로는 완벽하게 그럴듯하다. 그래서 판정 불가를
|
|
7
|
+
* 코드가 낸다.
|
|
8
|
+
*
|
|
9
|
+
* 두 가지를 의도적으로 만들지 않았다:
|
|
10
|
+
* - **비율·퍼센트 필드가 없다.** 있으면 쓰이고, 쓰이면 근거 없는 배수가 문서로 간다.
|
|
11
|
+
* 차이는 절대 단위 `delta` 로만 낸다
|
|
12
|
+
* - **`winner` 판정이 없다.** "차이가 관측됐다" 와 "A 를 골라라" 는 다른 주장이다.
|
|
13
|
+
* 지표의 방향(적을수록 좋은가)은 도메인 지식이고 코드는 그것을 모른다
|
|
14
|
+
*/
|
|
15
|
+
/** 실행 1회의 기록 — `readBudget` 산출물에서 뽑는다 */
|
|
16
|
+
export interface BenchRun {
|
|
17
|
+
armId: string;
|
|
18
|
+
/** 이 실행이 수행한 과제 셋의 해시 — 다른 일을 시킨 결과는 비교 대상이 아니다 */
|
|
19
|
+
taskSetHash: string;
|
|
20
|
+
gatesPassed: boolean;
|
|
21
|
+
iterations: number;
|
|
22
|
+
/** `iteration-cost.js` 의 신뢰도 규약을 그대로 잇는다 */
|
|
23
|
+
cost?: {
|
|
24
|
+
measured?: boolean;
|
|
25
|
+
truncated?: boolean;
|
|
26
|
+
toolCalls?: number | null;
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
/** 실행이 집계에서 빠진 사유 — 사유 없이 빠지면 분모가 조용히 줄어든다 */
|
|
30
|
+
export type BenchExclusion = 'cost-unmeasured' | 'cost-truncated';
|
|
31
|
+
export interface BenchRange {
|
|
32
|
+
min: number;
|
|
33
|
+
max: number;
|
|
34
|
+
mean: number;
|
|
35
|
+
}
|
|
36
|
+
export interface ArmSummary {
|
|
37
|
+
armId: string;
|
|
38
|
+
totalRuns: number;
|
|
39
|
+
usableRuns: number;
|
|
40
|
+
excluded: Record<BenchExclusion, number>;
|
|
41
|
+
/** 관측된 과제 셋 해시(중복 제거) — 2개 이상이면 이 arm 안에서 이미 섞였다 */
|
|
42
|
+
taskSetHashes: string[];
|
|
43
|
+
/** usable 실행 중 게이트를 통과한 수. **비율이 아니라 개수다** — 분모는 usableRuns */
|
|
44
|
+
gatePassed: number;
|
|
45
|
+
iterations: BenchRange | null;
|
|
46
|
+
toolCalls: BenchRange | null;
|
|
47
|
+
}
|
|
48
|
+
export type BenchVerdict = 'insufficient-runs' | 'mixed-task-sets' | 'inconclusive' | 'difference-observed';
|
|
49
|
+
export type BenchMetric = 'iterations' | 'toolCalls';
|
|
50
|
+
export interface BenchComparison {
|
|
51
|
+
metric: BenchMetric;
|
|
52
|
+
verdict: BenchVerdict;
|
|
53
|
+
reason: string;
|
|
54
|
+
arms: [ArmSummary, ArmSummary];
|
|
55
|
+
/**
|
|
56
|
+
* 두 번째 arm 의 평균 − 첫 번째 arm 의 평균. **절대 단위**이고 비율이 아니다.
|
|
57
|
+
* `difference-observed` 가 아니면 `null` — 판정하지 못한 차이는 수치로 내지 않는다.
|
|
58
|
+
*/
|
|
59
|
+
delta: number | null;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* arm 하나의 집계.
|
|
63
|
+
*
|
|
64
|
+
* 과제 셋이 다른 실행을 **여기서 조용히 버리지 않는다** — 관측된 해시를 전부 남기고
|
|
65
|
+
* 판정은 `compareArms` 가 한다. 버리면 사용자가 의도하지 않은 부분집합 위에서 비교가
|
|
66
|
+
* 진행되고, 그 사실이 결과 어디에도 안 보인다.
|
|
67
|
+
*/
|
|
68
|
+
export declare function summarizeArm(armId: string, runs: readonly BenchRun[]): ArmSummary;
|
|
69
|
+
/**
|
|
70
|
+
* 두 arm 을 하나의 지표로 비교한다.
|
|
71
|
+
*
|
|
72
|
+
* 판정 규칙은 **보수적이고 비모수적**이다: 두 arm 의 관측 범위(min~max)가 겹치면
|
|
73
|
+
* `inconclusive`. 유의성 검정이 아니며 그런 척하지 않는다 — 표본이 한 자릿수인데
|
|
74
|
+
* t-검정을 붙이면 정밀해 **보이는** 숫자가 나오고, 없는 정밀도를 만드는 것이 근거 없는
|
|
75
|
+
* 배수보다 나을 것이 없다.
|
|
76
|
+
*/
|
|
77
|
+
export declare function compareArms(a: ArmSummary, b: ArmSummary, metric?: BenchMetric, minRunsPerArm?: number): BenchComparison;
|
|
78
|
+
//# sourceMappingURL=benchCompare.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"benchCompare.d.ts","sourceRoot":"","sources":["../../../src/tools/bench/benchCompare.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAIH,yCAAyC;AACzC,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,mDAAmD;IACnD,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,OAAO,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,4CAA4C;IAC5C,IAAI,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,OAAO,CAAC;QAAC,SAAS,CAAC,EAAE,OAAO,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;CAC/E;AAED,8CAA8C;AAC9C,MAAM,MAAM,cAAc,GAAG,iBAAiB,GAAG,gBAAgB,CAAC;AAElE,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC,cAAc,EAAE,MAAM,CAAC,CAAC;IACzC,oDAAoD;IACpD,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,+DAA+D;IAC/D,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,UAAU,GAAG,IAAI,CAAC;IAC9B,SAAS,EAAE,UAAU,GAAG,IAAI,CAAC;CAC9B;AAED,MAAM,MAAM,YAAY,GACpB,mBAAmB,GACnB,iBAAiB,GACjB,cAAc,GACd,qBAAqB,CAAC;AAE1B,MAAM,MAAM,WAAW,GAAG,YAAY,GAAG,WAAW,CAAC;AAErD,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,WAAW,CAAC;IACpB,OAAO,EAAE,YAAY,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;IAC/B;;;OAGG;IACH,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAeD;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,QAAQ,EAAE,GAAG,UAAU,CAqBjF;AAWD;;;;;;;GAOG;AACH,wBAAgB,WAAW,CACzB,CAAC,EAAE,UAAU,EACb,CAAC,EAAE,UAAU,EACb,MAAM,GAAE,WAA0B,EAClC,aAAa,GAAE,MAAiC,GAC/C,eAAe,CAuCjB"}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 벤치 집계·판정 — **결론을 낼 수 없을 때 내지 않는 것**이 이 모듈의 목적이다.
|
|
3
|
+
*
|
|
4
|
+
* 벤치 산출물은 곧 문서의 수치가 된다. 판정을 사람의 절제에 맡기면 constitution §3.5 가
|
|
5
|
+
* 금지하는 배수·퍼센트가 바로 그 자리에서 만들어진다 — "5회 돌렸더니 A 가 30% 빨랐다" 는
|
|
6
|
+
* 표본이 5일 때 아무 의미가 없는데도 문장으로는 완벽하게 그럴듯하다. 그래서 판정 불가를
|
|
7
|
+
* 코드가 낸다.
|
|
8
|
+
*
|
|
9
|
+
* 두 가지를 의도적으로 만들지 않았다:
|
|
10
|
+
* - **비율·퍼센트 필드가 없다.** 있으면 쓰이고, 쓰이면 근거 없는 배수가 문서로 간다.
|
|
11
|
+
* 차이는 절대 단위 `delta` 로만 낸다
|
|
12
|
+
* - **`winner` 판정이 없다.** "차이가 관측됐다" 와 "A 를 골라라" 는 다른 주장이다.
|
|
13
|
+
* 지표의 방향(적을수록 좋은가)은 도메인 지식이고 코드는 그것을 모른다
|
|
14
|
+
*/
|
|
15
|
+
import { DEFAULT_MIN_RUNS_PER_ARM } from './benchDefinition.js';
|
|
16
|
+
function range(values) {
|
|
17
|
+
if (values.length === 0)
|
|
18
|
+
return null;
|
|
19
|
+
const sum = values.reduce((acc, v) => acc + v, 0);
|
|
20
|
+
return { min: Math.min(...values), max: Math.max(...values), mean: sum / values.length };
|
|
21
|
+
}
|
|
22
|
+
function exclusionOf(run) {
|
|
23
|
+
const cost = run.cost;
|
|
24
|
+
if (!cost || cost.measured !== true)
|
|
25
|
+
return 'cost-unmeasured';
|
|
26
|
+
if (cost.truncated === true)
|
|
27
|
+
return 'cost-truncated';
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* arm 하나의 집계.
|
|
32
|
+
*
|
|
33
|
+
* 과제 셋이 다른 실행을 **여기서 조용히 버리지 않는다** — 관측된 해시를 전부 남기고
|
|
34
|
+
* 판정은 `compareArms` 가 한다. 버리면 사용자가 의도하지 않은 부분집합 위에서 비교가
|
|
35
|
+
* 진행되고, 그 사실이 결과 어디에도 안 보인다.
|
|
36
|
+
*/
|
|
37
|
+
export function summarizeArm(armId, runs) {
|
|
38
|
+
const mine = runs.filter((run) => run.armId === armId);
|
|
39
|
+
const excluded = { 'cost-unmeasured': 0, 'cost-truncated': 0 };
|
|
40
|
+
const usable = [];
|
|
41
|
+
for (const run of mine) {
|
|
42
|
+
const reason = exclusionOf(run);
|
|
43
|
+
if (reason)
|
|
44
|
+
excluded[reason] += 1;
|
|
45
|
+
else
|
|
46
|
+
usable.push(run);
|
|
47
|
+
}
|
|
48
|
+
return {
|
|
49
|
+
armId,
|
|
50
|
+
totalRuns: mine.length,
|
|
51
|
+
usableRuns: usable.length,
|
|
52
|
+
excluded,
|
|
53
|
+
taskSetHashes: [...new Set(mine.map((run) => run.taskSetHash))].sort(),
|
|
54
|
+
gatePassed: usable.filter((run) => run.gatesPassed).length,
|
|
55
|
+
iterations: range(usable.map((run) => run.iterations)),
|
|
56
|
+
toolCalls: range(usable.map((run) => Number(run.cost?.toolCalls) || 0)),
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
function taskSetsDiffer(a, b) {
|
|
60
|
+
if (a.taskSetHashes.length !== 1 || b.taskSetHashes.length !== 1)
|
|
61
|
+
return true;
|
|
62
|
+
return a.taskSetHashes[0] !== b.taskSetHashes[0];
|
|
63
|
+
}
|
|
64
|
+
function overlaps(a, b) {
|
|
65
|
+
return a.min <= b.max && b.min <= a.max;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* 두 arm 을 하나의 지표로 비교한다.
|
|
69
|
+
*
|
|
70
|
+
* 판정 규칙은 **보수적이고 비모수적**이다: 두 arm 의 관측 범위(min~max)가 겹치면
|
|
71
|
+
* `inconclusive`. 유의성 검정이 아니며 그런 척하지 않는다 — 표본이 한 자릿수인데
|
|
72
|
+
* t-검정을 붙이면 정밀해 **보이는** 숫자가 나오고, 없는 정밀도를 만드는 것이 근거 없는
|
|
73
|
+
* 배수보다 나을 것이 없다.
|
|
74
|
+
*/
|
|
75
|
+
export function compareArms(a, b, metric = 'iterations', minRunsPerArm = DEFAULT_MIN_RUNS_PER_ARM) {
|
|
76
|
+
const base = { metric, arms: [a, b], delta: null };
|
|
77
|
+
if (taskSetsDiffer(a, b)) {
|
|
78
|
+
return {
|
|
79
|
+
...base,
|
|
80
|
+
verdict: 'mixed-task-sets',
|
|
81
|
+
reason: '두 arm 이 같은 과제 셋을 돌지 않았다 — 다른 일을 시킨 결과는 비교가 아니다.',
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
if (a.usableRuns < minRunsPerArm || b.usableRuns < minRunsPerArm) {
|
|
85
|
+
return {
|
|
86
|
+
...base,
|
|
87
|
+
verdict: 'insufficient-runs',
|
|
88
|
+
reason: `사용 가능한 실행이 arm 당 ${minRunsPerArm}회 미만이다 `
|
|
89
|
+
+ `(${a.armId}: ${a.usableRuns} · ${b.armId}: ${b.usableRuns}) — 어떤 차이든 우연과 구분되지 않는다.`,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
const left = a[metric];
|
|
93
|
+
const right = b[metric];
|
|
94
|
+
if (!left || !right) {
|
|
95
|
+
return { ...base, verdict: 'insufficient-runs', reason: `${metric} 관측값이 없다.` };
|
|
96
|
+
}
|
|
97
|
+
if (overlaps(left, right)) {
|
|
98
|
+
return {
|
|
99
|
+
...base,
|
|
100
|
+
verdict: 'inconclusive',
|
|
101
|
+
reason: `관측 범위가 겹친다 (${a.armId}: ${left.min}~${left.max} · ${b.armId}: ${right.min}~${right.max})`
|
|
102
|
+
+ ' — 이 표본으로는 차이를 말할 수 없다.',
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
return {
|
|
106
|
+
...base,
|
|
107
|
+
verdict: 'difference-observed',
|
|
108
|
+
reason: `관측 범위가 겹치지 않는다 (${a.armId}: ${left.min}~${left.max} · ${b.armId}: ${right.min}~${right.max}).`
|
|
109
|
+
+ ' 차이가 관측됐다는 뜻이며, 어느 쪽이 나은지는 지표의 방향에 달렸다 — 사람이 판단한다.',
|
|
110
|
+
delta: right.mean - left.mean,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=benchCompare.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"benchCompare.js","sourceRoot":"","sources":["../../../src/tools/bench/benchCompare.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 벤치 정의 노드 가드 — 비교할 수 없는 정의가 집계로 넘어가기 전에 잡는다.
|
|
3
|
+
*
|
|
4
|
+
* 비교의 전제는 "조건만 다르고 나머지는 같다" 이다. 그 전제가 깨진 정의는 집계 단계에서
|
|
5
|
+
* 그럴듯한 숫자를 낸다 — 다른 일을 시킨 두 결과를 나란히 놓아도 평균은 나오기 때문이다.
|
|
6
|
+
* 그래서 정의 단계에서 막는다 (`validateSpecDocument` 와 같은 노드 가드 규약).
|
|
7
|
+
*/
|
|
8
|
+
export type BenchFindingSeverity = 'P1' | 'P2';
|
|
9
|
+
export interface BenchFinding {
|
|
10
|
+
severity: BenchFindingSeverity;
|
|
11
|
+
code: string;
|
|
12
|
+
message: string;
|
|
13
|
+
}
|
|
14
|
+
/** 비교할 조건 하나 — 이 축만 다르고 나머지는 같아야 한다 */
|
|
15
|
+
export interface BenchArm {
|
|
16
|
+
id: string;
|
|
17
|
+
description: string;
|
|
18
|
+
/** 이 arm 이 바꾸는 설정 (예: `{ session: 'per-iteration' }`) */
|
|
19
|
+
config: Record<string, unknown>;
|
|
20
|
+
}
|
|
21
|
+
export interface BenchDefinition {
|
|
22
|
+
name: string;
|
|
23
|
+
/** 모든 arm 이 동일하게 수행할 과제 id 목록 */
|
|
24
|
+
taskSet: string[];
|
|
25
|
+
arms: BenchArm[];
|
|
26
|
+
/** arm 당 최소 사용 가능 실행 수 — 미달이면 판정하지 않는다 */
|
|
27
|
+
minRunsPerArm?: number;
|
|
28
|
+
}
|
|
29
|
+
export interface BenchValidationResult {
|
|
30
|
+
/** P1 이 하나도 없으면 통과 — P2 는 통과를 막지 않는다 */
|
|
31
|
+
valid: boolean;
|
|
32
|
+
findings: BenchFinding[];
|
|
33
|
+
}
|
|
34
|
+
/** arm 당 최소 실행 수 기본값 — 한 자릿수 표본에서 어떤 차이든 우연과 구분되지 않는다 */
|
|
35
|
+
export declare const DEFAULT_MIN_RUNS_PER_ARM = 5;
|
|
36
|
+
export declare function validateBenchDefinition(definition: BenchDefinition): BenchValidationResult;
|
|
37
|
+
//# sourceMappingURL=benchDefinition.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"benchDefinition.d.ts","sourceRoot":"","sources":["../../../src/tools/bench/benchDefinition.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,MAAM,oBAAoB,GAAG,IAAI,GAAG,IAAI,CAAC;AAE/C,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,oBAAoB,CAAC;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,uCAAuC;AACvC,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,yDAAyD;IACzD,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACjC;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,iCAAiC;IACjC,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,IAAI,EAAE,QAAQ,EAAE,CAAC;IACjB,0CAA0C;IAC1C,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,qBAAqB;IACpC,wCAAwC;IACxC,KAAK,EAAE,OAAO,CAAC;IACf,QAAQ,EAAE,YAAY,EAAE,CAAC;CAC1B;AAED,yDAAyD;AACzD,eAAO,MAAM,wBAAwB,IAAI,CAAC;AA8C1C,wBAAgB,uBAAuB,CAAC,UAAU,EAAE,eAAe,GAAG,qBAAqB,CAiB1F"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 벤치 정의 노드 가드 — 비교할 수 없는 정의가 집계로 넘어가기 전에 잡는다.
|
|
3
|
+
*
|
|
4
|
+
* 비교의 전제는 "조건만 다르고 나머지는 같다" 이다. 그 전제가 깨진 정의는 집계 단계에서
|
|
5
|
+
* 그럴듯한 숫자를 낸다 — 다른 일을 시킨 두 결과를 나란히 놓아도 평균은 나오기 때문이다.
|
|
6
|
+
* 그래서 정의 단계에서 막는다 (`validateSpecDocument` 와 같은 노드 가드 규약).
|
|
7
|
+
*/
|
|
8
|
+
/** arm 당 최소 실행 수 기본값 — 한 자릿수 표본에서 어떤 차이든 우연과 구분되지 않는다 */
|
|
9
|
+
export const DEFAULT_MIN_RUNS_PER_ARM = 5;
|
|
10
|
+
function checkArms(arms, findings) {
|
|
11
|
+
const list = Array.isArray(arms) ? arms : [];
|
|
12
|
+
if (list.length < 2) {
|
|
13
|
+
findings.push({
|
|
14
|
+
severity: 'P1',
|
|
15
|
+
code: 'too-few-arms',
|
|
16
|
+
message: '비교할 조건(arm)이 2개 미만이다 — 대조할 상대가 없으면 벤치가 아니라 단일 측정이다.',
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
const ids = list.map((arm) => arm && arm.id).filter(Boolean);
|
|
20
|
+
if (new Set(ids).size !== ids.length) {
|
|
21
|
+
findings.push({
|
|
22
|
+
severity: 'P1',
|
|
23
|
+
code: 'duplicate-arm-id',
|
|
24
|
+
message: 'arm id 가 중복이다 — 집계가 어느 조건의 결과인지 구분하지 못한다.',
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
if (ids.length !== list.length) {
|
|
28
|
+
findings.push({
|
|
29
|
+
severity: 'P1',
|
|
30
|
+
code: 'missing-arm-id',
|
|
31
|
+
message: 'id 없는 arm 이 있다.',
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
function checkTaskSet(taskSet, findings) {
|
|
36
|
+
if (!Array.isArray(taskSet) || taskSet.length === 0) {
|
|
37
|
+
findings.push({
|
|
38
|
+
severity: 'P1',
|
|
39
|
+
code: 'empty-task-set',
|
|
40
|
+
message: '과제 셋이 비어 있다 — 모든 arm 이 같은 일을 해야 비교가 성립한다.',
|
|
41
|
+
});
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
if (new Set(taskSet).size !== taskSet.length) {
|
|
45
|
+
findings.push({
|
|
46
|
+
severity: 'P2',
|
|
47
|
+
code: 'duplicate-task',
|
|
48
|
+
message: '과제 id 가 중복이다 — 같은 과제가 두 번 세어진다.',
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
export function validateBenchDefinition(definition) {
|
|
53
|
+
const findings = [];
|
|
54
|
+
if (!definition || typeof definition.name !== 'string' || definition.name.length === 0) {
|
|
55
|
+
findings.push({ severity: 'P1', code: 'missing-name', message: '벤치 이름이 없다.' });
|
|
56
|
+
}
|
|
57
|
+
checkArms(definition && definition.arms, findings);
|
|
58
|
+
checkTaskSet(definition && definition.taskSet, findings);
|
|
59
|
+
const minRuns = definition && definition.minRunsPerArm;
|
|
60
|
+
if (minRuns !== undefined && (!Number.isInteger(minRuns) || minRuns < 1)) {
|
|
61
|
+
findings.push({
|
|
62
|
+
severity: 'P1',
|
|
63
|
+
code: 'invalid-min-runs',
|
|
64
|
+
message: 'minRunsPerArm 은 1 이상의 정수여야 한다.',
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
return { valid: !findings.some((f) => f.severity === 'P1'), findings };
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=benchDefinition.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"benchDefinition.js","sourceRoot":"","sources":["../../../src/tools/bench/benchDefinition.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 벤치 리포트 — 관측값과 분모를 함께 적고, 결론을 대신 내지 않는다.
|
|
3
|
+
*
|
|
4
|
+
* 이 파일이 지키는 규칙 하나: **비율·퍼센트·배수를 쓰지 않는다.** 게이트 통과도
|
|
5
|
+
* "3/5 회" 로 적지 "60%" 로 적지 않는다. 분모가 5인 비율은 정밀해 보이지만 아무것도
|
|
6
|
+
* 말하지 않으며, 한 번 문서에 들어가면 출처 없이 유통된다 (constitution §3.5).
|
|
7
|
+
*/
|
|
8
|
+
import type { BenchComparison } from './benchCompare.js';
|
|
9
|
+
export interface BenchReportInput {
|
|
10
|
+
name: string;
|
|
11
|
+
/** ISO-8601 */
|
|
12
|
+
ranAt: string;
|
|
13
|
+
comparison: BenchComparison;
|
|
14
|
+
}
|
|
15
|
+
export declare function formatBenchReport(input: BenchReportInput): string;
|
|
16
|
+
//# sourceMappingURL=benchReport.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"benchReport.d.ts","sourceRoot":"","sources":["../../../src/tools/bench/benchReport.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAc,eAAe,EAAc,MAAM,mBAAmB,CAAC;AA0BjF,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,eAAe;IACf,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,eAAe,CAAC;CAC7B;AASD,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,gBAAgB,GAAG,MAAM,CA2BjE"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 벤치 리포트 — 관측값과 분모를 함께 적고, 결론을 대신 내지 않는다.
|
|
3
|
+
*
|
|
4
|
+
* 이 파일이 지키는 규칙 하나: **비율·퍼센트·배수를 쓰지 않는다.** 게이트 통과도
|
|
5
|
+
* "3/5 회" 로 적지 "60%" 로 적지 않는다. 분모가 5인 비율은 정밀해 보이지만 아무것도
|
|
6
|
+
* 말하지 않으며, 한 번 문서에 들어가면 출처 없이 유통된다 (constitution §3.5).
|
|
7
|
+
*/
|
|
8
|
+
function formatRange(label, value) {
|
|
9
|
+
if (!value)
|
|
10
|
+
return `- ${label}: 관측값 없음`;
|
|
11
|
+
return `- ${label}: ${value.min}~${value.max} (평균 ${value.mean.toFixed(2)})`;
|
|
12
|
+
}
|
|
13
|
+
function formatExclusions(arm) {
|
|
14
|
+
const entries = Object.entries(arm.excluded).filter(([, count]) => count > 0);
|
|
15
|
+
if (entries.length === 0)
|
|
16
|
+
return [];
|
|
17
|
+
return [`- 제외: ${entries.map(([reason, count]) => `${reason} ${count}건`).join(' · ')}`];
|
|
18
|
+
}
|
|
19
|
+
function formatArm(arm) {
|
|
20
|
+
return [
|
|
21
|
+
'',
|
|
22
|
+
`### ${arm.armId}`,
|
|
23
|
+
'',
|
|
24
|
+
`- 실행: ${arm.usableRuns}/${arm.totalRuns} 사용 가능`,
|
|
25
|
+
`- 게이트 통과: ${arm.gatePassed}/${arm.usableRuns}`,
|
|
26
|
+
formatRange('회전 수', arm.iterations),
|
|
27
|
+
formatRange('도구 호출 수', arm.toolCalls),
|
|
28
|
+
...formatExclusions(arm),
|
|
29
|
+
];
|
|
30
|
+
}
|
|
31
|
+
const VERDICT_LABEL = {
|
|
32
|
+
'insufficient-runs': '판정 불가 — 표본 부족',
|
|
33
|
+
'mixed-task-sets': '판정 불가 — 과제 셋 불일치',
|
|
34
|
+
inconclusive: '판정 불가 — 차이를 말할 수 없음',
|
|
35
|
+
'difference-observed': '차이 관측됨',
|
|
36
|
+
};
|
|
37
|
+
export function formatBenchReport(input) {
|
|
38
|
+
const { comparison } = input;
|
|
39
|
+
const [a, b] = comparison.arms;
|
|
40
|
+
const deltaLine = comparison.delta === null
|
|
41
|
+
? []
|
|
42
|
+
: [`- 차이(${b.armId} − ${a.armId}): ${comparison.delta.toFixed(2)} ${comparison.metric} (절대 단위)`];
|
|
43
|
+
return [
|
|
44
|
+
`# Bench — ${input.name}`,
|
|
45
|
+
'',
|
|
46
|
+
`- 실행 시각: ${input.ranAt}`,
|
|
47
|
+
`- 지표: ${comparison.metric}`,
|
|
48
|
+
`- 판정: **${VERDICT_LABEL[comparison.verdict]}**`,
|
|
49
|
+
'',
|
|
50
|
+
`> ${comparison.reason}`,
|
|
51
|
+
...deltaLine,
|
|
52
|
+
'',
|
|
53
|
+
'## Arms',
|
|
54
|
+
...formatArm(a),
|
|
55
|
+
...formatArm(b),
|
|
56
|
+
'',
|
|
57
|
+
'---',
|
|
58
|
+
'',
|
|
59
|
+
'이 리포트는 관측값만 적는다. 배수·퍼센트를 계산하지 않으며, 어느 조건을 골라야',
|
|
60
|
+
'하는지도 말하지 않는다 — 지표의 방향은 사람이 안다. 기본값 변경은 사람의 결정이다.',
|
|
61
|
+
'',
|
|
62
|
+
].join('\n');
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=benchReport.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"benchReport.js","sourceRoot":"","sources":["../../../src/tools/bench/benchReport.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,SAAS,WAAW,CAAC,KAAa,EAAE,KAAwB;IAC1D,IAAI,CAAC,KAAK;QAAE,OAAO,KAAK,KAAK,UAAU,CAAC;IACxC,OAAO,KAAK,KAAK,KAAK,KAAK,CAAC,GAAG,IAAI,KAAK,CAAC,GAAG,QAAQ,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC;AAC/E,CAAC;AAED,SAAS,gBAAgB,CAAC,GAAe;IACvC,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;IAC9E,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IACpC,OAAO,CAAC,SAAS,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,GAAG,MAAM,IAAI,KAAK,GAAG,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;AAC1F,CAAC;AAED,SAAS,SAAS,CAAC,GAAe;IAChC,OAAO;QACL,EAAE;QACF,OAAO,GAAG,CAAC,KAAK,EAAE;QAClB,EAAE;QACF,SAAS,GAAG,CAAC,UAAU,IAAI,GAAG,CAAC,SAAS,QAAQ;QAChD,aAAa,GAAG,CAAC,UAAU,IAAI,GAAG,CAAC,UAAU,EAAE;QAC/C,WAAW,CAAC,MAAM,EAAE,GAAG,CAAC,UAAU,CAAC;QACnC,WAAW,CAAC,SAAS,EAAE,GAAG,CAAC,SAAS,CAAC;QACrC,GAAG,gBAAgB,CAAC,GAAG,CAAC;KACzB,CAAC;AACJ,CAAC;AASD,MAAM,aAAa,GAA+C;IAChE,mBAAmB,EAAE,eAAe;IACpC,iBAAiB,EAAE,kBAAkB;IACrC,YAAY,EAAE,qBAAqB;IACnC,qBAAqB,EAAE,QAAQ;CAChC,CAAC;AAEF,MAAM,UAAU,iBAAiB,CAAC,KAAuB;IACvD,MAAM,EAAE,UAAU,EAAE,GAAG,KAAK,CAAC;IAC7B,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,UAAU,CAAC,IAAI,CAAC;IAC/B,MAAM,SAAS,GAAG,UAAU,CAAC,KAAK,KAAK,IAAI;QACzC,CAAC,CAAC,EAAE;QACJ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,KAAK,MAAM,CAAC,CAAC,KAAK,MAAM,UAAU,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,UAAU,CAAC,MAAM,UAAU,CAAC,CAAC;IAEnG,OAAO;QACL,aAAa,KAAK,CAAC,IAAI,EAAE;QACzB,EAAE;QACF,YAAY,KAAK,CAAC,KAAK,EAAE;QACzB,SAAS,UAAU,CAAC,MAAM,EAAE;QAC5B,WAAW,aAAa,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI;QAChD,EAAE;QACF,KAAK,UAAU,CAAC,MAAM,EAAE;QACxB,GAAG,SAAS;QACZ,EAAE;QACF,SAAS;QACT,GAAG,SAAS,CAAC,CAAC,CAAC;QACf,GAAG,SAAS,CAAC,CAAC,CAAC;QACf,EAAE;QACF,KAAK;QACL,EAAE;QACF,+CAA+C;QAC/C,kDAAkD;QAClD,EAAE;KACH,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bench Tools — 루프 설정 자기 대조. 결론을 낼 수 없을 때 내지 않는 것이 목적이다.
|
|
3
|
+
*/
|
|
4
|
+
export { validateBenchDefinition, DEFAULT_MIN_RUNS_PER_ARM } from './benchDefinition.js';
|
|
5
|
+
export type { BenchArm, BenchDefinition, BenchFinding, BenchFindingSeverity, BenchValidationResult, } from './benchDefinition.js';
|
|
6
|
+
export { summarizeArm, compareArms } from './benchCompare.js';
|
|
7
|
+
export type { ArmSummary, BenchComparison, BenchExclusion, BenchMetric, BenchRange, BenchRun, BenchVerdict, } from './benchCompare.js';
|
|
8
|
+
export { formatBenchReport } from './benchReport.js';
|
|
9
|
+
export type { BenchReportInput } from './benchReport.js';
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/tools/bench/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,uBAAuB,EAAE,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AACzF,YAAY,EACV,QAAQ,EACR,eAAe,EACf,YAAY,EACZ,oBAAoB,EACpB,qBAAqB,GACtB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAC9D,YAAY,EACV,UAAU,EACV,eAAe,EACf,cAAc,EACd,WAAW,EACX,UAAU,EACV,QAAQ,EACR,YAAY,GACb,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,YAAY,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bench Tools — 루프 설정 자기 대조. 결론을 낼 수 없을 때 내지 않는 것이 목적이다.
|
|
3
|
+
*/
|
|
4
|
+
export { validateBenchDefinition, DEFAULT_MIN_RUNS_PER_ARM } from './benchDefinition.js';
|
|
5
|
+
export { summarizeArm, compareArms } from './benchCompare.js';
|
|
6
|
+
export { formatBenchReport } from './benchReport.js';
|
|
7
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/tools/bench/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,uBAAuB,EAAE,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AASzF,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAW9D,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 에이전트 계약 — 런타임 축을 **빌드타임에** 잡는다.
|
|
3
|
+
*
|
|
4
|
+
* vibe 는 빌드타임만 다룬다. 그런데 사용자가 만드는 것이 에이전트 제품일 때, 그 에이전트가
|
|
5
|
+
* 사용자 앞에서 내리는 판단은 vibe 의 어떤 게이트도 보지 않는다. 그렇다고 vibe 를 런타임에
|
|
6
|
+
* 넣으면 loop-contract 의 push·release·배포 금지를 정면으로 깬다. 그래서 vibe 가 런타임에
|
|
7
|
+
* 들어가는 대신 **런타임 게이트를 빌드타임에 생성**한다.
|
|
8
|
+
*
|
|
9
|
+
* **판정 대상이 도구 호출 로그인 것이 핵심이다.** LLM 이 에이전트의 출력을 채점하는 형태였다면
|
|
10
|
+
* 그것은 Model Judge 이고 완료 권한이 없다 (loop-contract Judge 권한 경계). "금지된 도구를
|
|
11
|
+
* 불렀는가" 는 관측된 사실이라 차단할 수 있다.
|
|
12
|
+
*
|
|
13
|
+
* 그래서 이 모듈은 `reverseDrift.ts` 와 **반대 방향의 결정**을 한다. 거기서는 `blocking: false`
|
|
14
|
+
* 가 리터럴 타입이라 차단하는 분류를 만들 수 없다 — 판정 주체가 LLM 추출이기 때문이다.
|
|
15
|
+
* 여기서는 차단이 정당하다 — 판정 주체가 로그이기 때문이다. 두 파일의 차이가 곧 권한 경계의
|
|
16
|
+
* 예시다.
|
|
17
|
+
*
|
|
18
|
+
* 판정할 수 없는 것은 게이트로 만들지 않는다: 에스컬레이션 조건("결제가 임계를 넘으면 사람에게
|
|
19
|
+
* 묻는다")은 조건 충족 여부가 로그에 없다. 선언은 받되 advisory 로 낸다 — 억지로 게이트에
|
|
20
|
+
* 넣으면 통과 의식이 되고, 그건 없는 게이트보다 나쁘다.
|
|
21
|
+
*/
|
|
22
|
+
export interface AgentContract {
|
|
23
|
+
/** 비어 있으면 allowlist 미선언 — 미등재 호출을 검사하지 않는다 */
|
|
24
|
+
allowedTools: string[];
|
|
25
|
+
forbiddenTools: string[];
|
|
26
|
+
/** 되돌릴 수 없는 작업 — 호출에 승인 기록이 있어야 한다 */
|
|
27
|
+
irreversibleOps: string[];
|
|
28
|
+
/** 선언만 받는다. 도구 로그로는 판정할 수 없다 (advisory) */
|
|
29
|
+
escalations: string[];
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* SPEC 에서 에이전트 계약을 뽑는다.
|
|
33
|
+
*
|
|
34
|
+
* 섹션이 없으면 `null` — 모든 기능에 에이전트가 있는 것은 아니다. 섹션 존재가 곧 스위치인
|
|
35
|
+
* 관례는 `## API Contract` 와 같다 (spec-template).
|
|
36
|
+
*/
|
|
37
|
+
export declare function parseAgentContract(specContent: string): AgentContract | null;
|
|
38
|
+
export type AgentContractFindingSeverity = 'P1' | 'P2';
|
|
39
|
+
export interface AgentContractFinding {
|
|
40
|
+
severity: AgentContractFindingSeverity;
|
|
41
|
+
code: string;
|
|
42
|
+
message: string;
|
|
43
|
+
}
|
|
44
|
+
export interface AgentContractValidation {
|
|
45
|
+
valid: boolean;
|
|
46
|
+
findings: AgentContractFinding[];
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* 계약 정의 자체의 오류를 잡는다 (노드 가드).
|
|
50
|
+
*
|
|
51
|
+
* Allowed 와 Forbidden 이 겹칠 때 우선순위 규칙을 두지 않는 이유: 규칙을 기억해야 판정을
|
|
52
|
+
* 예측할 수 있고, 기억이 필요한 규칙은 틀린다. 정의 단계에서 막는다.
|
|
53
|
+
*/
|
|
54
|
+
export declare function validateAgentContract(contract: AgentContract): AgentContractValidation;
|
|
55
|
+
/** 에이전트 실행이 남긴 도구 호출 1건 — vibe 자신의 로그 형식을 쓴다 (step-counter) */
|
|
56
|
+
export interface AgentToolCall {
|
|
57
|
+
tool: string;
|
|
58
|
+
/** 되돌릴 수 없는 작업이 승인을 받았는가 */
|
|
59
|
+
approved?: boolean;
|
|
60
|
+
ts?: string;
|
|
61
|
+
}
|
|
62
|
+
export type AgentViolationKind = 'forbidden-tool' | 'unlisted-tool' | 'unapproved-irreversible';
|
|
63
|
+
export interface AgentViolation {
|
|
64
|
+
kind: AgentViolationKind;
|
|
65
|
+
tool: string;
|
|
66
|
+
/** 로그에서 몇 번째 호출인가 (0-indexed) */
|
|
67
|
+
index: number;
|
|
68
|
+
detail: string;
|
|
69
|
+
}
|
|
70
|
+
export interface AgentContractCheck {
|
|
71
|
+
/** 로그가 있어 실제로 검사했는가. `false` 는 **위반 0건이 아니다** */
|
|
72
|
+
checked: boolean;
|
|
73
|
+
violations: AgentViolation[];
|
|
74
|
+
/** 도구 로그로는 판정할 수 없어 사람 리뷰로 넘긴 항목 */
|
|
75
|
+
advisory: string[];
|
|
76
|
+
/** 관측된 위반이므로 차단이 정당하다 — reverseDrift 와 반대 방향의 결정 */
|
|
77
|
+
blocking: boolean;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* 도구 호출 로그에 대해 계약을 단언한다.
|
|
81
|
+
*
|
|
82
|
+
* 로그가 없으면 `checked: false` — **위반 0건으로 적지 않는다.** 안 본 것과 봤는데 깨끗한 것은
|
|
83
|
+
* 다르다 (회전 비용 계측 축과 같은 규약).
|
|
84
|
+
*/
|
|
85
|
+
export declare function checkAgentToolLog(contract: AgentContract, log?: readonly AgentToolCall[]): AgentContractCheck;
|
|
86
|
+
//# sourceMappingURL=agentContract.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agentContract.d.ts","sourceRoot":"","sources":["../../../src/tools/contract/agentContract.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,MAAM,WAAW,aAAa;IAC5B,8CAA8C;IAC9C,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,sCAAsC;IACtC,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,2CAA2C;IAC3C,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB;AA4BD;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI,CAe5E;AAED,MAAM,MAAM,4BAA4B,GAAG,IAAI,GAAG,IAAI,CAAC;AAEvD,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,4BAA4B,CAAC;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,uBAAuB;IACtC,KAAK,EAAE,OAAO,CAAC;IACf,QAAQ,EAAE,oBAAoB,EAAE,CAAC;CAClC;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,aAAa,GAAG,uBAAuB,CA8BtF;AAED,+DAA+D;AAC/D,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,4BAA4B;IAC5B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,EAAE,CAAC,EAAE,MAAM,CAAC;CACb;AAED,MAAM,MAAM,kBAAkB,GAAG,gBAAgB,GAAG,eAAe,GAAG,yBAAyB,CAAC;AAEhG,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,kBAAkB,CAAC;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,iCAAiC;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,kBAAkB;IACjC,iDAAiD;IACjD,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,cAAc,EAAE,CAAC;IAC7B,oCAAoC;IACpC,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,oDAAoD;IACpD,QAAQ,EAAE,OAAO,CAAC;CACnB;AAyBD;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,aAAa,EACvB,GAAG,CAAC,EAAE,SAAS,aAAa,EAAE,GAC7B,kBAAkB,CAapB"}
|