leerness 1.9.19 → 1.9.21
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/CHANGELOG.md +23 -0
- package/bin/harness.js +60 -13
- package/package.json +1 -1
- package/scripts/e2e.js +103 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,28 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.9.20 — 2026-05-14
|
|
4
|
+
|
|
5
|
+
**verify-claim 정확도 + 도메인 확장 — Godot/jest/mocha 지원, verify-code --bench**.
|
|
6
|
+
|
|
7
|
+
1.9.19를 실전 RPG 워크스페이스에 쓰면서 발견한 3가지 한계를 모두 보완. **Round 4 (rpg-godot) 작업에서 실제로 false negative 발생**한 케이스가 동기.
|
|
8
|
+
|
|
9
|
+
### Added / Changed
|
|
10
|
+
|
|
11
|
+
- **`verify-claim` file path 인식 확장**: 1.9.19까지는 `src/bin/tests/public/lib` prefix만 인식 → Godot의 `scenes/*.tscn`, `scripts/*.gd`, 루트 `project.godot` 등 미검출. 1.9.20부터 **확장자 화이트리스트 기반**으로 변경. dir prefix는 optional, 확장자는 길이 내림차순 정렬로 `.ts` vs `.tscn` 정확히 구분.
|
|
12
|
+
- 신규 지원 확장자: `tscn / tres / godot / gd / cs / py / rb / go / rs / kt / sh / mdx / json5 / yaml / scss / sass / less / gltf / dockerfile / webmanifest` 등
|
|
13
|
+
- **`verify-claim --run-tests` stdout 파싱 확장**: 1.9.19까지는 `X/Y 통과/passed/pass` 만 인식. 1.9.20부터 **jest** (`Tests: 12 passed, 12 total`), **mocha** (`7 passing`), **tap** (`# pass 5`) 형식도 자동 인식. evidence 컬럼 파싱에도 동일 패턴 적용.
|
|
14
|
+
- **`verify-code --bench`**: `package.json#scripts.bench`가 있으면 추가 실행. 성능 metric을 `.harness/review-evidence.md`에 자동 누적. 1.9.19에서 별도 `perf record` 명령 추가 대신 기존 verify-code 확장으로 통합 — 의존성 0, 워크플로 일관.
|
|
15
|
+
|
|
16
|
+
### Why
|
|
17
|
+
- 1.9.19를 사용한 RPG 워크스페이스에서 `verify-claim T-0002 --path _apps/rpg-godot` 실행 시 evidence "project.godot + scenes/main.tscn + scripts/network.gd + scripts/main.gd"가 0건 검출. 1.9.20에서 **4/4 모두 정확 검출** 확인.
|
|
18
|
+
- 외부 npm 패키지가 jest/mocha를 쓰는 경우 evidence나 stdout이 한국어가 아니어도 자동 인식.
|
|
19
|
+
- 부하 측정 같은 동적 metric을 회고에서 추적 가능하도록 evidence 누적 채널 확장.
|
|
20
|
+
|
|
21
|
+
### Migration
|
|
22
|
+
```bash
|
|
23
|
+
npx leerness@latest update . --yes
|
|
24
|
+
```
|
|
25
|
+
|
|
3
26
|
## 1.9.19 — 2026-05-14
|
|
4
27
|
|
|
5
28
|
**1.9.18 후속 다듬기 — verify-claim에 동적 실행, --strict-elements 정확도 강화**.
|
package/bin/harness.js
CHANGED
|
@@ -6,7 +6,7 @@ const path = require('path');
|
|
|
6
6
|
const cp = require('child_process');
|
|
7
7
|
const readline = require('readline');
|
|
8
8
|
|
|
9
|
-
const VERSION = '1.9.
|
|
9
|
+
const VERSION = '1.9.21';
|
|
10
10
|
const MARK = '<!-- leerness:managed -->';
|
|
11
11
|
const README_START = '<!-- leerness:project-readme:start -->';
|
|
12
12
|
const README_END = '<!-- leerness:project-readme:end -->';
|
|
@@ -1492,14 +1492,41 @@ function verifyClaimCmd(root, taskId) {
|
|
|
1492
1492
|
if (!row) return fail(`progress-tracker.md에 ${taskId} 없음.`);
|
|
1493
1493
|
|
|
1494
1494
|
const evidence = row.evidence || '';
|
|
1495
|
-
// 파일 경로
|
|
1496
|
-
|
|
1495
|
+
// 1.9.20: 파일 경로 추출 — 도메인 폴더 자동 인식 + 루트 메타파일
|
|
1496
|
+
// (1.9.19까지: src|bin|tests|public|lib 하드코딩 → Godot scenes/scripts 미검출)
|
|
1497
|
+
// 변경: 확장자 화이트리스트 기반. 디렉토리는 선택적 (project.godot 같은 루트 파일도 잡음).
|
|
1498
|
+
// 확장자는 길이 내림차순(긴 것 먼저 매치) + \b 종결로 .ts vs .tscn 구분.
|
|
1499
|
+
// 1.9.21: 설정/메타 파일 확장자 추가 — Godot export_presets.cfg 등 false negative 보완
|
|
1500
|
+
const FILE_EXTS = 'webmanifest|dockerfile|properties|tscn|tres|godot|json5|jsx|tsx|yaml|html|scss|sass|less|gltf|conf|json|toml|lock|mdx|xml|css|svg|yml|cfg|ini|env|md|js|ts|gd|cs|py|rb|go|rs|kt|sh|h';
|
|
1501
|
+
const FILE_RE = new RegExp(`(?:[A-Za-z][A-Za-z0-9_-]*\\/)?[A-Za-z][\\w./-]*\\.(?:${FILE_EXTS})\\b`, 'g');
|
|
1502
|
+
const filePatterns = evidence.match(FILE_RE) || [];
|
|
1503
|
+
// 중복 제거 + "tests/test.js" 같은 결과를 유지 (이미 `..` 없으니 그대로)
|
|
1497
1504
|
const files = Array.from(new Set(filePatterns));
|
|
1498
|
-
// 테스트 수
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1505
|
+
// 1.9.20: 테스트 수 파싱 확장 — 한국어 + jest/mocha/tap/vitest
|
|
1506
|
+
// 우선순위: 명시적 X/Y 비율 > N passing/passed > N개 테스트
|
|
1507
|
+
let declaredPass = null;
|
|
1508
|
+
let declaredTestCount = null;
|
|
1509
|
+
// 1) X/Y 통과·passed·pass (한·영)
|
|
1510
|
+
const m1 = evidence.match(/(\d+)\s*\/\s*(\d+)\s*(?:통과|passed|pass|passing)/i);
|
|
1511
|
+
if (m1) declaredPass = { num: parseInt(m1[1], 10), denom: parseInt(m1[2], 10) };
|
|
1512
|
+
// 2) jest: "Tests: N passed" 또는 "N passed, M failed"
|
|
1513
|
+
if (!declaredPass) {
|
|
1514
|
+
const m2 = evidence.match(/Tests?:\s*(?:\d+\s*failed,\s*)?(\d+)\s*passed(?:,\s*(\d+)\s*total)?/i);
|
|
1515
|
+
if (m2) declaredPass = { num: parseInt(m2[1], 10), denom: parseInt(m2[2] || m2[1], 10) };
|
|
1516
|
+
}
|
|
1517
|
+
// 3) mocha: "N passing" (실패 없을 때 total = passing)
|
|
1518
|
+
if (!declaredPass) {
|
|
1519
|
+
const m3 = evidence.match(/(\d+)\s+passing\b/i);
|
|
1520
|
+
if (m3) declaredPass = { num: parseInt(m3[1], 10), denom: parseInt(m3[1], 10) };
|
|
1521
|
+
}
|
|
1522
|
+
// 4) N개 테스트 (단순 카운트)
|
|
1523
|
+
const m4 = evidence.match(/(\d+)\s*개\s*테스트/);
|
|
1524
|
+
if (m4) declaredTestCount = parseInt(m4[1], 10);
|
|
1525
|
+
// 5) N tests (영문 단순 카운트)
|
|
1526
|
+
if (!declaredTestCount) {
|
|
1527
|
+
const m5 = evidence.match(/(\d+)\s*tests?\b/i);
|
|
1528
|
+
if (m5) declaredTestCount = parseInt(m5[1], 10);
|
|
1529
|
+
}
|
|
1503
1530
|
|
|
1504
1531
|
// 실제 파일 존재 검사
|
|
1505
1532
|
const fileChecks = files.map(f => ({ file: f, exists: exists(path.join(root, f)) }));
|
|
@@ -1530,13 +1557,31 @@ function verifyClaimCmd(root, taskId) {
|
|
|
1530
1557
|
} else {
|
|
1531
1558
|
const r = cp.spawnSync('npm test', [], { cwd: root, encoding: 'utf8', shell: true, timeout: 5 * 60 * 1000 });
|
|
1532
1559
|
const out = (r.stdout || '') + (r.stderr || '');
|
|
1533
|
-
//
|
|
1534
|
-
|
|
1560
|
+
// 1.9.20: 파싱 패턴 확장 — 한국어 + jest/mocha/tap/vitest
|
|
1561
|
+
let parsed = null;
|
|
1562
|
+
// 1) X/Y passing|passed|pass|통과
|
|
1563
|
+
let m = out.match(/(\d+)\s*\/\s*(\d+)\s*(?:passed|통과|pass|passing)/i);
|
|
1564
|
+
if (m) parsed = { num: parseInt(m[1], 10), denom: parseInt(m[2], 10) };
|
|
1565
|
+
// 2) jest: "Tests: N passed, M total" — 통과 + 총
|
|
1566
|
+
if (!parsed) {
|
|
1567
|
+
const m2 = out.match(/Tests?:\s*(?:\d+\s*failed,\s*)?(\d+)\s*passed(?:,\s*(\d+)\s*total)?/i);
|
|
1568
|
+
if (m2) parsed = { num: parseInt(m2[1], 10), denom: parseInt(m2[2] || m2[1], 10) };
|
|
1569
|
+
}
|
|
1570
|
+
// 3) mocha: "N passing" — 단독 패턴이면 total = passing
|
|
1571
|
+
if (!parsed) {
|
|
1572
|
+
const m3 = out.match(/^\s*(\d+)\s+passing\b/im);
|
|
1573
|
+
if (m3) parsed = { num: parseInt(m3[1], 10), denom: parseInt(m3[1], 10) };
|
|
1574
|
+
}
|
|
1575
|
+
// 4) tap: "# pass N" 또는 "ok N"
|
|
1576
|
+
if (!parsed) {
|
|
1577
|
+
const m4 = out.match(/#\s*pass\s+(\d+)/i);
|
|
1578
|
+
if (m4) parsed = { num: parseInt(m4[1], 10), denom: parseInt(m4[1], 10) };
|
|
1579
|
+
}
|
|
1535
1580
|
runResult = {
|
|
1536
1581
|
skipped: false,
|
|
1537
1582
|
exitCode: r.status,
|
|
1538
|
-
parsed
|
|
1539
|
-
allPassed: r.status === 0 && (!
|
|
1583
|
+
parsed,
|
|
1584
|
+
allPassed: r.status === 0 && (!parsed || (parsed && parsed.num === parsed.denom))
|
|
1540
1585
|
};
|
|
1541
1586
|
}
|
|
1542
1587
|
}
|
|
@@ -3116,6 +3161,8 @@ function verifyCodeCmd(root) {
|
|
|
3116
3161
|
else if (scripts.tsc) tasks.push({ name: 'typecheck', cmd: 'npm run tsc' });
|
|
3117
3162
|
else if (exists(path.join(root, 'tsconfig.json'))) tasks.push({ name: 'typecheck', cmd: 'npx --yes tsc --noEmit', optional: true });
|
|
3118
3163
|
if (has('--build') && scripts.build) tasks.push({ name: 'build', cmd: 'npm run build' });
|
|
3164
|
+
// 1.9.20: --bench → scripts.bench 자동 실행 (성능 metric을 evidence에 누적)
|
|
3165
|
+
if (has('--bench') && scripts.bench) tasks.push({ name: 'bench', cmd: 'npm run bench', optional: true });
|
|
3119
3166
|
if (!tasks.length) {
|
|
3120
3167
|
warn('실행할 검증 task 없음 (package.json#scripts에 test/lint/typecheck 추가하세요)');
|
|
3121
3168
|
return;
|
|
@@ -3619,7 +3666,7 @@ function viewworkInstall(root) {
|
|
|
3619
3666
|
}
|
|
3620
3667
|
|
|
3621
3668
|
function help() {
|
|
3622
|
-
log(`Leerness v${VERSION}\n\nUsage:\n leerness init [path] [--language auto|ko|en] [--skills recommended|all|a,b]\n leerness migrate [path] [--dry-run] [--force]\n leerness update [path] [--check|--yes|--force|--from <tarball>]\n leerness auto-update install [path]\n leerness status [path]\n leerness verify [path]\n leerness debug [path]\n leerness audit [path]\n leerness check [path]\n leerness scan secrets [path]\n leerness encoding check [path]\n leerness lazy detect [path]\n leerness memory search "query" [--limit 5]\n leerness handoff [path] [--all-apps] [--include p1,p2] [--since 24h|3d] [--json] # 1.9.17/18 워크스페이스\n leerness reuse-map [path] [--all-apps] [--include p1,p2] [--strict-elements] [--json] # 1.9.18 중복/잠재중복/depends-on\n leerness verify-claim <T-ID> [--path .] [--run-tests] [--json] # 1.9.18
|
|
3669
|
+
log(`Leerness v${VERSION}\n\nUsage:\n leerness init [path] [--language auto|ko|en] [--skills recommended|all|a,b]\n leerness migrate [path] [--dry-run] [--force]\n leerness update [path] [--check|--yes|--force|--from <tarball>]\n leerness auto-update install [path]\n leerness status [path]\n leerness verify [path]\n leerness debug [path]\n leerness audit [path]\n leerness check [path]\n leerness scan secrets [path]\n leerness encoding check [path]\n leerness lazy detect [path]\n leerness memory search "query" [--limit 5]\n leerness handoff [path] [--all-apps] [--include p1,p2] [--since 24h|3d] [--json] # 1.9.17/18 워크스페이스\n leerness reuse-map [path] [--all-apps] [--include p1,p2] [--strict-elements] [--json] # 1.9.18 중복/잠재중복/depends-on\n leerness verify-claim <T-ID> [--path .] [--run-tests] [--json] # 1.9.18-20 evidence 자동 검증 (1.9.20: scenes/scripts 등 도메인 폴더 + jest/mocha 파싱)\n leerness verify-code [path] [--build] [--bench] # 1.9.20 --bench: scripts.bench 추가 실행 + evidence 누적\n leerness session close [path]\n leerness viewwork install [path]\n leerness viewwork emit [path] [--action a] [--note n] [--agent x] [--tool t]\n leerness route <task-type>\n leerness self check [path]\n leerness readme sync [path]\n leerness consistency check [path]\n leerness consistency merge-design-guide [path]\n leerness plan show|init|add|drop|progress|sync [args]\n leerness task list|add|update|drop|fix-evidence|relink [args]\n leerness skill list|info <name>\n leerness skill learn <id> --doc <url> --command "..." --capability "..." [--note ...]\n leerness skill use <id> [--note ...]\n leerness skill optimize <id> --before "..." --after "..." [--note ...]\n leerness skill remove <id>\n leerness skill consolidate [--threshold 0.3]\n leerness gate [path] # verify+audit+scan+encoding+lazy
|
|
3623
3670
|
leerness retro [path] [--days 7] [--all-apps] [--include p1,p2] [--json] # 회고 (1.9.13~1.9.16)
|
|
3624
3671
|
leerness insights [path] [--all-apps] [--include p1,p2] [--json] # 누적 통계 (1.9.13~1.9.16)
|
|
3625
3672
|
leerness brainstorm "<주제>" [--all-apps] [--include p1,p2] [--json] # 브레인스토밍 (1.9.13~1.9.16)
|
package/package.json
CHANGED
package/scripts/e2e.js
CHANGED
|
@@ -543,6 +543,109 @@ total++;
|
|
|
543
543
|
if (!ok) { failed++; console.log(r.stdout.slice(0, 600)); }
|
|
544
544
|
}
|
|
545
545
|
|
|
546
|
+
// 1.9.20 회귀: verify-claim file regex 확장 + jest/mocha 파싱 + --bench
|
|
547
|
+
total++;
|
|
548
|
+
{
|
|
549
|
+
// verify-claim regex: 도메인 폴더 (scenes/scripts) + 루트 메타 파일 (project.godot)
|
|
550
|
+
const tmpG = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-godot-'));
|
|
551
|
+
cp.spawnSync(process.execPath, [CLI, 'init', tmpG, '--yes', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore', timeout: 30000 });
|
|
552
|
+
fs.writeFileSync(path.join(tmpG, 'project.godot'), 'config_version=5\n');
|
|
553
|
+
fs.mkdirSync(path.join(tmpG, 'scenes'), { recursive: true });
|
|
554
|
+
fs.mkdirSync(path.join(tmpG, 'scripts'), { recursive: true });
|
|
555
|
+
fs.writeFileSync(path.join(tmpG, 'scenes/main.tscn'), '[gd_scene]\n');
|
|
556
|
+
fs.writeFileSync(path.join(tmpG, 'scripts/network.gd'), 'extends Node\n');
|
|
557
|
+
fs.appendFileSync(path.join(tmpG, '.harness/progress-tracker.md'),
|
|
558
|
+
'| T-0020 | done | Godot 클라 | project.godot + scenes/main.tscn + scripts/network.gd | next | 2026-05-14 |\n');
|
|
559
|
+
const r = cp.spawnSync(process.execPath, [CLI, 'verify-claim', 'T-0020', '--path', tmpG], { encoding: 'utf8', timeout: 15000 });
|
|
560
|
+
const ok = r.status === 0
|
|
561
|
+
&& /✓ project\.godot/.test(r.stdout)
|
|
562
|
+
&& /✓ scenes\/main\.tscn/.test(r.stdout)
|
|
563
|
+
&& /✓ scripts\/network\.gd/.test(r.stdout)
|
|
564
|
+
&& !/scenes\/main\.ts\s/.test(r.stdout); // .tscn 이 .ts 로 잘못 매칭되지 않음
|
|
565
|
+
console.log(ok ? '✓ B(1.9.20) verify-claim regex: 도메인 폴더 + 루트 파일 + .tscn 정확' : '✗ regex 확장 실패');
|
|
566
|
+
if (!ok) { failed++; console.log(r.stdout.slice(0, 500)); }
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
// 1.9.21 회귀: 설정 파일 확장자 (.cfg/.ini/.env/.toml/.lock) 추가
|
|
570
|
+
total++;
|
|
571
|
+
{
|
|
572
|
+
const tmpC = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-cfg-'));
|
|
573
|
+
cp.spawnSync(process.execPath, [CLI, 'init', tmpC, '--yes', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore', timeout: 30000 });
|
|
574
|
+
fs.writeFileSync(path.join(tmpC, 'export_presets.cfg'), '[preset.0]\n');
|
|
575
|
+
fs.writeFileSync(path.join(tmpC, 'config.ini'), '[main]\n');
|
|
576
|
+
fs.writeFileSync(path.join(tmpC, 'Cargo.lock'), '# lock\n');
|
|
577
|
+
fs.appendFileSync(path.join(tmpC, '.harness/progress-tracker.md'),
|
|
578
|
+
'| T-0030 | done | 설정 | export_presets.cfg + config.ini + Cargo.lock | next | 2026-05-14 |\n');
|
|
579
|
+
const r = cp.spawnSync(process.execPath, [CLI, 'verify-claim', 'T-0030', '--path', tmpC], { encoding: 'utf8', timeout: 15000 });
|
|
580
|
+
const ok = r.status === 0
|
|
581
|
+
&& /✓ export_presets\.cfg/.test(r.stdout)
|
|
582
|
+
&& /✓ config\.ini/.test(r.stdout)
|
|
583
|
+
&& /✓ Cargo\.lock/.test(r.stdout);
|
|
584
|
+
console.log(ok ? '✓ B(1.9.21) verify-claim regex: .cfg/.ini/.lock 등 설정 메타 파일' : '✗ .cfg/.ini 확장 실패');
|
|
585
|
+
if (!ok) { failed++; console.log(r.stdout.slice(0, 500)); }
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
total++;
|
|
589
|
+
{
|
|
590
|
+
// jest 출력 파싱
|
|
591
|
+
const tmpJ = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-tparse-'));
|
|
592
|
+
cp.spawnSync(process.execPath, [CLI, 'init', tmpJ, '--yes', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore', timeout: 30000 });
|
|
593
|
+
fs.writeFileSync(path.join(tmpJ, 'package.json'), JSON.stringify({ name: 'tp', version: '0.0.1', scripts: { test: 'node tests/test.js' } }));
|
|
594
|
+
fs.mkdirSync(path.join(tmpJ, 'src'), { recursive: true });
|
|
595
|
+
fs.mkdirSync(path.join(tmpJ, 'tests'), { recursive: true });
|
|
596
|
+
fs.writeFileSync(path.join(tmpJ, 'src/foo.js'), 'module.exports={};\n');
|
|
597
|
+
fs.writeFileSync(path.join(tmpJ, 'tests/test.js'), "console.log('Tests: 12 passed, 12 total');\n");
|
|
598
|
+
fs.appendFileSync(path.join(tmpJ, '.harness/progress-tracker.md'),
|
|
599
|
+
'| T-0021 | done | jest 스타일 | src/foo.js + Tests: 12 passed, 12 total | next | 2026-05-14 |\n');
|
|
600
|
+
const r = cp.spawnSync(process.execPath, [CLI, 'verify-claim', 'T-0021', '--path', tmpJ, '--run-tests'], { encoding: 'utf8', timeout: 60000 });
|
|
601
|
+
const okEv = /주장 \(pass\): 12\/12/.test(r.stdout);
|
|
602
|
+
const okRun = /실행 결과: 12\/12 passed/.test(r.stdout);
|
|
603
|
+
const ok = r.status === 0 && okEv && okRun;
|
|
604
|
+
console.log(ok ? '✓ B(1.9.20) verify-claim: jest "Tests: N passed, N total" 파싱' : `✗ jest 파싱 실패 (ev=${okEv} run=${okRun})`);
|
|
605
|
+
if (!ok) { failed++; console.log(r.stdout.slice(0, 500)); }
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
total++;
|
|
609
|
+
{
|
|
610
|
+
// mocha "N passing"
|
|
611
|
+
const tmpM = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-mocha-'));
|
|
612
|
+
cp.spawnSync(process.execPath, [CLI, 'init', tmpM, '--yes', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore', timeout: 30000 });
|
|
613
|
+
fs.writeFileSync(path.join(tmpM, 'package.json'), JSON.stringify({ name: 'mc', version: '0.0.1', scripts: { test: 'node tests/test.js' } }));
|
|
614
|
+
fs.mkdirSync(path.join(tmpM, 'src'), { recursive: true });
|
|
615
|
+
fs.mkdirSync(path.join(tmpM, 'tests'), { recursive: true });
|
|
616
|
+
fs.writeFileSync(path.join(tmpM, 'src/x.js'), 'module.exports={};\n');
|
|
617
|
+
fs.writeFileSync(path.join(tmpM, 'tests/test.js'), "console.log(' 7 passing (12ms)');\n");
|
|
618
|
+
fs.appendFileSync(path.join(tmpM, '.harness/progress-tracker.md'),
|
|
619
|
+
'| T-0022 | done | mocha | src/x.js + 7 passing | next | 2026-05-14 |\n');
|
|
620
|
+
const r = cp.spawnSync(process.execPath, [CLI, 'verify-claim', 'T-0022', '--path', tmpM, '--run-tests'], { encoding: 'utf8', timeout: 60000 });
|
|
621
|
+
const ok = r.status === 0 && /주장 \(pass\): 7\/7/.test(r.stdout) && /실행 결과: 7\/7 passed/.test(r.stdout);
|
|
622
|
+
console.log(ok ? '✓ B(1.9.20) verify-claim: mocha "N passing" 파싱' : '✗ mocha 파싱 실패');
|
|
623
|
+
if (!ok) { failed++; console.log(r.stdout.slice(0, 500)); }
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
total++;
|
|
627
|
+
{
|
|
628
|
+
// verify-code --bench: scripts.bench 자동 실행 + evidence 누적
|
|
629
|
+
const tmpB = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-bench-'));
|
|
630
|
+
cp.spawnSync(process.execPath, [CLI, 'init', tmpB, '--yes', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore', timeout: 30000 });
|
|
631
|
+
fs.writeFileSync(path.join(tmpB, 'package.json'), JSON.stringify({
|
|
632
|
+
name: 'b', version: '0.0.1',
|
|
633
|
+
scripts: { test: 'node tests/test.js', bench: 'node tests/bench.js' }
|
|
634
|
+
}));
|
|
635
|
+
fs.mkdirSync(path.join(tmpB, 'tests'), { recursive: true });
|
|
636
|
+
fs.writeFileSync(path.join(tmpB, 'tests/test.js'), "console.log('1/1 passed');\n");
|
|
637
|
+
fs.writeFileSync(path.join(tmpB, 'tests/bench.js'), "console.log('# bench result 12345 ops/sec');\n");
|
|
638
|
+
const rNoBench = cp.spawnSync(process.execPath, [CLI, 'verify-code', tmpB], { encoding: 'utf8', timeout: 60000 });
|
|
639
|
+
const okBaseline = rNoBench.status === 0 && /verify-code \(1개\)/.test(rNoBench.stdout) && !/^## bench:/m.test(rNoBench.stdout);
|
|
640
|
+
const rWith = cp.spawnSync(process.execPath, [CLI, 'verify-code', tmpB, '--bench'], { encoding: 'utf8', timeout: 60000 });
|
|
641
|
+
const okBench = rWith.status === 0 && /verify-code \(2개\)/.test(rWith.stdout) && /bench passed/.test(rWith.stdout);
|
|
642
|
+
const evidence = fs.readFileSync(path.join(tmpB, '.harness/review-evidence.md'), 'utf8');
|
|
643
|
+
const okEvidence = /bench/.test(evidence) && /node tests\/bench\.js/.test(evidence);
|
|
644
|
+
const ok = okBaseline && okBench && okEvidence;
|
|
645
|
+
console.log(ok ? '✓ B(1.9.20) verify-code --bench: scripts.bench 자동 실행 + evidence 누적' : `✗ --bench 실패 (base=${okBaseline} bench=${okBench} ev=${okEvidence})`);
|
|
646
|
+
if (!ok) { failed++; console.log(rWith.stdout.slice(0, 500)); }
|
|
647
|
+
}
|
|
648
|
+
|
|
546
649
|
// 1.9.15 회귀: brainstorm 라인번호 / --all-apps / --include
|
|
547
650
|
total++;
|
|
548
651
|
{
|